diff --git a/.docker/frankenphp/Caddyfile b/.docker/frankenphp/Caddyfile new file mode 100644 index 00000000..83839304 --- /dev/null +++ b/.docker/frankenphp/Caddyfile @@ -0,0 +1,55 @@ +{ + {$CADDY_GLOBAL_OPTIONS} + + frankenphp { + {$FRANKENPHP_CONFIG} + } + + # https://caddyserver.com/docs/caddyfile/directives#sorting-algorithm + order mercure after encode + order vulcain after reverse_proxy + order php_server before file_server +} + +{$CADDY_EXTRA_CONFIG} + +{$SERVER_NAME:localhost} { + log { + # Redact the authorization query parameter that can be set by Mercure + format filter { + wrap console + fields { + uri query { + replace authorization REDACTED + } + } + } + } + + root * /app/public + encode zstd br gzip + + mercure { + # Transport to use (default to Bolt) + transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db} + # Publisher JWT key + publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG} + # Subscriber JWT key + subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG} + # Allow anonymous subscribers (double-check that it's what you want) + anonymous + # Enable the subscription API (double-check that it's what you want) + subscriptions + # Extra directives + {$MERCURE_EXTRA_DIRECTIVES} + } + + vulcain + + {$CADDY_SERVER_EXTRA_DIRECTIVES} + + # Disable Topics tracking if not enabled explicitly: https://github.com/jkarlin/topics + header ?Permissions-Policy "browsing-topics=()" + + php_server +} diff --git a/.docker/frankenphp/conf.d/app.dev.ini b/.docker/frankenphp/conf.d/app.dev.ini new file mode 100644 index 00000000..e50f43d0 --- /dev/null +++ b/.docker/frankenphp/conf.d/app.dev.ini @@ -0,0 +1,5 @@ +; See https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host +; See https://github.com/docker/for-linux/issues/264 +; The `client_host` below may optionally be replaced with `discover_client_host=yes` +; Add `start_with_request=yes` to start debug session on each request +xdebug.client_host = host.docker.internal diff --git a/.docker/frankenphp/conf.d/app.ini b/.docker/frankenphp/conf.d/app.ini new file mode 100644 index 00000000..10a062f2 --- /dev/null +++ b/.docker/frankenphp/conf.d/app.ini @@ -0,0 +1,18 @@ +expose_php = 0 +date.timezone = UTC +apc.enable_cli = 1 +session.use_strict_mode = 1 +zend.detect_unicode = 0 + +; https://symfony.com/doc/current/performance.html +realpath_cache_size = 4096K +realpath_cache_ttl = 600 +opcache.interned_strings_buffer = 16 +opcache.max_accelerated_files = 20000 +opcache.memory_consumption = 256 +opcache.enable_file_override = 1 + +memory_limit = 256M + +upload_max_filesize=256M +post_max_size=300M \ No newline at end of file diff --git a/.docker/frankenphp/conf.d/app.prod.ini b/.docker/frankenphp/conf.d/app.prod.ini new file mode 100644 index 00000000..3bcaa71e --- /dev/null +++ b/.docker/frankenphp/conf.d/app.prod.ini @@ -0,0 +1,2 @@ +opcache.preload_user = root +opcache.preload = /app/config/preload.php diff --git a/.docker/frankenphp/docker-entrypoint.sh b/.docker/frankenphp/docker-entrypoint.sh new file mode 100644 index 00000000..1655af5a --- /dev/null +++ b/.docker/frankenphp/docker-entrypoint.sh @@ -0,0 +1,60 @@ +#!/bin/sh +set -e + +if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then + # Install the project the first time PHP is started + # After the installation, the following block can be deleted + if [ ! -f composer.json ]; then + rm -Rf tmp/ + composer create-project "symfony/skeleton $SYMFONY_VERSION" tmp --stability="$STABILITY" --prefer-dist --no-progress --no-interaction --no-install + + cd tmp + cp -Rp . .. + cd - + rm -Rf tmp/ + + composer require "php:>=$PHP_VERSION" runtime/frankenphp-symfony + composer config --json extra.symfony.docker 'true' + + if grep -q ^DATABASE_URL= .env; then + echo "To finish the installation please press Ctrl+C to stop Docker Compose and run: docker compose up --build -d --wait" + sleep infinity + fi + fi + + if [ -z "$(ls -A 'vendor/' 2>/dev/null)" ]; then + composer install --prefer-dist --no-progress --no-interaction + fi + + if grep -q ^DATABASE_URL= .env; then + echo "Waiting for database to be ready..." + ATTEMPTS_LEFT_TO_REACH_DATABASE=60 + until [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ] || DATABASE_ERROR=$(php bin/console dbal:run-sql -q "SELECT 1" 2>&1); do + if [ $? -eq 255 ]; then + # If the Doctrine command exits with 255, an unrecoverable error occurred + ATTEMPTS_LEFT_TO_REACH_DATABASE=0 + break + fi + sleep 1 + ATTEMPTS_LEFT_TO_REACH_DATABASE=$((ATTEMPTS_LEFT_TO_REACH_DATABASE - 1)) + echo "Still waiting for database to be ready... Or maybe the database is not reachable. $ATTEMPTS_LEFT_TO_REACH_DATABASE attempts left." + done + + if [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ]; then + echo "The database is not up or not reachable:" + echo "$DATABASE_ERROR" + exit 1 + else + echo "The database is now ready and reachable" + fi + + if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then + php bin/console doctrine:migrations:migrate --no-interaction + fi + fi + + setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var + setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var +fi + +exec docker-php-entrypoint "$@" \ No newline at end of file diff --git a/.docker/frankenphp/worker.Caddyfile b/.docker/frankenphp/worker.Caddyfile new file mode 100644 index 00000000..d384ae4c --- /dev/null +++ b/.docker/frankenphp/worker.Caddyfile @@ -0,0 +1,4 @@ +worker { + file ./public/index.php + env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime +} diff --git a/.docker/partdb-entrypoint.sh b/.docker/partdb-entrypoint.sh index e73bc83b..ffd2b24a 100644 --- a/.docker/partdb-entrypoint.sh +++ b/.docker/partdb-entrypoint.sh @@ -39,6 +39,51 @@ if [ -d /var/www/html/var/db ]; then fi fi +# Start PHP-FPM (the PHP_VERSION is replaced by the configured version in the Dockerfile) +service phpPHP_VERSION-fpm start + + +# Run migrations if automigration is enabled via env variable DB_AUTOMIGRATE +if [ "$DB_AUTOMIGRATE" = "true" ]; then + echo "Waiting for database to be ready..." + ATTEMPTS_LEFT_TO_REACH_DATABASE=60 + until [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ] || DATABASE_ERROR=$(sudo -E -u www-data php bin/console dbal:run-sql -q "SELECT 1" 2>&1); do + if [ $? -eq 255 ]; then + # If the Doctrine command exits with 255, an unrecoverable error occurred + ATTEMPTS_LEFT_TO_REACH_DATABASE=0 + break + fi + sleep 1 + ATTEMPTS_LEFT_TO_REACH_DATABASE=$((ATTEMPTS_LEFT_TO_REACH_DATABASE - 1)) + echo "Still waiting for database to be ready... Or maybe the database is not reachable. $ATTEMPTS_LEFT_TO_REACH_DATABASE attempts left." + done + + if [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ]; then + echo "The database is not up or not reachable:" + echo "$DATABASE_ERROR" + exit 1 + else + echo "The database is now ready and reachable" + fi + + # Check if there are any available migrations to do, by executing doctrine:migrations:up-to-date + # and checking if the exit code is 0 (up to date) or 1 (not up to date) + if sudo -E -u www-data php bin/console doctrine:migrations:up-to-date --no-interaction; then + echo "Database is up to date, no migrations necessary." + else + echo "Migrations available..." + echo "Do backup of database..." + + sudo -E -u www-data mkdir -p /var/www/html/uploads/.automigration-backup/ + # Backup the database + sudo -E -u www-data php bin/console partdb:backup -n --database /var/www/html/uploads/.automigration-backup/backup-$(date +%Y-%m-%d_%H-%M-%S).zip + + # Check if there are any migration files + sudo -E -u www-data php bin/console doctrine:migrations:migrate --no-interaction + fi + +fi + # first arg is `-f` or `--some-option` (taken from https://github.com/docker-library/php/blob/master/8.2/bullseye/apache/docker-php-entrypoint) if [ "${1#-}" != "$1" ]; then set -- apache2-foreground "$@" diff --git a/.docker/symfony.conf b/.docker/symfony.conf index 01aa91f0..b5229bf6 100644 --- a/.docker/symfony.conf +++ b/.docker/symfony.conf @@ -25,14 +25,28 @@ CustomLog ${APACHE_LOG_DIR}/access.log combined # Pass the configuration from the docker env to the PHP environment (here you should list all .env options) - PassEnv APP_ENV APP_DEBUG APP_SECRET - PassEnv DATABASE_URL - PassEnv DEFAULT_LANG DEFAULT_TIMEZONE BASE_CURRENCY INSTANCE_NAME ALLOW_ATTACHMENT_DOWNLOADS USE_GRAVATAR + PassEnv APP_ENV APP_DEBUG APP_SECRET REDIRECT_TO_HTTPS DISABLE_YEAR2038_BUG_CHECK + PassEnv TRUSTED_PROXIES TRUSTED_HOSTS LOCK_DSN + PassEnv DATABASE_URL ENFORCE_CHANGE_COMMENTS_FOR DATABASE_MYSQL_USE_SSL_CA DATABASE_MYSQL_SSL_VERIFY_CERT + PassEnv DEFAULT_LANG DEFAULT_TIMEZONE BASE_CURRENCY INSTANCE_NAME ALLOW_ATTACHMENT_DOWNLOADS USE_GRAVATAR MAX_ATTACHMENT_FILE_SIZE DEFAULT_URI CHECK_FOR_UPDATES ATTACHMENT_DOWNLOAD_BY_DEFAULT PassEnv MAILER_DSN ALLOW_EMAIL_PW_RESET EMAIL_SENDER_EMAIL EMAIL_SENDER_NAME - PassEnv HISTORY_SAVE_CHANGED_FIELDS HISTORY_SAVE_CHANGED_DATA HISTORY_SAVE_REMOVED_DATA + PassEnv HISTORY_SAVE_CHANGED_FIELDS HISTORY_SAVE_CHANGED_DATA HISTORY_SAVE_REMOVED_DATA HISTORY_SAVE_NEW_DATA PassEnv ERROR_PAGE_ADMIN_EMAIL ERROR_PAGE_SHOW_HELP PassEnv DEMO_MODE NO_URL_REWRITE_AVAILABLE FIXER_API_KEY BANNER + # In old version the SAML sp private key env, was wrongly named SAMLP_SP_PRIVATE_KEY, keep it for backward compatibility + PassEnv SAML_ENABLED SAML_BEHIND_PROXY SAML_ROLE_MAPPING SAML_UPDATE_GROUP_ON_LOGIN SAML_IDP_ENTITY_ID SAML_IDP_SINGLE_SIGN_ON_SERVICE SAML_IDP_SINGLE_LOGOUT_SERVICE SAML_IDP_X509_CERT SAML_SP_ENTITY_ID SAML_SP_X509_CERT SAML_SP_PRIVATE_KEY SAMLP_SP_PRIVATE_KEY + PassEnv TABLE_DEFAULT_PAGE_SIZE TABLE_PARTS_DEFAULT_COLUMNS + PassEnv PROVIDER_DIGIKEY_CLIENT_ID PROVIDER_DIGIKEY_SECRET PROVIDER_DIGIKEY_CURRENCY PROVIDER_DIGIKEY_LANGUAGE PROVIDER_DIGIKEY_COUNTRY + PassEnv PROVIDER_ELEMENT14_KEY PROVIDER_ELEMENT14_STORE_ID + PassEnv PROVIDER_TME_KEY PROVIDER_TME_SECRET PROVIDER_TME_CURRENCY PROVIDER_TME_LANGUAGE PROVIDER_TME_COUNTRY PROVIDER_TME_GET_GROSS_PRICES + PassEnv PROVIDER_OCTOPART_CLIENT_ID PROVIDER_OCTOPART_SECRET PROVIDER_OCTOPART_CURRENCY PROVIDER_OCTOPART_COUNTRY PROVIDER_OCTOPART_SEARCH_LIMIT PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS + PassEnv PROVIDER_MOUSER_KEY PROVIDER_MOUSER_SEARCH_OPTION PROVIDER_MOUSER_SEARCH_LIMIT PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE + PassEnv PROVIDER_LCSC_ENABLED PROVIDER_LCSC_CURRENCY + PassEnv PROVIDER_OEMSECRETS_KEY PROVIDER_OEMSECRETS_COUNTRY_CODE PROVIDER_OEMSECRETS_CURRENCY PROVIDER_OEMSECRETS_ZERO_PRICE PROVIDER_OEMSECRETS_SET_PARAM PROVIDER_OEMSECRETS_SORT_CRITERIA + PassEnv PROVIDER_REICHELT_ENABLED PROVIDER_REICHELT_CURRENCY PROVIDER_REICHELT_COUNTRY PROVIDER_REICHELT_LANGUAGE PROVIDER_REICHELT_INCLUDE_VAT + PassEnv PROVIDER_POLLIN_ENABLED + PassEnv EDA_KICAD_CATEGORY_DEPTH # For most configuration files from conf-available/, which are # enabled or disabled at a global level, it is possible to diff --git a/.dockerignore b/.dockerignore index 8929729c..472b1bb3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,6 +5,8 @@ tests/ docs/ .git +/public/media/* + ###> symfony/framework-bundle ### /.env.local /.env.local.php @@ -42,3 +44,39 @@ yarn-error.log /phpunit.xml .phpunit.result.cache ###< phpunit/phpunit ### + + +### From frankenphp + +**/*.log +**/*.php~ +**/*.dist.php +**/*.dist +**/*.cache +**/._* +**/.dockerignore +**/.DS_Store +**/.git/ +**/.gitattributes +**/.gitignore +**/.gitmodules +**/compose.*.yaml +**/compose.*.yml +**/compose.yaml +**/compose.yml +**/docker-compose.*.yaml +**/docker-compose.*.yml +**/docker-compose.yaml +**/docker-compose.yml +**/Dockerfile +**/Thumbs.db +.github/ +public/bundles/ +var/ +vendor/ +.editorconfig +.env.*.local +.env.local +.env.local.php +.env.test + diff --git a/.env b/.env index 0e8adff6..1806e9c6 100644 --- a/.env +++ b/.env @@ -14,6 +14,19 @@ DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db" # Uncomment this line (and comment the line above to use a MySQL database #DATABASE_URL=mysql://root:@127.0.0.1:3306/part-db?serverVersion=5.7 +# Set this value to 1, if you want to use SSL to connect to the MySQL server. It will be tried to use the CA certificate +# otherwise a CA bundle shipped with PHP will be used. +# Leave it at 0, if you do not want to use SSL or if your server does not support it +DATABASE_MYSQL_USE_SSL_CA=0 + +# Set this value to 0, if you don't want to verify the CA certificate of the MySQL server +# Only do this, if you know what you are doing! +DATABASE_MYSQL_SSL_VERIFY_CERT=1 + +# Emulate natural sorting of strings even on databases that do not support it (like SQLite, MySQL or MariaDB < 10.7) +# This can be slow on big databases and might have some problems and quirks, so use it with caution +DATABASE_EMULATE_NATURAL_SORT=0 + ################################################################################### # General settings ################################################################################### @@ -29,8 +42,25 @@ 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 @@ -59,6 +89,9 @@ HISTORY_SAVE_CHANGED_FIELDS=1 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 @@ -69,6 +102,189 @@ 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 +################################################################################## + +# 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 single category in KiCad +EDA_KICAD_CATEGORY_DEPTH=0 + +################################################################################### +# SAML Single sign on-settings +################################################################################### +# Set this to 1 to enable SAML single sign on +# Be also sure to set the correct values for DEFAULT_URI +SAML_ENABLED=0 + +# Set to 1, if your Part-DB installation is behind a reverse proxy and you want to use SAML +SAML_BEHIND_PROXY=0 + +# A JSON encoded array of role mappings in the form { "saml_role": PARTDB_GROUP_ID, "*": PARTDB_GROUP_ID } +# The first match is used, so the order is important! Put the group mapping with the most privileges first. +# Please not to only use single quotes to enclose the JSON string +SAML_ROLE_MAPPING='{}' +# A mapping could look like the following +#SAML_ROLE_MAPPING='{ "*": 2, "admin": 1, "editor": 3}' + +# When this is set to 1, the group of SAML users will be updated everytime they login based on their SAML roles +SAML_UPDATE_GROUP_ON_LOGIN=1 + +# The entity ID of your SAML IDP (e.g. the realm name of your Keycloak server) +SAML_IDP_ENTITY_ID="https://idp.changeme.invalid/realms/master" +# The URL of your SAML IDP SingleSignOnService (e.g. the endpoint of your Keycloak server) +SAML_IDP_SINGLE_SIGN_ON_SERVICE="https://idp.changeme.invalid/realms/master/protocol/saml" +# The URL of your SAML IDP SingleLogoutService (e.g. the endpoint of your Keycloak server) +SAML_IDP_SINGLE_LOGOUT_SERVICE="https://idp.changeme.invalid/realms/master/protocol/saml" +# The public certificate of the SAML IDP (e.g. the certificate of your Keycloak server) +SAML_IDP_X509_CERT="MIIC..." + +# The entity of your SAML SP, must match the SP entityID configured in your SAML IDP (e.g. Keycloak). +# This should be a the domain name of your Part-DB installation, followed by "/sp" +SAML_SP_ENTITY_ID="https://partdb.changeme.invalid/sp" + +# The public certificate of the SAML SP +SAML_SP_X509_CERT="MIIC..." +# The private key of the SAML SP +SAML_SP_PRIVATE_KEY="MIIE..." + + ###################################################################################### # Other settings ###################################################################################### @@ -79,6 +295,9 @@ DEMO_MODE=0 # In that case all URL contains the index.php front controller in URL 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 @@ -89,9 +308,11 @@ BANNER="" APP_ENV=prod APP_SECRET=a03498528f5a5fc089273ec9ae5b2849 +# Set this to zero, if you want to disable the year 2038 bug check on 32-bit systems (it will cause errors with current 32-bit PHP versions) +DISABLE_YEAR2038_BUG_CHECK=0 # Set the trusted IPs here, when using an reverse proxy -#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 +#TRUSTED_PROXIES=127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 #TRUSTED_HOSTS='^(localhost|example\.com)$' @@ -100,3 +321,7 @@ APP_SECRET=a03498528f5a5fc089273ec9ae5b2849 # postgresql+advisory://db_user:db_password@localhost/db_name LOCK_DSN=flock ###< symfony/lock ### + +###> nelmio/cors-bundle ### +CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' +###< nelmio/cors-bundle ### diff --git a/.env.dev b/.env.dev new file mode 100644 index 00000000..e69de29b diff --git a/.env.test b/.env.test index a9b0cccf..3dbece81 100644 --- a/.env.test +++ b/.env.test @@ -5,5 +5,9 @@ SYMFONY_DEPRECATIONS_HELPER=999999 PANTHER_APP_ENV=panther PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots +DATABASE_URL="sqlite:///%kernel.project_dir%/var/app_test.db" # Doctrine automatically adds an _test suffix to database name in test env -DATABASE_URL=mysql://root:@127.0.0.1:3306/part-db \ No newline at end of file +#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 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..6a0a3e1f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# For sh files, always use LF line endings +*.sh text eol=lf \ No newline at end of file diff --git a/.github/assets/legacy_import/db_jbtronics.sql b/.github/assets/legacy_import/db_jbtronics.sql new file mode 100644 index 00000000..5237461f --- /dev/null +++ b/.github/assets/legacy_import/db_jbtronics.sql @@ -0,0 +1,752 @@ +-- phpMyAdmin SQL Dump +-- version 5.1.3 +-- https://www.phpmyadmin.net/ +-- +-- Host: 127.0.0.1 +-- Erstellungszeit: 07. Mai 2023 um 01:58 +-- Server-Version: 10.6.5-MariaDB-log +-- PHP-Version: 8.1.2 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- Datenbank: `partdb_demo` +-- + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `attachements` +-- + +CREATE TABLE `attachements` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `class_name` varchar(255) COLLATE utf8mb3_unicode_ci NOT NULL, + `element_id` int(11) NOT NULL, + `type_id` int(11) NOT NULL, + `filename` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `show_in_table` tinyint(1) NOT NULL DEFAULT 0, + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `attachements` +-- + +INSERT INTO `attachements` (`id`, `name`, `class_name`, `element_id`, `type_id`, `filename`, `show_in_table`, `last_modified`) VALUES +(1, 'BC547', 'Part', 2, 2, '%BASE%/data/media/bc547.pdf', 1, '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `attachement_types` +-- + +CREATE TABLE `attachement_types` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `attachement_types` +-- + +INSERT INTO `attachement_types` (`id`, `name`, `parent_id`, `comment`, `datetime_added`, `last_modified`) VALUES +(1, 'Bilder', NULL, NULL, '2017-10-21 17:58:48', '0000-00-00 00:00:00'), +(2, 'Datenblätter', NULL, NULL, '2017-10-21 17:58:48', '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `categories` +-- + +CREATE TABLE `categories` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `disable_footprints` tinyint(1) NOT NULL DEFAULT 0, + `disable_manufacturers` tinyint(1) NOT NULL DEFAULT 0, + `disable_autodatasheets` tinyint(1) NOT NULL DEFAULT 0, + `disable_properties` tinyint(1) NOT NULL DEFAULT 0, + `partname_regex` text COLLATE utf8mb3_unicode_ci NOT NULL, + `partname_hint` text COLLATE utf8mb3_unicode_ci NOT NULL, + `default_description` text COLLATE utf8mb3_unicode_ci NOT NULL, + `default_comment` text COLLATE utf8mb3_unicode_ci NOT NULL, + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `categories` +-- + +INSERT INTO `categories` (`id`, `name`, `parent_id`, `disable_footprints`, `disable_manufacturers`, `disable_autodatasheets`, `disable_properties`, `partname_regex`, `partname_hint`, `default_description`, `default_comment`, `comment`, `datetime_added`, `last_modified`) VALUES +(1, 'aktive Bauteile', NULL, 0, 0, 0, 0, '', '', '', '', NULL, '2017-10-21 17:58:49', '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `devices` +-- + +CREATE TABLE `devices` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `order_quantity` int(11) NOT NULL DEFAULT 0, + `order_only_missing_parts` tinyint(1) NOT NULL DEFAULT 0, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `devices` +-- + +INSERT INTO `devices` (`id`, `name`, `parent_id`, `order_quantity`, `order_only_missing_parts`, `datetime_added`, `last_modified`, `comment`) VALUES +(1, 'Test', NULL, 0, 0, '2015-04-16 15:08:56', '0000-00-00 00:00:00', NULL); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `device_parts` +-- + +CREATE TABLE `device_parts` ( + `id` int(11) NOT NULL, + `id_part` int(11) NOT NULL DEFAULT 0, + `id_device` int(11) NOT NULL DEFAULT 0, + `quantity` int(11) NOT NULL DEFAULT 0, + `mountnames` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `device_parts` +-- + +INSERT INTO `device_parts` (`id`, `id_part`, `id_device`, `quantity`, `mountnames`) VALUES +(1, 2, 1, 1, ''); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `footprints` +-- + +CREATE TABLE `footprints` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `filename` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `filename_3d` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `footprints` +-- + +INSERT INTO `footprints` (`id`, `name`, `filename`, `filename_3d`, `parent_id`, `comment`, `datetime_added`, `last_modified`) VALUES +(1, 'LEDs', '%BASE%/img/footprints/Optik/LEDs/Bedrahtet/LED-GELB_3MM.png', '', NULL, NULL, '2017-10-21 17:58:49', '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `groups` +-- + +CREATE TABLE `groups` ( + `id` int(11) NOT NULL, + `name` varchar(32) NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `comment` mediumtext DEFAULT NULL, + `perms_system` int(11) NOT NULL, + `perms_groups` int(11) NOT NULL, + `perms_users` int(11) NOT NULL, + `perms_self` int(11) NOT NULL, + `perms_system_config` int(11) NOT NULL, + `perms_system_database` int(11) NOT NULL, + `perms_parts` bigint(11) NOT NULL, + `perms_parts_name` smallint(6) NOT NULL, + `perms_parts_description` smallint(6) NOT NULL, + `perms_parts_instock` smallint(6) NOT NULL, + `perms_parts_mininstock` smallint(6) NOT NULL, + `perms_parts_footprint` smallint(6) NOT NULL, + `perms_parts_storelocation` smallint(6) NOT NULL, + `perms_parts_manufacturer` smallint(6) NOT NULL, + `perms_parts_comment` smallint(6) NOT NULL, + `perms_parts_order` smallint(6) NOT NULL, + `perms_parts_orderdetails` smallint(6) NOT NULL, + `perms_parts_prices` smallint(6) NOT NULL, + `perms_parts_attachements` smallint(6) NOT NULL, + `perms_devices` int(11) NOT NULL, + `perms_devices_parts` int(11) NOT NULL, + `perms_storelocations` int(11) NOT NULL, + `perms_footprints` int(11) NOT NULL, + `perms_categories` int(11) NOT NULL, + `perms_suppliers` int(11) NOT NULL, + `perms_manufacturers` int(11) NOT NULL, + `perms_attachement_types` int(11) NOT NULL, + `perms_tools` int(11) NOT NULL, + `perms_labels` smallint(6) NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + +-- +-- Daten für Tabelle `groups` +-- + +INSERT INTO `groups` (`id`, `name`, `parent_id`, `comment`, `perms_system`, `perms_groups`, `perms_users`, `perms_self`, `perms_system_config`, `perms_system_database`, `perms_parts`, `perms_parts_name`, `perms_parts_description`, `perms_parts_instock`, `perms_parts_mininstock`, `perms_parts_footprint`, `perms_parts_storelocation`, `perms_parts_manufacturer`, `perms_parts_comment`, `perms_parts_order`, `perms_parts_orderdetails`, `perms_parts_prices`, `perms_parts_attachements`, `perms_devices`, `perms_devices_parts`, `perms_storelocations`, `perms_footprints`, `perms_categories`, `perms_suppliers`, `perms_manufacturers`, `perms_attachement_types`, `perms_tools`, `perms_labels`, `datetime_added`, `last_modified`) VALUES +(1, 'admins', NULL, 'Users of this group can do everything: Read, Write and Administrative actions.', 21, 1365, 87381, 85, 85, 21, 1431655765, 5, 5, 5, 5, 5, 5, 5, 5, 5, 325, 325, 325, 5461, 325, 5461, 5461, 5461, 5461, 5461, 1365, 1365, 85, '2017-10-21 17:58:46', '2018-10-08 17:27:41'), +(2, 'readonly', NULL, 'Users of this group can only read informations, use tools, and don\'t have access to administrative tools.', 2, 2730, 43690, 25, 170, 42, 2778027689, 9, 9, 9, 9, 9, 9, 9, 9, 9, 649, 649, 649, 1705, 649, 1705, 1705, 1705, 1705, 1705, 681, 1366, 165, '2017-10-21 17:58:46', '2018-10-08 17:28:35'), +(3, 'users', NULL, 'Users of this group, can edit part informations, create new ones, etc. but are not allowed to use administrative tools. (But can read current configuration, and see Server status)', 42, 2730, 43689, 89, 105, 41, 1431655765, 5, 5, 5, 5, 5, 5, 5, 5, 5, 325, 325, 325, 5461, 325, 5461, 5461, 5461, 5461, 5461, 1365, 1365, 85, '2017-10-21 17:58:46', '2018-10-08 17:28:17'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `internal` +-- + +CREATE TABLE `internal` ( + `keyName` char(30) CHARACTER SET ascii NOT NULL, + `keyValue` varchar(255) COLLATE utf8mb3_unicode_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `internal` +-- + +INSERT INTO `internal` (`keyName`, `keyValue`) VALUES +('dbVersion', '26'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `log` +-- + +CREATE TABLE `log` ( + `id` int(11) NOT NULL, + `datetime` timestamp NOT NULL DEFAULT current_timestamp(), + `id_user` int(11) NOT NULL, + `level` tinyint(4) NOT NULL, + `type` smallint(6) NOT NULL, + `target_id` int(11) NOT NULL, + `target_type` smallint(6) NOT NULL, + `extra` mediumtext NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `manufacturers` +-- + +CREATE TABLE `manufacturers` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `address` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `phone_number` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `fax_number` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `email_address` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `website` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `auto_product_url` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `manufacturers` +-- + +INSERT INTO `manufacturers` (`id`, `name`, `parent_id`, `address`, `phone_number`, `fax_number`, `email_address`, `website`, `auto_product_url`, `datetime_added`, `comment`, `last_modified`) VALUES +(1, 'Atmel', NULL, '', '', '', '', '', '', '2015-03-01 11:27:10', NULL, '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `orderdetails` +-- + +CREATE TABLE `orderdetails` ( + `id` int(11) NOT NULL, + `part_id` int(11) NOT NULL, + `id_supplier` int(11) NOT NULL DEFAULT 0, + `supplierpartnr` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `obsolete` tinyint(1) DEFAULT 0, + `supplier_product_url` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `parts` +-- + +CREATE TABLE `parts` ( + `id` int(11) NOT NULL, + `id_category` int(11) NOT NULL DEFAULT 0, + `name` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `description` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `instock` int(11) NOT NULL DEFAULT 0, + `mininstock` int(11) NOT NULL DEFAULT 0, + `comment` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `visible` tinyint(1) NOT NULL, + `id_footprint` int(11) DEFAULT NULL, + `id_storelocation` int(11) DEFAULT NULL, + `order_orderdetails_id` int(11) DEFAULT NULL, + `order_quantity` int(11) NOT NULL DEFAULT 1, + `manual_order` tinyint(1) NOT NULL DEFAULT 0, + `id_manufacturer` int(11) DEFAULT NULL, + `id_master_picture_attachement` int(11) DEFAULT NULL, + `manufacturer_product_url` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `favorite` tinyint(1) NOT NULL DEFAULT 0 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `parts` +-- + +INSERT INTO `parts` (`id`, `id_category`, `name`, `description`, `instock`, `mininstock`, `comment`, `visible`, `id_footprint`, `id_storelocation`, `order_orderdetails_id`, `order_quantity`, `manual_order`, `id_manufacturer`, `id_master_picture_attachement`, `manufacturer_product_url`, `datetime_added`, `last_modified`, `favorite`) VALUES +(2, 1, 'BC547C', 'NPN 45V 0,1A 0,5W', 59, 0, '', 0, 1, 1, NULL, 1, 0, NULL, NULL, '', '2015-03-01 10:40:31', '2016-12-26 10:48:49', 0); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `pricedetails` +-- + +CREATE TABLE `pricedetails` ( + `id` int(11) NOT NULL, + `orderdetails_id` int(11) NOT NULL, + `price` decimal(11,5) DEFAULT NULL, + `price_related_quantity` int(11) NOT NULL DEFAULT 1, + `min_discount_quantity` int(11) NOT NULL DEFAULT 1, + `manual_input` tinyint(1) NOT NULL DEFAULT 1, + `last_modified` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `storelocations` +-- + +CREATE TABLE `storelocations` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `is_full` tinyint(1) NOT NULL DEFAULT 0, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `storelocations` +-- + +INSERT INTO `storelocations` (`id`, `name`, `parent_id`, `is_full`, `datetime_added`, `comment`, `last_modified`) VALUES +(1, 'Halbleiter I', NULL, 0, '2015-03-01 11:26:37', NULL, '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `suppliers` +-- + +CREATE TABLE `suppliers` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `address` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `phone_number` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `fax_number` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `email_address` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `website` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `auto_product_url` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `suppliers` +-- + +INSERT INTO `suppliers` (`id`, `name`, `parent_id`, `address`, `phone_number`, `fax_number`, `email_address`, `website`, `auto_product_url`, `datetime_added`, `comment`, `last_modified`) VALUES +(1, 'Test', NULL, '', '', '', '', '', 'Test', '2015-03-01 10:37:23', NULL, '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `users` +-- + +CREATE TABLE `users` ( + `id` int(11) NOT NULL, + `name` varchar(32) NOT NULL, + `password` varchar(255) DEFAULT NULL, + `first_name` tinytext DEFAULT NULL, + `last_name` tinytext DEFAULT NULL, + `department` tinytext DEFAULT NULL, + `email` tinytext DEFAULT NULL, + `need_pw_change` tinyint(1) NOT NULL DEFAULT 0, + `group_id` int(11) DEFAULT NULL, + `config_language` tinytext DEFAULT NULL, + `config_timezone` tinytext DEFAULT NULL, + `config_theme` tinytext DEFAULT NULL, + `config_currency` tinytext DEFAULT NULL, + `config_image_path` text NOT NULL, + `config_instock_comment_w` text NOT NULL, + `config_instock_comment_a` text NOT NULL, + `perms_system` int(11) NOT NULL, + `perms_groups` int(11) NOT NULL, + `perms_users` int(11) NOT NULL, + `perms_self` int(11) NOT NULL, + `perms_system_config` int(11) NOT NULL, + `perms_system_database` int(11) NOT NULL, + `perms_parts` bigint(11) NOT NULL, + `perms_parts_name` smallint(6) NOT NULL, + `perms_parts_description` smallint(6) NOT NULL, + `perms_parts_instock` smallint(6) NOT NULL, + `perms_parts_mininstock` smallint(6) NOT NULL, + `perms_parts_footprint` smallint(6) NOT NULL, + `perms_parts_storelocation` smallint(6) NOT NULL, + `perms_parts_manufacturer` smallint(6) NOT NULL, + `perms_parts_comment` smallint(6) NOT NULL, + `perms_parts_order` smallint(6) NOT NULL, + `perms_parts_orderdetails` smallint(6) NOT NULL, + `perms_parts_prices` smallint(6) NOT NULL, + `perms_parts_attachements` smallint(6) NOT NULL, + `perms_devices` int(11) NOT NULL, + `perms_devices_parts` int(11) NOT NULL, + `perms_storelocations` int(11) NOT NULL, + `perms_footprints` int(11) NOT NULL, + `perms_categories` int(11) NOT NULL, + `perms_suppliers` int(11) NOT NULL, + `perms_manufacturers` int(11) NOT NULL, + `perms_attachement_types` int(11) NOT NULL, + `perms_tools` int(11) NOT NULL, + `perms_labels` smallint(6) NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + +-- +-- Daten für Tabelle `users` +-- + +INSERT INTO `users` (`id`, `name`, `password`, `first_name`, `last_name`, `department`, `email`, `need_pw_change`, `group_id`, `config_language`, `config_timezone`, `config_theme`, `config_currency`, `config_image_path`, `config_instock_comment_w`, `config_instock_comment_a`, `perms_system`, `perms_groups`, `perms_users`, `perms_self`, `perms_system_config`, `perms_system_database`, `perms_parts`, `perms_parts_name`, `perms_parts_description`, `perms_parts_instock`, `perms_parts_mininstock`, `perms_parts_footprint`, `perms_parts_storelocation`, `perms_parts_manufacturer`, `perms_parts_comment`, `perms_parts_order`, `perms_parts_orderdetails`, `perms_parts_prices`, `perms_parts_attachements`, `perms_devices`, `perms_devices_parts`, `perms_storelocations`, `perms_footprints`, `perms_categories`, `perms_suppliers`, `perms_manufacturers`, `perms_attachement_types`, `perms_tools`, `perms_labels`, `datetime_added`, `last_modified`) VALUES +(1, 'anonymous', '', '', '', '', '', 0, 2, '', '', '', NULL, '', '', '', 21848, 20480, 0, 0, 0, 0, 0, 21840, 21840, 21840, 21840, 21840, 21840, 21840, 21840, 21840, 21520, 21520, 21520, 20480, 21520, 20480, 20480, 20480, 20480, 20480, 21504, 20480, 0, '2017-10-21 17:58:46', '2018-02-18 12:46:58'), +(2, 'admin', '$2a$12$j0RKrKlx60bzX1DWMyXwjeaW.pe3bFjAK8ByIGnvjrRnET2JtsFoe', 'Admin', 'Ad', NULL, 'admin@ras.pi', 0, 1, '', '', '', NULL, '', '', '', 21845, 21845, 21845, 21, 85, 21, 349525, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 0, '2017-10-21 17:58:46', '2017-12-23 11:04:48'); + +-- +-- Indizes der exportierten Tabellen +-- + +-- +-- Indizes für die Tabelle `attachements` +-- +ALTER TABLE `attachements` + ADD PRIMARY KEY (`id`), + ADD KEY `attachements_class_name_k` (`class_name`), + ADD KEY `attachements_element_id_k` (`element_id`), + ADD KEY `attachements_type_id_fk` (`type_id`); + +-- +-- Indizes für die Tabelle `attachement_types` +-- +ALTER TABLE `attachement_types` + ADD PRIMARY KEY (`id`), + ADD KEY `attachement_types_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `categories` +-- +ALTER TABLE `categories` + ADD PRIMARY KEY (`id`), + ADD KEY `categories_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `devices` +-- +ALTER TABLE `devices` + ADD PRIMARY KEY (`id`), + ADD KEY `devices_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `device_parts` +-- +ALTER TABLE `device_parts` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `device_parts_combination_uk` (`id_part`,`id_device`), + ADD KEY `device_parts_id_part_k` (`id_part`), + ADD KEY `device_parts_id_device_k` (`id_device`); + +-- +-- Indizes für die Tabelle `footprints` +-- +ALTER TABLE `footprints` + ADD PRIMARY KEY (`id`), + ADD KEY `footprints_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `groups` +-- +ALTER TABLE `groups` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `name` (`name`); + +-- +-- Indizes für die Tabelle `internal` +-- +ALTER TABLE `internal` + ADD UNIQUE KEY `keyName` (`keyName`); + +-- +-- Indizes für die Tabelle `log` +-- +ALTER TABLE `log` + ADD PRIMARY KEY (`id`), + ADD KEY `id_user` (`id_user`); + +-- +-- Indizes für die Tabelle `manufacturers` +-- +ALTER TABLE `manufacturers` + ADD PRIMARY KEY (`id`), + ADD KEY `manufacturers_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `orderdetails` +-- +ALTER TABLE `orderdetails` + ADD PRIMARY KEY (`id`), + ADD KEY `orderdetails_part_id_k` (`part_id`), + ADD KEY `orderdetails_id_supplier_k` (`id_supplier`); + +-- +-- Indizes für die Tabelle `parts` +-- +ALTER TABLE `parts` + ADD PRIMARY KEY (`id`), + ADD KEY `parts_id_category_k` (`id_category`), + ADD KEY `parts_id_footprint_k` (`id_footprint`), + ADD KEY `parts_id_storelocation_k` (`id_storelocation`), + ADD KEY `parts_order_orderdetails_id_k` (`order_orderdetails_id`), + ADD KEY `parts_id_manufacturer_k` (`id_manufacturer`), + ADD KEY `favorite` (`favorite`); + +-- +-- Indizes für die Tabelle `pricedetails` +-- +ALTER TABLE `pricedetails` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `pricedetails_combination_uk` (`orderdetails_id`,`min_discount_quantity`), + ADD KEY `pricedetails_orderdetails_id_k` (`orderdetails_id`); + +-- +-- Indizes für die Tabelle `storelocations` +-- +ALTER TABLE `storelocations` + ADD PRIMARY KEY (`id`), + ADD KEY `storelocations_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `suppliers` +-- +ALTER TABLE `suppliers` + ADD PRIMARY KEY (`id`), + ADD KEY `suppliers_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `users` +-- +ALTER TABLE `users` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `name` (`name`); + +-- +-- AUTO_INCREMENT für exportierte Tabellen +-- + +-- +-- AUTO_INCREMENT für Tabelle `attachements` +-- +ALTER TABLE `attachements` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=201; + +-- +-- AUTO_INCREMENT für Tabelle `attachement_types` +-- +ALTER TABLE `attachement_types` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; + +-- +-- AUTO_INCREMENT für Tabelle `categories` +-- +ALTER TABLE `categories` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=123; + +-- +-- AUTO_INCREMENT für Tabelle `devices` +-- +ALTER TABLE `devices` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; + +-- +-- AUTO_INCREMENT für Tabelle `device_parts` +-- +ALTER TABLE `device_parts` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=12; + +-- +-- AUTO_INCREMENT für Tabelle `footprints` +-- +ALTER TABLE `footprints` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=82; + +-- +-- AUTO_INCREMENT für Tabelle `groups` +-- +ALTER TABLE `groups` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; + +-- +-- AUTO_INCREMENT für Tabelle `log` +-- +ALTER TABLE `log` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=218; + +-- +-- AUTO_INCREMENT für Tabelle `manufacturers` +-- +ALTER TABLE `manufacturers` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; + +-- +-- AUTO_INCREMENT für Tabelle `orderdetails` +-- +ALTER TABLE `orderdetails` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=650; + +-- +-- AUTO_INCREMENT für Tabelle `parts` +-- +ALTER TABLE `parts` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1171; + +-- +-- AUTO_INCREMENT für Tabelle `pricedetails` +-- +ALTER TABLE `pricedetails` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=437; + +-- +-- AUTO_INCREMENT für Tabelle `storelocations` +-- +ALTER TABLE `storelocations` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=340; + +-- +-- AUTO_INCREMENT für Tabelle `suppliers` +-- +ALTER TABLE `suppliers` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5; + +-- +-- AUTO_INCREMENT für Tabelle `users` +-- +ALTER TABLE `users` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6; + +-- +-- Constraints der exportierten Tabellen +-- + +-- +-- Constraints der Tabelle `attachements` +-- +ALTER TABLE `attachements` + ADD CONSTRAINT `attachements_type_id_fk` FOREIGN KEY (`type_id`) REFERENCES `attachement_types` (`id`); + +-- +-- Constraints der Tabelle `attachement_types` +-- +ALTER TABLE `attachement_types` + ADD CONSTRAINT `attachement_types_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `attachement_types` (`id`); + +-- +-- Constraints der Tabelle `categories` +-- +ALTER TABLE `categories` + ADD CONSTRAINT `categories_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `categories` (`id`); + +-- +-- Constraints der Tabelle `devices` +-- +ALTER TABLE `devices` + ADD CONSTRAINT `devices_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `devices` (`id`); + +-- +-- Constraints der Tabelle `footprints` +-- +ALTER TABLE `footprints` + ADD CONSTRAINT `footprints_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `footprints` (`id`); + +-- +-- Constraints der Tabelle `manufacturers` +-- +ALTER TABLE `manufacturers` + ADD CONSTRAINT `manufacturers_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `manufacturers` (`id`); + +-- +-- Constraints der Tabelle `parts` +-- +ALTER TABLE `parts` + ADD CONSTRAINT `parts_id_footprint_fk` FOREIGN KEY (`id_footprint`) REFERENCES `footprints` (`id`), + ADD CONSTRAINT `parts_id_manufacturer_fk` FOREIGN KEY (`id_manufacturer`) REFERENCES `manufacturers` (`id`), + ADD CONSTRAINT `parts_id_storelocation_fk` FOREIGN KEY (`id_storelocation`) REFERENCES `storelocations` (`id`), + ADD CONSTRAINT `parts_order_orderdetails_id_fk` FOREIGN KEY (`order_orderdetails_id`) REFERENCES `orderdetails` (`id`); + +-- +-- Constraints der Tabelle `storelocations` +-- +ALTER TABLE `storelocations` + ADD CONSTRAINT `storelocations_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `storelocations` (`id`); + +-- +-- Constraints der Tabelle `suppliers` +-- +ALTER TABLE `suppliers` + ADD CONSTRAINT `suppliers_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `suppliers` (`id`); +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/.github/assets/legacy_import/db_minimal.sql b/.github/assets/legacy_import/db_minimal.sql new file mode 100644 index 00000000..7f29d5dc --- /dev/null +++ b/.github/assets/legacy_import/db_minimal.sql @@ -0,0 +1,736 @@ +-- phpMyAdmin SQL Dump +-- version 5.1.3 +-- https://www.phpmyadmin.net/ +-- +-- Host: 127.0.0.1 +-- Erstellungszeit: 07. Mai 2023 um 01:48 +-- Server-Version: 10.6.5-MariaDB-log +-- PHP-Version: 8.1.2 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- Datenbank: `partdb-legacy` +-- + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `attachements` +-- + +CREATE TABLE `attachements` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `class_name` varchar(255) COLLATE utf8mb3_unicode_ci NOT NULL, + `element_id` int(11) NOT NULL, + `type_id` int(11) NOT NULL, + `filename` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `show_in_table` tinyint(1) NOT NULL DEFAULT 0, + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `attachement_types` +-- + +CREATE TABLE `attachement_types` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `attachement_types` +-- + +INSERT INTO `attachement_types` (`id`, `name`, `parent_id`, `comment`, `datetime_added`, `last_modified`) VALUES +(1, 'Bilder', NULL, NULL, '2023-01-07 18:31:48', '0000-00-00 00:00:00'), +(2, 'Datenblätter', NULL, NULL, '2023-01-07 18:31:48', '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `categories` +-- + +CREATE TABLE `categories` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `disable_footprints` tinyint(1) NOT NULL DEFAULT 0, + `disable_manufacturers` tinyint(1) NOT NULL DEFAULT 0, + `disable_autodatasheets` tinyint(1) NOT NULL DEFAULT 0, + `disable_properties` tinyint(1) NOT NULL DEFAULT 0, + `partname_regex` text COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '', + `partname_hint` text COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '', + `default_description` text COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '', + `default_comment` text COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '', + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `categories` +-- + +INSERT INTO `categories` (`id`, `name`, `parent_id`, `disable_footprints`, `disable_manufacturers`, `disable_autodatasheets`, `disable_properties`, `partname_regex`, `partname_hint`, `default_description`, `default_comment`, `comment`, `datetime_added`, `last_modified`) VALUES +(1, 'Test', NULL, 0, 0, 0, 0, '', '', '', '', '', '2023-01-07 18:32:29', '2023-01-07 18:32:29'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `devices` +-- + +CREATE TABLE `devices` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `order_quantity` int(11) NOT NULL DEFAULT 0, + `order_only_missing_parts` tinyint(1) NOT NULL DEFAULT 0, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `device_parts` +-- + +CREATE TABLE `device_parts` ( + `id` int(11) NOT NULL, + `id_part` int(11) NOT NULL DEFAULT 0, + `id_device` int(11) NOT NULL DEFAULT 0, + `quantity` int(11) NOT NULL DEFAULT 0, + `mountnames` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `footprints` +-- + +CREATE TABLE `footprints` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `filename` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `filename_3d` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `groups` +-- + +CREATE TABLE `groups` ( + `id` int(11) NOT NULL, + `name` varchar(32) NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `comment` mediumtext DEFAULT NULL, + `perms_system` int(11) NOT NULL, + `perms_groups` int(11) NOT NULL, + `perms_users` int(11) NOT NULL, + `perms_self` int(11) NOT NULL, + `perms_system_config` int(11) NOT NULL, + `perms_system_database` int(11) NOT NULL, + `perms_parts` bigint(11) NOT NULL, + `perms_parts_name` smallint(6) NOT NULL, + `perms_parts_description` smallint(6) NOT NULL, + `perms_parts_instock` smallint(6) NOT NULL, + `perms_parts_mininstock` smallint(6) NOT NULL, + `perms_parts_footprint` smallint(6) NOT NULL, + `perms_parts_storelocation` smallint(6) NOT NULL, + `perms_parts_manufacturer` smallint(6) NOT NULL, + `perms_parts_comment` smallint(6) NOT NULL, + `perms_parts_order` smallint(6) NOT NULL, + `perms_parts_orderdetails` smallint(6) NOT NULL, + `perms_parts_prices` smallint(6) NOT NULL, + `perms_parts_attachements` smallint(6) NOT NULL, + `perms_devices` int(11) NOT NULL, + `perms_devices_parts` int(11) NOT NULL, + `perms_storelocations` int(11) NOT NULL, + `perms_footprints` int(11) NOT NULL, + `perms_categories` int(11) NOT NULL, + `perms_suppliers` int(11) NOT NULL, + `perms_manufacturers` int(11) NOT NULL, + `perms_attachement_types` int(11) NOT NULL, + `perms_tools` int(11) NOT NULL, + `perms_labels` smallint(6) NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Daten für Tabelle `groups` +-- + +INSERT INTO `groups` (`id`, `name`, `parent_id`, `comment`, `perms_system`, `perms_groups`, `perms_users`, `perms_self`, `perms_system_config`, `perms_system_database`, `perms_parts`, `perms_parts_name`, `perms_parts_description`, `perms_parts_instock`, `perms_parts_mininstock`, `perms_parts_footprint`, `perms_parts_storelocation`, `perms_parts_manufacturer`, `perms_parts_comment`, `perms_parts_order`, `perms_parts_orderdetails`, `perms_parts_prices`, `perms_parts_attachements`, `perms_devices`, `perms_devices_parts`, `perms_storelocations`, `perms_footprints`, `perms_categories`, `perms_suppliers`, `perms_manufacturers`, `perms_attachement_types`, `perms_tools`, `perms_labels`, `datetime_added`, `last_modified`) VALUES +(1, 'admins', NULL, 'Users of this group can do everything: Read, Write and Administrative actions.', 21, 1365, 87381, 85, 85, 21, 1431655765, 5, 5, 5, 5, 5, 5, 5, 5, 5, 325, 325, 325, 5461, 325, 5461, 5461, 5461, 5461, 5461, 1365, 1365, 85, '2023-01-07 18:31:48', '0000-00-00 00:00:00'), +(2, 'readonly', NULL, 'Users of this group can only read informations, use tools, and don\'t have access to administrative tools.', 42, 2730, 174762, 154, 170, 42, -1516939607, 9, 9, 9, 9, 9, 9, 9, 9, 9, 649, 649, 649, 1705, 649, 1705, 1705, 1705, 1705, 1705, 681, 1366, 165, '2023-01-07 18:31:48', '0000-00-00 00:00:00'), +(3, 'users', NULL, 'Users of this group, can edit part informations, create new ones, etc. but are not allowed to use administrative tools. (But can read current configuration, and see Server status)', 42, 2730, 109226, 89, 105, 41, 1431655765, 5, 5, 5, 5, 5, 5, 5, 5, 5, 325, 325, 325, 5461, 325, 5461, 5461, 5461, 5461, 5461, 1365, 1365, 85, '2023-01-07 18:31:48', '0000-00-00 00:00:00'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `internal` +-- + +CREATE TABLE `internal` ( + `keyName` char(30) CHARACTER SET ascii NOT NULL, + `keyValue` varchar(255) COLLATE utf8mb3_unicode_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `internal` +-- + +INSERT INTO `internal` (`keyName`, `keyValue`) VALUES +('dbVersion', '26'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `log` +-- + +CREATE TABLE `log` ( + `id` int(11) NOT NULL, + `datetime` timestamp NOT NULL DEFAULT current_timestamp(), + `id_user` int(11) NOT NULL, + `level` tinyint(4) NOT NULL, + `type` smallint(6) NOT NULL, + `target_id` int(11) NOT NULL, + `target_type` smallint(6) NOT NULL, + `extra` mediumtext NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Daten für Tabelle `log` +-- + +INSERT INTO `log` (`id`, `datetime`, `id_user`, `level`, `type`, `target_id`, `target_type`, `extra`) VALUES +(1, '2023-01-07 18:31:48', 1, 4, 10, 0, 0, '{\"o\":0,\"n\":26,\"s\":true}'), +(2, '2023-01-07 18:32:13', 2, 6, 1, 2, 1, '{\"i\":\"::\"}'), +(3, '2023-01-07 18:32:29', 2, 6, 6, 1, 4, '[]'), +(4, '2023-01-07 18:32:53', 2, 6, 6, 1, 12, '[]'), +(5, '2023-01-07 18:33:26', 2, 6, 6, 1, 10, '{\"i\":0}'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `manufacturers` +-- + +CREATE TABLE `manufacturers` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `address` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `phone_number` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `fax_number` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `email_address` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `website` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `auto_product_url` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `orderdetails` +-- + +CREATE TABLE `orderdetails` ( + `id` int(11) NOT NULL, + `part_id` int(11) NOT NULL, + `id_supplier` int(11) NOT NULL DEFAULT 0, + `supplierpartnr` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `obsolete` tinyint(1) DEFAULT 0, + `supplier_product_url` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `orderdetails` +-- + +INSERT INTO `orderdetails` (`id`, `part_id`, `id_supplier`, `supplierpartnr`, `obsolete`, `supplier_product_url`, `datetime_added`) VALUES +(1, 1, 1, 'BC547', 0, '', '2023-01-07 18:45:59'), +(2, 1, 1, 'Test', 0, '', '2023-01-07 18:46:09'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `parts` +-- + +CREATE TABLE `parts` ( + `id` int(11) NOT NULL, + `id_category` int(11) NOT NULL DEFAULT 0, + `name` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `description` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `instock` int(11) NOT NULL DEFAULT 0, + `mininstock` int(11) NOT NULL DEFAULT 0, + `comment` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `visible` tinyint(1) NOT NULL, + `id_footprint` int(11) DEFAULT NULL, + `id_storelocation` int(11) DEFAULT NULL, + `order_orderdetails_id` int(11) DEFAULT NULL, + `order_quantity` int(11) NOT NULL DEFAULT 1, + `manual_order` tinyint(1) NOT NULL DEFAULT 0, + `id_manufacturer` int(11) DEFAULT NULL, + `id_master_picture_attachement` int(11) DEFAULT NULL, + `manufacturer_product_url` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `favorite` tinyint(1) NOT NULL DEFAULT 0 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `parts` +-- + +INSERT INTO `parts` (`id`, `id_category`, `name`, `description`, `instock`, `mininstock`, `comment`, `visible`, `id_footprint`, `id_storelocation`, `order_orderdetails_id`, `order_quantity`, `manual_order`, `id_manufacturer`, `id_master_picture_attachement`, `manufacturer_product_url`, `datetime_added`, `last_modified`, `favorite`) VALUES +(1, 1, 'BC547', '', 0, 0, '', 0, NULL, NULL, NULL, 1, 0, NULL, NULL, '', '2023-01-07 18:33:26', '2023-01-07 18:33:26', 0); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `pricedetails` +-- + +CREATE TABLE `pricedetails` ( + `id` int(11) NOT NULL, + `orderdetails_id` int(11) NOT NULL, + `price` decimal(11,5) DEFAULT NULL, + `price_related_quantity` int(11) NOT NULL DEFAULT 1, + `min_discount_quantity` int(11) NOT NULL DEFAULT 1, + `manual_input` tinyint(1) NOT NULL DEFAULT 1, + `last_modified` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `pricedetails` +-- + +INSERT INTO `pricedetails` (`id`, `orderdetails_id`, `price`, `price_related_quantity`, `min_discount_quantity`, `manual_input`, `last_modified`) VALUES +(1, 2, '3.55000', 1, 1, 1, '2023-01-07 18:46:19'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `storelocations` +-- + +CREATE TABLE `storelocations` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `is_full` tinyint(1) NOT NULL DEFAULT 0, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `suppliers` +-- + +CREATE TABLE `suppliers` ( + `id` int(11) NOT NULL, + `name` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `address` mediumtext COLLATE utf8mb3_unicode_ci NOT NULL, + `phone_number` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `fax_number` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `email_address` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `website` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `auto_product_url` tinytext COLLATE utf8mb3_unicode_ci NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `comment` text COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- +-- Daten für Tabelle `suppliers` +-- + +INSERT INTO `suppliers` (`id`, `name`, `parent_id`, `address`, `phone_number`, `fax_number`, `email_address`, `website`, `auto_product_url`, `datetime_added`, `comment`, `last_modified`) VALUES +(1, 'Reichelt', NULL, '', '', '', '', '', '', '2023-01-07 18:32:53', '', '2023-01-07 18:32:53'); + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `users` +-- + +CREATE TABLE `users` ( + `id` int(11) NOT NULL, + `name` varchar(32) NOT NULL, + `password` varchar(255) DEFAULT NULL, + `first_name` tinytext DEFAULT NULL, + `last_name` tinytext DEFAULT NULL, + `department` tinytext DEFAULT NULL, + `email` tinytext DEFAULT NULL, + `need_pw_change` tinyint(1) NOT NULL DEFAULT 0, + `group_id` int(11) DEFAULT NULL, + `config_language` tinytext DEFAULT NULL, + `config_timezone` tinytext DEFAULT NULL, + `config_theme` tinytext DEFAULT NULL, + `config_currency` tinytext DEFAULT NULL, + `config_image_path` text NOT NULL, + `config_instock_comment_w` text NOT NULL, + `config_instock_comment_a` text NOT NULL, + `perms_system` int(11) NOT NULL, + `perms_groups` int(11) NOT NULL, + `perms_users` int(11) NOT NULL, + `perms_self` int(11) NOT NULL, + `perms_system_config` int(11) NOT NULL, + `perms_system_database` int(11) NOT NULL, + `perms_parts` bigint(11) NOT NULL, + `perms_parts_name` smallint(6) NOT NULL, + `perms_parts_description` smallint(6) NOT NULL, + `perms_parts_instock` smallint(6) NOT NULL, + `perms_parts_mininstock` smallint(6) NOT NULL, + `perms_parts_footprint` smallint(6) NOT NULL, + `perms_parts_storelocation` smallint(6) NOT NULL, + `perms_parts_manufacturer` smallint(6) NOT NULL, + `perms_parts_comment` smallint(6) NOT NULL, + `perms_parts_order` smallint(6) NOT NULL, + `perms_parts_orderdetails` smallint(6) NOT NULL, + `perms_parts_prices` smallint(6) NOT NULL, + `perms_parts_attachements` smallint(6) NOT NULL, + `perms_devices` int(11) NOT NULL, + `perms_devices_parts` int(11) NOT NULL, + `perms_storelocations` int(11) NOT NULL, + `perms_footprints` int(11) NOT NULL, + `perms_categories` int(11) NOT NULL, + `perms_suppliers` int(11) NOT NULL, + `perms_manufacturers` int(11) NOT NULL, + `perms_attachement_types` int(11) NOT NULL, + `perms_tools` int(11) NOT NULL, + `perms_labels` smallint(6) NOT NULL, + `datetime_added` timestamp NOT NULL DEFAULT current_timestamp(), + `last_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Daten für Tabelle `users` +-- + +INSERT INTO `users` (`id`, `name`, `password`, `first_name`, `last_name`, `department`, `email`, `need_pw_change`, `group_id`, `config_language`, `config_timezone`, `config_theme`, `config_currency`, `config_image_path`, `config_instock_comment_w`, `config_instock_comment_a`, `perms_system`, `perms_groups`, `perms_users`, `perms_self`, `perms_system_config`, `perms_system_database`, `perms_parts`, `perms_parts_name`, `perms_parts_description`, `perms_parts_instock`, `perms_parts_mininstock`, `perms_parts_footprint`, `perms_parts_storelocation`, `perms_parts_manufacturer`, `perms_parts_comment`, `perms_parts_order`, `perms_parts_orderdetails`, `perms_parts_prices`, `perms_parts_attachements`, `perms_devices`, `perms_devices_parts`, `perms_storelocations`, `perms_footprints`, `perms_categories`, `perms_suppliers`, `perms_manufacturers`, `perms_attachement_types`, `perms_tools`, `perms_labels`, `datetime_added`, `last_modified`) VALUES +(1, 'anonymous', '', '', '', '', '', 0, 2, NULL, NULL, NULL, NULL, '', '', '', 21844, 20480, 0, 0, 0, 0, 0, 21840, 21840, 21840, 21840, 21840, 21840, 21840, 21840, 21840, 21520, 21520, 21520, 20480, 21520, 20480, 20480, 20480, 20480, 20480, 21504, 20480, 0, '2023-01-07 18:31:48', '0000-00-00 00:00:00'), +(2, 'admin', '$2a$12$j0RKrKlx60bzX1DWMyXwjeaW.pe3bFjAK8ByIGnvjrRnET2JtsFoe$2a$12$j0RKrKlx60bzX1DWMyXwjeaW.pe3bFjAK8ByIGnvjrRnET2JtsFoe', '', '', '', '', 1, 1, NULL, NULL, NULL, NULL, '', '', '', 21845, 21845, 21845, 21, 85, 21, 349525, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 0, '2023-01-07 18:31:48', '0000-00-00 00:00:00'); + +-- +-- Indizes der exportierten Tabellen +-- + +-- +-- Indizes für die Tabelle `attachements` +-- +ALTER TABLE `attachements` + ADD PRIMARY KEY (`id`), + ADD KEY `attachements_class_name_k` (`class_name`), + ADD KEY `attachements_element_id_k` (`element_id`), + ADD KEY `attachements_type_id_fk` (`type_id`); + +-- +-- Indizes für die Tabelle `attachement_types` +-- +ALTER TABLE `attachement_types` + ADD PRIMARY KEY (`id`), + ADD KEY `attachement_types_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `categories` +-- +ALTER TABLE `categories` + ADD PRIMARY KEY (`id`), + ADD KEY `categories_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `devices` +-- +ALTER TABLE `devices` + ADD PRIMARY KEY (`id`), + ADD KEY `devices_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `device_parts` +-- +ALTER TABLE `device_parts` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `device_parts_combination_uk` (`id_part`,`id_device`), + ADD KEY `device_parts_id_part_k` (`id_part`), + ADD KEY `device_parts_id_device_k` (`id_device`); + +-- +-- Indizes für die Tabelle `footprints` +-- +ALTER TABLE `footprints` + ADD PRIMARY KEY (`id`), + ADD KEY `footprints_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `groups` +-- +ALTER TABLE `groups` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `name` (`name`); + +-- +-- Indizes für die Tabelle `internal` +-- +ALTER TABLE `internal` + ADD UNIQUE KEY `keyName` (`keyName`); + +-- +-- Indizes für die Tabelle `log` +-- +ALTER TABLE `log` + ADD PRIMARY KEY (`id`), + ADD KEY `id_user` (`id_user`); + +-- +-- Indizes für die Tabelle `manufacturers` +-- +ALTER TABLE `manufacturers` + ADD PRIMARY KEY (`id`), + ADD KEY `manufacturers_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `orderdetails` +-- +ALTER TABLE `orderdetails` + ADD PRIMARY KEY (`id`), + ADD KEY `orderdetails_part_id_k` (`part_id`), + ADD KEY `orderdetails_id_supplier_k` (`id_supplier`); + +-- +-- Indizes für die Tabelle `parts` +-- +ALTER TABLE `parts` + ADD PRIMARY KEY (`id`), + ADD KEY `parts_id_category_k` (`id_category`), + ADD KEY `parts_id_footprint_k` (`id_footprint`), + ADD KEY `parts_id_storelocation_k` (`id_storelocation`), + ADD KEY `parts_order_orderdetails_id_k` (`order_orderdetails_id`), + ADD KEY `parts_id_manufacturer_k` (`id_manufacturer`), + ADD KEY `favorite` (`favorite`); + +-- +-- Indizes für die Tabelle `pricedetails` +-- +ALTER TABLE `pricedetails` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `pricedetails_combination_uk` (`orderdetails_id`,`min_discount_quantity`), + ADD KEY `pricedetails_orderdetails_id_k` (`orderdetails_id`); + +-- +-- Indizes für die Tabelle `storelocations` +-- +ALTER TABLE `storelocations` + ADD PRIMARY KEY (`id`), + ADD KEY `storelocations_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `suppliers` +-- +ALTER TABLE `suppliers` + ADD PRIMARY KEY (`id`), + ADD KEY `suppliers_parent_id_k` (`parent_id`); + +-- +-- Indizes für die Tabelle `users` +-- +ALTER TABLE `users` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `name` (`name`); + +-- +-- AUTO_INCREMENT für exportierte Tabellen +-- + +-- +-- AUTO_INCREMENT für Tabelle `attachements` +-- +ALTER TABLE `attachements` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT für Tabelle `attachement_types` +-- +ALTER TABLE `attachement_types` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; + +-- +-- AUTO_INCREMENT für Tabelle `categories` +-- +ALTER TABLE `categories` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; + +-- +-- AUTO_INCREMENT für Tabelle `devices` +-- +ALTER TABLE `devices` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT für Tabelle `device_parts` +-- +ALTER TABLE `device_parts` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT für Tabelle `footprints` +-- +ALTER TABLE `footprints` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT für Tabelle `groups` +-- +ALTER TABLE `groups` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; + +-- +-- AUTO_INCREMENT für Tabelle `log` +-- +ALTER TABLE `log` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6; + +-- +-- AUTO_INCREMENT für Tabelle `manufacturers` +-- +ALTER TABLE `manufacturers` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT für Tabelle `orderdetails` +-- +ALTER TABLE `orderdetails` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; + +-- +-- AUTO_INCREMENT für Tabelle `parts` +-- +ALTER TABLE `parts` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; + +-- +-- AUTO_INCREMENT für Tabelle `pricedetails` +-- +ALTER TABLE `pricedetails` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; + +-- +-- AUTO_INCREMENT für Tabelle `storelocations` +-- +ALTER TABLE `storelocations` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT für Tabelle `suppliers` +-- +ALTER TABLE `suppliers` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; + +-- +-- AUTO_INCREMENT für Tabelle `users` +-- +ALTER TABLE `users` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; + +-- +-- Constraints der exportierten Tabellen +-- + +-- +-- Constraints der Tabelle `attachements` +-- +ALTER TABLE `attachements` + ADD CONSTRAINT `attachements_type_id_fk` FOREIGN KEY (`type_id`) REFERENCES `attachement_types` (`id`); + +-- +-- Constraints der Tabelle `attachement_types` +-- +ALTER TABLE `attachement_types` + ADD CONSTRAINT `attachement_types_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `attachement_types` (`id`); + +-- +-- Constraints der Tabelle `categories` +-- +ALTER TABLE `categories` + ADD CONSTRAINT `categories_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `categories` (`id`); + +-- +-- Constraints der Tabelle `devices` +-- +ALTER TABLE `devices` + ADD CONSTRAINT `devices_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `devices` (`id`); + +-- +-- Constraints der Tabelle `footprints` +-- +ALTER TABLE `footprints` + ADD CONSTRAINT `footprints_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `footprints` (`id`); + +-- +-- Constraints der Tabelle `manufacturers` +-- +ALTER TABLE `manufacturers` + ADD CONSTRAINT `manufacturers_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `manufacturers` (`id`); + +-- +-- Constraints der Tabelle `parts` +-- +ALTER TABLE `parts` + ADD CONSTRAINT `parts_id_footprint_fk` FOREIGN KEY (`id_footprint`) REFERENCES `footprints` (`id`), + ADD CONSTRAINT `parts_id_manufacturer_fk` FOREIGN KEY (`id_manufacturer`) REFERENCES `manufacturers` (`id`), + ADD CONSTRAINT `parts_id_storelocation_fk` FOREIGN KEY (`id_storelocation`) REFERENCES `storelocations` (`id`), + ADD CONSTRAINT `parts_order_orderdetails_id_fk` FOREIGN KEY (`order_orderdetails_id`) REFERENCES `orderdetails` (`id`); + +-- +-- Constraints der Tabelle `storelocations` +-- +ALTER TABLE `storelocations` + ADD CONSTRAINT `storelocations_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `storelocations` (`id`); + +-- +-- Constraints der Tabelle `suppliers` +-- +ALTER TABLE `suppliers` + ADD CONSTRAINT `suppliers_parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `suppliers` (`id`); +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/.github/assets/legacy_import/test_legacy_import.sh b/.github/assets/legacy_import/test_legacy_import.sh new file mode 100644 index 00000000..54637c4c --- /dev/null +++ b/.github/assets/legacy_import/test_legacy_import.sh @@ -0,0 +1,54 @@ +# +# This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). +# +# Copyright (C) 2019 - 2023 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 . +# + +#!/bin/bash + +# This script is used to test the legacy import of Part-DB + +SQL_FILES_TO_TEST=("db_minimal.sql" "db_jbtronics.sql") + +DB_NAME="legacy_db_test" +DB_USER="root" +DB_PASSWORD="root" + +# Iterate over all given SQL files and import them into the mysql database with the given name, drop the database if it already exists before +for SQL_FILE in "${SQL_FILES_TO_TEST[@]}" +do + echo "Testing for $SQL_FILE" + mysql -u $DB_USER --password=$DB_PASSWORD -e "DROP DATABASE IF EXISTS $DB_NAME; CREATE DATABASE $DB_NAME;" + # If the last command failed, exit the script + if [ $? -ne 0 ]; then + echo "Failed to create database $DB_NAME" + exit 1 + fi + # Import the SQL file into the database. The file pathes are relative to the current script location + mysql -u $DB_USER --password=$DB_PASSWORD $DB_NAME < .github/assets/legacy_import/$SQL_FILE + # If the last command failed, exit the script + if [ $? -ne 0 ]; then + echo "Failed to import $SQL_FILE into database $DB_NAME" + exit 1 + fi + # Run doctrine migrations, this will migrate the database to the current version. This process should not fail + php bin/console doctrine:migrations:migrate -n + # If the last command failed, exit the script + if [ $? -ne 0 ]; then + echo "Failed to migrate database $DB_NAME" + exit 1 + fi +done \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..90e05c40 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/assets_artifact_build.yml b/.github/workflows/assets_artifact_build.yml index 0c3c1568..0bbfe432 100644 --- a/.github/workflows/assets_artifact_build.yml +++ b/.github/workflows/assets_artifact_build.yml @@ -19,14 +19,22 @@ jobs: APP_ENV: prod steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + ini-values: xdebug.max_nesting_level=1000 + extensions: mbstring, intl, gd, xsl, gmp, bcmath, :php-psr - name: Get Composer Cache Directory id: composer-cache run: | echo "::set-output name=dir::$(composer config cache-files-dir)" - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} @@ -40,7 +48,7 @@ jobs: id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} @@ -49,7 +57,7 @@ jobs: ${{ runner.os }}-yarn- - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18' @@ -69,13 +77,13 @@ jobs: run: zip -r /tmp/partdb_assets.zip public/build/ vendor/ - name: Upload assets artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Only dependencies and built assets path: /tmp/partdb_assets.zip - name: Upload full artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Full Part-DB including dependencies and built assets path: /tmp/partdb_with_assets.zip diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 757bd044..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [master, ] - pull_request: - # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 14 * * 3' - -jobs: - analyse: - name: Analyse - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - # Override language selection by uncommenting this and choosing your languages - # with: - # languages: go, javascript, csharp, python, cpp, java - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index 4f9b3d44..64287d83 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -10,9 +10,6 @@ on: tags: - 'v*.*.*' - 'v*.*.*-**' - pull_request: - branches: - - 'master' jobs: docker: @@ -20,11 +17,11 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Docker meta id: docker_meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: # list of Docker images to use as base name for tags images: | @@ -52,23 +49,23 @@ jobs: - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 with: platforms: 'arm64,arm' - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7 diff --git a/.github/workflows/docker_frankenphp.yml b/.github/workflows/docker_frankenphp.yml new file mode 100644 index 00000000..d8cd0695 --- /dev/null +++ b/.github/workflows/docker_frankenphp.yml @@ -0,0 +1,77 @@ +name: Docker Image Build (FrankenPHP) + +on: + #schedule: + # - cron: '0 10 * * *' # everyday at 10am + push: + branches: + - '**' + - '!l10n_**' + tags: + - 'v*.*.*' + - 'v*.*.*-**' + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Docker meta + id: docker_meta + uses: docker/metadata-action@v5 + with: + # list of Docker images to use as base name for tags + images: | + partdborg/part-db + # Mark the image build from master as latest (as we dont have really releases yet) + tags: | + type=edge,branch=master + type=ref,event=branch, + type=ref,event=tag, + type=schedule + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=ref,event=branch + type=ref,event=pr + labels: | + org.opencontainers.image.source=${{ github.event.repository.clone_url }} + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.title=Part-DB + org.opencontainers.image.description=Part-DB is a web application for managing electronic components and your inventory. + org.opencontainers.image.url=https://github.com/Part-DB/Part-DB-server + org.opencontainers.image.source=https://github.com/Part-DB/Part-DB-server + org.opencontainers.image.authors=Jan Böhmer + org.opencontainers.licenses=AGPL-3.0-or-later + + - + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: 'arm64,arm' + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - + name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile-frankenphp + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 78df6a1d..20150b28 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -16,14 +16,22 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + ini-values: xdebug.max_nesting_level=1000 + extensions: mbstring, intl, gd, xsl, gmp, bcmath, :php-psr - name: Get Composer Cache Directory id: composer-cache run: | echo "::set-output name=dir::$(composer config cache-files-dir)" - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} @@ -38,18 +46,20 @@ jobs: - name: Lint twig templates run: ./bin/console lint:twig templates --env=prod - - - name: Lint translations - run: ./bin/console lint:xliff translations + + # This causes problems with emtpy language files + #- name: Lint translations + # run: ./bin/console lint:xliff translations - name: Check dependencies for security - uses: symfonycorp/security-checker-action@v3 + uses: symfonycorp/security-checker-action@v5 - name: Check doctrine mapping run: ./bin/console doctrine:schema:validate --skip-sync -vvv --no-interaction - + + # Use the -d option to raise the max nesting level - name: Generate dev container - run: ./bin/console cache:clear --env dev + run: php -d xdebug.max_nesting_level=1000 ./bin/console cache:clear --env dev - name: Run PHPstan run: composer phpstan diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5cb4f454..8e6ea54c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,13 +13,13 @@ on: jobs: phpunit: name: PHPUnit and coverage Test (PHP ${{ matrix.php-versions }}, ${{ matrix.db-type }}) - # Ubuntu 20.04 ships MySQL 8.0 which causes problems with login, so we just use ubuntu 18.04 for now... runs-on: ubuntu-22.04 strategy: + fail-fast: false matrix: - php-versions: [ '7.4', '8.0', '8.1', '8.2' ] - db-type: [ 'mysql', 'sqlite' ] + php-versions: [ '8.1', '8.2', '8.3', '8.4' ] + db-type: [ 'mysql', 'sqlite', 'postgres' ] env: # Note that we set DATABASE URL later based on our db-type matrix value @@ -27,30 +27,45 @@ jobs: SYMFONY_DEPRECATIONS_HELPER: disabled PHP_VERSION: ${{ matrix.php-versions }} DB_TYPE: ${{ matrix.db-type }} + CHECK_FOR_UPDATES: false # Disable update checks for tests steps: - name: Set Database env for MySQL - run: echo "DATABASE_URL=mysql://root:root@127.0.0.1:3306/test" >> $GITHUB_ENV + run: echo "DATABASE_URL=mysql://root:root@127.0.0.1:3306/partdb?serverVersion=8.0.35" >> $GITHUB_ENV if: matrix.db-type == 'mysql' - name: Set Database env for SQLite run: echo "DATABASE_URL="sqlite:///%kernel.project_dir%/var/app_test.db"" >> $GITHUB_ENV if: matrix.db-type == 'sqlite' + - name: Set Database env for PostgreSQL + run: echo "DATABASE_URL=postgresql://postgres:postgres @127.0.0.1:5432/partdb?serverVersion=14&charset=utf8" >> $GITHUB_ENV + if: matrix.db-type == 'postgres' + - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} coverage: pcov - extensions: mbstring, intl, gd, xsl, gmp, bcmath + ini-values: xdebug.max_nesting_level=1000 + extensions: mbstring, intl, gd, xsl, gmp, bcmath, :php-psr - name: Start MySQL run: sudo systemctl start mysql.service + if: matrix.db-type == 'mysql' - #- name: Setup MySQL + # Replace the scram-sha-256 with trust for host connections, to avoid password authentication + - name: Start PostgreSQL + run: | + sudo sed -i 's/^\(host.*all.*all.*\)scram-sha-256/\1trust/' /etc/postgresql/14/main/pg_hba.conf + sudo systemctl start postgresql.service + sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';" + if: matrix.db-type == 'postgres' + + #- name: Setup MySQL # uses: mirromutth/mysql-action@v1.1 # with: # mysql version: 5.7 @@ -63,7 +78,7 @@ jobs: id: composer-cache run: | echo "::set-output name=dir::$(composer config cache-files-dir)" - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} @@ -74,7 +89,7 @@ jobs: id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} @@ -86,7 +101,7 @@ jobs: run: composer install --prefer-dist --no-progress - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18' @@ -98,26 +113,24 @@ jobs: - name: Create DB run: php bin/console --env test doctrine:database:create --if-not-exists -n - if: matrix.db-type == 'mysql' - - # Checkinf for existance is not supported for sqlite, so do it without it - - name: Create DB - run: php bin/console --env test doctrine:database:create -n - if: matrix.db-type == 'sqlite' + if: matrix.db-type == 'mysql' || matrix.db-type == 'postgres' - name: Do migrations run: php bin/console --env test doctrine:migrations:migrate -n - + + # Use our own custom fixtures loading command to circumvent some problems with reset the autoincrement values - name: Load fixtures - run: php bin/console --env test doctrine:fixtures:load -n --purger reset_autoincrement_purger + run: php bin/console --env test partdb:fixtures:load -n - name: Run PHPunit and generate coverage run: ./bin/phpunit --coverage-clover=coverage.xml - name: Upload coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 with: env_vars: PHP_VERSION,DB_TYPE + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true - name: Test app:clean-attachments run: php bin/console partdb:attachments:clean-unused -n @@ -130,4 +143,12 @@ jobs: - name: Test check-requirements command run: php bin/console partdb:check-requirements -n - + + - name: Test partdb:backup command + run: php bin/console partdb:backup -n --full /tmp/test_backup.zip + + - name: Test legacy Part-DB import + run: bash .github/assets/legacy_import/test_legacy_import.sh + if: matrix.db-type == 'mysql' && matrix.php-versions == '8.2' + env: + DATABASE_URL: mysql://root:root@localhost:3306/legacy_db diff --git a/.gitignore b/.gitignore index 1d28a771..b726f64c 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,7 @@ yarn-error.log /phpunit.xml .phpunit.result.cache ###< phpunit/phpunit ### + +###> phpstan/phpstan ### +phpstan.neon +###< phpstan/phpstan ### diff --git a/Dockerfile b/Dockerfile index 720ca792..0f909f16 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,64 @@ -FROM debian:bullseye-slim +ARG BASE_IMAGE=debian:bookworm-slim +ARG PHP_VERSION=8.3 + +FROM ${BASE_IMAGE} AS base +ARG PHP_VERSION # Install needed dependencies for PHP build #RUN apt-get update && apt-get install -y pkg-config curl libcurl4-openssl-dev libicu-dev \ # libpng-dev libjpeg-dev libfreetype6-dev gnupg zip libzip-dev libjpeg62-turbo-dev libonig-dev libxslt-dev libwebp-dev vim \ # && apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/* -RUN apt-get update && apt-get -y install apt-transport-https lsb-release ca-certificates curl zip \ +RUN apt-get update && apt-get -y install \ + apt-transport-https \ + lsb-release \ + ca-certificates \ + curl \ + zip \ + mariadb-client \ + postgresql-client \ && curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg \ && sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list' \ && apt-get update && apt-get upgrade -y \ - && apt-get install -y apache2 php8.1 libapache2-mod-php8.1 php8.1-opcache php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-bcmath php8.1-intl php8.1-zip php8.1-xsl php8.1-sqlite3 php8.1-mysql gpg \ - && apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/*; - -ENV APACHE_CONFDIR /etc/apache2 -ENV APACHE_ENVVARS $APACHE_CONFDIR/envvars - + && apt-get install -y \ + apache2 \ + php${PHP_VERSION} \ + php${PHP_VERSION}-fpm \ + php${PHP_VERSION}-opcache \ + php${PHP_VERSION}-curl \ + php${PHP_VERSION}-gd \ + php${PHP_VERSION}-mbstring \ + php${PHP_VERSION}-xml \ + php${PHP_VERSION}-bcmath \ + php${PHP_VERSION}-intl \ + php${PHP_VERSION}-zip \ + php${PHP_VERSION}-xsl \ + php${PHP_VERSION}-sqlite3 \ + php${PHP_VERSION}-mysql \ + php${PHP_VERSION}-pgsql \ + gpg \ + sudo \ + && apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/* \ # Create workdir and set permissions if directory does not exists -RUN mkdir -p /var/www/html && chown -R www-data:www-data /var/www/html + && mkdir -p /var/www/html \ + && chown -R www-data:www-data /var/www/html \ +# delete the "index.html" that installing Apache drops in here + && rm -rvf /var/www/html/* + +# Install node and yarn +RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ + echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ + curl -sL https://deb.nodesource.com/setup_20.x | bash - && \ + apt-get update && apt-get install -y \ + nodejs \ + yarn \ + && apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/* + +# Install composer +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +ENV APACHE_CONFDIR=/etc/apache2 +ENV APACHE_ENVVARS=$APACHE_CONFDIR/envvars # Configure apache 2 (taken from https://github.com/docker-library/php/blob/master/8.2/bullseye/apache/Dockerfile) # generically convert lines like @@ -27,8 +69,6 @@ RUN mkdir -p /var/www/html && chown -R www-data:www-data /var/www/html # so that they can be overridden at runtime ("-e APACHE_RUN_USER=...") RUN sed -ri 's/^export ([^=]+)=(.*)$/: ${\1:=\2}\nexport \1/' "$APACHE_ENVVARS"; \ set -eux; . "$APACHE_ENVVARS"; \ - # delete the "index.html" that installing Apache drops in here - rm -rvf /var/www/html/*; \ \ # logs should go to stdout / stderr ln -sfT /dev/stderr "$APACHE_LOG_DIR/error.log"; \ @@ -36,66 +76,87 @@ RUN sed -ri 's/^export ([^=]+)=(.*)$/: ${\1:=\2}\nexport \1/' "$APACHE_ENVVARS" ln -sfT /dev/stdout "$APACHE_LOG_DIR/other_vhosts_access.log"; \ chown -R --no-dereference "$APACHE_RUN_USER:$APACHE_RUN_GROUP" "$APACHE_LOG_DIR"; -# Enable mpm_prefork -RUN a2dismod mpm_event && a2enmod mpm_prefork +# --- + +FROM scratch AS apache-config +ARG PHP_VERSION +# Configure php-fpm to log to stdout of the container (stdout of PID 1) +# We have to use /proc/1/fd/1 because /dev/stdout or /proc/self/fd/1 does not point to the container stdout (because we use apache as entrypoint) +# We also disable the clear_env option to allow the use of environment variables in php-fpm +COPY <'; \ - echo '\tSetHandler application/x-httpd-php'; \ - echo ''; \ - echo; \ - echo 'DirectoryIndex disabled'; \ - echo 'DirectoryIndex index.php index.html'; \ - echo; \ - echo ''; \ - echo '\tOptions -Indexes'; \ - echo '\tAllowOverride All'; \ - echo ''; \ - } | tee "$APACHE_CONFDIR/conf-available/docker-php.conf" \ - && a2enconf docker-php +COPY < + SetHandler application/x-httpd-php + + +DirectoryIndex disabled +DirectoryIndex index.php index.html + + + Options -Indexes + AllowOverride All + +EOF # Enable opcache and configure it recommended for symfony (see https://symfony.com/doc/current/performance.html) -RUN \ - { \ - echo 'opcache.memory_consumption=256'; \ - echo 'opcache.max_accelerated_files=20000'; \ - echo 'opcache.validate_timestamp=0'; \ - # Configure Realpath cache for performance - echo 'realpath_cache_size=4096K'; \ - echo 'realpath_cache_ttl=600'; \ - } > /etc/php/8.1/apache2/conf.d/symfony-recommended.ini +COPY < /etc/php/8.1/apache2/conf.d/partdb.ini +# Increase upload limit and enable preloading +COPY < ## Features -* Inventory management of your electronic parts. Each part can be assigned to a category, footprint, manufacturer -and multiple store locations and price information. Parts can be grouped using tags. You can associate various files like datasheets or pictures with the parts. -* Multi-Language support (currently German, English, Russian, Japanese and French (experimental)) + +* Inventory management of your electronic parts. Each part can be assigned to a category, footprint, manufacturer, + and multiple store locations and price information. Parts can be grouped using tags. You can associate various files + like datasheets or pictures with the parts. +* Multi-language support (currently German, English, Russian, Japanese, French, Czech, Danish, and Chinese) * Barcodes/Labels generator for parts and storage locations, scan barcodes via webcam using the builtin barcode scanner -* User system with groups and detailed (fine granular) permissions. -Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups. Password reset via email can be setuped. -* Import/Export system (partial working) -* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build this project and directly withdraw all components needed from DB -* Event log: Track what changes happens to your inventory, track which user does what. Revert your parts to older versions. -* Responsive design: You can use Part-DB on your PC, your tablet and your smartphone using the same interface. -* MySQL and SQLite supported as database backends +* User system with groups and detailed (fine granular) permissions. + Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups. + Password reset via email can be set up. +* Optional support for single sign-on (SSO) via SAML (using an intermediate service + like [Keycloak](https://www.keycloak.org/) you can connect Part-DB to an existing LDAP or Active Directory server) +* Import/Export system for parts and data structure. BOM import for projects from KiCAD is supported. +* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build + this project and directly withdraw all components needed from DB +* Event log: Track what changes happen to your inventory, track which user does what. Revert your parts to older + versions. +* Responsive design: You can use Part-DB on your PC, your tablet, and your smartphone using the same interface. +* MySQL, SQLite and PostgreSQL are supported as database backends * Support for rich text descriptions and comments in parts * Support for multiple currencies and automatic update of exchange rates supported * Powerful search and filter function, including parametric search (search for parts according to some specifications) * Automatic thumbnail generation for pictures +* Use cloud providers (like Octopart, Digikey, Farnell, LCSC or TME) to automatically get part information, datasheets, and + prices for parts +* API to access Part-DB from other applications/scripts +* [Integration with KiCad](https://docs.part-db.de/usage/eda_integration.html): Use Part-DB as the central datasource for your + KiCad and see available parts from Part-DB directly inside KiCad. - -With these features Part-DB is useful to hobbyists, who want to keep track of their private electronic parts inventory, -or makerspaces, where many users have should have (controlled) access to the shared inventory. +With these features, Part-DB is useful to hobbyists, who want to keep track of their private electronic parts inventory, +or maker spaces, where many users should have (controlled) access to the shared inventory. Part-DB is also used by small companies and universities for managing their inventory. ## Requirements - * A **web server** (like Apache2 or nginx) that is capable of running [Symfony 5](https://symfony.com/doc/current/reference/requirements.html), - this includes a minimum PHP version of **PHP 7.4** - * A **MySQL** (at least 5.7) /**MariaDB** (at least 10.2.2) database server if you do not want to use SQLite. - * Shell access to your server is highly suggested! - * For building the client side assets **yarn** and **nodejs** is needed. - + +* A **web server** (like Apache2 or nginx) that is capable of + running [Symfony 6](https://symfony.com/doc/current/reference/requirements.html), + this includes a minimum PHP version of **PHP 8.1** +* A **MySQL** (at least 5.7) /**MariaDB** (at least 10.4) database server, or **PostgreSQL** 10+ if you do not want to use SQLite. +* Shell access to your server is highly recommended! +* For building the client-side assets **yarn** and **nodejs** (>= 18.0) is needed. + ## Installation -If you want to upgrade your legacy (< 1.0.0) version of Part-DB to this version, please read [this](https://docs.part-db.de/upgrade_legacy.html) first. -*Hint:* A docker image is available under [jbtronics/part-db1](https://hub.docker.com/r/jbtronics/part-db1). How to set up Part-DB via docker is described [here](https://docs.part-db.de/installation/installation_docker.html). +If you want to upgrade your legacy (< 1.0.0) version of Part-DB to this version, please +read [this](https://docs.part-db.de/upgrade_legacy.html) first. -**Below you find some very rough outline of the installation process, see [here](https://docs.part-db.de/installation/) for a detailed guide how to install Part-DB.** +*Hint:* A docker image is available under [jbtronics/part-db1](https://hub.docker.com/r/jbtronics/part-db1). How to set +up Part-DB via docker is described [here](https://docs.part-db.de/installation/installation_docker.html). + +**Below you find a very rough outline of the installation process, see [here](https://docs.part-db.de/installation/) +for a detailed guide on how to install Part-DB.** 1. Copy or clone this repository into a folder on your server. -2. Configure your webserver to serve from the `public/` folder. See [here](https://symfony.com/doc/current/setup/web_server_configuration.html) -for additional information. +2. Configure your webserver to serve from the `public/` folder. + See [here](https://symfony.com/doc/current/setup/web_server_configuration.html) + for additional information. 3. Copy the global config file `cp .env .env.local` and edit `.env.local`: * Change the line `APP_ENV=dev` to `APP_ENV=prod` - * If you do not want to use SQLite, change the value of `DATABASE_URL=` to your needs (see [here](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url)) for the format. - In bigger instances with concurrent accesses, MySQL is more performant. This can not be changed easily later, so choose wisely. + * If you do not want to use SQLite, change the value of `DATABASE_URL=` to your needs ( + see [here](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url)) + for the format. + In bigger instances with concurrent accesses, MySQL is more performant. This can not be changed easily later, so + choose wisely. 4. Install composer dependencies and generate autoload files: `composer install -o --no-dev` -5. If you have put Part-DB into a sub-directory on your server (like `part-db/`), you have to edit the file -`webpack.config.js` and uncomment the lines (remove the `//` before the lines) `.setPublicPath('/part-db/build')` (line 43) and - `.setManifestKeyPrefix('build/')` (line 44). You have to replace `/part-db` with your own path on line 44. -6. Install client side dependencies and build it: `yarn install` and `yarn build` -7. _Optional_ (speeds up first load): Warmup cache: `php bin/console cache:warmup` -8. Upgrade database to new scheme (or create it, when it was empty): `php bin/console doctrine:migrations:migrate` and follow the instructions given. During the process the password for the admin is user is shown. Copy it. **Caution**: This steps tamper with your database and could potentially destroy it. So make sure to make a backup of your database. -9. You can configure Part-DB via `config/parameters.yaml`. You should check if settings match your expectations, after you installed/upgraded Part-DB. Check if `partdb.default_currency` matches your mainly used currency (this can not be changed after creating price informations). - Run `php bin/console cache:clear` when you changed something. -10. Access Part-DB in your browser (under the URL you put it) and login with user *admin*. Password is the one outputted during DB setup. - If you can not remember the password, set a new one with `php bin/console app:set-password admin`. You can create new users with the admin user and start using Part-DB. +5. Install client side dependencies and build it: `yarn install` and `yarn build` +6. _Optional_ (speeds up first load): Warmup cache: `php bin/console cache:warmup` +7. Upgrade database to new scheme (or create it, when it was empty): `php bin/console doctrine:migrations:migrate` and + follow the instructions given. During the process the password for the admin is user is shown. Copy it. **Caution**: + These steps tamper with your database and could potentially destroy it. So make sure to make a backup of your + database. +8. You can configure Part-DB via `config/parameters.yaml`. You should check if settings match your expectations after + you installed/upgraded Part-DB. Check if `partdb.default_currency` matches your mainly used currency (this can not be + changed after creating price information). + Run `php bin/console cache:clear` when you change something. +9. Access Part-DB in your browser (under the URL you put it) and log in with user *admin*. Password is the one outputted + during DB setup. + If you can not remember the password, set a new one with `php bin/console app:set-password admin`. You can create + new users with the admin user and start using Part-DB. When you want to upgrade to a newer version, then just copy the new files into the folder and repeat the steps 4. to 7. -Normally a random password is generated when the admin user is created during inital database creation, -however you can set the inital admin password, by setting the `INITIAL_ADMIN_PW` env var. +Normally a random password is generated when the admin user is created during initial database creation, +however, you can set the initial admin password, by setting the `INITIAL_ADMIN_PW` env var. -You can configure Part-DB to your needs by changing environment variables in the `.env.local` file. +You can configure Part-DB to your needs by changing environment variables in the `.env.local` file. See [here](https://docs.part-db.de/configuration.html) for more information. ### Reverse proxy -If you are using a reverse proxy, you have to ensure that the proxies sets the `X-Forwarded-*` headers correctly, or you will get HTTP/HTTPS mixup and wrong hostnames. -If the reverse proxy is on a different server (or it cannot access Part-DB via localhost) you have to set the `TRUSTED_PROXIES` env variable to match your reverse proxies IP-address (or IP block). You can do this in your `.env.local` or (when using docker) in your `docker-compose.yml` file. + +If you are using a reverse proxy, you have to ensure that the proxies set the `X-Forwarded-*` headers correctly, or you +will get HTTP/HTTPS mixup and wrong hostnames. +If the reverse proxy is on a different server (or it cannot access Part-DB via localhost) you have to set +the `TRUSTED_PROXIES` env variable to match your reverse proxy's IP address (or IP block). You can do this in +your `.env.local` or (when using docker) in your `docker-compose.yml` file. ## Donate for development + If you want to donate to the Part-DB developer, see the sponsor button in the top bar (next to the repo name). -There you will find various methods to support development on a monthly or a one time base. +There you will find various methods to support development on a monthly or a one-time base. ## Built with + * [Symfony 5](https://symfony.com/): The main framework used for the serverside PHP * [Bootstrap 5](https://getbootstrap.com/) and [Bootswatch](https://bootswatch.com/): Used as website theme * [Fontawesome](https://fontawesome.com/): Used as icon set -* [Hotwire Stimulus](https://stimulus.hotwired.dev/) and [Hotwire Turbo](https://turbo.hotwired.dev/): Frontend Javascript +* [Hotwire Stimulus](https://stimulus.hotwired.dev/) and [Hotwire Turbo](https://turbo.hotwired.dev/): Frontend + Javascript ## Authors -* **Jan Böhmer** - *Inital work* - [Github](https://github.com/jbtronics/) -See also the list of [contributors](https://github.com/Part-DB/Part-DB-server/graphs/contributors) who participated in this project. +* **Jan Böhmer** - *Initial work* - [GitHub](https://github.com/jbtronics/) + +See also the list of [contributors](https://github.com/Part-DB/Part-DB-server/graphs/contributors) who participated in +this project. Based on the original Part-DB by Christoph Lechner and K. Jacobs ## License + Part-DB is licensed under the GNU Affero General Public License v3.0 (or at your opinion any later). This mostly means that you can use Part-DB for whatever you want (even use it commercially) as long as you publish the source code for every change you make under the AGPL, too. diff --git a/SECURITY.md b/SECURITY.md index 02775f95..a9234a01 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -9,4 +9,4 @@ fixed before the next release. However, if you find a security vulnerability in ## Reporting a Vulnerability -If you find a security vulnerability, contact the maintainer directly (Email: security@part-db.de). +If you find a security vulnerability, report a vulnerability in the [security section of GitHub](https://github.com/Part-DB/Part-DB-server/security/advisories) or contact the maintainer directly (Email: security@part-db.de) diff --git a/VERSION b/VERSION index afaf360d..511a76e6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.17.1 diff --git a/assets/bootstrap.js b/assets/bootstrap.js index 58308a6b..c26293e2 100644 --- a/assets/bootstrap.js +++ b/assets/bootstrap.js @@ -4,8 +4,7 @@ import { startStimulusApp } from '@symfony/stimulus-bridge'; export const app = startStimulusApp(require.context( '@symfony/stimulus-bridge/lazy-controller-loader!./controllers', true, - /\.(j|t)sx?$/ + /\.[jt]sx?$/ )); - // register any custom, 3rd party controllers here // app.register('some_controller_name', SomeImportedController); diff --git a/assets/ckeditor/html_label.js b/assets/ckeditor/html_label.js index b5ca5c3e..9040f3c7 100644 --- a/assets/ckeditor/html_label.js +++ b/assets/ckeditor/html_label.js @@ -181,7 +181,8 @@ Editor.defaultConfig = { 'DejaVu Serif, serif', 'Helvetica, Arial, sans-serif', 'Times New Roman, Times, serif', - 'Courier New, Courier, monospace' + 'Courier New, Courier, monospace', + 'Unifont, monospace', ], supportAllValues: true }, diff --git a/assets/ckeditor/plugins/PartDBLabel/PartDBLabelUI.js b/assets/ckeditor/plugins/PartDBLabel/PartDBLabelUI.js index 5dddbb01..37e1dcbe 100644 --- a/assets/ckeditor/plugins/PartDBLabel/PartDBLabelUI.js +++ b/assets/ckeditor/plugins/PartDBLabel/PartDBLabelUI.js @@ -76,6 +76,7 @@ const PLACEHOLDERS = [ ['[[FOOTPRINT_FULL]]', 'Footprint (Full path)'], ['[[MASS]]', 'Mass'], ['[[MPN]]', 'Manufacturer Product Number (MPN)'], + ['[[IPN]]', 'Internal Part Number (IPN)'], ['[[TAGS]]', 'Tags'], ['[[M_STATUS]]', 'Manufacturing status'], ['[[DESCRIPTION]]', 'Description'], @@ -84,6 +85,9 @@ const PLACEHOLDERS = [ ['[[COMMENT_T]]', 'Comment (plain text)'], ['[[LAST_MODIFIED]]', 'Last modified datetime'], ['[[CREATION_DATE]]', 'Creation datetime'], + ['[[IPN_BARCODE_QR]]', 'IPN as QR code'], + ['[[IPN_BARCODE_C128]]', 'IPN as Code 128 barcode'], + ['[[IPN_BARCODE_C39]]', 'IPN as Code 39 barcode'], ] }, { @@ -96,6 +100,8 @@ const PLACEHOLDERS = [ ['[[AMOUNT]]', 'Lot amount'], ['[[LOCATION]]', 'Storage location'], ['[[LOCATION_FULL]]', 'Storage location (Full path)'], + ['[[OWNER]]', 'Full name of the lot owner'], + ['[[OWNER_USERNAME]]', 'Username of the lot owner'], ] }, { @@ -110,6 +116,8 @@ const PLACEHOLDERS = [ ['[[COMMENT_T]]', 'Comment (plain text)'], ['[[LAST_MODIFIED]]', 'Last modified datetime'], ['[[CREATION_DATE]]', 'Creation datetime'], + ['[[OWNER]]', 'Full name of the location owner'], + ['[[OWNER_USERNAME]]', 'Username of the location owner'], ] }, { @@ -120,6 +128,8 @@ const PLACEHOLDERS = [ ['[[BARCODE_QR]]', 'QR code linking to this element'], ['[[BARCODE_C128]]', 'Code 128 barcode linking to this element'], ['[[BARCODE_C39]]', 'Code 39 barcode linking to this element'], + ['[[BARCODE_C93]]', 'Code 93 barcode linking to this element'], + ['[[BARCODE_DATAMATRIX]]', 'Datamatrix code linking to this element'], ] }, { diff --git a/assets/ckeditor/plugins/PartDBLabel/lang/de.js b/assets/ckeditor/plugins/PartDBLabel/lang/de.js index d49a26ed..748b1607 100644 --- a/assets/ckeditor/plugins/PartDBLabel/lang/de.js +++ b/assets/ckeditor/plugins/PartDBLabel/lang/de.js @@ -39,6 +39,7 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, { 'Footprint (Full path)': 'Footprint (Vollständiger Pfad)', 'Mass': 'Gewicht', 'Manufacturer Product Number (MPN)': 'Hersteller Produktnummer (MPN)', + 'Internal Part Number (IPN)': 'Internal Part Number (IPN)', 'Tags': 'Tags', 'Manufacturing status': 'Herstellungsstatus', 'Description': 'Beschreibung', @@ -47,6 +48,9 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, { 'Comment (plain text)': 'Kommentar (Nur-Text)', 'Last modified datetime': 'Zuletzt geändert', 'Creation datetime': 'Erstellt', + 'IPN as QR code': 'IPN als QR Code', + 'IPN as Code 128 barcode': 'IPN als Code 128 Barcode', + 'IPN as Code 39 barcode': 'IPN als Code 39 Barcode', 'Lot ID': 'Lot ID', 'Lot name': 'Lot Name', @@ -55,6 +59,8 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, { 'Lot amount': 'Lot Menge', 'Storage location': 'Lagerort', 'Storage location (Full path)': 'Lagerort (Vollständiger Pfad)', + 'Full name of the lot owner': 'Name des Besitzers des Lots', + 'Username of the lot owner': 'Benutzername des Besitzers des Lots', 'Barcodes': 'Barcodes', @@ -63,12 +69,16 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, { 'QR code linking to this element': 'QR Code verknüpft mit diesem Element', 'Code 128 barcode linking to this element': 'Code 128 Barcode verknüpft mit diesem Element', 'Code 39 barcode linking to this element': 'Code 39 Barcode verknüpft mit diesem Element', + 'Code 93 barcode linking to this element': 'Code 93 Barcode verknüpft mit diesem Element', + 'Datamatrix code linking to this element': 'Datamatrix Code verknüpft mit diesem Element', 'Location ID': 'Lagerort ID', 'Name': 'Name', 'Full path': 'Vollständiger Pfad', 'Parent name': 'Name des Übergeordneten Elements', 'Parent full path': 'Ganzer Pfad des Übergeordneten Elements', + 'Full name of the location owner': 'Name des Besitzers des Lagerorts', + 'Username of the location owner': 'Benutzername des Besitzers des Lagerorts', 'Username': 'Benutzername', 'Username (including name)': 'Benutzername (inklusive Name)', diff --git a/assets/ckeditor/plugins/special_characters_emoji.js b/assets/ckeditor/plugins/special_characters_emoji.js index 9877e6f6..1d4ec000 100644 --- a/assets/ckeditor/plugins/special_characters_emoji.js +++ b/assets/ckeditor/plugins/special_characters_emoji.js @@ -30,9 +30,73 @@ export default class SpecialCharactersEmoji extends Plugin { const editor = this.editor; const specialCharsPlugin = editor.plugins.get('SpecialCharacters'); + //Add greek characters to special characters + specialCharsPlugin.addItems('Greek', this.getGreek()); + + //Add Emojis to special characters specialCharsPlugin.addItems('Emoji', this.getEmojis()); } + getGreek() { + return [ + { title: 'Alpha', character: 'Α' }, + { title: 'Beta', character: 'Β' }, + { title: 'Gamma', character: 'Γ' }, + { title: 'Delta', character: 'Δ' }, + { title: 'Epsilon', character: 'Ε' }, + { title: 'Zeta', character: 'Ζ' }, + { title: 'Eta', character: 'Η' }, + { title: 'Theta', character: 'Θ' }, + { title: 'Iota', character: 'Ι' }, + { title: 'Kappa', character: 'Κ' }, + { title: 'Lambda', character: 'Λ' }, + { title: 'Mu', character: 'Μ' }, + { title: 'Nu', character: 'Ν' }, + { title: 'Xi', character: 'Ξ' }, + { title: 'Omicron', character: 'Ο' }, + { title: 'Pi', character: 'Π' }, + { title: 'Rho', character: 'Ρ' }, + { title: 'Sigma', character: 'Σ' }, + { title: 'Tau', character: 'Τ' }, + { title: 'Upsilon', character: 'Υ' }, + { title: 'Phi', character: 'Φ' }, + { title: 'Chi', character: 'Χ' }, + { title: 'Psi', character: 'Ψ' }, + { title: 'Omega', character: 'Ω' }, + { title: 'alpha', character: 'α' }, + { title: 'beta', character: 'β' }, + { title: 'gamma', character: 'γ' }, + { title: 'delta', character: 'δ' }, + { title: 'epsilon', character: 'ε' }, + { title: 'zeta', character: 'ζ' }, + { title: 'eta', character: 'η' }, + { title: 'theta', character: 'θ' }, + { title: 'alternate theta', character: 'ϑ' }, + { title: 'iota', character: 'ι' }, + { title: 'kappa', character: 'κ' }, + { title: 'lambda', character: 'λ' }, + { title: 'mu', character: 'μ' }, + { title: 'nu', character: 'ν' }, + { title: 'xi', character: 'ξ' }, + { title: 'omicron', character: 'ο' }, + { title: 'pi', character: 'π' }, + { title: 'rho', character: 'ρ' }, + { title: 'sigma', character: 'σ' }, + { title: 'tau', character: 'τ' }, + { title: 'upsilon', character: 'υ' }, + { title: 'phi', character: 'φ' }, + { title: 'chi', character: 'χ' }, + { title: 'psi', character: 'ψ' }, + { title: 'omega', character: 'ω' }, + { title: 'digamma', character: 'Ϝ' }, + { title: 'stigma', character: 'Ϛ' }, + { title: 'heta', character: 'Ͱ' }, + { title: 'sampi', character: 'Ϡ' }, + { title: 'koppa', character: 'Ϟ' }, + { title: 'san', character: 'Ϻ' }, + ]; + } + getEmojis() { //Map our emoji data to the format the plugin expects return emoji.map(emoji => { diff --git a/assets/controllers/common/darkmode_controller.js b/assets/controllers/common/darkmode_controller.js index e7c18e67..71111166 100644 --- a/assets/controllers/common/darkmode_controller.js +++ b/assets/controllers/common/darkmode_controller.js @@ -18,43 +18,118 @@ */ import {Controller} from "@hotwired/stimulus"; -import Darkmode from "darkmode-js/src"; -import "darkmode-js" export default class extends Controller { - _darkmode; - connect() { - if (typeof window.getComputedStyle(document.body).mixBlendMode == 'undefined') { - console.warn("The browser does not support mix blend mode. Darkmode will not work."); + this.setMode(this.getMode()); + document.querySelectorAll('input[name="darkmode"]').forEach((radio) => { + radio.addEventListener('change', this._radioChanged.bind(this)); + }); + } + + /** + * Event listener for the change of radio buttons + * @private + */ + _radioChanged(event) { + const new_mode = this.getSelectedMode(); + this.setMode(new_mode); + } + + /** + * Get the current mode from the local storage + * @return {'dark', 'light', 'auto'} + */ + getMode() { + return localStorage.getItem('darkmode') ?? 'auto'; + } + + /** + * Set the mode in the local storage and apply it and change the state of the radio buttons + * @param mode + */ + setMode(mode) { + if (mode !== 'dark' && mode !== 'light' && mode !== 'auto') { + console.warn('Invalid darkmode mode: ' + mode); + mode = 'auto'; + } + + localStorage.setItem('darkmode', mode); + + this.setButtonMode(mode); + + if (mode === 'auto') { + this._setDarkmodeAuto(); + } else if (mode === 'dark') { + this._enableDarkmode(); + } else if (mode === 'light') { + this._disableDarkmode(); + } + } + + /** + * Get the selected mode via the radio buttons + * @return {'dark', 'light', 'auto'} + */ + getSelectedMode() { + return document.querySelector('input[name="darkmode"]:checked').value; + } + + /** + * Set the state of the radio buttons + * @param mode + */ + setButtonMode(mode) { + document.querySelector('input[name="darkmode"][value="' + mode + '"]').checked = true; + } + + /** + * Enable darkmode by adding the data-bs-theme="dark" to the html tag + * @private + */ + _enableDarkmode() { + //Add data-bs-theme="dark" to the html tag + document.documentElement.setAttribute('data-bs-theme', 'dark'); + } + + /** + * Disable darkmode by adding the data-bs-theme="light" to the html tag + * @private + */ + _disableDarkmode() { + //Set data-bs-theme to light + document.documentElement.setAttribute('data-bs-theme', 'light'); + } + + + /** + * Set the darkmode to auto and enable/disable it depending on the system settings, also add + * an event listener to change the darkmode if the system settings change + * @private + */ + _setDarkmodeAuto() { + if (this.getMode() !== 'auto') { return; } - try { - const darkmode = new Darkmode(); - this._darkmode = darkmode; - - //Unhide darkmode button - this._showWidget(); - - //Set the switch according to our current darkmode state - const toggler = document.getElementById("toggleDarkmode"); - toggler.checked = darkmode.isActivated(); - } - catch (e) - { - console.error(e); + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + this._enableDarkmode(); + } else { + this._disableDarkmode(); } - + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + console.log('Prefered color scheme changed to ' + event.matches ? 'dark' : 'light'); + this._setDarkmodeAuto(); + }); } - _showWidget() { - this.element.classList.remove('hidden'); - } - - toggleDarkmode() { - this._darkmode.toggle(); + /** + * Check if darkmode is activated + * @return {boolean} + */ + isDarkmodeActivated() { + return document.documentElement.getAttribute('data-bs-theme') === 'dark'; } } \ No newline at end of file diff --git a/assets/controllers/common/hide_sidebar_controller.js b/assets/controllers/common/hide_sidebar_controller.js index d4c1b5e2..4be304ff 100644 --- a/assets/controllers/common/hide_sidebar_controller.js +++ b/assets/controllers/common/hide_sidebar_controller.js @@ -88,5 +88,8 @@ export default class extends Controller { } else { this.hideSidebar(); } + + //Hide the tootip on the button + this._toggle_button.blur(); } } \ No newline at end of file diff --git a/assets/controllers/common/markdown_controller.js b/assets/controllers/common/markdown_controller.js index 08c013ca..b6ef0034 100644 --- a/assets/controllers/common/markdown_controller.js +++ b/assets/controllers/common/markdown_controller.js @@ -20,16 +20,26 @@ 'use strict'; import { Controller } from '@hotwired/stimulus'; -import { marked } from "marked"; +import { Marked } from "marked"; +import { mangle } from "marked-mangle"; +import { gfmHeadingId } from "marked-gfm-heading-id"; import DOMPurify from 'dompurify'; import "../../css/app/markdown.css"; -export default class extends Controller { +export default class MarkdownController extends Controller { + + static _marked = new Marked([ + { + gfm: true, + }, + gfmHeadingId(), + mangle(), + ]) + ; connect() { - this.configureMarked(); this.render(); //Dispatch an event that we are now finished @@ -43,7 +53,7 @@ export default class extends Controller { let raw = this.element.dataset['markdown']; //Apply purified parsed markdown - this.element.innerHTML = DOMPurify.sanitize(marked(this.unescapeHTML(raw))); + this.element.innerHTML = DOMPurify.sanitize(MarkdownController._marked.parse(this.unescapeHTML(raw))); for(let a of this.element.querySelectorAll('a')) { //Mark all links as external @@ -79,10 +89,23 @@ export default class extends Controller { /** * Configure the marked parser */ - configureMarked() + /*static newMarked() { + const marked = new Marked([ + { + gfm: true, + }, + gfmHeadingId(), + mangle(), + ]) + ; + + marked.use(mangle()); + marked.use(gfmHeadingId({ + })); + marked.setOptions({ gfm: true, }); - } + }*/ } \ No newline at end of file diff --git a/assets/controllers/elements/attachment_autocomplete_controller.js b/assets/controllers/elements/attachment_autocomplete_controller.js index fe44baee..f8bc301e 100644 --- a/assets/controllers/elements/attachment_autocomplete_controller.js +++ b/assets/controllers/elements/attachment_autocomplete_controller.js @@ -23,6 +23,12 @@ import "tom-select/dist/css/tom-select.bootstrap5.css"; import '../../css/components/tom-select_extensions.css'; import TomSelect from "tom-select"; +import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit' +import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed' + +TomSelect.define('click_to_edit', TomSelect_click_to_edit) +TomSelect.define('autoselect_typed', TomSelect_autoselect_typed) + export default class extends Controller { _tomSelect; @@ -46,6 +52,12 @@ export default class extends Controller { } return '
' + escape(data.label) + '
'; } + }, + plugins: { + 'autoselect_typed': {}, + 'click_to_edit': {}, + 'clear_button': {}, + "restore_on_backspace": {} } }; diff --git a/assets/controllers/elements/ckeditor_controller.js b/assets/controllers/elements/ckeditor_controller.js index 4de536fe..079ee2ad 100644 --- a/assets/controllers/elements/ckeditor_controller.js +++ b/assets/controllers/elements/ckeditor_controller.js @@ -53,6 +53,7 @@ export default class extends Controller { const config = { language: language, + licenseKey: "GPL", } const watchdog = new EditorWatchdog(); @@ -70,7 +71,9 @@ export default class extends Controller { editor_div.classList.add(...new_classes.split(",")); } - console.log(editor); + //This return is important! Otherwise we get mysterious errors in the console + //See: https://github.com/ckeditor/ckeditor5/issues/5897#issuecomment-628471302 + return editor; }) .catch(error => { console.error(error); diff --git a/assets/controllers/elements/collection_type_controller.js b/assets/controllers/elements/collection_type_controller.js index 488c6421..8b816f30 100644 --- a/assets/controllers/elements/collection_type_controller.js +++ b/assets/controllers/elements/collection_type_controller.js @@ -27,6 +27,7 @@ export default class extends Controller { deleteMessage: String, prototype: String, rowsToDelete: Number, //How many rows (including the current one) shall be deleted after the current row + fieldPlaceholder: String } static targets = ["target"]; @@ -60,24 +61,63 @@ export default class extends Controller { if(!prototype) { console.warn("Prototype is not set, we cannot create a new element. This is most likely due to missing permissions."); - bootbox.alert("You do not have the permsissions to create a new element. (No protoype element is set)"); + bootbox.alert("You do not have the permissions to create a new element. (No protoype element is set)"); return; } + const regexString = this.fieldPlaceholderValue || "__name__"; + const regex = new RegExp(regexString, "g"); + //Apply the index to prototype to create our element to insert - const newElementStr = this.htmlDecode(prototype.replace(/__name__/g, this.generateUID())); + const newElementStr = this.htmlDecode(prototype.replace(regex, this.generateUID())); //Insert new html after the last child element //If the table has a tbody, insert it there + //Afterwards return the newly created row if(targetTable.tBodies[0]) { targetTable.tBodies[0].insertAdjacentHTML('beforeend', newElementStr); + return targetTable.tBodies[0].lastElementChild; } else { //Otherwise just insert it targetTable.insertAdjacentHTML('beforeend', newElementStr); + return targetTable.lastElementChild; } } + /** + * This action opens a file dialog to select multiple files and then creates a new element for each file, where + * the file is assigned to the input field. + * This should only be used for attachments collection types + * @param event + */ + uploadMultipleFiles(event) { + //Open a file dialog to select multiple files + const input = document.createElement('input'); + input.type = 'file'; + input.multiple = true; + input.click(); + + input.addEventListener('change', (event) => { + //Create a element for each file + + for (let i = 0; i < input.files.length; i++) { + const file = input.files[i]; + + const newElement = this.createElement(event); + const rowInput = newElement.querySelector("input[type='file']"); + + //We can not directly assign the file to the input, so we have to create a new DataTransfer object + const dataTransfer = new DataTransfer(); + dataTransfer.items.add(file); + + rowInput.files = dataTransfer.files; + } + + }); + + } + /** * Similar to createEvent Pricedetails need some special handling to fill min amount * @param event diff --git a/assets/controllers/elements/datatables/datatables_controller.js b/assets/controllers/elements/datatables/datatables_controller.js index 2c23d855..5a50623d 100644 --- a/assets/controllers/elements/datatables/datatables_controller.js +++ b/assets/controllers/elements/datatables/datatables_controller.js @@ -24,18 +24,25 @@ import 'datatables.net-bs5/css/dataTables.bootstrap5.css' import 'datatables.net-buttons-bs5/css/buttons.bootstrap5.css' import 'datatables.net-fixedheader-bs5/css/fixedHeader.bootstrap5.css' import 'datatables.net-responsive-bs5/css/responsive.bootstrap5.css'; -import 'datatables.net-select-bs5/css/select.bootstrap5.css'; + +//Use our own styles for the select extension which fit the bootstrap theme better +//import 'datatables.net-select-bs5/css/select.bootstrap5.css'; +import '../../../css/components/datatables_select_bs5.css'; //JS import 'datatables.net-bs5'; import 'datatables.net-buttons-bs5'; import 'datatables.net-buttons/js/buttons.colVis.js'; import 'datatables.net-fixedheader-bs5'; -import 'datatables.net-select-bs5'; import 'datatables.net-colreorder-bs5'; import 'datatables.net-responsive-bs5'; import '../../../js/lib/datatables'; +//import 'datatables.net-select-bs5'; +//Use the local version containing the fix for the select extension +import '../../../js/lib/dataTables.select.mjs'; + + const EVENT_DT_LOADED = 'dt:loaded'; export default class extends Controller { @@ -65,8 +72,22 @@ export default class extends Controller { localStorage.setItem( this.getStateSaveKey(), JSON.stringify(data) ); } - stateLoadCallback(settings) { - return JSON.parse( localStorage.getItem(this.getStateSaveKey()) ); + stateLoadCallback() { + const json = localStorage.getItem(this.getStateSaveKey()); + if(json === null || json === undefined) { + return null; + } + + const data = JSON.parse(json); + + if (data) { + //Do not save the start value (current page), as we want to always start at the first page on a page reload + delete data.start; + //Reset the data length to the default value by deleting the length property + delete data.length; + } + + return data; } connect() { @@ -81,6 +102,19 @@ export default class extends Controller { //Add url info, as the one available in the history is not enough, as Turbo may have not changed it yet settings.url = this.element.dataset.dtUrl; + //Add initial_order info to the settings, so that the order on the initial page load is the one saved in the state + const saved_state = this.stateLoadCallback(); + if (saved_state !== null) { + const raw_order = saved_state.order; + + settings.initial_order = raw_order.map((order) => { + return { + column: order[0], + dir: order[1] + } + }); + } + let options = { colReorder: true, responsive: true, @@ -90,7 +124,7 @@ export default class extends Controller { }, buttons: [{ "extend": 'colvis', - 'className': 'mr-2 btn-light', + 'className': 'mr-2 btn-outline-secondary', 'columns': ':not(.no-colvis)', "text": "" }], @@ -105,7 +139,7 @@ export default class extends Controller { if(this.isSelectable()) { options.select = { style: 'multi+shift', - selector: 'td.select-checkbox' + selector: 'td.dt-select', }; } @@ -116,6 +150,28 @@ export default class extends Controller { console.error("Error initializing datatables: " + err); }); + //Fix height of the length selector + promise.then((dt) => { + + //Draw the rows to make sure the correct status text is displayed ("No matching records found" instead of "Loading...") + if (dt.data().length === 0) { + dt.rows().draw() + } + + //Find all length selectors (select with name dt_length), which are inside a label + const lengthSelectors = document.querySelectorAll('label select[name="dt_length"]'); + //And remove the surrounding label, while keeping the select with all event handlers + lengthSelectors.forEach((selector) => { + selector.parentElement.replaceWith(selector); + }); + + //Find all column visibility buttons (button with buttons-colvis class) and remove the btn-secondary class + const colVisButtons = document.querySelectorAll('button.buttons-colvis'); + colVisButtons.forEach((button) => { + button.classList.remove('btn-secondary'); + }); + }); + //Dispatch an event to let others know that the datatables has been loaded promise.then((dt) => { const event = new CustomEvent(EVENT_DT_LOADED, {bubbles: true}); @@ -175,4 +231,16 @@ export default class extends Controller { return this.element.dataset.select ?? false; } -} \ No newline at end of file + invertSelection() { + //Do nothing if the datatable is not selectable + if(!this.isSelectable()) { + return; + } + + //Invert the selected rows on the datatable + const selected_rows = this._dt.rows({selected: true}); + this._dt.rows().select(); + selected_rows.deselect(); + } + +} diff --git a/assets/controllers/elements/datatables/parts_controller.js b/assets/controllers/elements/datatables/parts_controller.js index 33362648..1fe11a20 100644 --- a/assets/controllers/elements/datatables/parts_controller.js +++ b/assets/controllers/elements/datatables/parts_controller.js @@ -107,6 +107,13 @@ export default class extends DatatablesController { //Hide the select element (the tomselect button is the sibling of the select element) select_target.nextElementSibling.classList.add('d-none'); } + + //If the selected option has a data-turbo attribute, set it to the form + if (selected_option.dataset.turbo) { + this.element.dataset.turbo = selected_option.dataset.turbo; + } else { + this.element.dataset.turbo = true; + } } confirmDeletionAtSubmit(event) { diff --git a/assets/controllers/elements/delete_btn_controller.js b/assets/controllers/elements/delete_btn_controller.js index 21a25328..9ab15f7d 100644 --- a/assets/controllers/elements/delete_btn_controller.js +++ b/assets/controllers/elements/delete_btn_controller.js @@ -29,62 +29,47 @@ export default class extends Controller this._confirmed = false; } - click(event) { - //If a user has not already confirmed the deletion, just let turbo do its work - if(this._confirmed) { - this._confirmed = false; - return; - } - - event.preventDefault(); - - const message = this.element.dataset.deleteMessage; - const title = this.element.dataset.deleteTitle; - - const that = this; - - const confirm = bootbox.confirm({ - message: message, title: title, callback: function (result) { - //If the dialog was confirmed, then submit the form. - if (result) { - that._confirmed = true; - event.target.click(); - } else { - that._confirmed = false; - } - } - }); - } - submit(event) { //If a user has not already confirmed the deletion, just let turbo do its work - if(this._confirmed) { + if (this._confirmed) { this._confirmed = false; return; } //Prevent turbo from doing its work event.preventDefault(); + event.stopPropagation(); const message = this.element.dataset.deleteMessage; const title = this.element.dataset.deleteTitle; - const form = this.element; + //Use event target, to find the form, where the submit button was clicked + const form = event.target; + const submitter = event.submitter; const that = this; - //Create a clone of the event with the same submitter, so we can redispatch it if needed - //We need to do this that way, as we need the submitter info, just calling form.submit() would not work - this._our_event = new SubmitEvent('submit', { - submitter: event.submitter, - bubbles: true, //This line is important, otherwise Turbo will not receive the event - }); - const confirm = bootbox.confirm({ message: message, title: title, callback: function (result) { //If the dialog was confirmed, then submit the form. if (result) { + //Set a flag to prevent the dialog from popping up again and allowing turbo to submit the form that._confirmed = true; - form.dispatchEvent(that._our_event); + + //Create a submit button in the form and click it to submit the form + //Before a submit event was dispatched, but this caused weird issues on Firefox causing the delete request being posted twice (and the second time was returning 404). See https://github.com/Part-DB/Part-DB-server/issues/273 + const submit_btn = document.createElement('button'); + submit_btn.type = 'submit'; + submit_btn.style.display = 'none'; + + //If the clicked button has a value, set it on the submit button + if (submitter.value) { + submit_btn.value = submitter.value; + } + if (submitter.name) { + submit_btn.name = submitter.name; + } + form.appendChild(submit_btn); + submit_btn.click(); } else { that._confirmed = false; } diff --git a/assets/controllers/elements/json_formatter_controller.js b/assets/controllers/elements/json_formatter_controller.js new file mode 100644 index 00000000..c72814e3 --- /dev/null +++ b/assets/controllers/elements/json_formatter_controller.js @@ -0,0 +1,40 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2023 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 . + */ + + +import {Controller} from "@hotwired/stimulus"; + +import JSONFormatter from 'json-formatter-js'; + +/** + * This controller implements an element that renders a JSON object as a collapsible tree. + * The JSON object is passed as a data attribute. + * You have to apply the controller to a div element or similar block element which can contain other elements. + */ +export default class extends Controller { + connect() { + const depth_to_open = this.element.dataset.depthToOpen ?? 0; + const json_string = this.element.dataset.json; + const json_object = JSON.parse(json_string); + + const formatter = new JSONFormatter(json_object, depth_to_open); + + this.element.appendChild(formatter.render()); + } +} \ No newline at end of file diff --git a/assets/controllers/elements/link_confirm_controller.js b/assets/controllers/elements/link_confirm_controller.js new file mode 100644 index 00000000..3d59b492 --- /dev/null +++ b/assets/controllers/elements/link_confirm_controller.js @@ -0,0 +1,72 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2022 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 . + */ + +import {Controller} from "@hotwired/stimulus"; + +import * as bootbox from "bootbox"; +import "../../css/components/bootbox_extensions.css"; + +export default class extends Controller +{ + + static values = { + message: String, + title: String + } + + + + connect() + { + this._confirmed = false; + + this.element.addEventListener('click', this._onClick.bind(this)); + } + + _onClick(event) + { + + //If a user has not already confirmed the deletion, just let turbo do its work + if (this._confirmed) { + this._confirmed = false; + return; + } + + event.preventDefault(); + event.stopPropagation(); + + const that = this; + + bootbox.confirm({ + title: this.titleValue, + message: this.messageValue, + callback: (result) => { + if (result) { + //Set a flag to prevent the dialog from popping up again and allowing turbo to submit the form + that._confirmed = true; + + //Click the link + that.element.click(); + } else { + that._confirmed = false; + } + } + }); + } +} \ No newline at end of file diff --git a/assets/controllers/elements/localStorage_checkbox_controller.js b/assets/controllers/elements/localStorage_checkbox_controller.js new file mode 100644 index 00000000..70ef877d --- /dev/null +++ b/assets/controllers/elements/localStorage_checkbox_controller.js @@ -0,0 +1,67 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2023 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 . + */ + +import {Controller} from "@hotwired/stimulus"; + +export default class extends Controller +{ + static values = { + id: String + } + + connect() { + this.loadState() + this.element.addEventListener('change', () => { + this.saveState() + }); + } + + loadState() { + let storageKey = this.getStorageKey(); + let value = localStorage.getItem(storageKey); + if (value === null) { + return; + } + + if (value === 'true') { + this.element.checked = true + } + if (value === 'false') { + this.element.checked = false + } + } + + saveState() { + let storageKey = this.getStorageKey(); + + if (this.element.checked) { + localStorage.setItem(storageKey, 'true'); + } else { + localStorage.setItem(storageKey, 'false'); + } + } + + getStorageKey() { + if (this.hasIdValue) { + return 'persistent_checkbox_' + this.idValue + } + + return 'persistent_checkbox_' + this.element.id; + } +} diff --git a/assets/controllers/elements/part_search_controller.js b/assets/controllers/elements/part_search_controller.js new file mode 100644 index 00000000..c33cece0 --- /dev/null +++ b/assets/controllers/elements/part_search_controller.js @@ -0,0 +1,200 @@ +/* + * 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 . + */ + +import { Controller } from "@hotwired/stimulus"; +import { autocomplete } from '@algolia/autocomplete-js'; +//import "@algolia/autocomplete-theme-classic/dist/theme.css"; +import "../../css/components/autocomplete_bootstrap_theme.css"; +import { createLocalStorageRecentSearchesPlugin } from '@algolia/autocomplete-plugin-recent-searches'; +import {marked} from "marked"; + +import { + trans, + SEARCH_PLACEHOLDER, + SEARCH_SUBMIT, + STATISTICS_PARTS +} from '../../translator'; + + +/** + * This controller is responsible for the search fields in the navbar and the homepage. + * It uses the Algolia Autocomplete library to provide a fast and responsive search. + */ +export default class extends Controller { + + static targets = ["input"]; + + _autocomplete; + + // Highlight the search query in the results + _highlight = (text, query) => { + if (!text) return text; + if (!query) return text; + + const HIGHLIGHT_PRE_TAG = '__aa-highlight__' + const HIGHLIGHT_POST_TAG = '__/aa-highlight__' + + const escaped = query.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); + const regex = new RegExp(escaped, 'gi'); + + return text.replace(regex, (match) => `${HIGHLIGHT_PRE_TAG}${match}${HIGHLIGHT_POST_TAG}`); + } + + initialize() { + // The endpoint for searching parts + const base_url = this.element.dataset.autocomplete; + // The URL template for the part detail pages + const part_detail_uri_template = this.element.dataset.detailUrl; + + //The URL of the placeholder picture + const placeholder_image = this.element.dataset.placeholderImage; + + //If the element is in navbar mode, or not + const navbar_mode = this.element.dataset.navbarMode === "true"; + + const that = this; + + const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({ + key: 'RECENT_SEARCH', + limit: 5, + }); + + this._autocomplete = autocomplete({ + container: this.element, + //Place the panel in the navbar, if the element is in navbar mode + panelContainer: navbar_mode ? document.getElementById("navbar-search-form") : document.body, + panelPlacement: this.element.dataset.panelPlacement, + plugins: [recentSearchesPlugin], + openOnFocus: true, + placeholder: trans(SEARCH_PLACEHOLDER), + translations: { + submitButtonTitle: trans(SEARCH_SUBMIT) + }, + + // Use a navigator compatible with turbo: + navigator: { + navigate({ itemUrl }) { + window.Turbo.visit(itemUrl, { action: "advance" }); + }, + navigateNewTab({ itemUrl }) { + const windowReference = window.open(itemUrl, '_blank', 'noopener'); + + if (windowReference) { + windowReference.focus(); + } + }, + navigateNewWindow({ itemUrl }) { + window.open(itemUrl, '_blank', 'noopener'); + }, + }, + + // If the form is submitted, forward the term to the form + onSubmit({state, event, ...setters}) { + //Put the current text into each target input field + const input = that.inputTarget; + + if (!input) { + return; + } + + //Do not submit the form, if the input is empty + if (state.query === "") { + return; + } + + input.value = state.query; + input.form.requestSubmit(); + }, + + + getSources({ query }) { + return [ + // The parts source + { + sourceId: 'parts', + getItems() { + const url = base_url.replace('__QUERY__', encodeURIComponent(query)); + + const data = fetch(url) + .then((response) => response.json()) + ; + + //Iterate over all fields besides the id and highlight them + const fields = ["name", "description", "category", "footprint"]; + + data.then((items) => { + items.forEach((item) => { + for (const field of fields) { + item[field] = that._highlight(item[field], query); + } + }); + }); + + return data; + }, + getItemUrl({ item }) { + return part_detail_uri_template.replace('__ID__', item.id); + }, + templates: { + header({ html }) { + return html`${trans(STATISTICS_PARTS)} +
`; + }, + item({item, components, html}) { + const details_url = part_detail_uri_template.replace('__ID__', item.id); + + return html` + +
+
+ ${item.name} +
+
+
+ + ${components.Highlight({hit: item, attribute: 'name'})} + +
+
+ ${components.Highlight({hit: item, attribute: 'description'})} + ${item.category ? html`

${components.Highlight({hit: item, attribute: 'category'})}

` : ""} + ${item.footprint ? html`

${components.Highlight({hit: item, attribute: 'footprint'})}

` : ""} +
+
+
+
+ `; + }, + }, + }, + ]; + }, + }); + + //Try to find the input field and register a defocus handler. This is necessarry, as by default the autocomplete + //lib has problems when multiple inputs are present on the page. (see https://github.com/algolia/autocomplete/issues/1216) + const inputs = this.element.getElementsByClassName('aa-Input'); + for (const input of inputs) { + input.addEventListener('blur', () => { + this._autocomplete.setIsOpen(false); + }); + } + + } +} \ No newline at end of file diff --git a/assets/controllers/elements/part_select_controller.js b/assets/controllers/elements/part_select_controller.js index c7507636..5abd5ba3 100644 --- a/assets/controllers/elements/part_select_controller.js +++ b/assets/controllers/elements/part_select_controller.js @@ -27,7 +27,7 @@ export default class extends Controller { } let tmp = '
' + - "
" + + "
" + (data.image ? "" : "") + "
" + "
" + diff --git a/assets/controllers/elements/password_strength_estimate_controller.js b/assets/controllers/elements/password_strength_estimate_controller.js new file mode 100644 index 00000000..0fc9c578 --- /dev/null +++ b/assets/controllers/elements/password_strength_estimate_controller.js @@ -0,0 +1,123 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2023 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 . + */ + + +import {Controller} from "@hotwired/stimulus"; +import { zxcvbn, zxcvbnOptions } from '@zxcvbn-ts/core'; +import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'; +import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en'; +import * as zxcvbnDePackage from '@zxcvbn-ts/language-de'; +import * as zxcvbnFrPackage from '@zxcvbn-ts/language-fr'; +import * as zxcvbnJaPackage from '@zxcvbn-ts/language-ja'; +import {trans, USER_PASSWORD_STRENGTH_VERY_WEAK, USER_PASSWORD_STRENGTH_WEAK, USER_PASSWORD_STRENGTH_MEDIUM, + USER_PASSWORD_STRENGTH_STRONG, USER_PASSWORD_STRENGTH_VERY_STRONG} from '../../translator.js'; + +/* stimulusFetch: 'lazy' */ +export default class extends Controller { + + _passwordInput; + + static targets = ["badge", "warning"] + + _getTranslations() { + //Get the current locale + const locale = document.documentElement.lang; + if (locale.includes('de')) { + return zxcvbnDePackage.translations; + } else if (locale.includes('fr')) { + return zxcvbnFrPackage.translations; + } else if (locale.includes('ja')) { + return zxcvbnJaPackage.translations; + } + + //Fallback to english + return zxcvbnEnPackage.translations; + } + + connect() { + //Find the password input field + this._passwordInput = this.element.querySelector('input[type="password"]'); + + //Configure zxcvbn + const options = { + graphs: zxcvbnCommonPackage.adjacencyGraphs, + dictionary: { + ...zxcvbnCommonPackage.dictionary, + // We could use the english dictionary here too, but it is very big. So we just use the common words + //...zxcvbnEnPackage.dictionary, + }, + translations: this._getTranslations(), + }; + zxcvbnOptions.setOptions(options); + + //Add event listener to the password input field + this._passwordInput.addEventListener('input', this._onPasswordInput.bind(this)); + } + + _onPasswordInput() { + //Retrieve the password + const password = this._passwordInput.value; + + //Estimate the password strength + const result = zxcvbn(password); + + //Update the badge + this.badgeTarget.parentElement.classList.remove("d-none"); + this._setBadgeToLevel(result.score); + + this.warningTarget.innerHTML = result.feedback.warning; + } + + _setBadgeToLevel(level) { + let text, classes; + + switch (level) { + case 0: + text = trans(USER_PASSWORD_STRENGTH_VERY_WEAK); + classes = "bg-danger badge-danger"; + break; + case 1: + text = trans(USER_PASSWORD_STRENGTH_WEAK); + classes = "bg-warning badge-warning"; + break; + case 2: + text = trans(USER_PASSWORD_STRENGTH_MEDIUM) + classes = "bg-info badge-info"; + break; + case 3: + text = trans(USER_PASSWORD_STRENGTH_STRONG); + classes = "bg-primary badge-primary"; + break; + case 4: + text = trans(USER_PASSWORD_STRENGTH_VERY_STRONG); + classes = "bg-success badge-success"; + break; + default: + text = "???"; + classes = "bg-secondary badge-secondary"; + } + + this.badgeTarget.innerHTML = text; + //Remove all classes + this.badgeTarget.className = ''; + //Re-add the classes + this.badgeTarget.classList.add("badge"); + this.badgeTarget.classList.add(...classes.split(" ")); + } +} \ No newline at end of file diff --git a/assets/controllers/elements/static_file_autocomplete_controller.js b/assets/controllers/elements/static_file_autocomplete_controller.js new file mode 100644 index 00000000..31ca0314 --- /dev/null +++ b/assets/controllers/elements/static_file_autocomplete_controller.js @@ -0,0 +1,106 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2023 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 . + */ + +import {Controller} from "@hotwired/stimulus"; + +import "tom-select/dist/css/tom-select.bootstrap5.css"; +import '../../css/components/tom-select_extensions.css'; +import TomSelect from "tom-select"; + +import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit' +import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed' + +TomSelect.define('click_to_edit', TomSelect_click_to_edit) +TomSelect.define('autoselect_typed', TomSelect_autoselect_typed) + +/** + * This is the frontend controller for StaticFileAutocompleteType form element. + * Basically it loads a text file from the given url (via data-url) and uses it as a source for the autocomplete. + * The file is just a list of strings, one per line, which will be used as the autocomplete options. + * Lines starting with # will be ignored. + */ +export default class extends Controller { + _tomSelect; + + connect() { + + let settings = { + persistent: false, + create: true, + maxItems: 1, + maxOptions: 100, + createOnBlur: true, + selectOnTab: true, + valueField: 'text', + searchField: 'text', + orderField: 'text', + + //This a an ugly solution to disable the delimiter parsing of the TomSelect plugin + delimiter: 'VERY_L0NG_D€LIMITER_WHICH_WILL_NEVER_BE_ENCOUNTERED_IN_A_STRING', + plugins: { + 'autoselect_typed': {}, + 'click_to_edit': {}, + 'clear_button': {}, + 'restore_on_backspace': {} + } + }; + + if (this.element.dataset.url) { + const url = this.element.dataset.url; + settings.load = (query, callback) => { + const self = this; + if (self.loading > 1) { + callback(); + return; + } + + fetch(url) + .then(response => response.text()) + .then(text => { + // Convert the text file to array + let lines = text.split("\n"); + //Remove all lines beginning with # + lines = lines.filter(x => !x.startsWith("#")); + + //Convert the array to an object, where each line is in the text field + lines = lines.map(x => { + return {text: x}; + }); + + + //Unset the load function to prevent endless recursion + self._tomSelect.settings.load = null; + + callback(lines); + }).catch(() => { + callback(); + }); + }; + } + + this._tomSelect = new TomSelect(this.element, settings); + } + + disconnect() { + super.disconnect(); + //Destroy the TomSelect instance + this._tomSelect.destroy(); + } + +} diff --git a/assets/controllers/elements/structural_entity_select_controller.js b/assets/controllers/elements/structural_entity_select_controller.js index 38480cfa..a1114a97 100644 --- a/assets/controllers/elements/structural_entity_select_controller.js +++ b/assets/controllers/elements/structural_entity_select_controller.js @@ -22,6 +22,10 @@ import '../../css/components/tom-select_extensions.css'; import TomSelect from "tom-select"; import {Controller} from "@hotwired/stimulus"; +import {trans, ENTITY_SELECT_GROUP_NEW_NOT_ADDED_TO_DB} from '../../translator.js' + +import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed' +TomSelect.define('autoselect_typed', TomSelect_autoselect_typed) export default class extends Controller { _tomSelect; @@ -36,12 +40,20 @@ export default class extends Controller { const allowAdd = this.element.getAttribute("data-allow-add") === "true"; const addHint = this.element.getAttribute("data-add-hint") ?? ""; + + + let settings = { allowEmptyOption: true, selectOnTab: true, maxOptions: null, - create: allowAdd, - createFilter: /\D/, //Must contain a non-digit character, otherwise they would be recognized as DB ID + create: allowAdd ? this.createItem.bind(this) : false, + createFilter: this.createFilter.bind(this), + + // This three options allow us to paste element names with commas: (see issue #538) + maxItems: 1, + delimiter: "$$VERY_LONG_DELIMITER_THAT_SHOULD_NEVER_APPEAR$$", + splitOn: null, searchField: [ {field: "text", weight : 2}, @@ -52,15 +64,108 @@ export default class extends Controller { render: { item: this.renderItem.bind(this), option: this.renderOption.bind(this), - option_create: function(data, escape) { + option_create: (data, escape) => { + //If the input starts with "->", we prepend the current selected value, for easier extension of existing values + //This here handles the display part, while the createItem function handles the actual creation + if (data.input.startsWith("->")) { + //Get current selected value + const current = this._tomSelect.getItem(this._tomSelect.getValue()).textContent.replaceAll("→", "->").trim(); + //Prepend it to the input + if (current) { + data.input = current + " " + data.input; + } else { + //If there is no current value, we remove the "->" + data.input = data.input.substring(2); + } + } + return '
 ' + escape(data.input) + '… ' + '(' + addHint +')' + '
'; }, + }, + + //Add callbacks to update validity + onInitialize: this.updateValidity.bind(this), + onChange: this.updateValidity.bind(this), + + plugins: { + "autoselect_typed": {}, } }; + //Add clear button plugin, if an empty option is present + if (this.element.querySelector("option[value='']") !== null) { + settings.plugins["clear_button"] = {}; + } + this._tomSelect = new TomSelect(this.element, settings); + //Do not do a sync here as this breaks the initial rendering of the empty option + //this._tomSelect.sync(); + } + + createItem(input, callback) { + + //If the input starts with "->", we prepend the current selected value, for easier extension of existing values + if (input.startsWith("->")) { + //Get current selected value + let current = this._tomSelect.getItem(this._tomSelect.getValue()).textContent.replaceAll("→", "->").trim(); + //Replace no break spaces with normal spaces + current = current.replaceAll("\u00A0", " "); + //Prepend it to the input + if (current) { + input = current + " " + input; + } else { + //If there is no current value, we remove the "->" + input = input.substring(2); + } + } + + callback({ + //$%$ is a special value prefix, that is used to identify items, that are not yet in the DB + value: '$%$' + input, + text: input, + not_in_db_yet: true, + }); + } + + createFilter(input) { + + //Normalize the input (replace spacing around arrows) + if (input.includes("->")) { + const inputs = input.split("->"); + inputs.forEach((value, index) => { + inputs[index] = value.trim(); + }); + input = inputs.join("->"); + } else { + input = input.trim(); + } + + const options = this._tomSelect.options; + //Iterate over all options and check if the input is already present + for (let index in options) { + const option = options[index]; + if (option.path === input) { + return false; + } + } + + return true; + } + + + updateValidity() { + //Mark this input as invalid, if the selected option is disabled + + const input = this.element; + const selectedOption = input.options[input.selectedIndex]; + + if (selectedOption && selectedOption.disabled) { + input.setCustomValidity("This option was disabled. Please select another option."); + } else { + input.setCustomValidity(""); + } } getTomSelect() { @@ -78,14 +183,27 @@ export default class extends Controller { } if (data.short) { - return '
' + escape(data.short) + '
'; + let short = escape(data.short) + + //Make text italic, if the item is not yet in the DB + if (data.not_in_db_yet) { + short = '' + short + ''; + } + + return '
' + short + '
'; } let name = ""; if (data.parent) { name += escape(data.parent) + " → "; } - name += "" + escape(data.text) + ""; + + if (data.not_in_db_yet) { + //Not yet added items are shown italic and with a badge + name += "" + escape(data.text) + "" + "" + trans(ENTITY_SELECT_GROUP_NEW_NOT_ADDED_TO_DB) + ""; + } else { + name += "" + escape(data.text) + ""; + } return '
' + (data.image ? "" : "") + name + '
'; } diff --git a/assets/controllers/elements/tagsinput_controller.js b/assets/controllers/elements/tagsinput_controller.js index acfcd0fa..1f10c457 100644 --- a/assets/controllers/elements/tagsinput_controller.js +++ b/assets/controllers/elements/tagsinput_controller.js @@ -23,14 +23,21 @@ import "tom-select/dist/css/tom-select.bootstrap5.css"; import '../../css/components/tom-select_extensions.css'; import TomSelect from "tom-select"; +import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit' +import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed' + +TomSelect.define('click_to_edit', TomSelect_click_to_edit) +TomSelect.define('autoselect_typed', TomSelect_autoselect_typed) + export default class extends Controller { _tomSelect; connect() { let settings = { plugins: { - remove_button:{ - } + remove_button:{}, + 'autoselect_typed': {}, + 'click_to_edit': {}, }, persistent: false, selectOnTab: true, diff --git a/assets/controllers/elements/tree_controller.js b/assets/controllers/elements/tree_controller.js index bdb150c2..bb64839c 100644 --- a/assets/controllers/elements/tree_controller.js +++ b/assets/controllers/elements/tree_controller.js @@ -81,31 +81,71 @@ export default class extends Controller { this._tree.remove(); } + const BS53Theme = { + getOptions() { + return { + onhoverColor: 'var(--bs-secondary-bg)', + }; + } + } + this._tree = new BSTreeView(this.treeTarget, { levels: 1, showTags: this._showTags, data: data, showIcon: true, + preventUnselect: true, + allowReselect: true, onNodeSelected: (event) => { const node = event.detail.node; if (node.href) { window.Turbo.visit(node.href, {action: "advance"}); + this._registerURLWatcher(node); } }, - //onNodeContextmenu: contextmenu_handler, - }, [BS5Theme, FAIconTheme]); + }, [BS5Theme, BS53Theme, FAIconTheme]); this.treeTarget.addEventListener(EVENT_INITIALIZED, (event) => { /** @type {BSTreeView} */ const treeView = event.detail.treeView; treeView.revealNode(treeView.getSelected()); + //Add the url watcher to all selected nodes + for (const node of treeView.getSelected()) { + this._registerURLWatcher(node); + } + //Add contextmenu event listener to the tree, which allows us to open the links in a new tab with a right click treeView.getTreeElement().addEventListener("contextmenu", this._onContextMenu.bind(this)); }); } + _registerURLWatcher(node) + { + //Register a watcher for a location change, which will unselect the node, if the location changes + const desired_url = node.href; + + //Ensure that the node is unselected, if the location changes + const unselectNode = () => { + //Parse url so we can properly compare them + const desired = new URL(node.href, window.location.origin); + + //We only compare the pathname, because the hash and parameters should not matter + if(window.location.pathname !== desired.pathname) { + //The ignore parameter is important here, otherwise the node will not be unselected + node.setSelected(false, {silent: true, ignorePreventUnselect: true}); + + //Unregister the watcher + document.removeEventListener('turbo:load', unselectNode); + } + }; + + //Register the watcher via hotwire turbo + //We must just load to have the new url in window.location + document.addEventListener('turbo:load', unselectNode); + } + _onContextMenu(event) { //Find the node that was clicked and open link in new tab diff --git a/assets/controllers/pages/association_edit_type_select_controller.js b/assets/controllers/pages/association_edit_type_select_controller.js new file mode 100644 index 00000000..10badf9c --- /dev/null +++ b/assets/controllers/pages/association_edit_type_select_controller.js @@ -0,0 +1,44 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2023 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 . + */ + +import {Controller} from "@hotwired/stimulus"; + +export default class extends Controller { + + static targets = [ "display", "select" ] + + connect() + { + this.update(); + this.selectTarget.addEventListener('change', this.update.bind(this)); + } + + update() + { + //If the select value is 0, then we show the input field + if( this.selectTarget.value === '0') + { + this.displayTarget.classList.remove('d-none'); + } + else + { + this.displayTarget.classList.add('d-none'); + } + } +} \ No newline at end of file diff --git a/assets/controllers/pages/barcode_scan_controller.js b/assets/controllers/pages/barcode_scan_controller.js index 5f82a39e..368fef43 100644 --- a/assets/controllers/pages/barcode_scan_controller.js +++ b/assets/controllers/pages/barcode_scan_controller.js @@ -20,7 +20,7 @@ import {Controller} from "@hotwired/stimulus"; //import * as ZXing from "@zxing/library"; -import {Html5QrcodeScanner, Html5Qrcode} from "html5-qrcode"; +import {Html5QrcodeScanner, Html5Qrcode} from "@part-db/html5-qrcode"; /* stimulusFetch: 'lazy' */ export default class extends Controller { @@ -50,7 +50,7 @@ export default class extends Controller { }); this._scanner = new Html5QrcodeScanner(this.element.id, { - fps: 2, + fps: 10, qrbox: qrboxFunction, experimentalFeatures: { //This option improves reading quality on android chrome @@ -61,6 +61,11 @@ export default class extends Controller { this._scanner.render(this.onScanSuccess.bind(this)); } + disconnect() { + this._scanner.pause(); + this._scanner.clear(); + } + onScanSuccess(decodedText, decodedResult) { //Put our decoded Text into the input box document.getElementById('scan_dialog_input').value = decodedText; diff --git a/assets/controllers/pages/dont_check_quantity_checkbox_controller.js b/assets/controllers/pages/dont_check_quantity_checkbox_controller.js new file mode 100644 index 00000000..2abd3d77 --- /dev/null +++ b/assets/controllers/pages/dont_check_quantity_checkbox_controller.js @@ -0,0 +1,65 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2022 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 . + */ + +import {Controller} from "@hotwired/stimulus"; + +/** + * This controller is used on a checkbox, which toggles the max value of all number input fields + */ +export default class extends Controller { + + _checkbox; + + getCheckbox() { + if (this._checkbox) { + return this._checkbox; + } + + //Find the checkbox inside the controller element + this._checkbox = this.element.querySelector('input[type="checkbox"]'); + return this._checkbox; + } + + connect() { + //Add event listener to the checkbox + this.getCheckbox().addEventListener('change', this.toggleInputLimits.bind(this)); + } + + toggleInputLimits() { + //Find all input fields with the data-toggle-input-limits-target="max" + const inputFields = document.querySelectorAll("input[type='number']"); + + inputFields.forEach((inputField) => { + //Ensure that the input field has either a max or a data-max attribute + if (!inputField.hasAttribute('max') && !inputField.hasAttribute('data-max')) { + return; + } + + //If the checkbox is checked, rename the max attribute to data-max + if (this.getCheckbox().checked) { + inputField.setAttribute('data-max', inputField.getAttribute('max')); + inputField.removeAttribute('max'); + } else { + //If the checkbox is not checked, rename the data-max attribute back to max + inputField.setAttribute('max', inputField.getAttribute('data-max')); + inputField.removeAttribute('data-max'); + } + }); + } +} \ No newline at end of file diff --git a/assets/controllers/pages/latex_preview_controller.js b/assets/controllers/pages/latex_preview_controller.js index c836faa6..6113393a 100644 --- a/assets/controllers/pages/latex_preview_controller.js +++ b/assets/controllers/pages/latex_preview_controller.js @@ -25,9 +25,20 @@ import "katex/dist/katex.css"; export default class extends Controller { static targets = ["input", "preview"]; + static values = { + unit: {type: Boolean, default: false} //Render as upstanding (non-italic) text, useful for units + } + updatePreview() { - katex.render(this.inputTarget.value, this.previewTarget, { + let value = ""; + if (this.unitValue) { + value = "\\mathrm{" + this.inputTarget.value + "}"; + } else { + value = this.inputTarget.value; + } + + katex.render(value, this.previewTarget, { throwOnError: false, }); } diff --git a/assets/controllers/pages/parameters_autocomplete_controller.js b/assets/controllers/pages/parameters_autocomplete_controller.js index f6504990..cd82875a 100644 --- a/assets/controllers/pages/parameters_autocomplete_controller.js +++ b/assets/controllers/pages/parameters_autocomplete_controller.js @@ -22,6 +22,13 @@ import TomSelect from "tom-select"; import katex from "katex"; import "katex/dist/katex.css"; + +import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit' +import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed' + +TomSelect.define('click_to_edit', TomSelect_click_to_edit) +TomSelect.define('autoselect_typed', TomSelect_autoselect_typed) + /* stimulusFetch: 'lazy' */ export default class extends Controller { @@ -53,7 +60,10 @@ export default class extends Controller connect() { const settings = { plugins: { - clear_button:{} + 'autoselect_typed': {}, + 'click_to_edit': {}, + 'clear_button': {}, + 'restore_on_backspace': {} }, persistent: false, maxItems: 1, diff --git a/assets/controllers/pages/part_merge_modal_controller.js b/assets/controllers/pages/part_merge_modal_controller.js new file mode 100644 index 00000000..e9e41302 --- /dev/null +++ b/assets/controllers/pages/part_merge_modal_controller.js @@ -0,0 +1,68 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2023 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 . + */ + +import {Controller} from "@hotwired/stimulus"; + +export default class extends Controller +{ + static targets = ['link', 'mode', 'otherSelect']; + static values = { + targetId: Number, + }; + + connect() { + } + + update() { + const link = this.linkTarget; + const other_select = this.otherSelectTarget; + + //Extract the mode using the mode radio buttons (we filter the array to get the checked one) + const mode = (this.modeTargets.filter((e)=>e.checked))[0].value; + + if (other_select.value === '') { + link.classList.add('disabled'); + return; + } + + //Extract href template from data attribute on link target + let href = link.getAttribute('data-href-template'); + + let target, other; + if (mode === '1') { + target = this.targetIdValue; + other = other_select.value; + } else if (mode === '2') { + target = other_select.value; + other = this.targetIdValue; + } else { + throw 'Invalid mode'; + } + + //Replace placeholder with actual target id + href = href.replace('__target__', target); + //Replace placeholder with selected value of the select (the event sender) + href = href.replace('__other__', other); + + //Assign new href to link + link.setAttribute('href', href); + //Make link clickable + link.classList.remove('disabled'); + } +} \ No newline at end of file diff --git a/assets/css/app/bs-overrides.css b/assets/css/app/bs-overrides.css index 6043aafe..070f353d 100644 --- a/assets/css/app/bs-overrides.css +++ b/assets/css/app/bs-overrides.css @@ -99,10 +99,25 @@ label:not(.form-check-label, .custom-control-label) { form .col-form-label.required:after, form label.required:after { bottom: 4px; - color: var(--bs-dark); + color: var(--bs-secondary-color); content: "\2022"; filter: opacity(75%); position: relative; right: -2px; z-index: 700; +} + +/**************************************** + * HTML diff styling + ****************************************/ + +/* Insertations are marked with green background and bold */ +ins { + background-color: #95f095; + font-weight: bold; +} + +del { + background-color: #f09595; + font-weight: bold; } \ No newline at end of file diff --git a/assets/css/app/helpers.css b/assets/css/app/helpers.css index acc2682e..8e7b6fa3 100644 --- a/assets/css/app/helpers.css +++ b/assets/css/app/helpers.css @@ -67,7 +67,6 @@ ul.structural_link { padding-bottom: 7px; padding-left: 0; list-style: none; - background-color: inherit; } /* Display list items side by side */ @@ -79,7 +78,7 @@ ul.structural_link li { /* Add a slash symbol (/) before/behind each list item */ ul.structural_link li+li:before { padding: 2px; - color: grey; + color: var(--bs-tertiary-color); /*content: "/\00a0";*/ font-family: "Font Awesome 5 Free"; font-weight: 900; @@ -89,13 +88,13 @@ ul.structural_link li+li:before { /* Add a color to all links inside the list */ ul.structural_link li a { - color: #0275d8; + color: var(--bs-link-color); text-decoration: none; } /* Add a color on mouse-over */ ul.structural_link li a:hover { - color: #01447e; + color: var(--bs-link-hover-color); text-decoration: underline; } @@ -112,4 +111,11 @@ ul.structural_link li a:hover { .permission-checkbox:checked { background-color: var(--bs-success); border-color: var(--bs-success); +} + +/*********************************************** + * Katex rendering with same height as text + ***********************************************/ +.katex-same-height-as-text .katex { + font-size: 1.0em; } \ No newline at end of file diff --git a/assets/css/app/images.css b/assets/css/app/images.css index 51760058..214776e7 100644 --- a/assets/css/app/images.css +++ b/assets/css/app/images.css @@ -46,4 +46,18 @@ height: 1.5em; max-width: 2.0em; object-fit: contain; -} \ No newline at end of file +} + +.part-table-image { + max-height: 40px; + object-fit: contain; +} + +.part-info-image { + max-height: 350px; + object-fit: contain; +} + +.object-fit-cover { + object-fit: cover; +} diff --git a/assets/css/app/layout.css b/assets/css/app/layout.css index 059fc131..4be123a7 100644 --- a/assets/css/app/layout.css +++ b/assets/css/app/layout.css @@ -78,8 +78,6 @@ body { overflow: -moz-scrollbars-none; /* Use standard version for hiding the scrollbar */ scrollbar-width: none; - - background-color: var(--light); } #sidebar-container { @@ -110,8 +108,8 @@ body { .back-to-top { cursor: pointer; position: fixed; - bottom: 20px; - right: 20px; + bottom: 60px; + right: 40px; display:none; z-index: 1030; } diff --git a/assets/css/app/tables.css b/assets/css/app/tables.css index 13708772..ae892f50 100644 --- a/assets/css/app/tables.css +++ b/assets/css/app/tables.css @@ -63,10 +63,6 @@ table.dataTable > tbody > tr.selected > td > a { margin-block-end: 0; } -.card-footer-table { - padding-top: 0; -} - table.dataTable { margin-top: 0 !important; } @@ -84,13 +80,24 @@ th.select-checkbox { * Datatables definitions/overrides ********************************************************************/ -.dataTables_length { +.dt-length { display: inline-flex; } /** Fix datatables select-checkbox position */ -table.dataTable tr.selected td.select-checkbox:after, table.dataTable tr.selected th.select-checkbox:after { - margin-top: -28px !important; +table.dataTable tr.selected td.select-checkbox:after +{ + margin-top: -20px !important; +} + +/** Show pagination right aligned */ +.dt-paging .pagination { + justify-content: flex-end; +} + +/** Fix table row coloring */ +table.table.dataTable > :not(caption) > * > * { + background-color: var(--bs-table-bg); } diff --git a/assets/css/components/autocomplete_bootstrap_theme.css b/assets/css/components/autocomplete_bootstrap_theme.css new file mode 100644 index 00000000..d86232e5 --- /dev/null +++ b/assets/css/components/autocomplete_bootstrap_theme.css @@ -0,0 +1,1120 @@ +/* + * 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 . + */ + +/*** + * This file is based on the autocomplete-theme-classic from Algolia and modifies it to fit better into the bootstrap 5 + * theme of Part-DB. + */ + +/*! @algolia/autocomplete-theme-classic 1.17.0 | MIT License | © Algolia, Inc. and contributors | https://github.com/algolia/autocomplete */ +/* ----------------*/ +/* 1. CSS Variables*/ +/* 2. Dark Mode*/ +/* 3. Autocomplete*/ +/* 4. Panel*/ +/* 5. Sources*/ +/* 6. Hit Layout*/ +/* 7. Panel Header*/ +/* 8. Panel Footer*/ +/* 9. Detached Mode*/ +/* 10. Gradients*/ +/* 11. Utilities*/ +/* ----------------*/ +/* Note:*/ +/* This theme reflects the markup structure of autocomplete with SCSS indentation.*/ +/* We use the SASS `@at-root` function to keep specificity low.*/ +/* ----------------*/ +/* 1. CSS Variables*/ +/* ----------------*/ +:root { + /* Input*/ + --aa-search-input-height: 44px; + --aa-input-icon-size: 20px; + /* Size and spacing*/ + --aa-base-unit: 16; + --aa-spacing-factor: 1; + --aa-spacing: calc(var(--aa-base-unit) * var(--aa-spacing-factor) * 1px); + --aa-spacing-half: calc(var(--aa-spacing) / 2); + --aa-panel-max-height: 650px; + /* Z-index*/ + --aa-base-z-index: 9999; + /* Font*/ + --aa-font-size: calc(var(--aa-base-unit) * 1px); + --aa-font-family: inherit; + --aa-font-weight-medium: 500; + --aa-font-weight-semibold: 600; + --aa-font-weight-bold: 700; + /* Icons*/ + --aa-icon-size: 20px; + --aa-icon-stroke-width: 1.6; + --aa-icon-color-rgb: 119, 119, 163; + --aa-icon-color-alpha: 1; + --aa-action-icon-size: 20px; + /* Text colors*/ + --aa-text-color-rgb: 38, 38, 39; + --aa-text-color-alpha: 1; + --aa-primary-color-rgb: 62, 52, 211; + --aa-primary-color-alpha: 0.2; + --aa-muted-color-rgb: 128, 126, 163; + --aa-muted-color-alpha: 0.6; + /* Border colors*/ + --aa-panel-border-color-rgb: 128, 126, 163; + --aa-panel-border-color-alpha: 0.3; + --aa-input-border-color-rgb: 128, 126, 163; + --aa-input-border-color-alpha: 0.8; + /* Background colors*/ + --aa-background-color-rgb: 255, 255, 255; + --aa-background-color-alpha: 1; + --aa-input-background-color-rgb: 255, 255, 255; + --aa-input-background-color-alpha: 1; + --aa-selected-color-rgb: 179, 173, 214; + --aa-selected-color-alpha: 0.205; + --aa-description-highlight-background-color-rgb: 245, 223, 77; + --aa-description-highlight-background-color-alpha: 0.5; + /* Detached mode*/ + --aa-detached-media-query: (max-width: 680px); + --aa-detached-modal-media-query: (min-width: 680px); + --aa-detached-modal-max-width: 680px; + --aa-detached-modal-max-height: 500px; + --aa-overlay-color-rgb: 115, 114, 129; + --aa-overlay-color-alpha: 0.4; + /* Shadows*/ + --aa-panel-shadow: 0 0 0 1px rgba(35, 38, 59, .1), + 0 6px 16px -4px rgba(35, 38, 59, .15); + /* Scrollbar*/ + --aa-scrollbar-width: 13px; + --aa-scrollbar-track-background-color-rgb: 234, 234, 234; + --aa-scrollbar-track-background-color-alpha: 1; + --aa-scrollbar-thumb-background-color-rgb: var(--aa-background-color-rgb); + --aa-scrollbar-thumb-background-color-alpha: 1; + /* Touch screens*/ +} +@media (hover: none) and (pointer: coarse) { + :root { + --aa-spacing-factor: 1.2; + --aa-action-icon-size: 22px; + } +} + +/* ----------------*/ +/* 2. Dark Mode*/ +/* ----------------*/ +body { + /* stylelint-disable selector-no-qualifying-type, selector-class-pattern */ + /* stylelint-enable selector-no-qualifying-type, selector-class-pattern */ +} + +/* Reset for `@extend`*/ +.aa-Panel *, .aa-Autocomplete *, +.aa-DetachedFormContainer * { + box-sizing: border-box; +} + +/* Init for `@extend`*/ +.aa-Panel, .aa-Autocomplete, +.aa-DetachedFormContainer { + color: rgba(var(--aa-text-color-rgb), var(--aa-text-color-alpha)); + color: var(--bs-body-color); + font-family: inherit; + font-weight: normal; + line-height: 1em; + margin: 0; + padding: 0; + text-align: left; +} + +/* ----------------*/ +/* 3. Autocomplete*/ +/* ----------------*/ +.aa-Autocomplete, +.aa-DetachedFormContainer { + /* Search box*/ +} +.aa-Form { + align-items: center; + background-color: var(--bs-body-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + color: var(--bs-body-color); + transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; + display: flex; + line-height: 1em; + margin: 0; + position: relative; + width: 100%; +} +.aa-Form:focus-within { + background-color: var(--bs-body-bg); + border-color: #86b7fe; + box-shadow: 0 0 0 0.25rem rgba(13,110,253,.25); + color: var(--bs-body-color); + outline: 0; +} +.aa-InputWrapperPrefix { + align-items: center; + display: flex; + flex-shrink: 0; + height: 44px; + height: var(--aa-search-input-height); + order: 1; + /* Container for search and loading icons*/ +} +.aa-Label, +.aa-LoadingIndicator { + cursor: auto; + cursor: initial; + flex-shrink: 0; + height: 100%; + padding: 0; + text-align: left; +} +.aa-Label svg, +.aa-LoadingIndicator svg { + color: rgba(var(--bs-primary-rgb), 1.0); + height: auto; + max-height: 20px; + max-height: var(--aa-input-icon-size); + stroke-width: 1.6; + stroke-width: var(--aa-icon-stroke-width); + width: 20px; + width: var(--aa-input-icon-size); +} + +.aa-SubmitButton, +.aa-LoadingIndicator { + height: 100%; + padding-left: calc((16 * 1 * 1px) * 0.75 - 1px); + padding-left: calc(calc(16 * 1 * 1px) * 0.75 - 1px); + padding-left: calc(var(--aa-spacing) * 0.75 - 1px); + padding-right: calc((16 * 1 * 1px) / 2); + padding-right: calc(calc(16 * 1 * 1px) / 2); + padding-right: var(--aa-spacing-half); + width: calc((16 * 1 * 1px) * 1.75 + 20px - 1px); + width: calc(calc(16 * 1 * 1px) * 1.75 + 20px - 1px); + width: calc(var(--aa-spacing) * 1.75 + var(--aa-icon-size) - 1px); +} +@media (hover: none) and (pointer: coarse) { + .aa-SubmitButton, + .aa-LoadingIndicator { + padding-left: calc(((16 * 1 * 1px) / 2) / 2 - 1px); + padding-left: calc(calc(calc(16 * 1 * 1px) / 2) / 2 - 1px); + padding-left: calc(var(--aa-spacing-half) / 2 - 1px); + width: calc(20px + (16 * 1 * 1px) * 1.25 - 1px); + width: calc(20px + calc(16 * 1 * 1px) * 1.25 - 1px); + width: calc(var(--aa-icon-size) + var(--aa-spacing) * 1.25 - 1px); + } +} + +.aa-SubmitButton { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: none; + border: 0; + margin: 0; +} + +.aa-LoadingIndicator { + align-items: center; + display: flex; + justify-content: center; +} +.aa-LoadingIndicator[hidden] { + display: none; +} + +.aa-InputWrapper { + order: 3; + position: relative; + width: 100%; + /* Search box input (with placeholder and query)*/ +} +.aa-Input { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: none; + border: 0; + color: var(--bs-body-color); + font: inherit; + height: 44px; + height: var(--aa-search-input-height); + padding: 0; + width: 100%; + /* Focus is set and styled on the parent, it isn't necessary here*/ + /* Remove native appearance*/ +} +.aa-Input::-moz-placeholder { + color: var(--bs-secondary-color); + opacity: 1; +} +.aa-Input::placeholder { + color: var(--bs-secondary-color); + opacity: 1; +} +.aa-Input:focus { + box-shadow: none; + outline: none; +} +.aa-Input::-webkit-search-decoration, .aa-Input::-webkit-search-cancel-button, .aa-Input::-webkit-search-results-button, .aa-Input::-webkit-search-results-decoration { + -webkit-appearance: none; + appearance: none; +} + +.aa-InputWrapperSuffix { + align-items: center; + display: flex; + height: 44px; + height: var(--aa-search-input-height); + order: 4; + /* Accelerator to clear the query*/ +} +.aa-ClearButton { + align-items: center; + background: none; + border: 0; + color: var(--bs-secondary-color); + cursor: pointer; + display: flex; + height: 100%; + margin: 0; + padding: 0 calc((16 * 1 * 1px) * 0.8333333333 - 0.5px); + padding: 0 calc(calc(16 * 1 * 1px) * 0.8333333333 - 0.5px); + padding: 0 calc(var(--aa-spacing) * 0.8333333333 - 0.5px); +} +@media (hover: none) and (pointer: coarse) { + .aa-ClearButton { + padding: 0 calc((16 * 1 * 1px) * 0.6666666667 - 0.5px); + padding: 0 calc(calc(16 * 1 * 1px) * 0.6666666667 - 0.5px); + padding: 0 calc(var(--aa-spacing) * 0.6666666667 - 0.5px); + } +} +.aa-ClearButton:hover, .aa-ClearButton:focus { + color: var(--bs-body-color); +} +.aa-ClearButton[hidden] { + display: none; +} +.aa-ClearButton svg { + stroke-width: 1.6; + stroke-width: var(--aa-icon-stroke-width); + width: 20px; + width: var(--aa-icon-size); +} + +/* ----------------*/ +/* 4. Panel*/ +/* ----------------*/ +.aa-Panel { + --bs-dropdown-header-padding-x: 1rem; + --bs-dropdown-header-padding-y: 0.5rem; + --bs-dropdown-font-size: 1rem; + --bs-dropdown-color: var(--bs-body-color); + --bs-dropdown-bg: var(--bs-body-bg); + --bs-dropdown-border-color: var(--bs-border-color-translucent); + --bs-dropdown-border-radius: var(--bs-border-radius); + --bs-dropdown-border-width: var(--bs-border-width); + + z-index: 1000; + + box-shadow: 0 0 0 1px rgba(35, 38, 59, 0.1); + overflow: hidden; + position: absolute; + transition: opacity 200ms ease-in, filter 200ms ease-in; + /* When a request isn't resolved yet*/ + + padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x); + margin: 0; + font-size: var(--bs-dropdown-font-size); + color: var(--bs-dropdown-color); + background-color: var(--bs-dropdown-bg); + background-clip: padding-box; + border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); + border-radius: var(--bs-dropdown-border-radius); +} +@media screen and (prefers-reduced-motion) { + .aa-Panel { + transition: none; + } +} +.aa-Panel button { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: none; + border: 0; + margin: 0; + padding: 0; +} +.aa-PanelLayout { + height: 100%; + margin: 0; + max-height: 650px; + max-height: var(--aa-panel-max-height); + overflow-y: auto; + padding: 0; + position: relative; + text-align: left; +} +.aa-PanelLayoutColumns--twoGolden { + display: grid; + grid-template-columns: 39.2% auto; + overflow: hidden; + padding: 0; +} + +.aa-PanelLayoutColumns--two { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + overflow: hidden; + padding: 0; +} + +.aa-PanelLayoutColumns--three { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + overflow: hidden; + padding: 0; +} + +.aa-Panel--stalled .aa-Source { + filter: grayscale(1); + opacity: 0.8; +} + +.aa-Panel--scrollable { + margin: 0; + max-height: 650px; + max-height: var(--aa-panel-max-height); + overflow-x: hidden; + overflow-y: auto; + padding: calc((16 * 1 * 1px) / 2); + padding: calc(calc(16 * 1 * 1px) / 2); + padding: var(--aa-spacing-half); + scrollbar-color: rgba(255, 255, 255, 1) rgba(234, 234, 234, 1); + scrollbar-color: rgba(var(--aa-scrollbar-thumb-background-color-rgb), var(--aa-scrollbar-thumb-background-color-alpha)) rgba(var(--aa-scrollbar-track-background-color-rgb), var(--aa-scrollbar-track-background-color-alpha)); + scrollbar-width: thin; +} +.aa-Panel--scrollable::-webkit-scrollbar { + width: 13px; + width: var(--aa-scrollbar-width); +} +.aa-Panel--scrollable::-webkit-scrollbar-track { + background-color: rgba(234, 234, 234, 1); + background-color: rgba(var(--aa-scrollbar-track-background-color-rgb), var(--aa-scrollbar-track-background-color-alpha)); +} +.aa-Panel--scrollable::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 1); + background-color: rgba(var(--aa-scrollbar-thumb-background-color-rgb), var(--aa-scrollbar-thumb-background-color-alpha)); + border-color: rgba(234, 234, 234, 1); + border-color: rgba(var(--aa-scrollbar-track-background-color-rgb), var(--aa-scrollbar-track-background-color-alpha)); + border-radius: 9999px; + border-style: solid; + border-width: 3px 2px 3px 3px; +} + +/* ----------------*/ +/* 5. Sources*/ +/* Each source can be styled independently*/ +/* ----------------*/ +.aa-Source { + margin: 0; + padding: 0; + position: relative; + width: 100%; + /* List of results inside the source*/ + /* Source title*/ + /* See all button*/ +} +.aa-Source:empty { + /* Hide empty section*/ + display: none; +} +.aa-SourceNoResults { + font-size: 1em; + margin: 0; + padding: calc(16 * 1 * 1px); + padding: var(--aa-spacing); +} + +.aa-List { + list-style: none; + margin: 0; + padding: 0; + position: relative; +} + +.aa-SourceHeader { + margin: calc((16 * 1 * 1px) / 2) 0.5em calc((16 * 1 * 1px) / 2) 0; + margin: calc(calc(16 * 1 * 1px) / 2) 0.5em calc(calc(16 * 1 * 1px) / 2) 0; + margin: var(--aa-spacing-half) 0.5em var(--aa-spacing-half) 0; + padding: 0; + position: relative; + /* Hide empty header*/ + /* Title typography*/ + /* Line separator*/ +} +.aa-SourceHeader:empty { + display: none; +} +.aa-SourceHeaderTitle { + background: var(--bs-body-bg); + color: rgba(var(--bs-primary-rgb), 1.0); + display: inline-block; + font-size: 0.8em; + font-weight: 600; + font-weight: var(--aa-font-weight-semibold); + margin: 0; + padding: 0 calc((16 * 1 * 1px) / 2) 0 0; + padding: 0 calc(calc(16 * 1 * 1px) / 2) 0 0; + padding: 0 var(--aa-spacing-half) 0 0; + position: relative; + z-index: 9999; + z-index: var(--aa-base-z-index); +} + +.aa-SourceHeaderLine { + border-bottom: solid 1px rgba(var(--bs-primary-rgb), 1.0); + display: block; + height: 2px; + left: 0; + margin: 0; + opacity: 0.3; + padding: 0; + position: absolute; + right: 0; + top: calc((16 * 1 * 1px) / 2); + top: calc(calc(16 * 1 * 1px) / 2); + top: var(--aa-spacing-half); + z-index: calc(9999 - 1); + z-index: calc(var(--aa-base-z-index) - 1); +} + +.aa-SourceFooterSeeAll { + background: linear-gradient(180deg, var(--bs-body-bg), rgba(128, 126, 163, 0.14)); + border: 1px solid var(--bs-secondary-color); + border-radius: 5px; + box-shadow: inset 0 0 2px #fff, 0 2px 2px -1px rgba(76, 69, 88, 0.15); + color: inherit; + font-size: 0.95em; + font-weight: 500; + padding: 0.475em 1em 0.6em; + -webkit-text-decoration: none; + text-decoration: none; +} +.aa-SourceFooterSeeAll:focus, .aa-SourceFooterSeeAll:hover { + border: 1px solid rgba(62, 52, 211, 1); + border: 1px solid rgba(var(--bs-primary-rgb), 1); + color: rgba(62, 52, 211, 1); + color: rgba(var(--bs-primary-rgb), 1); +} + +/* ----------------*/ +/* 6. Hit Layout*/ +/* ----------------*/ +.aa-Item { + align-items: center; + border-radius: 3px; + cursor: pointer; + display: grid; + min-height: calc((16 * 1 * 1px) * 2.5); + min-height: calc(calc(16 * 1 * 1px) * 2.5); + min-height: calc(var(--aa-spacing) * 2.5); + padding: calc(((16 * 1 * 1px) / 2) / 2); + padding: calc(calc(calc(16 * 1 * 1px) / 2) / 2); + padding: calc(var(--aa-spacing-half) / 2); + /* When the result is active*/ + /* The result type icon inlined SVG or image*/ + /* wrap hit with url but we don't need to see it*/ + /* Secondary click actions*/ +} +.aa-Item[aria-selected=true] { + background-color: var(--bs-tertiary-bg); +} +.aa-Item[aria-selected=true] .aa-ItemActionButton, +.aa-Item[aria-selected=true] .aa-ActiveOnly { + visibility: visible; +} +.aa-ItemIcon { + align-items: center; + background: var(--bs-body-bg); + border-radius: 3px; + box-shadow: inset 0 0 0 1px rgba(128, 126, 163, 0.3); + box-shadow: inset 0 0 0 1px rgba(var(--aa-panel-border-color-rgb), var(--aa-panel-border-color-alpha)); + color: rgba(119, 119, 163, 1); + color: rgba(var(--aa-icon-color-rgb), var(--aa-icon-color-alpha)); + display: flex; + flex-shrink: 0; + font-size: 0.7em; + height: calc(20px + ((16 * 1 * 1px) / 2)); + height: calc(20px + calc(calc(16 * 1 * 1px) / 2)); + height: calc(var(--aa-icon-size) + var(--aa-spacing-half)); + justify-content: center; + overflow: hidden; + stroke-width: 1.6; + stroke-width: var(--aa-icon-stroke-width); + text-align: center; + width: calc(20px + ((16 * 1 * 1px) / 2)); + width: calc(20px + calc(calc(16 * 1 * 1px) / 2)); + width: calc(var(--aa-icon-size) + var(--aa-spacing-half)); +} +.aa-ItemIcon img { + height: auto; + max-height: calc(20px + ((16 * 1 * 1px) / 2) - 8px); + max-height: calc(20px + calc(calc(16 * 1 * 1px) / 2) - 8px); + max-height: calc(var(--aa-icon-size) + var(--aa-spacing-half) - 8px); + max-width: calc(20px + ((16 * 1 * 1px) / 2) - 8px); + max-width: calc(20px + calc(calc(16 * 1 * 1px) / 2) - 8px); + max-width: calc(var(--aa-icon-size) + var(--aa-spacing-half) - 8px); + width: auto; +} +.aa-ItemIcon svg { + height: 20px; + height: var(--aa-icon-size); + width: 20px; + width: var(--aa-icon-size); +} +.aa-ItemIcon--alignTop { + align-self: flex-start; +} + +.aa-ItemIcon--noBorder { + background: none; + box-shadow: none; +} + +.aa-ItemIcon--picture { + height: 96px; + width: 96px; +} +.aa-ItemIcon--picture img { + max-height: 100%; + max-width: 100%; + padding: calc((16 * 1 * 1px) / 2); + padding: calc(calc(16 * 1 * 1px) / 2); + padding: var(--aa-spacing-half); +} + +.aa-ItemContent { + align-items: center; + cursor: pointer; + display: grid; + gap: calc((16 * 1 * 1px) / 2); + gap: calc(calc(16 * 1 * 1px) / 2); + grid-gap: calc((16 * 1 * 1px) / 2); + grid-gap: calc(calc(16 * 1 * 1px) / 2); + grid-gap: var(--aa-spacing-half); + gap: var(--aa-spacing-half); + grid-auto-flow: column; + line-height: 1.25em; + overflow: hidden; +} +.aa-ItemContent:empty { + display: none; +} +.aa-ItemContent mark { + background: var(--bs-highlight-bg); + color: var(--bs-body-color); + font-style: normal; + padding: 0; + font-weight: 700; + font-weight: var(--aa-font-weight-bold); +} +.aa-ItemContent--dual { + display: flex; + flex-direction: column; + justify-content: space-between; + text-align: left; +} +.aa-ItemContent--dual .aa-ItemContentTitle, +.aa-ItemContent--dual .aa-ItemContentSubtitle { + display: block; +} + +.aa-ItemContent--indented { + padding-left: calc(20px + (16 * 1 * 1px)); + padding-left: calc(20px + calc(16 * 1 * 1px)); + padding-left: calc(var(--aa-icon-size) + var(--aa-spacing)); +} + +.aa-ItemContentBody { + display: grid; + gap: calc(((16 * 1 * 1px) / 2) / 2); + gap: calc(calc(calc(16 * 1 * 1px) / 2) / 2); + grid-gap: calc(((16 * 1 * 1px) / 2) / 2); + grid-gap: calc(calc(calc(16 * 1 * 1px) / 2) / 2); + grid-gap: calc(var(--aa-spacing-half) / 2); + gap: calc(var(--aa-spacing-half) / 2); +} + +.aa-ItemContentTitle { + display: inline-block; + margin: 0 0.5em 0 0; + max-width: 100%; + overflow: hidden; + padding: 0; + text-overflow: ellipsis; + white-space: nowrap; +} + +.aa-ItemContentSubtitle { + font-size: 0.92em; +} +.aa-ItemContentSubtitleIcon::before { + border-color: var(--bs-tertiary-color); + border-style: solid; + content: ""; + display: inline-block; + left: 1px; + position: relative; + top: -3px; +} + +.aa-ItemContentSubtitle--inline .aa-ItemContentSubtitleIcon::before { + border-width: 0 0 1.5px; + margin-left: calc((16 * 1 * 1px) / 2); + margin-left: calc(calc(16 * 1 * 1px) / 2); + margin-left: var(--aa-spacing-half); + margin-right: calc(((16 * 1 * 1px) / 2) / 2); + margin-right: calc(calc(calc(16 * 1 * 1px) / 2) / 2); + margin-right: calc(var(--aa-spacing-half) / 2); + width: calc(((16 * 1 * 1px) / 2) + 2px); + width: calc(calc(calc(16 * 1 * 1px) / 2) + 2px); + width: calc(var(--aa-spacing-half) + 2px); +} + +.aa-ItemContentSubtitle--standalone { + align-items: center; + color: var(--bs-body-color); + display: grid; + gap: calc((16 * 1 * 1px) / 2); + gap: calc(calc(16 * 1 * 1px) / 2); + grid-gap: calc((16 * 1 * 1px) / 2); + grid-gap: calc(calc(16 * 1 * 1px) / 2); + grid-gap: var(--aa-spacing-half); + gap: var(--aa-spacing-half); + grid-auto-flow: column; + justify-content: start; +} +.aa-ItemContentSubtitle--standalone .aa-ItemContentSubtitleIcon::before { + border-radius: 0 0 0 3px; + border-width: 0 0 1.5px 1.5px; + height: calc((16 * 1 * 1px) / 2); + height: calc(calc(16 * 1 * 1px) / 2); + height: var(--aa-spacing-half); + width: calc((16 * 1 * 1px) / 2); + width: calc(calc(16 * 1 * 1px) / 2); + width: var(--aa-spacing-half); +} + +.aa-ItemContentSubtitleCategory { + color: var(--bs-secondary-color); + font-weight: 500; +} + +.aa-ItemContentDescription { + color: var(--bs-body-color); + font-size: 0.85em; + max-width: 100%; + overflow-x: hidden; + text-overflow: ellipsis; +} +.aa-ItemContentDescription:empty { + display: none; +} +.aa-ItemContentDescription mark { + background: rgba(245, 223, 77, 0.5); + background: rgba(var(--aa-description-highlight-background-color-rgb), var(--aa-description-highlight-background-color-alpha)); + color: rgba(38, 38, 39, 1); + color: rgba(var(--aa-text-color-rgb), var(--aa-text-color-alpha)); + font-style: normal; + font-weight: 500; + font-weight: var(--aa-font-weight-medium); +} + +.aa-ItemContentDash { + color: var(--bs-secondary-color); + display: none; + opacity: 0.4; +} + +.aa-ItemContentTag { + color: rgba(var(--bs-primary-rgb), 1.0);; + border-radius: 3px; + margin: 0 0.4em 0 0; + padding: 0.08em 0.3em; +} + +.aa-ItemWrapper, +.aa-ItemLink { + align-items: center; + color: inherit; + display: grid; + gap: calc(((16 * 1 * 1px) / 2) / 2); + gap: calc(calc(calc(16 * 1 * 1px) / 2) / 2); + grid-gap: calc(((16 * 1 * 1px) / 2) / 2); + grid-gap: calc(calc(calc(16 * 1 * 1px) / 2) / 2); + grid-gap: calc(var(--aa-spacing-half) / 2); + gap: calc(var(--aa-spacing-half) / 2); + grid-auto-flow: column; + justify-content: space-between; + width: 100%; +} + +.aa-ItemLink { + color: inherit; + -webkit-text-decoration: none; + text-decoration: none; +} + +.aa-ItemActions { + display: grid; + grid-auto-flow: column; + height: 100%; + justify-self: end; + margin: 0 calc((16 * 1 * 1px) / -3); + margin: 0 calc(calc(16 * 1 * 1px) / -3); + margin: 0 calc(var(--aa-spacing) / -3); + padding: 0 2px 0 0; +} + +.aa-ItemActionButton { + align-items: center; + background: none; + border: 0; + color: var(--bs-secondary-color); + cursor: pointer; + display: flex; + flex-shrink: 0; + padding: 0; +} +.aa-ItemActionButton:hover svg, .aa-ItemActionButton:focus svg { + color: var(--bs-body-color); +} +@media (hover: none) and (pointer: coarse) { + .aa-ItemActionButton:hover svg, .aa-ItemActionButton:focus svg { + color: inherit; + } +} +.aa-ItemActionButton svg { + color: var(--bs-secondary-color); + margin: 0; + margin: calc(calc(16 * 1 * 1px) / 3); + margin: calc(var(--aa-spacing) / 3); + stroke-width: 1.6; + stroke-width: var(--aa-icon-stroke-width); + width: 20px; + width: var(--aa-action-icon-size); +} + +.aa-ActiveOnly { + visibility: hidden; +} + +/*----------------*/ +/* 7. Panel Header*/ +/*----------------*/ +.aa-PanelHeader { + align-items: center; + background: var(--bs-primary-bg-subtle); + color: #fff; + display: grid; + height: var(--aa-modal-header-height); + margin: 0; + padding: calc((16 * 1 * 1px) / 2) calc(16 * 1 * 1px); + padding: calc(calc(16 * 1 * 1px) / 2) calc(16 * 1 * 1px); + padding: var(--aa-spacing-half) var(--aa-spacing); + position: relative; +} +.aa-PanelHeader::after { + background-image: linear-gradient(rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); + background-image: linear-gradient(rgba(var(--aa-background-color-rgb), 1), rgba(var(--aa-background-color-rgb), 0)); + bottom: calc(((16 * 1 * 1px) / 2) * -1); + bottom: calc(calc(calc(16 * 1 * 1px) / 2) * -1); + bottom: calc(var(--aa-spacing-half) * -1); + content: ""; + height: calc((16 * 1 * 1px) / 2); + height: calc(calc(16 * 1 * 1px) / 2); + height: var(--aa-spacing-half); + left: 0; + pointer-events: none; + position: absolute; + right: 0; + z-index: 9999; + z-index: var(--aa-base-z-index); +} + +/*----------------*/ +/* 8. Panel Footer*/ +/*----------------*/ +.aa-PanelFooter { + background-color: var(--bs-body-bg); + box-shadow: inset 0 1px 0 var(--bs-dropdown-border-color); + display: flex; + justify-content: space-between; + margin: 0; + padding: calc(16 * 1 * 1px); + padding: var(--aa-spacing); + position: relative; + z-index: 9999; + z-index: var(--aa-base-z-index); +} +.aa-PanelFooter::after { + background-image: linear-gradient(rgba(255, 255, 255, 0), rgba(128, 126, 163, 0.6)); + background-image: linear-gradient(rgba(var(--aa-background-color-rgb), 0), rgba(var(--aa-muted-color-rgb), var(--aa-muted-color-alpha))); + content: ""; + height: calc(16 * 1 * 1px); + height: var(--aa-spacing); + left: 0; + opacity: 0.12; + pointer-events: none; + position: absolute; + right: 0; + top: calc((16 * 1 * 1px) * -1); + top: calc(calc(16 * 1 * 1px) * -1); + top: calc(var(--aa-spacing) * -1); + z-index: calc(9999 - 1); + z-index: calc(var(--aa-base-z-index) - 1); +} + +/*----------------*/ +/* 9. Detached Mode*/ +/*----------------*/ +.aa-DetachedContainer { + background: var(--bs-body-bg); + bottom: 0; + box-shadow: 0 0 0 1px rgba(35, 38, 59, 0.1), + 0 6px 16px -4px rgba(35, 38, 59, 0.15); + box-shadow: var(--aa-panel-shadow); + display: flex; + flex-direction: column; + left: 0; + margin: 0; + overflow: hidden; + padding: 0; + position: fixed; + right: 0; + top: 0; + z-index: 9999; + z-index: var(--aa-base-z-index); +} +.aa-DetachedContainer::after { + height: 32px; +} +.aa-DetachedContainer .aa-SourceHeader { + margin: calc((16 * 1 * 1px) / 2) 0 calc((16 * 1 * 1px) / 2) 2px; + margin: calc(calc(16 * 1 * 1px) / 2) 0 calc(calc(16 * 1 * 1px) / 2) 2px; + margin: var(--aa-spacing-half) 0 var(--aa-spacing-half) 2px; +} +.aa-DetachedContainer .aa-Panel { + background-color: var(--bs-body-bg); + border-radius: 0; + box-shadow: none; + flex-grow: 1; + margin: 0; + padding: 0; + position: relative; +} +.aa-DetachedContainer .aa-PanelLayout { + bottom: 0; + box-shadow: none; + left: 0; + margin: 0; + max-height: none; + overflow-y: auto; + position: absolute; + right: 0; + top: 0; + width: 100%; +} +.aa-DetachedFormContainer { + border-bottom: solid 1px rgba(128, 126, 163, 0.3); + border-bottom: solid 1px rgba(var(--aa-panel-border-color-rgb), var(--aa-panel-border-color-alpha)); + display: flex; + flex-direction: row; + justify-content: space-between; + margin: 0; + padding: calc((16 * 1 * 1px) / 2); + padding: calc(calc(16 * 1 * 1px) / 2); + padding: var(--aa-spacing-half); +} +.aa-DetachedCancelButton { + background: none; + border: 0; + border-radius: 3px; + color: var(--bs-body-color); + cursor: pointer; + font: inherit; + margin: 0 0 0 calc((16 * 1 * 1px) / 2); + margin: 0 0 0 calc(calc(16 * 1 * 1px) / 2); + margin: 0 0 0 var(--aa-spacing-half); + padding: 0 calc((16 * 1 * 1px) / 2); + padding: 0 calc(calc(16 * 1 * 1px) / 2); + padding: 0 var(--aa-spacing-half); +} +.aa-DetachedCancelButton:hover, .aa-DetachedCancelButton:focus { + box-shadow: inset 0 0 0 1px rgba(128, 126, 163, 0.3); + box-shadow: inset 0 0 0 1px rgba(var(--aa-panel-border-color-rgb), var(--aa-panel-border-color-alpha)); +} + +.aa-DetachedContainer--modal { + border-radius: 6px; + bottom: inherit; + height: auto; + margin: 0 auto; + max-width: 680px; + max-width: var(--aa-detached-modal-max-width); + position: absolute; + top: 3%; +} +.aa-DetachedContainer--modal .aa-PanelLayout { + max-height: 500px; + max-height: var(--aa-detached-modal-max-height); + padding-bottom: calc((16 * 1 * 1px) / 2); + padding-bottom: calc(calc(16 * 1 * 1px) / 2); + padding-bottom: var(--aa-spacing-half); + position: static; +} +.aa-DetachedContainer--modal .aa-PanelLayout:empty { + display: none; +} + +/* Search Button*/ +.aa-DetachedSearchButton { + align-items: center; + background-color: var(--bs-body-bg); + border: 1px solid var(--bs-secondary-border-subtle); + border-radius: 3px; + color: var(--bs-secondary-color); + cursor: pointer; + display: flex; + font: inherit; + font-family: inherit; + font-family: var(--aa-font-family); + font-size: calc(16 * 1px); + font-size: var(--aa-font-size); + height: 44px; + height: var(--aa-search-input-height); + margin: 0; + padding: 0 calc(44px / 8); + padding: 0 calc(var(--aa-search-input-height) / 8); + position: relative; + text-align: left; + width: 100%; +} +.aa-DetachedSearchButton:focus { + border-color: var(--bs-primary-border-subtle); + box-shadow: rgba(62, 52, 211, 0.2) 0 0 0 3px, inset rgba(62, 52, 211, 0.2) 0 0 0 2px; + box-shadow: var(--bs-primary-border-subtle) 0 0 0 3px, inset var(--bs-primary-border-subtle) 0 0 0 2px; + outline: currentColor none medium; +} +.aa-DetachedSearchButtonIcon { + align-items: center; + color: rgba(var(--bs-primary-rgb), 1.0); + cursor: auto; + cursor: initial; + display: flex; + flex-shrink: 0; + height: 100%; + justify-content: center; + width: calc(20px + (16 * 1 * 1px)); + width: calc(20px + calc(16 * 1 * 1px)); + width: calc(var(--aa-icon-size) + var(--aa-spacing)); +} + +.aa-DetachedSearchButtonQuery { + color: var(--bs-body-color); + line-height: 1.25em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.aa-DetachedSearchButtonPlaceholder[hidden] { + display: none; +} + +/* Remove scroll on `body`*/ +.aa-Detached { + height: 100vh; + overflow: hidden; +} + +.aa-DetachedOverlay { + background-color: rgba(115, 114, 129, 0.4); + background-color: rgba(var(--aa-overlay-color-rgb), var(--aa-overlay-color-alpha)); + height: 100vh; + left: 0; + margin: 0; + padding: 0; + position: fixed; + right: 0; + top: 0; + z-index: calc(9999 - 1); + z-index: calc(var(--aa-base-z-index) - 1); +} + +/*----------------*/ +/* 10. Gradients*/ +/*----------------*/ +.aa-GradientTop, +.aa-GradientBottom { + height: calc((16 * 1 * 1px) / 2); + height: calc(calc(16 * 1 * 1px) / 2); + height: var(--aa-spacing-half); + left: 0; + pointer-events: none; + position: absolute; + right: 0; + z-index: 9999; + z-index: var(--aa-base-z-index); +} + +.aa-GradientTop { + background-image: linear-gradient(rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); + background-image: linear-gradient(rgba(var(--aa-background-color-rgb), 1), rgba(var(--aa-background-color-rgb), 0)); + top: 0; +} + +.aa-GradientBottom { + background-image: linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)); + background-image: linear-gradient(rgba(var(--aa-background-color-rgb), 0), rgba(var(--aa-background-color-rgb), 1)); + border-bottom-left-radius: calc((16 * 1 * 1px) / 4); + border-bottom-left-radius: calc(calc(16 * 1 * 1px) / 4); + border-bottom-left-radius: calc(var(--aa-spacing) / 4); + border-bottom-right-radius: calc((16 * 1 * 1px) / 4); + border-bottom-right-radius: calc(calc(16 * 1 * 1px) / 4); + border-bottom-right-radius: calc(var(--aa-spacing) / 4); + bottom: 0; +} + +/*----------------*/ +/* 11. Utilities*/ +/*----------------*/ +@media (hover: none) and (pointer: coarse) { + .aa-DesktopOnly { + display: none; + } +} + +@media (hover: hover) { + .aa-TouchOnly { + display: none; + } +} \ No newline at end of file diff --git a/assets/css/components/ckeditor.css b/assets/css/components/ckeditor.css index 1904e701..d6b3def4 100644 --- a/assets/css/components/ckeditor.css +++ b/assets/css/components/ckeditor.css @@ -24,9 +24,8 @@ /** Should be the same settings, as in label_style.css */ .ck-html-label .ck-content { font-family: "DejaVu Sans Mono", monospace; - font-size: 12px; + font-size: 12pt; line-height: 1.0; - font-size-adjust: 1.5; } .ck-html-label .ck-content p { @@ -36,3 +35,42 @@ .ck-html-label .ck-content hr { margin: 2px; } + +/*********************************************** + * Hide CKEditor powered by message + ***********************************************/ +.ck-powered-by { + display: none; +} + + + + +/*********************************************** + * Use Bootstrap color vars for CKEditor + ***********************************************/ +:root { + --ck-color-base-foreground: var(--bs-secondary-bg); + --ck-color-base-background: var(--bs-body-bg); + --ck-color-base-border: var(--bs-border-color); + --ck-color-base-action: var(--bs-success); + --ck-color-base-focus: var(--bs-primary-border-subtle); + --ck-color-base-text: var(--bs-body-color); + --ck-color-base-active: var(--bs-primary-bg-subtle); + --ck-color-base-active-focus: var(--bs-primary); + --ck-color-base-error: var(--bs-danger); + + /* Improve contrast between text and toolbar */ + --ck-color-toolbar-background: var(--bs-tertiary-bg); + + /* Buttons */ + --ck-color-button-default-hover-background: var(--bs-secondary-bg); + --ck-color-button-default-active-background: var(--bs-secondary-bg); + + --ck-color-button-on-background: var(--bs-body-bg); + --ck-color-button-on-hover-background: var(--bs-secondary-bg); + --ck-color-button-on-active-background: var(--bs-secondary-bg); + --ck-color-button-on-disabled-background: var(--bs-secondary-bg); + --ck-color-button-on-color: var(--bs-primary) + +} \ No newline at end of file diff --git a/assets/css/components/datatables_select_bs5.css b/assets/css/components/datatables_select_bs5.css new file mode 100644 index 00000000..7c717bf4 --- /dev/null +++ b/assets/css/components/datatables_select_bs5.css @@ -0,0 +1,69 @@ +/****************************************************************************************** +* This styles the checkboxes of the select extension exactly like the ones in bootstrap 5 +******************************************************************************************/ + +table.dataTable > tbody > tr > .selected { + background-color: var(--bs-primary-bg-subtle) !important; + color: white; +} +table.dataTable > tbody > tr > .dt-select { + text-align: center; + vertical-align: middle; +} +table.dataTable > thead > tr > .dt-select { + text-align: center; +} +table.dataTable input.dt-select-checkbox { + --bs-form-check-bg: var(--bs-body-bg); + flex-shrink: 0; + width: 1em; + height: 1em; + margin-top: 0.25em; + vertical-align: top; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--bs-form-check-bg); + background-image: var(--bs-form-check-bg-image); + background-repeat: no-repeat; + background-position: center; + background-size: contain; + border: var(--bs-border-width) solid var(--bs-border-color); + -webkit-print-color-adjust: exact; + color-adjust: exact; + print-color-adjust: exact; + border-radius: 0.25em; +} + +table.dataTable input.dt-select-checkbox:checked { + background-color: rgb(var(--bs-secondary-rgb)); + border-color: rgb(var(--bs-secondary-rgb)); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); +} + +table.dataTable input.dt-select-checkbox:indeterminate { + background-color: rgb(var(--bs-secondary-rgb)); + border-color: rgb(var(--bs-secondary-rgb)); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); +} + + + + +div.dt-container span.select-info, +div.dt-container span.select-item { + margin-left: 0.5em; +} + + +@media screen and (max-width: 640px) { + div.dt-container span.select-info, + div.dt-container span.select-item { + margin-left: 0; + display: block; + } +} +table.dataTable.table-sm tbody td.select-checkbox::before { + margin-top: -9px; +} + diff --git a/assets/css/components/tom-select_extensions.css b/assets/css/components/tom-select_extensions.css index 326731c9..b0529c25 100644 --- a/assets/css/components/tom-select_extensions.css +++ b/assets/css/components/tom-select_extensions.css @@ -18,6 +18,29 @@ */ .tagsinput.ts-wrapper.multi .ts-control > div { - background: var(--bs-secondary); - color: var(--bs-white); -} \ No newline at end of file + background: var(--bs-secondary-bg); + color: var(--bs-body-color); +} + +/********* + * BS 5.3 compatible dark mode + ***************/ + +.ts-dropdown .active { + background-color: var(--bs-secondary-bg) !important; + color: var(--bs-body-color) !important; +} + +.ts-dropdown, .ts-control, .ts-control input { + color: var(--bs-body-color) !important; +} + +.ts-dropdown, .ts-dropdown.form-control, .ts-dropdown.form-select { + background: var(--bs-body-bg); +} + +.ts-dropdown .optgroup-header { + color: var(--bs-tertiary-color); + background: var(--bs-body-bg); + cursor: default; +} diff --git a/assets/css/email/foundation-emails.css b/assets/css/email/foundation-emails.css index 723728d3..2b62bc09 100644 --- a/assets/css/email/foundation-emails.css +++ b/assets/css/email/foundation-emails.css @@ -1,594 +1,708 @@ -/* - * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). +/** + * Copyright (c) 2017 ZURB, inc. * - * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics) + * MIT License * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following conditions: * - * 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. + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ .wrapper { - width: 100%; } + width: 100%; +} #outlook a { - padding: 0; } + padding: 0; +} body { - width: 100% !important; - min-width: 100%; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; - margin: 0; - Margin: 0; - padding: 0; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } + width: 100% !important; + min-width: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + margin: 0; + Margin: 0; + padding: 0; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} .ExternalClass { - width: 100%; } - .ExternalClass, - .ExternalClass p, - .ExternalClass span, - .ExternalClass font, - .ExternalClass td, - .ExternalClass div { - line-height: 100%; } + width: 100%; +} + +.ExternalClass, +.ExternalClass p, +.ExternalClass span, +.ExternalClass font, +.ExternalClass td, +.ExternalClass div { + line-height: 100%; +} #backgroundTable { - margin: 0; - Margin: 0; - padding: 0; - width: 100% !important; - line-height: 100% !important; } + margin: 0; + Margin: 0; + padding: 0; + width: 100% !important; + line-height: 100% !important; +} img { - outline: none; - text-decoration: none; - -ms-interpolation-mode: bicubic; - width: auto; - max-width: 100%; - clear: both; - display: block; } + outline: none; + text-decoration: none; + -ms-interpolation-mode: bicubic; + width: auto; + max-width: 100%; + clear: both; + display: block; +} center { - width: 100%; - min-width: 580px; } + width: 100%; + min-width: 580px; +} a img { - border: none; } + border: none; +} p { - margin: 0 0 0 10px; - Margin: 0 0 0 10px; } + margin: 0 0 0 10px; + Margin: 0 0 0 10px; +} table { - border-spacing: 0; - border-collapse: collapse; } + border-spacing: 0; + border-collapse: collapse; +} td { - word-wrap: break-word; - -webkit-hyphens: auto; - -moz-hyphens: auto; - hyphens: auto; - border-collapse: collapse !important; } + word-wrap: break-word; + -webkit-hyphens: auto; + -moz-hyphens: auto; + hyphens: auto; + border-collapse: collapse !important; +} table, tr, td { - padding: 0; - vertical-align: top; - text-align: left; } + padding: 0; + vertical-align: top; + text-align: left; +} @media only screen { - html { - min-height: 100%; - background: #f3f3f3; } } + html { + min-height: 100%; + background: #f3f3f3; + } +} table.body { - background: #f3f3f3; - height: 100%; - width: 100%; } + background: #f3f3f3; + height: 100%; + width: 100%; +} table.container { - background: #fefefe; - width: 580px; - margin: 0 auto; - Margin: 0 auto; - text-align: inherit; } + background: #fefefe; + width: 580px; + margin: 0 auto; + Margin: 0 auto; + text-align: inherit; +} table.row { - padding: 0; - width: 100%; - position: relative; } + padding: 0; + width: 100%; + position: relative; +} table.spacer { - width: 100%; } - table.spacer td { - mso-line-height-rule: exactly; } + width: 100%; +} + +table.spacer td { + mso-line-height-rule: exactly; +} table.container table.row { - display: table; } + display: table; +} td.columns, td.column, th.columns, th.column { - margin: 0 auto; - Margin: 0 auto; - padding-left: 16px; - padding-bottom: 16px; } - td.columns .column, - td.columns .columns, - td.column .column, - td.column .columns, - th.columns .column, - th.columns .columns, - th.column .column, - th.column .columns { + margin: 0 auto; + Margin: 0 auto; + padding-left: 16px; + padding-bottom: 16px; +} + +td.columns .column, +td.columns .columns, +td.column .column, +td.column .columns, +th.columns .column, +th.columns .columns, +th.column .column, +th.column .columns { padding-left: 0 !important; - padding-right: 0 !important; } - td.columns .column center, - td.columns .columns center, - td.column .column center, - td.column .columns center, - th.columns .column center, - th.columns .columns center, - th.column .column center, - th.column .columns center { - min-width: none !important; } + padding-right: 0 !important; +} + +td.columns .column center, +td.columns .columns center, +td.column .column center, +td.column .columns center, +th.columns .column center, +th.columns .columns center, +th.column .column center, +th.column .columns center { + min-width: none !important; +} td.columns.last, td.column.last, th.columns.last, th.column.last { - padding-right: 16px; } + padding-right: 16px; +} td.columns table:not(.button), td.column table:not(.button), th.columns table:not(.button), th.column table:not(.button) { - width: 100%; } + width: 100%; +} td.large-1, th.large-1 { - width: 32.33333px; - padding-left: 8px; - padding-right: 8px; } + width: 32.33333px; + padding-left: 8px; + padding-right: 8px; +} td.large-1.first, th.large-1.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-1.last, th.large-1.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-1, .collapse > tbody > tr > th.large-1 { - padding-right: 0; - padding-left: 0; - width: 48.33333px; } + padding-right: 0; + padding-left: 0; + width: 48.33333px; +} .collapse td.large-1.first, .collapse th.large-1.first, .collapse td.large-1.last, .collapse th.large-1.last { - width: 56.33333px; } + width: 56.33333px; +} td.large-1 center, th.large-1 center { - min-width: 0.33333px; } + min-width: 0.33333px; +} .body .columns td.large-1, .body .column td.large-1, .body .columns th.large-1, .body .column th.large-1 { - width: 8.33333%; } + width: 8.33333%; +} td.large-2, th.large-2 { - width: 80.66667px; - padding-left: 8px; - padding-right: 8px; } + width: 80.66667px; + padding-left: 8px; + padding-right: 8px; +} td.large-2.first, th.large-2.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-2.last, th.large-2.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-2, .collapse > tbody > tr > th.large-2 { - padding-right: 0; - padding-left: 0; - width: 96.66667px; } + padding-right: 0; + padding-left: 0; + width: 96.66667px; +} .collapse td.large-2.first, .collapse th.large-2.first, .collapse td.large-2.last, .collapse th.large-2.last { - width: 104.66667px; } + width: 104.66667px; +} td.large-2 center, th.large-2 center { - min-width: 48.66667px; } + min-width: 48.66667px; +} .body .columns td.large-2, .body .column td.large-2, .body .columns th.large-2, .body .column th.large-2 { - width: 16.66667%; } + width: 16.66667%; +} td.large-3, th.large-3 { - width: 129px; - padding-left: 8px; - padding-right: 8px; } + width: 129px; + padding-left: 8px; + padding-right: 8px; +} td.large-3.first, th.large-3.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-3.last, th.large-3.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-3, .collapse > tbody > tr > th.large-3 { - padding-right: 0; - padding-left: 0; - width: 145px; } + padding-right: 0; + padding-left: 0; + width: 145px; +} .collapse td.large-3.first, .collapse th.large-3.first, .collapse td.large-3.last, .collapse th.large-3.last { - width: 153px; } + width: 153px; +} td.large-3 center, th.large-3 center { - min-width: 97px; } + min-width: 97px; +} .body .columns td.large-3, .body .column td.large-3, .body .columns th.large-3, .body .column th.large-3 { - width: 25%; } + width: 25%; +} td.large-4, th.large-4 { - width: 177.33333px; - padding-left: 8px; - padding-right: 8px; } + width: 177.33333px; + padding-left: 8px; + padding-right: 8px; +} td.large-4.first, th.large-4.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-4.last, th.large-4.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-4, .collapse > tbody > tr > th.large-4 { - padding-right: 0; - padding-left: 0; - width: 193.33333px; } + padding-right: 0; + padding-left: 0; + width: 193.33333px; +} .collapse td.large-4.first, .collapse th.large-4.first, .collapse td.large-4.last, .collapse th.large-4.last { - width: 201.33333px; } + width: 201.33333px; +} td.large-4 center, th.large-4 center { - min-width: 145.33333px; } + min-width: 145.33333px; +} .body .columns td.large-4, .body .column td.large-4, .body .columns th.large-4, .body .column th.large-4 { - width: 33.33333%; } + width: 33.33333%; +} td.large-5, th.large-5 { - width: 225.66667px; - padding-left: 8px; - padding-right: 8px; } + width: 225.66667px; + padding-left: 8px; + padding-right: 8px; +} td.large-5.first, th.large-5.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-5.last, th.large-5.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-5, .collapse > tbody > tr > th.large-5 { - padding-right: 0; - padding-left: 0; - width: 241.66667px; } + padding-right: 0; + padding-left: 0; + width: 241.66667px; +} .collapse td.large-5.first, .collapse th.large-5.first, .collapse td.large-5.last, .collapse th.large-5.last { - width: 249.66667px; } + width: 249.66667px; +} td.large-5 center, th.large-5 center { - min-width: 193.66667px; } + min-width: 193.66667px; +} .body .columns td.large-5, .body .column td.large-5, .body .columns th.large-5, .body .column th.large-5 { - width: 41.66667%; } + width: 41.66667%; +} td.large-6, th.large-6 { - width: 274px; - padding-left: 8px; - padding-right: 8px; } + width: 274px; + padding-left: 8px; + padding-right: 8px; +} td.large-6.first, th.large-6.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-6.last, th.large-6.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-6, .collapse > tbody > tr > th.large-6 { - padding-right: 0; - padding-left: 0; - width: 290px; } + padding-right: 0; + padding-left: 0; + width: 290px; +} .collapse td.large-6.first, .collapse th.large-6.first, .collapse td.large-6.last, .collapse th.large-6.last { - width: 298px; } + width: 298px; +} td.large-6 center, th.large-6 center { - min-width: 242px; } + min-width: 242px; +} .body .columns td.large-6, .body .column td.large-6, .body .columns th.large-6, .body .column th.large-6 { - width: 50%; } + width: 50%; +} td.large-7, th.large-7 { - width: 322.33333px; - padding-left: 8px; - padding-right: 8px; } + width: 322.33333px; + padding-left: 8px; + padding-right: 8px; +} td.large-7.first, th.large-7.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-7.last, th.large-7.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-7, .collapse > tbody > tr > th.large-7 { - padding-right: 0; - padding-left: 0; - width: 338.33333px; } + padding-right: 0; + padding-left: 0; + width: 338.33333px; +} .collapse td.large-7.first, .collapse th.large-7.first, .collapse td.large-7.last, .collapse th.large-7.last { - width: 346.33333px; } + width: 346.33333px; +} td.large-7 center, th.large-7 center { - min-width: 290.33333px; } + min-width: 290.33333px; +} .body .columns td.large-7, .body .column td.large-7, .body .columns th.large-7, .body .column th.large-7 { - width: 58.33333%; } + width: 58.33333%; +} td.large-8, th.large-8 { - width: 370.66667px; - padding-left: 8px; - padding-right: 8px; } + width: 370.66667px; + padding-left: 8px; + padding-right: 8px; +} td.large-8.first, th.large-8.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-8.last, th.large-8.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-8, .collapse > tbody > tr > th.large-8 { - padding-right: 0; - padding-left: 0; - width: 386.66667px; } + padding-right: 0; + padding-left: 0; + width: 386.66667px; +} .collapse td.large-8.first, .collapse th.large-8.first, .collapse td.large-8.last, .collapse th.large-8.last { - width: 394.66667px; } + width: 394.66667px; +} td.large-8 center, th.large-8 center { - min-width: 338.66667px; } + min-width: 338.66667px; +} .body .columns td.large-8, .body .column td.large-8, .body .columns th.large-8, .body .column th.large-8 { - width: 66.66667%; } + width: 66.66667%; +} td.large-9, th.large-9 { - width: 419px; - padding-left: 8px; - padding-right: 8px; } + width: 419px; + padding-left: 8px; + padding-right: 8px; +} td.large-9.first, th.large-9.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-9.last, th.large-9.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-9, .collapse > tbody > tr > th.large-9 { - padding-right: 0; - padding-left: 0; - width: 435px; } + padding-right: 0; + padding-left: 0; + width: 435px; +} .collapse td.large-9.first, .collapse th.large-9.first, .collapse td.large-9.last, .collapse th.large-9.last { - width: 443px; } + width: 443px; +} td.large-9 center, th.large-9 center { - min-width: 387px; } + min-width: 387px; +} .body .columns td.large-9, .body .column td.large-9, .body .columns th.large-9, .body .column th.large-9 { - width: 75%; } + width: 75%; +} td.large-10, th.large-10 { - width: 467.33333px; - padding-left: 8px; - padding-right: 8px; } + width: 467.33333px; + padding-left: 8px; + padding-right: 8px; +} td.large-10.first, th.large-10.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-10.last, th.large-10.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-10, .collapse > tbody > tr > th.large-10 { - padding-right: 0; - padding-left: 0; - width: 483.33333px; } + padding-right: 0; + padding-left: 0; + width: 483.33333px; +} .collapse td.large-10.first, .collapse th.large-10.first, .collapse td.large-10.last, .collapse th.large-10.last { - width: 491.33333px; } + width: 491.33333px; +} td.large-10 center, th.large-10 center { - min-width: 435.33333px; } + min-width: 435.33333px; +} .body .columns td.large-10, .body .column td.large-10, .body .columns th.large-10, .body .column th.large-10 { - width: 83.33333%; } + width: 83.33333%; +} td.large-11, th.large-11 { - width: 515.66667px; - padding-left: 8px; - padding-right: 8px; } + width: 515.66667px; + padding-left: 8px; + padding-right: 8px; +} td.large-11.first, th.large-11.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-11.last, th.large-11.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-11, .collapse > tbody > tr > th.large-11 { - padding-right: 0; - padding-left: 0; - width: 531.66667px; } + padding-right: 0; + padding-left: 0; + width: 531.66667px; +} .collapse td.large-11.first, .collapse th.large-11.first, .collapse td.large-11.last, .collapse th.large-11.last { - width: 539.66667px; } + width: 539.66667px; +} td.large-11 center, th.large-11 center { - min-width: 483.66667px; } + min-width: 483.66667px; +} .body .columns td.large-11, .body .column td.large-11, .body .columns th.large-11, .body .column th.large-11 { - width: 91.66667%; } + width: 91.66667%; +} td.large-12, th.large-12 { - width: 564px; - padding-left: 8px; - padding-right: 8px; } + width: 564px; + padding-left: 8px; + padding-right: 8px; +} td.large-12.first, th.large-12.first { - padding-left: 16px; } + padding-left: 16px; +} td.large-12.last, th.large-12.last { - padding-right: 16px; } + padding-right: 16px; +} .collapse > tbody > tr > td.large-12, .collapse > tbody > tr > th.large-12 { - padding-right: 0; - padding-left: 0; - width: 580px; } + padding-right: 0; + padding-left: 0; + width: 580px; +} .collapse td.large-12.first, .collapse th.large-12.first, .collapse td.large-12.last, .collapse th.large-12.last { - width: 588px; } + width: 588px; +} td.large-12 center, th.large-12 center { - min-width: 532px; } + min-width: 532px; +} .body .columns td.large-12, .body .column td.large-12, .body .columns th.large-12, .body .column th.large-12 { - width: 100%; } + width: 100%; +} td.large-offset-1, td.large-offset-1.first, @@ -596,7 +710,8 @@ td.large-offset-1.last, th.large-offset-1, th.large-offset-1.first, th.large-offset-1.last { - padding-left: 64.33333px; } + padding-left: 64.33333px; +} td.large-offset-2, td.large-offset-2.first, @@ -604,7 +719,8 @@ td.large-offset-2.last, th.large-offset-2, th.large-offset-2.first, th.large-offset-2.last { - padding-left: 112.66667px; } + padding-left: 112.66667px; +} td.large-offset-3, td.large-offset-3.first, @@ -612,7 +728,8 @@ td.large-offset-3.last, th.large-offset-3, th.large-offset-3.first, th.large-offset-3.last { - padding-left: 161px; } + padding-left: 161px; +} td.large-offset-4, td.large-offset-4.first, @@ -620,7 +737,8 @@ td.large-offset-4.last, th.large-offset-4, th.large-offset-4.first, th.large-offset-4.last { - padding-left: 209.33333px; } + padding-left: 209.33333px; +} td.large-offset-5, td.large-offset-5.first, @@ -628,7 +746,8 @@ td.large-offset-5.last, th.large-offset-5, th.large-offset-5.first, th.large-offset-5.last { - padding-left: 257.66667px; } + padding-left: 257.66667px; +} td.large-offset-6, td.large-offset-6.first, @@ -636,7 +755,8 @@ td.large-offset-6.last, th.large-offset-6, th.large-offset-6.first, th.large-offset-6.last { - padding-left: 306px; } + padding-left: 306px; +} td.large-offset-7, td.large-offset-7.first, @@ -644,7 +764,8 @@ td.large-offset-7.last, th.large-offset-7, th.large-offset-7.first, th.large-offset-7.last { - padding-left: 354.33333px; } + padding-left: 354.33333px; +} td.large-offset-8, td.large-offset-8.first, @@ -652,7 +773,8 @@ td.large-offset-8.last, th.large-offset-8, th.large-offset-8.first, th.large-offset-8.last { - padding-left: 402.66667px; } + padding-left: 402.66667px; +} td.large-offset-9, td.large-offset-9.first, @@ -660,7 +782,8 @@ td.large-offset-9.last, th.large-offset-9, th.large-offset-9.first, th.large-offset-9.last { - padding-left: 451px; } + padding-left: 451px; +} td.large-offset-10, td.large-offset-10.first, @@ -668,7 +791,8 @@ td.large-offset-10.last, th.large-offset-10, th.large-offset-10.first, th.large-offset-10.last { - padding-left: 499.33333px; } + padding-left: 499.33333px; +} td.large-offset-11, td.large-offset-11.first, @@ -676,45 +800,58 @@ td.large-offset-11.last, th.large-offset-11, th.large-offset-11.first, th.large-offset-11.last { - padding-left: 547.66667px; } + padding-left: 547.66667px; +} td.expander, th.expander { - visibility: hidden; - width: 0; - padding: 0 !important; } + visibility: hidden; + width: 0; + padding: 0 !important; +} table.container.radius { - border-radius: 0; - border-collapse: separate; } + border-radius: 0; + border-collapse: separate; +} .block-grid { - width: 100%; - max-width: 580px; } - .block-grid td { + width: 100%; + max-width: 580px; +} + +.block-grid td { display: inline-block; - padding: 8px; } + padding: 8px; +} .up-2 td { - width: 274px !important; } + width: 274px !important; +} .up-3 td { - width: 177px !important; } + width: 177px !important; +} .up-4 td { - width: 129px !important; } + width: 129px !important; +} .up-5 td { - width: 100px !important; } + width: 100px !important; +} .up-6 td { - width: 80px !important; } + width: 80px !important; +} .up-7 td { - width: 66px !important; } + width: 66px !important; +} .up-8 td { - width: 56px !important; } + width: 56px !important; +} table.text-center, th.text-center, @@ -727,7 +864,8 @@ h5.text-center, h6.text-center, p.text-center, span.text-center { - text-align: center; } + text-align: center; +} table.text-left, th.text-left, @@ -740,7 +878,8 @@ h5.text-left, h6.text-left, p.text-left, span.text-left { - text-align: left; } + text-align: left; +} table.text-right, th.text-right, @@ -753,85 +892,110 @@ h5.text-right, h6.text-right, p.text-right, span.text-right { - text-align: right; } + text-align: right; +} span.text-center { - display: block; - width: 100%; - text-align: center; } + display: block; + width: 100%; + text-align: center; +} @media only screen and (max-width: 596px) { - .small-float-center { - margin: 0 auto !important; - float: none !important; - text-align: center !important; } - .small-text-center { - text-align: center !important; } - .small-text-left { - text-align: left !important; } - .small-text-right { - text-align: right !important; } } + .small-float-center { + margin: 0 auto !important; + float: none !important; + text-align: center !important; + } + + .small-text-center { + text-align: center !important; + } + + .small-text-left { + text-align: left !important; + } + + .small-text-right { + text-align: right !important; + } +} img.float-left { - float: left; - text-align: left; } + float: left; + text-align: left; +} img.float-right { - float: right; - text-align: right; } + float: right; + text-align: right; +} img.float-center, img.text-center { - margin: 0 auto; - Margin: 0 auto; - float: none; - text-align: center; } + margin: 0 auto; + Margin: 0 auto; + float: none; + text-align: center; +} table.float-center, td.float-center, th.float-center { - margin: 0 auto; - Margin: 0 auto; - float: none; - text-align: center; } + margin: 0 auto; + Margin: 0 auto; + float: none; + text-align: center; +} .hide-for-large { - display: none !important; - mso-hide: all; - overflow: hidden; - max-height: 0; - font-size: 0; - width: 0; - line-height: 0; } - @media only screen and (max-width: 596px) { + display: none !important; + mso-hide: all; + overflow: hidden; + max-height: 0; + font-size: 0; + width: 0; + line-height: 0; +} + +@media only screen and (max-width: 596px) { .hide-for-large { - display: block !important; - width: auto !important; - overflow: visible !important; - max-height: none !important; - font-size: inherit !important; - line-height: inherit !important; } } + display: block !important; + width: auto !important; + overflow: visible !important; + max-height: none !important; + font-size: inherit !important; + line-height: inherit !important; + } +} table.body table.container .hide-for-large * { - mso-hide: all; } - -@media only screen and (max-width: 596px) { - table.body table.container .hide-for-large, - table.body table.container .row.hide-for-large { - display: table !important; - width: 100% !important; } } - -@media only screen and (max-width: 596px) { - table.body table.container .callout-inner.hide-for-large { - display: table-cell !important; - width: 100% !important; } } - -@media only screen and (max-width: 596px) { - table.body table.container .show-for-large { - display: none !important; - width: 0; mso-hide: all; - overflow: hidden; } } +} + +@media only screen and (max-width: 596px) { + table.body table.container .hide-for-large, + table.body table.container .row.hide-for-large { + display: table !important; + width: 100% !important; + } +} + +@media only screen and (max-width: 596px) { + table.body table.container .callout-inner.hide-for-large { + display: table-cell !important; + width: 100% !important; + } +} + +@media only screen and (max-width: 596px) { + table.body table.container .show-for-large { + display: none !important; + width: 0; + mso-hide: all; + overflow: hidden; + } +} body, table.body, @@ -845,14 +1009,15 @@ p, td, th, a { - color: #0a0a0a; - font-family: Helvetica, Arial, sans-serif; - font-weight: normal; - padding: 0; - margin: 0; - Margin: 0; - text-align: left; - line-height: 1.3; } + color: #0a0a0a; + font-family: Helvetica, Arial, sans-serif; + font-weight: normal; + padding: 0; + margin: 0; + Margin: 0; + text-align: left; + line-height: 1.3; +} h1, h2, @@ -860,67 +1025,88 @@ h3, h4, h5, h6 { - color: inherit; - word-wrap: normal; - font-family: Helvetica, Arial, sans-serif; - font-weight: normal; - margin-bottom: 10px; - Margin-bottom: 10px; } + color: inherit; + word-wrap: normal; + font-family: Helvetica, Arial, sans-serif; + font-weight: normal; + margin-bottom: 10px; + Margin-bottom: 10px; +} h1 { - font-size: 34px; } + font-size: 34px; +} h2 { - font-size: 30px; } + font-size: 30px; +} h3 { - font-size: 28px; } + font-size: 28px; +} h4 { - font-size: 24px; } + font-size: 24px; +} h5 { - font-size: 20px; } + font-size: 20px; +} h6 { - font-size: 18px; } + font-size: 18px; +} body, table.body, p, td, th { - font-size: 16px; - line-height: 1.3; } + font-size: 16px; + line-height: 1.3; +} p { - margin-bottom: 10px; - Margin-bottom: 10px; } - p.lead { + margin-bottom: 10px; + Margin-bottom: 10px; +} + +p.lead { font-size: 20px; - line-height: 1.6; } - p.subheader { + line-height: 1.6; +} + +p.subheader { margin-top: 4px; margin-bottom: 8px; Margin-top: 4px; Margin-bottom: 8px; font-weight: normal; line-height: 1.4; - color: #8a8a8a; } + color: #8a8a8a; +} small { - font-size: 80%; - color: #cacaca; } + font-size: 80%; + color: #cacaca; +} a { - color: #2199e8; - text-decoration: none; } - a:hover { - color: #147dc2; } - a:active { - color: #147dc2; } - a:visited { - color: #2199e8; } + color: #2199e8; + text-decoration: none; +} + +a:hover { + color: #147dc2; +} + +a:active { + color: #147dc2; +} + +a:visited { + color: #2199e8; +} h1 a, h1 a:visited, @@ -934,24 +1120,34 @@ h5 a, h5 a:visited, h6 a, h6 a:visited { - color: #2199e8; } + color: #2199e8; +} pre { - background: #f3f3f3; - margin: 30px 0; - Margin: 30px 0; } - pre code { - color: #cacaca; } - pre code span.callout { - color: #8a8a8a; - font-weight: bold; } - pre code span.callout-strong { - color: #ff6908; - font-weight: bold; } + background: #f3f3f3; + margin: 30px 0; + Margin: 30px 0; +} + +pre code { + color: #cacaca; +} + +pre code span.callout { + color: #8a8a8a; + font-weight: bold; +} + +pre code span.callout-strong { + color: #ff6908; + font-weight: bold; +} table.hr { - width: 100%; } - table.hr th { + width: 100%; +} + +table.hr th { height: 0; max-width: 580px; border-top: 0; @@ -960,52 +1156,66 @@ table.hr { border-left: 0; margin: 20px auto; Margin: 20px auto; - clear: both; } + clear: both; +} .stat { - font-size: 40px; - line-height: 1; } - p + .stat { + font-size: 40px; + line-height: 1; +} + +p + .stat { margin-top: -16px; - Margin-top: -16px; } + Margin-top: -16px; +} span.preheader { - display: none !important; - visibility: hidden; - mso-hide: all !important; - font-size: 1px; - color: #f3f3f3; - line-height: 1px; - max-height: 0px; - max-width: 0px; - opacity: 0; - overflow: hidden; } + display: none !important; + visibility: hidden; + mso-hide: all !important; + font-size: 1px; + color: #f3f3f3; + line-height: 1px; + max-height: 0px; + max-width: 0px; + opacity: 0; + overflow: hidden; +} table.button { - width: auto; - margin: 0 0 16px 0; - Margin: 0 0 16px 0; } - table.button table td { + width: auto; + margin: 0 0 16px 0; + Margin: 0 0 16px 0; +} + +table.button table td { text-align: left; color: #fefefe; background: #2199e8; - border: 2px solid #2199e8; } - table.button table td a { - font-family: Helvetica, Arial, sans-serif; - font-size: 16px; - font-weight: bold; - color: #fefefe; - text-decoration: none; - display: inline-block; - padding: 8px 16px 8px 16px; - border: 0 solid #2199e8; - border-radius: 3px; } - table.button.radius table td { + border: 2px solid #2199e8; +} + +table.button table td a { + font-family: Helvetica, Arial, sans-serif; + font-size: 16px; + font-weight: bold; + color: #fefefe; + text-decoration: none; + display: inline-block; + padding: 8px 16px 8px 16px; + border: 0 solid #2199e8; border-radius: 3px; - border: none; } - table.button.rounded table td { +} + +table.button.radius table td { + border-radius: 3px; + border: none; +} + +table.button.rounded table td { border-radius: 500px; - border: none; } + border: none; +} table.button:hover table tr td a, table.button:active table tr td a, @@ -1019,349 +1229,491 @@ table.button.small table tr td a:visited, table.button.large:hover table tr td a, table.button.large:active table tr td a, table.button.large table tr td a:visited { - color: #fefefe; } + color: #fefefe; +} table.button.tiny table td, table.button.tiny table a { - padding: 4px 8px 4px 8px; } + padding: 4px 8px 4px 8px; +} table.button.tiny table a { - font-size: 10px; - font-weight: normal; } + font-size: 10px; + font-weight: normal; +} table.button.small table td, table.button.small table a { - padding: 5px 10px 5px 10px; - font-size: 12px; } + padding: 5px 10px 5px 10px; + font-size: 12px; +} table.button.large table a { - padding: 10px 20px 10px 20px; - font-size: 20px; } + padding: 10px 20px 10px 20px; + font-size: 20px; +} table.button.expand, table.button.expanded { - width: 100% !important; } - table.button.expand table, - table.button.expanded table { - width: 100%; } - table.button.expand table a, - table.button.expanded table a { - text-align: center; - width: 100%; - padding-left: 0; - padding-right: 0; } - table.button.expand center, - table.button.expanded center { - min-width: 0; } + width: 100% !important; +} + +table.button.expand table, +table.button.expanded table { + width: 100%; +} + +table.button.expand table a, +table.button.expanded table a { + text-align: center; + width: 100%; + padding-left: 0; + padding-right: 0; +} + +table.button.expand center, +table.button.expanded center { + min-width: 0; +} table.button:hover table td, table.button:visited table td, table.button:active table td { - background: #147dc2; - color: #fefefe; } + background: #147dc2; + color: #fefefe; +} table.button:hover table a, table.button:visited table a, table.button:active table a { - border: 0 solid #147dc2; } + border: 0 solid #147dc2; +} table.button.secondary table td { - background: #777777; - color: #fefefe; - border: 0px solid #777777; } + background: #777777; + color: #fefefe; + border: 0px solid #777777; +} table.button.secondary table a { - color: #fefefe; - border: 0 solid #777777; } + color: #fefefe; + border: 0 solid #777777; +} table.button.secondary:hover table td { - background: #919191; - color: #fefefe; } + background: #919191; + color: #fefefe; +} table.button.secondary:hover table a { - border: 0 solid #919191; } + border: 0 solid #919191; +} table.button.secondary:hover table td a { - color: #fefefe; } + color: #fefefe; +} table.button.secondary:active table td a { - color: #fefefe; } + color: #fefefe; +} table.button.secondary table td a:visited { - color: #fefefe; } + color: #fefefe; +} table.button.success table td { - background: #3adb76; - border: 0px solid #3adb76; } + background: #3adb76; + border: 0px solid #3adb76; +} table.button.success table a { - border: 0 solid #3adb76; } + border: 0 solid #3adb76; +} table.button.success:hover table td { - background: #23bf5d; } + background: #23bf5d; +} table.button.success:hover table a { - border: 0 solid #23bf5d; } + border: 0 solid #23bf5d; +} table.button.alert table td { - background: #ec5840; - border: 0px solid #ec5840; } + background: #ec5840; + border: 0px solid #ec5840; +} table.button.alert table a { - border: 0 solid #ec5840; } + border: 0 solid #ec5840; +} table.button.alert:hover table td { - background: #e23317; } + background: #e23317; +} table.button.alert:hover table a { - border: 0 solid #e23317; } + border: 0 solid #e23317; +} table.button.warning table td { - background: #ffae00; - border: 0px solid #ffae00; } + background: #ffae00; + border: 0px solid #ffae00; +} table.button.warning table a { - border: 0px solid #ffae00; } + border: 0px solid #ffae00; +} table.button.warning:hover table td { - background: #cc8b00; } + background: #cc8b00; +} table.button.warning:hover table a { - border: 0px solid #cc8b00; } + border: 0px solid #cc8b00; +} table.callout { - margin-bottom: 16px; - Margin-bottom: 16px; } + margin-bottom: 16px; + Margin-bottom: 16px; +} th.callout-inner { - width: 100%; - border: 1px solid #cbcbcb; - padding: 10px; - background: #fefefe; } - th.callout-inner.primary { + width: 100%; + border: 1px solid #cbcbcb; + padding: 10px; + background: #fefefe; +} + +th.callout-inner.primary { background: #def0fc; border: 1px solid #444444; - color: #0a0a0a; } - th.callout-inner.secondary { + color: #0a0a0a; +} + +th.callout-inner.secondary { background: #ebebeb; border: 1px solid #444444; - color: #0a0a0a; } - th.callout-inner.success { + color: #0a0a0a; +} + +th.callout-inner.success { background: #e1faea; border: 1px solid #1b9448; - color: #fefefe; } - th.callout-inner.warning { + color: #fefefe; +} + +th.callout-inner.warning { background: #fff3d9; border: 1px solid #996800; - color: #fefefe; } - th.callout-inner.alert { + color: #fefefe; +} + +th.callout-inner.alert { background: #fce6e2; border: 1px solid #b42912; - color: #fefefe; } + color: #fefefe; +} .thumbnail { - border: solid 4px #fefefe; - box-shadow: 0 0 0 1px rgba(10, 10, 10, 0.2); - display: inline-block; - line-height: 0; - max-width: 100%; - transition: box-shadow 200ms ease-out; - border-radius: 3px; - margin-bottom: 16px; } - .thumbnail:hover, .thumbnail:focus { - box-shadow: 0 0 6px 1px rgba(33, 153, 232, 0.5); } + border: solid 4px #fefefe; + box-shadow: 0 0 0 1px rgba(10, 10, 10, 0.2); + display: inline-block; + line-height: 0; + max-width: 100%; + transition: box-shadow 200ms ease-out; + border-radius: 3px; + margin-bottom: 16px; +} + +.thumbnail:hover, .thumbnail:focus { + box-shadow: 0 0 6px 1px rgba(33, 153, 232, 0.5); +} table.menu { - width: 580px; } - table.menu td.menu-item, - table.menu th.menu-item { + width: 580px; +} + +table.menu td.menu-item, +table.menu th.menu-item { padding: 10px; - padding-right: 10px; } - table.menu td.menu-item a, - table.menu th.menu-item a { - color: #2199e8; } + padding-right: 10px; +} + +table.menu td.menu-item a, +table.menu th.menu-item a { + color: #2199e8; +} table.menu.vertical td.menu-item, table.menu.vertical th.menu-item { - padding: 10px; - padding-right: 0; - display: block; } - table.menu.vertical td.menu-item a, - table.menu.vertical th.menu-item a { - width: 100%; } + padding: 10px; + padding-right: 0; + display: block; +} + +table.menu.vertical td.menu-item a, +table.menu.vertical th.menu-item a { + width: 100%; +} table.menu.vertical td.menu-item table.menu.vertical td.menu-item, table.menu.vertical td.menu-item table.menu.vertical th.menu-item, table.menu.vertical th.menu-item table.menu.vertical td.menu-item, table.menu.vertical th.menu-item table.menu.vertical th.menu-item { - padding-left: 10px; } + padding-left: 10px; +} table.menu.text-center a { - text-align: center; } + text-align: center; +} .menu[align="center"] { - width: auto !important; } + width: auto !important; +} body.outlook p { - display: inline !important; } + display: inline !important; +} @media only screen and (max-width: 596px) { - table.body img { - width: auto; - height: auto; } - table.body center { - min-width: 0 !important; } - table.body .container { - width: 95% !important; } - table.body .columns, - table.body .column { - height: auto !important; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - padding-left: 16px !important; - padding-right: 16px !important; } + table.body img { + width: auto; + height: auto; + } + + table.body center { + min-width: 0 !important; + } + + table.body .container { + width: 95% !important; + } + + table.body .columns, + table.body .column { + height: auto !important; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding-left: 16px !important; + padding-right: 16px !important; + } + table.body .columns .column, table.body .columns .columns, table.body .column .column, table.body .column .columns { - padding-left: 0 !important; - padding-right: 0 !important; } - table.body .collapse .columns, - table.body .collapse .column { - padding-left: 0 !important; - padding-right: 0 !important; } - td.small-1, - th.small-1 { - display: inline-block !important; - width: 8.33333% !important; } - td.small-2, - th.small-2 { - display: inline-block !important; - width: 16.66667% !important; } - td.small-3, - th.small-3 { - display: inline-block !important; - width: 25% !important; } - td.small-4, - th.small-4 { - display: inline-block !important; - width: 33.33333% !important; } - td.small-5, - th.small-5 { - display: inline-block !important; - width: 41.66667% !important; } - td.small-6, - th.small-6 { - display: inline-block !important; - width: 50% !important; } - td.small-7, - th.small-7 { - display: inline-block !important; - width: 58.33333% !important; } - td.small-8, - th.small-8 { - display: inline-block !important; - width: 66.66667% !important; } - td.small-9, - th.small-9 { - display: inline-block !important; - width: 75% !important; } - td.small-10, - th.small-10 { - display: inline-block !important; - width: 83.33333% !important; } - td.small-11, - th.small-11 { - display: inline-block !important; - width: 91.66667% !important; } - td.small-12, - th.small-12 { - display: inline-block !important; - width: 100% !important; } - .columns td.small-12, - .column td.small-12, - .columns th.small-12, - .column th.small-12 { - display: block !important; - width: 100% !important; } - table.body td.small-offset-1, - table.body th.small-offset-1 { - margin-left: 8.33333% !important; - Margin-left: 8.33333% !important; } - table.body td.small-offset-2, - table.body th.small-offset-2 { - margin-left: 16.66667% !important; - Margin-left: 16.66667% !important; } - table.body td.small-offset-3, - table.body th.small-offset-3 { - margin-left: 25% !important; - Margin-left: 25% !important; } - table.body td.small-offset-4, - table.body th.small-offset-4 { - margin-left: 33.33333% !important; - Margin-left: 33.33333% !important; } - table.body td.small-offset-5, - table.body th.small-offset-5 { - margin-left: 41.66667% !important; - Margin-left: 41.66667% !important; } - table.body td.small-offset-6, - table.body th.small-offset-6 { - margin-left: 50% !important; - Margin-left: 50% !important; } - table.body td.small-offset-7, - table.body th.small-offset-7 { - margin-left: 58.33333% !important; - Margin-left: 58.33333% !important; } - table.body td.small-offset-8, - table.body th.small-offset-8 { - margin-left: 66.66667% !important; - Margin-left: 66.66667% !important; } - table.body td.small-offset-9, - table.body th.small-offset-9 { - margin-left: 75% !important; - Margin-left: 75% !important; } - table.body td.small-offset-10, - table.body th.small-offset-10 { - margin-left: 83.33333% !important; - Margin-left: 83.33333% !important; } - table.body td.small-offset-11, - table.body th.small-offset-11 { - margin-left: 91.66667% !important; - Margin-left: 91.66667% !important; } - table.body table.columns td.expander, - table.body table.columns th.expander { - display: none !important; } - table.body .right-text-pad, - table.body .text-pad-right { - padding-left: 10px !important; } - table.body .left-text-pad, - table.body .text-pad-left { - padding-right: 10px !important; } - table.menu { - width: 100% !important; } + padding-left: 0 !important; + padding-right: 0 !important; + } + + table.body .collapse .columns, + table.body .collapse .column { + padding-left: 0 !important; + padding-right: 0 !important; + } + + td.small-1, + th.small-1 { + display: inline-block !important; + width: 8.33333% !important; + } + + td.small-2, + th.small-2 { + display: inline-block !important; + width: 16.66667% !important; + } + + td.small-3, + th.small-3 { + display: inline-block !important; + width: 25% !important; + } + + td.small-4, + th.small-4 { + display: inline-block !important; + width: 33.33333% !important; + } + + td.small-5, + th.small-5 { + display: inline-block !important; + width: 41.66667% !important; + } + + td.small-6, + th.small-6 { + display: inline-block !important; + width: 50% !important; + } + + td.small-7, + th.small-7 { + display: inline-block !important; + width: 58.33333% !important; + } + + td.small-8, + th.small-8 { + display: inline-block !important; + width: 66.66667% !important; + } + + td.small-9, + th.small-9 { + display: inline-block !important; + width: 75% !important; + } + + td.small-10, + th.small-10 { + display: inline-block !important; + width: 83.33333% !important; + } + + td.small-11, + th.small-11 { + display: inline-block !important; + width: 91.66667% !important; + } + + td.small-12, + th.small-12 { + display: inline-block !important; + width: 100% !important; + } + + .columns td.small-12, + .column td.small-12, + .columns th.small-12, + .column th.small-12 { + display: block !important; + width: 100% !important; + } + + table.body td.small-offset-1, + table.body th.small-offset-1 { + margin-left: 8.33333% !important; + Margin-left: 8.33333% !important; + } + + table.body td.small-offset-2, + table.body th.small-offset-2 { + margin-left: 16.66667% !important; + Margin-left: 16.66667% !important; + } + + table.body td.small-offset-3, + table.body th.small-offset-3 { + margin-left: 25% !important; + Margin-left: 25% !important; + } + + table.body td.small-offset-4, + table.body th.small-offset-4 { + margin-left: 33.33333% !important; + Margin-left: 33.33333% !important; + } + + table.body td.small-offset-5, + table.body th.small-offset-5 { + margin-left: 41.66667% !important; + Margin-left: 41.66667% !important; + } + + table.body td.small-offset-6, + table.body th.small-offset-6 { + margin-left: 50% !important; + Margin-left: 50% !important; + } + + table.body td.small-offset-7, + table.body th.small-offset-7 { + margin-left: 58.33333% !important; + Margin-left: 58.33333% !important; + } + + table.body td.small-offset-8, + table.body th.small-offset-8 { + margin-left: 66.66667% !important; + Margin-left: 66.66667% !important; + } + + table.body td.small-offset-9, + table.body th.small-offset-9 { + margin-left: 75% !important; + Margin-left: 75% !important; + } + + table.body td.small-offset-10, + table.body th.small-offset-10 { + margin-left: 83.33333% !important; + Margin-left: 83.33333% !important; + } + + table.body td.small-offset-11, + table.body th.small-offset-11 { + margin-left: 91.66667% !important; + Margin-left: 91.66667% !important; + } + + table.body table.columns td.expander, + table.body table.columns th.expander { + display: none !important; + } + + table.body .right-text-pad, + table.body .text-pad-right { + padding-left: 10px !important; + } + + table.body .left-text-pad, + table.body .text-pad-left { + padding-right: 10px !important; + } + + table.menu { + width: 100% !important; + } + table.menu td, table.menu th { - width: auto !important; - display: inline-block !important; } + width: auto !important; + display: inline-block !important; + } + table.menu.vertical td, table.menu.vertical th, table.menu.small-vertical td, table.menu.small-vertical th { - display: block !important; } - table.menu[align="center"] { - width: auto !important; } - table.button.small-expand, - table.button.small-expanded { - width: 100% !important; } + display: block !important; + } + + table.menu[align="center"] { + width: auto !important; + } + + table.button.small-expand, + table.button.small-expanded { + width: 100% !important; + } + table.button.small-expand table, table.button.small-expanded table { - width: 100%; } - table.button.small-expand table a, - table.button.small-expanded table a { + width: 100%; + } + + table.button.small-expand table a, + table.button.small-expanded table a { text-align: center !important; width: 100% !important; padding-left: 0 !important; - padding-right: 0 !important; } + padding-right: 0 !important; + } + table.button.small-expand center, table.button.small-expanded center { - min-width: 0; } } + min-width: 0; + } +} diff --git a/assets/fonts/dompdf/.gitignore b/assets/fonts/dompdf/.gitignore new file mode 100644 index 00000000..085ad9f3 --- /dev/null +++ b/assets/fonts/dompdf/.gitignore @@ -0,0 +1,3 @@ +# Ignore font files +*.otf +*.ttf \ No newline at end of file diff --git a/assets/fonts/dompdf/README.md b/assets/fonts/dompdf/README.md new file mode 100644 index 00000000..d80c64f4 --- /dev/null +++ b/assets/fonts/dompdf/README.md @@ -0,0 +1 @@ +Put your font ttf files in this folder to make them available to the label generator. \ No newline at end of file diff --git a/assets/js/app.js b/assets/js/app.js index 8242331f..43acec5d 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -22,7 +22,6 @@ import '../css/app/layout.css'; import '../css/app/helpers.css'; -import '../css/app/darkmode.css'; import '../css/app/tables.css'; import '../css/app/bs-overrides.css'; import '../css/app/treeview.css'; @@ -45,4 +44,18 @@ import "./register_events"; import "./tristate_checkboxes"; //Define jquery globally -window.$ = window.jQuery = require("jquery") \ No newline at end of file +window.$ = window.jQuery = require("jquery"); + +//Use the local WASM file for the ZXing library +import { + setZXingModuleOverrides, +} from "barcode-detector/pure"; +import wasmFile from "../../node_modules/zxing-wasm/dist/reader/zxing_reader.wasm"; +setZXingModuleOverrides({ + locateFile: (path, prefix) => { + if (path.endsWith(".wasm")) { + return wasmFile; + } + return prefix + path; + }, +}); \ No newline at end of file diff --git a/assets/js/error_handler.js b/assets/js/error_handler.js index f4659269..7f047af9 100644 --- a/assets/js/error_handler.js +++ b/assets/js/error_handler.js @@ -27,14 +27,67 @@ class ErrorHandlerHelper { constructor() { console.log('Error Handler registered'); - const content = document.getElementById('content'); - content.addEventListener('turbo:before-fetch-response', (event) => this.handleError(event)); + //const content = document.getElementById('content'); + //It seems that the content element is unreliable for these events, so we use the document instead + const content = document; + //content.addEventListener('turbo:before-fetch-response', (event) => this.handleError(event)); + content.addEventListener('turbo:fetch-request-error', (event) => this.handleError(event)); + content.addEventListener('turbo:frame-missing', (event) => this.handleError(event)); $(document).ajaxError(this.handleJqueryErrror.bind(this)); } _showAlert(statusText, statusCode, location, responseHTML) { + const httpStatusToText = { + '200': 'OK', + '201': 'Created', + '202': 'Accepted', + '203': 'Non-Authoritative Information', + '204': 'No Content', + '205': 'Reset Content', + '206': 'Partial Content', + '300': 'Multiple Choices', + '301': 'Moved Permanently', + '302': 'Found', + '303': 'See Other', + '304': 'Not Modified', + '305': 'Use Proxy', + '306': 'Unused', + '307': 'Temporary Redirect', + '400': 'Bad Request', + '401': 'Unauthorized', + '402': 'Payment Required', + '403': 'Forbidden', + '404': 'Not Found', + '405': 'Method Not Allowed', + '406': 'Not Acceptable', + '407': 'Proxy Authentication Required', + '408': 'Request Timeout', + '409': 'Conflict', + '410': 'Gone', + '411': 'Length Required', + '412': 'Precondition Required', + '413': 'Request Entry Too Large', + '414': 'Request-URI Too Long', + '415': 'Unsupported Media Type', + '416': 'Requested Range Not Satisfiable', + '417': 'Expectation Failed', + '418': 'I\'m a teapot', + '429': 'Too Many Requests', + '500': 'Internal Server Error', + '501': 'Not Implemented', + '502': 'Bad Gateway', + '503': 'Service Unavailable', + '504': 'Gateway Timeout', + '505': 'HTTP Version Not Supported', + }; + + //If the statusText is empty, we use the status code as text + if (!statusText) { + statusText = httpStatusToText[statusCode]; + } + //Create error text const title = statusText + ' (Status ' + statusCode + ')'; @@ -87,8 +140,10 @@ class ErrorHandlerHelper { } handleError(event) { - const fetchResponse = event.detail.fetchResponse; - const response = fetchResponse.response; + //Prevent default error handling + event.preventDefault(); + + const response = event.detail.response; //Ignore aborted requests. if (response.statusText === 'abort' || response.status == 0) { @@ -100,11 +155,17 @@ class ErrorHandlerHelper { return; } - if(fetchResponse.failed) { + //Skip 404 errors, on admin pages (as this causes a popup on deletion in firefox) + if (response.status == 404 && event.target.id === 'admin-content-frame') { + return; + } + + + if(!response.ok) { response.text().then(responseHTML => { - this._showAlert(response.statusText, response.status, fetchResponse.location.toString(), responseHTML); + this._showAlert(response.statusText, response.status, response.url, responseHTML); }).catch(err => { - this._showAlert(response.statusText, response.status, fetchResponse.location.toString(), '
' + err + '
'); + this._showAlert(response.statusText, response.status, response.url, '
' + err + '
'); }); } } diff --git a/assets/js/lib/dataTables.select.mjs b/assets/js/lib/dataTables.select.mjs new file mode 100644 index 00000000..bba97692 --- /dev/null +++ b/assets/js/lib/dataTables.select.mjs @@ -0,0 +1,1538 @@ +/********************* + * This is the fixed version of the select extension for DataTables with the fix for the issue with the select extension + * (https://github.com/DataTables/Select/issues/51) + * We use this instead of the yarn version until the PR (https://github.com/DataTables/Select/pull/52) is merged and released + * /*******************/ + + +/*! Select for DataTables 2.0.0 + * © SpryMedia Ltd - datatables.net/license/mit + */ + +import jQuery from 'jquery'; +import DataTable from 'datatables.net'; + +// Allow reassignment of the $ variable +let $ = jQuery; + + +// Version information for debugger +DataTable.select = {}; + +DataTable.select.version = '2.0.0'; + +DataTable.select.init = function (dt) { + var ctx = dt.settings()[0]; + + if (!DataTable.versionCheck('2')) { + throw 'Warning: Select requires DataTables 2 or newer'; + } + + if (ctx._select) { + return; + } + + var savedSelected = dt.state.loaded(); + + var selectAndSave = function (e, settings, data) { + if (data === null || data.select === undefined) { + return; + } + + // Clear any currently selected rows, before restoring state + // None will be selected on first initialisation + if (dt.rows({ selected: true }).any()) { + dt.rows().deselect(); + } + if (data.select.rows !== undefined) { + dt.rows(data.select.rows).select(); + } + + if (dt.columns({ selected: true }).any()) { + dt.columns().deselect(); + } + if (data.select.columns !== undefined) { + dt.columns(data.select.columns).select(); + } + + if (dt.cells({ selected: true }).any()) { + dt.cells().deselect(); + } + if (data.select.cells !== undefined) { + for (var i = 0; i < data.select.cells.length; i++) { + dt.cell(data.select.cells[i].row, data.select.cells[i].column).select(); + } + } + + dt.state.save(); + }; + + dt.on('stateSaveParams', function (e, settings, data) { + data.select = {}; + data.select.rows = dt.rows({ selected: true }).ids(true).toArray(); + data.select.columns = dt.columns({ selected: true })[0]; + data.select.cells = dt.cells({ selected: true })[0].map(function (coords) { + return { row: dt.row(coords.row).id(true), column: coords.column }; + }); + }) + .on('stateLoadParams', selectAndSave) + .one('init', function () { + selectAndSave(undefined, undefined, savedSelected); + }); + + var init = ctx.oInit.select; + var defaults = DataTable.defaults.select; + var opts = init === undefined ? defaults : init; + + // Set defaults + var items = 'row'; + var style = 'api'; + var blurable = false; + var toggleable = true; + var info = true; + var selector = 'td, th'; + var className = 'selected'; + var headerCheckbox = true; + var setStyle = false; + + ctx._select = { + infoEls: [] + }; + + // Initialisation customisations + if (opts === true) { + style = 'os'; + setStyle = true; + } + else if (typeof opts === 'string') { + style = opts; + setStyle = true; + } + else if ($.isPlainObject(opts)) { + if (opts.blurable !== undefined) { + blurable = opts.blurable; + } + + if (opts.toggleable !== undefined) { + toggleable = opts.toggleable; + } + + if (opts.info !== undefined) { + info = opts.info; + } + + if (opts.items !== undefined) { + items = opts.items; + } + + if (opts.style !== undefined) { + style = opts.style; + setStyle = true; + } + else { + style = 'os'; + setStyle = true; + } + + if (opts.selector !== undefined) { + selector = opts.selector; + } + + if (opts.className !== undefined) { + className = opts.className; + } + + if (opts.headerCheckbox !== undefined) { + headerCheckbox = opts.headerCheckbox; + } + } + + dt.select.selector(selector); + dt.select.items(items); + dt.select.style(style); + dt.select.blurable(blurable); + dt.select.toggleable(toggleable); + dt.select.info(info); + ctx._select.className = className; + + // If the init options haven't enabled select, but there is a selectable + // class name, then enable + if (!setStyle && $(dt.table().node()).hasClass('selectable')) { + dt.select.style('os'); + } + + // Insert a checkbox into the header if needed - might need to wait + // for init complete, or it might already be done + if (headerCheckbox) { + initCheckboxHeader(dt); + + dt.on('init', function () { + initCheckboxHeader(dt); + }); + } +}; + +/* + +Select is a collection of API methods, event handlers, event emitters and +buttons (for the `Buttons` extension) for DataTables. It provides the following +features, with an overview of how they are implemented: + +## Selection of rows, columns and cells. Whether an item is selected or not is + stored in: + +* rows: a `_select_selected` property which contains a boolean value of the + DataTables' `aoData` object for each row +* columns: a `_select_selected` property which contains a boolean value of the + DataTables' `aoColumns` object for each column +* cells: a `_selected_cells` property which contains an array of boolean values + of the `aoData` object for each row. The array is the same length as the + columns array, with each element of it representing a cell. + +This method of using boolean flags allows Select to operate when nodes have not +been created for rows / cells (DataTables' defer rendering feature). + +## API methods + +A range of API methods are available for triggering selection and de-selection +of rows. Methods are also available to configure the selection events that can +be triggered by an end user (such as which items are to be selected). To a large +extent, these of API methods *is* Select. It is basically a collection of helper +functions that can be used to select items in a DataTable. + +Configuration of select is held in the object `_select` which is attached to the +DataTables settings object on initialisation. Select being available on a table +is not optional when Select is loaded, but its default is for selection only to +be available via the API - so the end user wouldn't be able to select rows +without additional configuration. + +The `_select` object contains the following properties: + +``` +{ + items:string - Can be `rows`, `columns` or `cells`. Defines what item + will be selected if the user is allowed to activate row + selection using the mouse. + style:string - Can be `none`, `single`, `multi` or `os`. Defines the + interaction style when selecting items + blurable:boolean - If row selection can be cleared by clicking outside of + the table + toggleable:boolean - If row selection can be cancelled by repeated clicking + on the row + info:boolean - If the selection summary should be shown in the table + information elements + infoEls:element[] - List of HTML elements with info elements for a table +} +``` + +In addition to the API methods, Select also extends the DataTables selector +options for rows, columns and cells adding a `selected` option to the selector +options object, allowing the developer to select only selected items or +unselected items. + +## Mouse selection of items + +Clicking on items can be used to select items. This is done by a simple event +handler that will select the items using the API methods. + + */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Local functions + */ + +/** + * Add one or more cells to the selection when shift clicking in OS selection + * style cell selection. + * + * Cell range is more complicated than row and column as we want to select + * in the visible grid rather than by index in sequence. For example, if you + * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1 + * should also be selected (and not 1-3, 1-4. etc) + * + * @param {DataTable.Api} dt DataTable + * @param {object} idx Cell index to select to + * @param {object} last Cell index to select from + * @private + */ +function cellRange(dt, idx, last) { + var indexes; + var columnIndexes; + var rowIndexes; + var selectColumns = function (start, end) { + if (start > end) { + var tmp = end; + end = start; + start = tmp; + } + + var record = false; + return dt + .columns(':visible') + .indexes() + .filter(function (i) { + if (i === start) { + record = true; + } + + if (i === end) { + // not else if, as start might === end + record = false; + return true; + } + + return record; + }); + }; + + var selectRows = function (start, end) { + var indexes = dt.rows({ search: 'applied' }).indexes(); + + // Which comes first - might need to swap + if (indexes.indexOf(start) > indexes.indexOf(end)) { + var tmp = end; + end = start; + start = tmp; + } + + var record = false; + return indexes.filter(function (i) { + if (i === start) { + record = true; + } + + if (i === end) { + record = false; + return true; + } + + return record; + }); + }; + + if (!dt.cells({ selected: true }).any() && !last) { + // select from the top left cell to this one + columnIndexes = selectColumns(0, idx.column); + rowIndexes = selectRows(0, idx.row); + } + else { + // Get column indexes between old and new + columnIndexes = selectColumns(last.column, idx.column); + rowIndexes = selectRows(last.row, idx.row); + } + + indexes = dt.cells(rowIndexes, columnIndexes).flatten(); + + if (!dt.cells(idx, { selected: true }).any()) { + // Select range + dt.cells(indexes).select(); + } + else { + // Deselect range + dt.cells(indexes).deselect(); + } +} + +/** + * Disable mouse selection by removing the selectors + * + * @param {DataTable.Api} dt DataTable to remove events from + * @private + */ +function disableMouseSelection(dt) { + var ctx = dt.settings()[0]; + var selector = ctx._select.selector; + + $(dt.table().container()) + .off('mousedown.dtSelect', selector) + .off('mouseup.dtSelect', selector) + .off('click.dtSelect', selector); + + $('body').off('click.dtSelect' + _safeId(dt.table().node())); +} + +/** + * Attach mouse listeners to the table to allow mouse selection of items + * + * @param {DataTable.Api} dt DataTable to remove events from + * @private + */ +function enableMouseSelection(dt) { + var container = $(dt.table().container()); + var ctx = dt.settings()[0]; + var selector = ctx._select.selector; + var matchSelection; + + container + .on('mousedown.dtSelect', selector, function (e) { + // Disallow text selection for shift clicking on the table so multi + // element selection doesn't look terrible! + if (e.shiftKey || e.metaKey || e.ctrlKey) { + container + .css('-moz-user-select', 'none') + .one('selectstart.dtSelect', selector, function () { + return false; + }); + } + + if (window.getSelection) { + matchSelection = window.getSelection(); + } + }) + .on('mouseup.dtSelect', selector, function () { + // Allow text selection to occur again, Mozilla style (tested in FF + // 35.0.1 - still required) + container.css('-moz-user-select', ''); + }) + .on('click.dtSelect', selector, function (e) { + var items = dt.select.items(); + var idx; + + // If text was selected (click and drag), then we shouldn't change + // the row's selected state + if (matchSelection) { + var selection = window.getSelection(); + + // If the element that contains the selection is not in the table, we can ignore it + // This can happen if the developer selects text from the click event + if ( + !selection.anchorNode || + $(selection.anchorNode).closest('table')[0] === dt.table().node() + ) { + if (selection !== matchSelection) { + return; + } + } + } + + var ctx = dt.settings()[0]; + var container = dt.table().container(); + + // Ignore clicks inside a sub-table + if ($(e.target).closest('div.dt-container')[0] != container) { + return; + } + + var cell = dt.cell($(e.target).closest('td, th')); + + // Check the cell actually belongs to the host DataTable (so child + // rows, etc, are ignored) + if (!cell.any()) { + return; + } + + var event = $.Event('user-select.dt'); + eventTrigger(dt, event, [items, cell, e]); + + if (event.isDefaultPrevented()) { + return; + } + + var cellIndex = cell.index(); + if (items === 'row') { + idx = cellIndex.row; + typeSelect(e, dt, ctx, 'row', idx); + } + else if (items === 'column') { + idx = cell.index().column; + typeSelect(e, dt, ctx, 'column', idx); + } + else if (items === 'cell') { + idx = cell.index(); + typeSelect(e, dt, ctx, 'cell', idx); + } + + ctx._select_lastCell = cellIndex; + }); + + // Blurable + $('body').on('click.dtSelect' + _safeId(dt.table().node()), function (e) { + if (ctx._select.blurable) { + // If the click was inside the DataTables container, don't blur + if ($(e.target).parents().filter(dt.table().container()).length) { + return; + } + + // Ignore elements which have been removed from the DOM (i.e. paging + // buttons) + if ($(e.target).parents('html').length === 0) { + return; + } + + // Don't blur in Editor form + if ($(e.target).parents('div.DTE').length) { + return; + } + + var event = $.Event('select-blur.dt'); + eventTrigger(dt, event, [e.target, e]); + + if (event.isDefaultPrevented()) { + return; + } + + clear(ctx, true); + } + }); +} + +/** + * Trigger an event on a DataTable + * + * @param {DataTable.Api} api DataTable to trigger events on + * @param {boolean} selected true if selected, false if deselected + * @param {string} type Item type acting on + * @param {boolean} any Require that there are values before + * triggering + * @private + */ +function eventTrigger(api, type, args, any) { + if (any && !api.flatten().length) { + return; + } + + if (typeof type === 'string') { + type = type + '.dt'; + } + + args.unshift(api); + + $(api.table().node()).trigger(type, args); +} + +/** + * Update the information element of the DataTable showing information about the + * items selected. This is done by adding tags to the existing text + * + * @param {DataTable.Api} api DataTable to update + * @private + */ +function info(api, node) { + if (api.select.style() === 'api' || api.select.info() === false) { + return; + } + + var rows = api.rows({ selected: true }).flatten().length; + var columns = api.columns({ selected: true }).flatten().length; + var cells = api.cells({ selected: true }).flatten().length; + + var add = function (el, name, num) { + el.append( + $('').append( + api.i18n( + 'select.' + name + 's', + { _: '%d ' + name + 's selected', 0: '', 1: '1 ' + name + ' selected' }, + num + ) + ) + ); + }; + + var el = $(node); + var output = $(''); + + add(output, 'row', rows); + add(output, 'column', columns); + add(output, 'cell', cells); + + var existing = el.children('span.select-info'); + + if (existing.length) { + existing.remove(); + } + + if (output.text() !== '') { + el.append(output); + } +} + +/** + * Add a checkbox to the header for checkbox columns, allowing all rows to + * be selected, deselected or just to show the state. + * + * @param {*} dt API + */ +function initCheckboxHeader( dt ) { + // Find any checkbox column(s) + dt.columns('.dt-select').every(function () { + var header = this.header(); + + if (! $('input', header).length) { + // If no checkbox yet, insert one + var input = $('') + .attr({ + class: 'dt-select-checkbox', + type: 'checkbox', + 'aria-label': dt.i18n('select.aria.headerCheckbox') || 'Select all rows' + }) + .appendTo(header) + .on('change', function () { + if (this.checked) { + dt.rows({search: 'applied'}).select(); + } + else { + dt.rows({selected: true}).deselect(); + } + }) + .on('click', function (e) { + e.stopPropagation(); + }); + + // Update the header checkbox's state when the selection in the + // table changes + dt.on('draw select deselect', function (e, pass, type) { + if (type === 'row' || ! type) { + var count = dt.rows({selected: true}).count(); + var search = dt.rows({search: 'applied', selected: true}).count(); + var available = dt.rows({search: 'applied'}).count(); + + if (search && search <= count && search === available) { + input + .prop('checked', true) + .prop('indeterminate', false); + } + else if (search === 0 && count === 0) { + input + .prop('checked', false) + .prop('indeterminate', false); + } + else { + input + .prop('checked', false) + .prop('indeterminate', true); + } + } + }); + } + }); +} + +/** + * Initialisation of a new table. Attach event handlers and callbacks to allow + * Select to operate correctly. + * + * This will occur _after_ the initial DataTables initialisation, although + * before Ajax data is rendered, if there is ajax data + * + * @param {DataTable.settings} ctx Settings object to operate on + * @private + */ +function init(ctx) { + var api = new DataTable.Api(ctx); + ctx._select_init = true; + + // Row callback so that classes can be added to rows and cells if the item + // was selected before the element was created. This will happen with the + // `deferRender` option enabled. + // + // This method of attaching to `aoRowCreatedCallback` is a hack until + // DataTables has proper events for row manipulation If you are reviewing + // this code to create your own plug-ins, please do not do this! + ctx.aoRowCreatedCallback.push(function (row, data, index) { + var i, ien; + var d = ctx.aoData[index]; + + // Row + if (d._select_selected) { + $(row).addClass(ctx._select.className); + } + + // Cells and columns - if separated out, we would need to do two + // loops, so it makes sense to combine them into a single one + for (i = 0, ien = ctx.aoColumns.length; i < ien; i++) { + if ( + ctx.aoColumns[i]._select_selected || + (d._selected_cells && d._selected_cells[i]) + ) { + $(d.anCells[i]).addClass(ctx._select.className); + } + } + } + ); + + // On Ajax reload we want to reselect all rows which are currently selected, + // if there is an rowId (i.e. a unique value to identify each row with) + api.on('preXhr.dt.dtSelect', function (e, settings) { + if (settings !== api.settings()[0]) { + // Not triggered by our DataTable! + return; + } + + // note that column selection doesn't need to be cached and then + // reselected, as they are already selected + var rows = api + .rows({ selected: true }) + .ids(true) + .filter(function (d) { + return d !== undefined; + }); + + var cells = api + .cells({ selected: true }) + .eq(0) + .map(function (cellIdx) { + var id = api.row(cellIdx.row).id(true); + return id ? { row: id, column: cellIdx.column } : undefined; + }) + .filter(function (d) { + return d !== undefined; + }); + + // On the next draw, reselect the currently selected items + api.one('draw.dt.dtSelect', function () { + api.rows(rows).select(); + + // `cells` is not a cell index selector, so it needs a loop + if (cells.any()) { + cells.each(function (id) { + api.cells(id.row, id.column).select(); + }); + } + }); + }); + + // Update the table information element with selected item summary + api.on('info.dt', function (e, ctx, node) { + // Store the info node for updating on select / deselect + if (!ctx._select.infoEls.includes(node)) { + ctx._select.infoEls.push(node); + } + + info(api, node); + }); + + api.on('select.dtSelect.dt deselect.dtSelect.dt', function () { + ctx._select.infoEls.forEach(function (el) { + info(api, el); + }); + + api.state.save(); + }); + + // Clean up and release + api.on('destroy.dtSelect', function () { + // Remove class directly rather than calling deselect - which would trigger events + $(api.rows({ selected: true }).nodes()).removeClass(api.settings()[0]._select.className); + + disableMouseSelection(api); + api.off('.dtSelect'); + $('body').off('.dtSelect' + _safeId(api.table().node())); + }); +} + +/** + * Add one or more items (rows or columns) to the selection when shift clicking + * in OS selection style + * + * @param {DataTable.Api} dt DataTable + * @param {string} type Row or column range selector + * @param {object} idx Item index to select to + * @param {object} last Item index to select from + * @private + */ +function rowColumnRange(dt, type, idx, last) { + // Add a range of rows from the last selected row to this one + var indexes = dt[type + 's']({ search: 'applied' }).indexes(); + var idx1 = indexes.indexOf(last); + var idx2 = indexes.indexOf(idx); + + if (!dt[type + 's']({ selected: true }).any() && idx1 === -1) { + // select from top to here - slightly odd, but both Windows and Mac OS + // do this + indexes.splice(indexes.indexOf(idx) + 1, indexes.length); + } + else { + // reverse so we can shift click 'up' as well as down + if (idx1 > idx2) { + var tmp = idx2; + idx2 = idx1; + idx1 = tmp; + } + + indexes.splice(idx2 + 1, indexes.length); + indexes.splice(0, idx1); + } + + if (!dt[type](idx, { selected: true }).any()) { + // Select range + dt[type + 's'](indexes).select(); + } + else { + // Deselect range - need to keep the clicked on row selected + indexes.splice(indexes.indexOf(idx), 1); + dt[type + 's'](indexes).deselect(); + } +} + +/** + * Clear all selected items + * + * @param {DataTable.settings} ctx Settings object of the host DataTable + * @param {boolean} [force=false] Force the de-selection to happen, regardless + * of selection style + * @private + */ +function clear(ctx, force) { + if (force || ctx._select.style === 'single') { + var api = new DataTable.Api(ctx); + + api.rows({ selected: true }).deselect(); + api.columns({ selected: true }).deselect(); + api.cells({ selected: true }).deselect(); + } +} + +/** + * Select items based on the current configuration for style and items. + * + * @param {object} e Mouse event object + * @param {DataTables.Api} dt DataTable + * @param {DataTable.settings} ctx Settings object of the host DataTable + * @param {string} type Items to select + * @param {int|object} idx Index of the item to select + * @private + */ +function typeSelect(e, dt, ctx, type, idx) { + var style = dt.select.style(); + var toggleable = dt.select.toggleable(); + var isSelected = dt[type](idx, { selected: true }).any(); + + if (isSelected && !toggleable) { + return; + } + + if (style === 'os') { + if (e.ctrlKey || e.metaKey) { + // Add or remove from the selection + dt[type](idx).select(!isSelected); + } + else if (e.shiftKey) { + if (type === 'cell') { + cellRange(dt, idx, ctx._select_lastCell || null); + } + else { + rowColumnRange( + dt, + type, + idx, + ctx._select_lastCell ? ctx._select_lastCell[type] : null + ); + } + } + else { + // No cmd or shift click - deselect if selected, or select + // this row only + var selected = dt[type + 's']({ selected: true }); + + if (isSelected && selected.flatten().length === 1) { + dt[type](idx).deselect(); + } + else { + selected.deselect(); + dt[type](idx).select(); + } + } + } + else if (style == 'multi+shift') { + if (e.shiftKey) { + if (type === 'cell') { + cellRange(dt, idx, ctx._select_lastCell || null); + } + else { + rowColumnRange( + dt, + type, + idx, + ctx._select_lastCell ? ctx._select_lastCell[type] : null + ); + } + } + else { + dt[type](idx).select(!isSelected); + } + } + else { + dt[type](idx).select(!isSelected); + } +} + +function _safeId(node) { + return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-'); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables selectors + */ + +// row and column are basically identical just assigned to different properties +// and checking a different array, so we can dynamically create the functions to +// reduce the code size +$.each( + [ + { type: 'row', prop: 'aoData' }, + { type: 'column', prop: 'aoColumns' } + ], + function (i, o) { + DataTable.ext.selector[o.type].push(function (settings, opts, indexes) { + var selected = opts.selected; + var data; + var out = []; + + if (selected !== true && selected !== false) { + return indexes; + } + + for (var i = 0, ien = indexes.length; i < ien; i++) { + data = settings[o.prop][indexes[i]]; + + if ( + data && ( + (selected === true && data._select_selected === true) || + (selected === false && !data._select_selected) + ) + ) { + out.push(indexes[i]); + } + } + + return out; + }); + } +); + +DataTable.ext.selector.cell.push(function (settings, opts, cells) { + var selected = opts.selected; + var rowData; + var out = []; + + if (selected === undefined) { + return cells; + } + + for (var i = 0, ien = cells.length; i < ien; i++) { + rowData = settings.aoData[cells[i].row]; + + if ( + rowData && ( + (selected === true && + rowData._selected_cells && + rowData._selected_cells[cells[i].column] === true) || + (selected === false && + (!rowData._selected_cells || !rowData._selected_cells[cells[i].column])) + ) + ) { + out.push(cells[i]); + } + } + + return out; +}); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables API + * + * For complete documentation, please refer to the docs/api directory or the + * DataTables site + */ + +// Local variables to improve compression +var apiRegister = DataTable.Api.register; +var apiRegisterPlural = DataTable.Api.registerPlural; + +apiRegister('select()', function () { + return this.iterator('table', function (ctx) { + DataTable.select.init(new DataTable.Api(ctx)); + }); +}); + +apiRegister('select.blurable()', function (flag) { + if (flag === undefined) { + return this.context[0]._select.blurable; + } + + return this.iterator('table', function (ctx) { + ctx._select.blurable = flag; + }); +}); + +apiRegister('select.toggleable()', function (flag) { + if (flag === undefined) { + return this.context[0]._select.toggleable; + } + + return this.iterator('table', function (ctx) { + ctx._select.toggleable = flag; + }); +}); + +apiRegister('select.info()', function (flag) { + if (flag === undefined) { + return this.context[0]._select.info; + } + + return this.iterator('table', function (ctx) { + ctx._select.info = flag; + }); +}); + +apiRegister('select.items()', function (items) { + if (items === undefined) { + return this.context[0]._select.items; + } + + return this.iterator('table', function (ctx) { + ctx._select.items = items; + + eventTrigger(new DataTable.Api(ctx), 'selectItems', [items]); + }); +}); + +// Takes effect from the _next_ selection. None disables future selection, but +// does not clear the current selection. Use the `deselect` methods for that +apiRegister('select.style()', function (style) { + if (style === undefined) { + return this.context[0]._select.style; + } + + return this.iterator('table', function (ctx) { + if (!ctx._select) { + DataTable.select.init(new DataTable.Api(ctx)); + } + + if (!ctx._select_init) { + init(ctx); + } + + ctx._select.style = style; + + // Add / remove mouse event handlers. They aren't required when only + // API selection is available + var dt = new DataTable.Api(ctx); + disableMouseSelection(dt); + + if (style !== 'api') { + enableMouseSelection(dt); + } + + eventTrigger(new DataTable.Api(ctx), 'selectStyle', [style]); + }); +}); + +apiRegister('select.selector()', function (selector) { + if (selector === undefined) { + return this.context[0]._select.selector; + } + + return this.iterator('table', function (ctx) { + disableMouseSelection(new DataTable.Api(ctx)); + + ctx._select.selector = selector; + + if (ctx._select.style !== 'api') { + enableMouseSelection(new DataTable.Api(ctx)); + } + }); +}); + +apiRegister('select.last()', function (set) { + let ctx = this.context[0]; + + if (set) { + ctx._select_lastCell = set; + return this; + } + + return ctx._select_lastCell; +}); + +apiRegisterPlural('rows().select()', 'row().select()', function (select) { + var api = this; + + if (select === false) { + return this.deselect(); + } + + this.iterator('row', function (ctx, idx) { + clear(ctx); + + // There is a good amount of knowledge of DataTables internals in + // this function. It _could_ be done without that, but it would hurt + // performance (or DT would need new APIs for this work) + var dtData = ctx.aoData[idx]; + var dtColumns = ctx.aoColumns; + + $(dtData.nTr).addClass(ctx._select.className); + dtData._select_selected = true; + + for (var i=0 ; i 0); + }); + + this.disable(); + }, + destroy: function (dt, node, config) { + dt.off(config._eventNamespace); + } + }, + showSelected: { + text: i18n('showSelected', 'Show only selected'), + className: 'buttons-show-selected', + action: function (e, dt) { + if (dt.search.fixed('dt-select')) { + // Remove existing function + dt.search.fixed('dt-select', null); + + this.active(false); + } + else { + // Use a fixed filtering function to match on selected rows + // This needs to reference the internal aoData since that is + // where Select stores its reference for the selected state + var dataSrc = dt.settings()[0].aoData; + + dt.search.fixed('dt-select', function (text, data, idx) { + // _select_selected is set by Select on the data object for the row + return dataSrc[idx]._select_selected; + }); + + this.active(true); + } + + dt.draw(); + } + } +}); + +$.each(['Row', 'Column', 'Cell'], function (i, item) { + var lc = item.toLowerCase(); + + DataTable.ext.buttons['select' + item + 's'] = { + text: i18n('select' + item + 's', 'Select ' + lc + 's'), + className: 'buttons-select-' + lc + 's', + action: function () { + this.select.items(lc); + }, + init: function (dt) { + var that = this; + + dt.on('selectItems.dt.DT', function (e, ctx, items) { + that.active(items === lc); + }); + } + }; +}); + +DataTable.type('select-checkbox', { + className: 'dt-select', + detect: function (data) { + // Rendering function will tell us if it is a checkbox type + return data === 'select-checkbox' ? data : false; + }, + order: { + pre: function (d) { + return d === 'X' ? -1 : 0; + } + } +}); + +$.extend(true, DataTable.defaults.oLanguage, { + select: { + aria: { + rowCheckbox: 'Select row' + } + } +}); + +DataTable.render.select = function (valueProp, nameProp) { + var valueFn = valueProp ? DataTable.util.get(valueProp) : null; + var nameFn = nameProp ? DataTable.util.get(nameProp) : null; + + return function (data, type, row, meta) { + var dtRow = meta.settings.aoData[meta.row]; + var selected = dtRow._select_selected; + var ariaLabel = meta.settings.oLanguage.select.aria.rowCheckbox; + + if (type === 'display') { + return $('') + .attr({ + 'aria-label': ariaLabel, + class: 'dt-select-checkbox', + name: nameFn ? nameFn(row) : null, + type: 'checkbox', + value: valueFn ? valueFn(row) : null, + checked: selected + })[0]; + } + else if (type === 'type') { + return 'select-checkbox'; + } + else if (type === 'filter') { + return ''; + } + + return selected ? 'X' : ''; + } +} + +// Legacy checkbox ordering +DataTable.ext.order['select-checkbox'] = function (settings, col) { + return this.api() + .column(col, { order: 'index' }) + .nodes() + .map(function (td) { + if (settings._select.items === 'row') { + return $(td).parent().hasClass(settings._select.className); + } + else if (settings._select.items === 'cell') { + return $(td).hasClass(settings._select.className); + } + return false; + }); +}; + +$.fn.DataTable.select = DataTable.select; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Initialisation + */ + +// DataTables creation - check if select has been defined in the options. Note +// this required that the table be in the document! If it isn't then something +// needs to trigger this method unfortunately. The next major release of +// DataTables will rework the events and address this. +$(document).on('preInit.dt.dtSelect', function (e, ctx) { + if (e.namespace !== 'dt') { + return; + } + + DataTable.select.init(new DataTable.Api(ctx)); +}); + + +export default DataTable; diff --git a/assets/js/lib/datatables.js b/assets/js/lib/datatables.js index 5c94799a..8e39548b 100644 --- a/assets/js/lib/datatables.js +++ b/assets/js/lib/datatables.js @@ -47,7 +47,8 @@ method: config.method, data: { _dt: config.name, - _init: true + _init: true, + order: config.initial_order ?? undefined, } }).done(function(data) { var baseState; @@ -72,6 +73,17 @@ } } else { request._dt = config.name; + + //Try to resolve the original column index when the column was reordered (using the ColReorder plugin) + //Only do this when _ColReorder_iOrigCol is available + if (settings.aoColumns && settings.aoColumns.length && settings.aoColumns[0]._ColReorder_iOrigCol !== undefined) { + if (request.order && request.order.length) { + request.order.forEach(function (order) { + order.column = settings.aoColumns[order.column]._ColReorder_iOrigCol; + }); + } + } + $.ajax(typeof config.url === 'function' ? config.url(dt) : config.url, { method: config.method, data: request @@ -86,6 +98,15 @@ dtOpts = config.options(dtOpts); } + //Choose the column where the className contains "select-column" and apply the select extension to its render field + //Added for Part-DB + for (let column of dtOpts.columns) { + if (column.className && column.className.includes('dt-select')) { + column.render = $.fn.dataTable.render.select(); + } + } + + root.html(data.template); dt = $('table', root).DataTable(dtOpts); if (config.state !== 'none') { diff --git a/assets/js/register_events.js b/assets/js/register_events.js index 9fcbd6cc..9732c0c1 100644 --- a/assets/js/register_events.js +++ b/assets/js/register_events.js @@ -20,6 +20,8 @@ 'use strict'; import {Dropdown} from "bootstrap"; +import ClipboardJS from "clipboard"; +import {Modal} from "bootstrap"; class RegisterEventHelper { constructor() { @@ -27,7 +29,14 @@ class RegisterEventHelper { this.configureDropdowns(); this.registerSpecialCharInput(); + //Initialize ClipboardJS + this.registerLoadHandler(() => { + new ClipboardJS('.btn'); + }); + this.registerModalDropRemovalOnFormSubmit(); + + } registerModalDropRemovalOnFormSubmit() { @@ -37,6 +46,15 @@ class RegisterEventHelper { if (back_drop) { back_drop.remove(); } + + //Remove scroll-lock if it is still active + if (document.body.classList.contains('modal-open')) { + document.body.classList.remove('modal-open'); + + //Remove the padding-right and overflow:hidden from the body + document.body.style.paddingRight = ''; + document.body.style.overflow = ''; + } }); } @@ -59,76 +77,239 @@ class RegisterEventHelper { } registerTooltips() { - this.registerLoadHandler(() => { + const handler = () => { $(".tooltip").remove(); //Exclude dropdown buttons from tooltips, otherwise we run into endless errors from bootstrap (bootstrap.esm.js:614 Bootstrap doesn't allow more than one instance per element. Bound instance: bs.dropdown.) - $('a[title], button[title]:not([data-bs-toggle="dropdown"]), p[title], span[title], h6[title], h3[title], i.fas[title]') + $('a[title], label[title], button[title]:not([data-bs-toggle="dropdown"]), p[title], span[title], h6[title], h3[title], i[title], small[title]') //@ts-ignore .tooltip("hide").tooltip({container: "body", placement: "auto", boundary: 'window'}); - }); + }; + + this.registerLoadHandler(handler); + document.addEventListener('dt:loaded', handler); } registerSpecialCharInput() { this.registerLoadHandler(() => { //@ts-ignore $("input[type=text], input[type=search]").unbind("keydown").keydown(function (event) { - let greek = event.altKey; + let use_special_char = event.altKey; let greek_char = ""; - if (greek){ + if (use_special_char){ + //Use the key property to determine the greek letter (as it is independent of the keyboard layout) switch(event.key) { - case "w": //Omega - greek_char = '\u2126'; - break; - case "u": - case "m": //Micro - greek_char = "\u00B5"; - break; - case "p": //Phi - greek_char = "\u03C6"; - break; - case "a": //Alpha + //Greek letters + case "a": //Alpha (lowercase) greek_char = "\u03B1"; break; - case "b": //Beta + case "A": //Alpha (uppercase) + greek_char = "\u0391"; + break; + case "b": //Beta (lowercase) greek_char = "\u03B2"; break; - case "c": //Gamma + case "B": //Beta (uppercase) + greek_char = "\u0392"; + break; + case "g": //Gamma (lowercase) greek_char = "\u03B3"; break; - case "d": //Delta + case "G": //Gamma (uppercase) + greek_char = "\u0393"; + break; + case "d": //Delta (lowercase) greek_char = "\u03B4"; break; - case "l": //Pound - greek_char = "\u00A3"; + case "D": //Delta (uppercase) + greek_char = "\u0394"; break; - case "y": //Yen - greek_char = "\u00A5"; + case "e": //Epsilon (lowercase) + greek_char = "\u03B5"; break; - case "o": //Yen - greek_char = "\u00A4"; + case "E": //Epsilon (uppercase) + greek_char = "\u0395"; break; - case "1": //Sum symbol - greek_char = "\u2211"; + case "z": //Zeta (lowercase) + greek_char = "\u03B6"; break; - case "2": //Integral - greek_char = "\u222B"; + case "Z": //Zeta (uppercase) + greek_char = "\u0396"; break; - case "3": //Less-than or equal - greek_char = "\u2264"; + case "h": //Eta (lowercase) + greek_char = "\u03B7"; break; - case "4": //Greater than or equal - greek_char = "\u2265"; + case "H": //Eta (uppercase) + greek_char = "\u0397"; break; - case "5": //PI - greek_char = "\u03c0"; + case "q": //Theta (lowercase) + greek_char = "\u03B8"; break; - case "q": //Copyright - greek_char = "\u00A9"; + case "Q": //Theta (uppercase) + greek_char = "\u0398"; break; - case "e": //Euro - greek_char = "\u20AC"; + case "i": //Iota (lowercase) + greek_char = "\u03B9"; break; + case "I": //Iota (uppercase) + greek_char = "\u0399"; + break; + case "k": //Kappa (lowercase) + greek_char = "\u03BA"; + break; + case "K": //Kappa (uppercase) + greek_char = "\u039A"; + break; + case "l": //Lambda (lowercase) + greek_char = "\u03BB"; + break; + case "L": //Lambda (uppercase) + greek_char = "\u039B"; + break; + case "m": //Mu (lowercase) + greek_char = "\u03BC"; + break; + case "M": //Mu (uppercase) + greek_char = "\u039C"; + break; + case "n": //Nu (lowercase) + greek_char = "\u03BD"; + break; + case "N": //Nu (uppercase) + greek_char = "\u039D"; + break; + case "x": //Xi (lowercase) + greek_char = "\u03BE"; + break; + case "X": //Xi (uppercase) + greek_char = "\u039E"; + break; + case "o": //Omicron (lowercase) + greek_char = "\u03BF"; + break; + case "O": //Omicron (uppercase) + greek_char = "\u039F"; + break; + case "p": //Pi (lowercase) + greek_char = "\u03C0"; + break; + case "P": //Pi (uppercase) + greek_char = "\u03A0"; + break; + case "r": //Rho (lowercase) + greek_char = "\u03C1"; + break; + case "R": //Rho (uppercase) + greek_char = "\u03A1"; + break; + case "s": //Sigma (lowercase) + greek_char = "\u03C3"; + break; + case "S": //Sigma (uppercase) + greek_char = "\u03A3"; + break; + case "t": //Tau (lowercase) + greek_char = "\u03C4"; + break; + case "T": //Tau (uppercase) + greek_char = "\u03A4"; + break; + case "u": //Upsilon (lowercase) + greek_char = "\u03C5"; + break; + case "U": //Upsilon (uppercase) + greek_char = "\u03A5"; + break; + case "f": //Phi (lowercase) + greek_char = "\u03C6"; + break; + case "F": //Phi (uppercase) + greek_char = "\u03A6"; + break; + case "c": //Chi (lowercase) + greek_char = "\u03C7"; + break; + case "C": //Chi (uppercase) + greek_char = "\u03A7"; + break; + case "y": //Psi (lowercase) + greek_char = "\u03C8"; + break; + case "Y": //Psi (uppercase) + greek_char = "\u03A8"; + break; + case "w": //Omega (lowercase) + greek_char = "\u03C9"; + break; + case "W": //Omega (uppercase) + greek_char = "\u03A9"; + break; + } + + //Use keycodes for special characters as the shift char on the number keys are layout dependent + switch (event.keyCode) { + case 49: //1 key + //Product symbol on shift, sum on no shift + greek_char = event.shiftKey ? "\u220F" : "\u2211"; + break; + case 50: //2 key + //Integral on no shift, partial derivative on shift + greek_char = event.shiftKey ? "\u2202" : "\u222B"; + break; + case 51: //3 key + //Less than or equal on no shift, greater than or equal on shift + greek_char = event.shiftKey ? "\u2265" : "\u2264"; + break; + case 52: //4 key + //Empty set on shift, infinity on no shift + greek_char = event.shiftKey ? "\u2205" : "\u221E"; + break; + case 53: //5 key + //Not equal on shift, approx equal on no shift + greek_char = event.shiftKey ? "\u2260" : "\u2248"; + break; + case 54: //6 key + //Element of on no shift, not element of on shift + greek_char = event.shiftKey ? "\u2209" : "\u2208"; + break; + case 55: //7 key + //And on shift, or on no shift + greek_char = event.shiftKey ? "\u2227" : "\u2228"; + break; + case 56: //8 key + //Proportional to on shift, angle on no shift + greek_char = event.shiftKey ? "\u221D" : "\u2220"; + break; + case 57: //9 key + //Cube root on shift, square root on no shift + greek_char = event.shiftKey ? "\u221B" : "\u221A"; + break; + case 48: //0 key + //Minus-Plus on shift, plus-minus on no shift + greek_char = event.shiftKey ? "\u2213" : "\u00B1"; + break; + + //Special characters + case 219: //hyphen (or ß on german layout) + //Copyright on no shift, TM on shift + greek_char = event.shiftKey ? "\u2122" : "\u00A9"; + break; + case 191: //forward slash (or # on german layout) + //Generic currency on no shift, paragraph on shift + greek_char = event.shiftKey ? "\u00B6" : "\u00A4"; + break; + + //Currency symbols + case 192: //: or (ö on german layout) + //Euro on no shift, pound on shift + greek_char = event.shiftKey ? "\u00A3" : "\u20AC"; + break; + case 221: //; or (ä on german layout) + //Yen on no shift, dollar on shift + greek_char = event.shiftKey ? "\u0024" : "\u00A5"; + break; + + } if(greek_char=="") return; diff --git a/assets/js/tab_remember.js b/assets/js/tab_remember.js index 7405fcfa..1bf35db5 100644 --- a/assets/js/tab_remember.js +++ b/assets/js/tab_remember.js @@ -19,7 +19,7 @@ "use strict"; -import {Tab} from "bootstrap"; +import {Tab, Dropdown, Collapse} from "bootstrap"; import tab from "bootstrap/js/src/tab"; /** @@ -54,6 +54,7 @@ class TabRememberHelper { const first_element = merged[0] ?? null; if(first_element) { this.revealElementOnTab(first_element); + this.revealElementInCollapse(first_element); } } @@ -62,7 +63,27 @@ class TabRememberHelper { * @param event */ onInvalid(event) { + this.revealElementInCollapse(event.target); this.revealElementOnTab(event.target); + this.revealElementInDropdown(event.target); + } + + revealElementInCollapse(element) { + let collapse = element.closest('.collapse'); + + if(collapse) { + let bs_collapse = Collapse.getOrCreateInstance(collapse); + bs_collapse.show(); + } + } + + revealElementInDropdown(element) { + let dropdown = element.closest('.dropdown-menu'); + + if(dropdown) { + let bs_dropdown = Dropdown.getOrCreateInstance(dropdown); + bs_dropdown.show(); + } } revealElementOnTab(element) { diff --git a/assets/js/webauthn_tfa.js b/assets/js/webauthn_tfa.js index a2e00595..4d54efc0 100644 --- a/assets/js/webauthn_tfa.js +++ b/assets/js/webauthn_tfa.js @@ -21,8 +21,13 @@ class WebauthnTFA { -// Decodes a Base64Url string - _base64UrlDecode = (input) => { + _b64UrlSafeEncode = (str) => { + const b64 = btoa(str); + return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); + } + + // Decodes a Base64Url string + _b64UrlSafeDecode = (input) => { input = input .replace(/-/g, '+') .replace(/_/g, '/'); @@ -39,13 +44,16 @@ class WebauthnTFA { }; // Converts an array of bytes into a Base64Url string - _arrayToBase64String = (a) => btoa(String.fromCharCode(...a)); + _arrayToBase64String = (a) => { + const str = String.fromCharCode(...a); + return this._b64UrlSafeEncode(str); + } // Prepares the public key options object returned by the Webauthn Framework _preparePublicKeyOptions = publicKey => { //Convert challenge from Base64Url string to Uint8Array publicKey.challenge = Uint8Array.from( - this._base64UrlDecode(publicKey.challenge), + this._b64UrlSafeDecode(publicKey.challenge), c => c.charCodeAt(0) ); @@ -67,7 +75,7 @@ class WebauthnTFA { return { ...data, id: Uint8Array.from( - this._base64UrlDecode(data.id), + this._b64UrlSafeDecode(data.id), c => c.charCodeAt(0) ), }; @@ -81,7 +89,7 @@ class WebauthnTFA { return { ...data, id: Uint8Array.from( - this._base64UrlDecode(data.id), + this._b64UrlSafeDecode(data.id), c => c.charCodeAt(0) ), }; diff --git a/assets/tomselect/autoselect_typed/autoselect_typed.js b/assets/tomselect/autoselect_typed/autoselect_typed.js new file mode 100644 index 00000000..8a426be7 --- /dev/null +++ b/assets/tomselect/autoselect_typed/autoselect_typed.js @@ -0,0 +1,63 @@ +/** + * Autoselect Typed plugin for Tomselect + * + * This plugin allows automatically selecting an option matching the typed text when the Tomselect element goes out of + * focus (is blurred) and/or when the delimiter is typed. + * + * #select_on_blur option + * Tomselect natively supports the "createOnBlur" option. This option picks up any remaining text in the input field + * and uses it to create a new option and selects that option. It does behave a bit strangely though, in that it will + * not select an already existing option when the input is blurred, so if you typed something that matches an option in + * the list and then click outside the box (without pressing enter) the entered text is just removed (unless you have + * allow duplicates on in which case it will create a new option). + * This plugin fixes that, such that Tomselect will first try to select an option matching the remaining uncommitted + * text and only when no matching option is found tries to create a new one (if createOnBlur and create is on) + * + * #select_on_delimiter option + * Normally when typing the delimiter (space by default) Tomselect will try to create a new option (and select it) (if + * create is on), but if the typed text matches an option (and allow duplicates is off) it refuses to react at all until + * you press enter. With this option, the delimiter will also allow selecting an option, not just creating it. + */ +function select_current_input(self){ + if(self.isLocked){ + return + } + + const val = self.inputValue() + //Do nothing if the input is empty + if (!val) { + return + } + + if (self.options[val]) { + self.addItem(val) + self.setTextboxValue() + } +} + +export default function(plugin_options_) { + const plugin_options = Object.assign({ + //Autoselect the typed text when the input element goes out of focus + select_on_blur: true, + //Autoselect the typed text when the delimiter is typed + select_on_delimiter: true, + }, plugin_options_); + + const self = this + + if(plugin_options.select_on_blur) { + this.hook("before", "onBlur", function () { + select_current_input(self) + }) + } + + if(plugin_options.select_on_delimiter) { + this.hook("before", "onKeyPress", function (e) { + const character = String.fromCharCode(e.keyCode || e.which); + if (self.settings.mode === 'multi' && character === self.settings.delimiter) { + select_current_input(self) + } + }) + } + +} \ No newline at end of file diff --git a/assets/tomselect/click_to_edit/click_to_edit.js b/assets/tomselect/click_to_edit/click_to_edit.js new file mode 100644 index 00000000..b7dcab03 --- /dev/null +++ b/assets/tomselect/click_to_edit/click_to_edit.js @@ -0,0 +1,93 @@ +/** + * click_to_edit plugin for Tomselect + * + * This plugin allows editing (and selecting text in) any selected item by clicking it. + * + * Usually, when the user typed some text and created an item in Tomselect that item cannot be edited anymore. To make + * a change, the item has to be deleted and retyped completely. There is also generally no way to copy text out of a + * tomselect item. The "restore_on_backspace" plugin improves that somewhat, by allowing the user to edit an item after + * pressing backspace. However, it is somewhat confusing to first have to focus the field an then hit backspace in order + * to copy a piece of text. It may also not be immediately obvious for editing. + * This plugin transforms an item into editable text when it is clicked, e.g. when the user tries to place the caret + * within an item or when they try to drag across the text to highlight it. + * It also plays nice with the remove_button plugin which still removes (deselects) an option entirely. + * + * It is recommended to also enable the autoselect_typed plugin when using this plugin. Without it, the text in the + * input field (i.e. the item that was just clicked) is lost when the user clicks outside the field. Also, when the user + * clicks an option (making it text) and then tries to enter another one by entering the delimiter (e.g. space) nothing + * happens until enter is pressed or the text is changed from what it was. + */ + +/** + * Return a dom element from either a dom query string, jQuery object, a dom element or html string + * https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518 + * + * param query should be {} + */ +const getDom = query => { + if (query.jquery) { + return query[0]; + } + if (query instanceof HTMLElement) { + return query; + } + if (isHtmlString(query)) { + var tpl = document.createElement('template'); + tpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result + return tpl.content.firstChild; + } + return document.querySelector(query); +}; +const isHtmlString = arg => { + if (typeof arg === 'string' && arg.indexOf('<') > -1) { + return true; + } + return false; +}; + +function plugin(plugin_options_) { + const self = this + + const plugin_options = Object.assign({ + //If there is unsubmitted text in the input field, should that text be automatically used to select a matching + //element? If this is off, clicking on item1 and then clicking on item2 will result in item1 being deselected + auto_select_before_edit: true, + //If there is unsubmitted text in the input field, should that text be automatically used to create a matching + //element if no matching element was found or auto_select_before_edit is off? + auto_create_before_edit: true, + //customize this function to change which text the item is replaced with when clicking on it + text: option => { + return option[self.settings.labelField]; + } + }, plugin_options_); + + + self.hook('after', 'setupTemplates', () => { + const orig_render_item = self.settings.render.item; + self.settings.render.item = (data, escape) => { + const item = getDom(orig_render_item.call(self, data, escape)); + + item.addEventListener('click', evt => { + if (self.isLocked) { + return; + } + const val = self.inputValue(); + + if (self.options[val]) { + self.addItem(val) + } else if (self.settings.create) { + self.createItem(); + } + const option = self.options[item.dataset.value] + self.setTextboxValue(plugin_options.text.call(self, option)); + self.focus(); + self.removeItem(item); + } + ); + + return item; + } + }); + +} +export { plugin as default }; \ No newline at end of file diff --git a/assets/translator.js b/assets/translator.js new file mode 100644 index 00000000..0d5ae86b --- /dev/null +++ b/assets/translator.js @@ -0,0 +1,16 @@ +import { localeFallbacks } from '../var/translations/configuration'; +import { trans, getLocale, setLocale, setLocaleFallbacks } from '@symfony/ux-translator'; +/* + * This file is part of the Symfony UX Translator package. + * + * If folder "../var/translations" does not exist, or some translations are missing, + * you must warmup your Symfony cache to refresh JavaScript translations. + * + * If you use TypeScript, you can rename this file to "translator.ts" to take advantage of types checking. + */ + +setLocaleFallbacks(localeFallbacks); + +export { trans }; + +export * from '../var/translations'; \ No newline at end of file diff --git a/bin/console b/bin/console index c933dc53..256c0a60 100755 --- a/bin/console +++ b/bin/console @@ -4,6 +4,17 @@ use App\Kernel; use Symfony\Bundle\FrameworkBundle\Console\Application; +if (!is_dir(dirname(__DIR__).'/vendor')) { + throw new LogicException('Dependencies are missing. Try running "composer install".'); +} + +//Increase xdebug.max_nesting_level to 1000 if required (see issue #411) +//Check if xdebug extension is active, and xdebug.max_nesting_level is set to 256 or lower +if (extension_loaded('xdebug') && ((int) ini_get('xdebug.max_nesting_level')) <= 256) { + //Increase xdebug.max_nesting_level to 1000 + ini_set('xdebug.max_nesting_level', '1000'); +} + if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".'); } diff --git a/bin/phpunit b/bin/phpunit index f26f2c72..692baccb 100755 --- a/bin/phpunit +++ b/bin/phpunit @@ -6,9 +6,13 @@ if (!ini_get('date.timezone')) { } if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) { - define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php'); - require PHPUNIT_COMPOSER_INSTALL; - PHPUnit\TextUI\Command::main(); + if (PHP_VERSION_ID >= 80000) { + require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit'; + } else { + define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php'); + require PHPUNIT_COMPOSER_INSTALL; + PHPUnit\TextUI\Command::main(); + } } else { if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) { echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n"; diff --git a/codecov.yml b/codecov.yml index 34acb532..6ac0375c 100644 --- a/codecov.yml +++ b/codecov.yml @@ -5,4 +5,5 @@ coverage: status: project: default: - threshold: 5% \ No newline at end of file + threshold: 10% + target: 40% diff --git a/composer.json b/composer.json index 33ffde89..e57ce652 100644 --- a/composer.json +++ b/composer.json @@ -1,101 +1,118 @@ { + "name": "part-db/part-db-server", "type": "project", "license": "AGPL-3.0-or-later", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.1", "ext-ctype": "*", + "ext-dom": "*", "ext-gd": "*", "ext-iconv": "*", "ext-intl": "*", "ext-json": "*", "ext-mbstring": "*", + "amphp/http-client": "^5.1", + "api-platform/core": "^3.1", "beberlei/doctrineextensions": "^1.2", - "brick/math": "^0.8.15", - "composer/package-versions-deprecated": "1.11.99.4", - "doctrine/annotations": "^1.6", - "doctrine/dbal": "^3.4.6", + "brick/math": "0.12.1 as 0.11.0", + "composer/ca-bundle": "^1.5", + "composer/package-versions-deprecated": "^1.11.99.5", + "doctrine/data-fixtures": "^2.0.0", + "doctrine/dbal": "^4.0.0", "doctrine/doctrine-bundle": "^2.0", "doctrine/doctrine-migrations-bundle": "^3.0", - "doctrine/orm": "^2.9", - "dompdf/dompdf": "^2.0.0", + "doctrine/orm": "^3.2.0", + "dompdf/dompdf": "^v3.0.0", "erusev/parsedown": "^1.7", "florianv/swap": "^4.0", "florianv/swap-bundle": "dev-master", "gregwar/captcha-bundle": "^2.1.0", - "jbtronics/2fa-webauthn": "^1.0.0", + "hshn/base64-encoded-file": "^5.0", + "jbtronics/2fa-webauthn": "^v2.2.0", + "jbtronics/dompdf-font-loader-bundle": "^1.0.0", + "jfcherng/php-diff": "^6.14", + "knpuniversity/oauth2-client-bundle": "^2.15", + "league/csv": "^9.8.0", "league/html-to-markdown": "^5.0.1", "liip/imagine-bundle": "^2.2", + "nbgrp/onelogin-saml-bundle": "^1.3", "nelexa/zip": "^4.0", + "nelmio/cors-bundle": "^2.3", "nelmio/security-bundle": "^3.0", "nyholm/psr7": "^1.1", - "ocramius/proxy-manager": "2.2.*", - "omines/datatables-bundle": "^0.5.0", - "php-translation/symfony-bundle": "^0.12.0", - "phpdocumentor/reflection-docblock": "^5.2", + "omines/datatables-bundle": "^0.9.1", + "paragonie/sodium_compat": "^1.21", + "part-db/label-fonts": "^1.0", + "rhukster/dom-sanitizer": "^1.0", + "runtime/frankenphp-symfony": "^0.2.0", "s9e/text-formatter": "^2.1", - "scheb/2fa-backup-code": "^5.13", - "scheb/2fa-bundle": "^5.13", - "scheb/2fa-google-authenticator": "^5.13", - "scheb/2fa-trusted-device": "^5.13", - "sensio/framework-extra-bundle": "^6.1.1", + "scheb/2fa-backup-code": "^6.8.0", + "scheb/2fa-bundle": "^6.8.0", + "scheb/2fa-google-authenticator": "^6.8.0", + "scheb/2fa-trusted-device": "^6.8.0", "shivas/versioning-bundle": "^4.0", - "spatie/db-dumper": "^2.21", + "spatie/db-dumper": "^3.3.1", "symfony/apache-pack": "^1.0", - "symfony/asset": "5.4.*", - "symfony/console": "5.4.*", - "symfony/dotenv": "5.4.*", - "symfony/expression-language": "5.4.*", - "symfony/flex": "^1.1", - "symfony/form": "5.4.*", - "symfony/framework-bundle": "5.4.*", - "symfony/http-client": "5.4.*", - "symfony/http-kernel": "5.4.*", - "symfony/mailer": "5.4.*", + "symfony/asset": "6.4.*", + "symfony/console": "6.4.*", + "symfony/css-selector": "6.4.*", + "symfony/dom-crawler": "6.4.*", + "symfony/dotenv": "6.4.*", + "symfony/expression-language": "6.4.*", + "symfony/flex": "^v2.3.1", + "symfony/form": "6.4.*", + "symfony/framework-bundle": "6.4.*", + "symfony/http-client": "6.4.*", + "symfony/http-kernel": "6.4.*", + "symfony/mailer": "6.4.*", "symfony/monolog-bundle": "^3.1", - "symfony/process": "5.4.*", - "symfony/property-access": "5.4.*", - "symfony/property-info": "5.4.*", - "symfony/proxy-manager-bridge": "5.4.*", - "symfony/rate-limiter": "5.4.*", - "symfony/runtime": "5.4.*", - "symfony/security-bundle": "5.4.*", - "symfony/serializer": "5.4.*", - "symfony/translation": "5.4.*", - "symfony/twig-bundle": "5.4.*", + "symfony/polyfill-php82": "^1.28", + "symfony/process": "6.4.*", + "symfony/property-access": "6.4.*", + "symfony/property-info": "6.4.*", + "symfony/rate-limiter": "6.4.*", + "symfony/runtime": "6.4.*", + "symfony/security-bundle": "6.4.*", + "symfony/serializer": "6.4.*", + "symfony/string": "6.4.*", + "symfony/translation": "6.4.*", + "symfony/twig-bundle": "6.4.*", + "symfony/ux-translator": "^2.10", "symfony/ux-turbo": "^2.0", - "symfony/validator": "5.4.*", - "symfony/web-link": "5.4.*", - "symfony/webpack-encore-bundle": "^1.1", - "symfony/yaml": "5.4.*", - "tecnickcom/tc-lib-barcode": "^1.15", + "symfony/validator": "6.4.*", + "symfony/web-link": "6.4.*", + "symfony/webpack-encore-bundle": "^v2.0.1", + "symfony/yaml": "6.4.*", + "tecnickcom/tc-lib-barcode": "^2.1.4", "twig/cssinliner-extra": "^3.0", - "twig/extra-bundle": "^3.0", - "twig/html-extra": "^3.0", + "twig/extra-bundle": "^3.8", + "twig/html-extra": "^3.8", "twig/inky-extra": "^3.0", - "twig/intl-extra": "^3.0", - "twig/markdown-extra": "^3.0", - "web-auth/webauthn-symfony-bundle": "^3.3", - "webmozart/assert": "^1.4" + "twig/intl-extra": "^3.8", + "twig/markdown-extra": "^3.8", + "twig/string-extra": "^3.8", + "web-auth/webauthn-symfony-bundle": "^4.0.0" }, "require-dev": { - "dama/doctrine-test-bundle": "^7.0", - "doctrine/doctrine-fixtures-bundle": "^3.2", - "ekino/phpstan-banned-code": "^v1.0.0", + "dama/doctrine-test-bundle": "^v8.0.0", + "doctrine/doctrine-fixtures-bundle": "^4.0.0", + "ekino/phpstan-banned-code": "^v3.0.0", + "jbtronics/translation-editor-bundle": "^1.0", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.4.7", - "phpstan/phpstan-doctrine": "^1.2.11", - "phpstan/phpstan-symfony": "^1.1.7", - "psalm/plugin-symfony": "^v5.0.1", + "phpstan/phpstan": "^2.0.4", + "phpstan/phpstan-doctrine": "^2.0.1", + "phpstan/phpstan-strict-rules": "^2.0.1", + "phpstan/phpstan-symfony": "^2.0.0", + "phpunit/phpunit": "^9.5", + "rector/rector": "^2.0.4", "roave/security-advisories": "dev-latest", - "symfony/browser-kit": "^5.2", - "symfony/css-selector": "^5.2", - "symfony/debug-bundle": "^5.2", + "symfony/browser-kit": "6.4.*", + "symfony/debug-bundle": "6.4.*", "symfony/maker-bundle": "^1.13", - "symfony/phpunit-bridge": "5.4.*", - "symfony/stopwatch": "^5.2", - "symfony/web-profiler-bundle": "^5.2", - "symplify/easy-coding-standard": "^11.0", - "vimeo/psalm": "^5.6.0" + "symfony/phpunit-bridge": "6.4.*", + "symfony/stopwatch": "6.4.*", + "symfony/web-profiler-bundle": "6.4.*", + "symplify/easy-coding-standard": "^12.0" }, "suggest": { "ext-bcmath": "Used to improve price calculation performance", @@ -106,7 +123,7 @@ "*": "dist" }, "platform": { - "php": "7.4.0" + "php": "8.1.0" }, "sort-packages": true, "allow-plugins": { @@ -138,7 +155,7 @@ "post-update-cmd": [ "@auto-scripts" ], - "phpstan": "vendor/bin/phpstan analyse src --level 2 --memory-limit 1G" + "phpstan": "vendor/bin/phpstan analyse src --level 5 --memory-limit 1G" }, "conflict": { "symfony/symfony": "*" @@ -146,9 +163,8 @@ "extra": { "symfony": { "allow-contrib": false, - "require": "5.4.*" + "require": "6.4.*", + "docker": true } - }, - "repositories": [ - ] + } } diff --git a/composer.lock b/composer.lock index db7ddfee..4d658092 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,1201 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3a1c7d677a101ae6256c68bf69919414", + "content-hash": "27cd0d915eab5e7cb57215a4c0b529fb", "packages": [ { - "name": "beberlei/assert", - "version": "v3.3.2", + "name": "amphp/amp", + "version": "v3.1.0", "source": { "type": "git", - "url": "https://github.com/beberlei/assert.git", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655" + "url": "https://github.com/amphp/amp.git", + "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/cb70015c04be1baee6f5f5c953703347c0ac1655", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655", + "url": "https://api.github.com/repos/amphp/amp/zipball/7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9", + "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-26T16:07:39+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T17:10:27+00:00" + }, + { + "name": "amphp/cache", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Cache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", + "support": { + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" + }, + { + "name": "amphp/dns", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/dns.git", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/process": "^2", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "ext-json": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], + "support": { + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-19T15:43:40+00:00" + }, + { + "name": "amphp/hpack", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/hpack.git", + "reference": "4f293064b15682a2b178b1367ddf0b8b5feb0239" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/hpack/zipball/4f293064b15682a2b178b1367ddf0b8b5feb0239", + "reference": "4f293064b15682a2b178b1367ddf0b8b5feb0239", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "http2jp/hpack-test-case": "^1", + "nikic/php-fuzzer": "^0.0.10", + "phpunit/phpunit": "^7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Amp\\Http\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "HTTP/2 HPack implementation.", + "homepage": "https://github.com/amphp/hpack", + "keywords": [ + "headers", + "hpack", + "http-2" + ], + "support": { + "issues": "https://github.com/amphp/hpack/issues", + "source": "https://github.com/amphp/hpack/tree/v3.2.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:00:16+00:00" + }, + { + "name": "amphp/http", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/http.git", + "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http/zipball/3680d80bd38b5d6f3c2cef2214ca6dd6cef26588", + "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588", + "shasum": "" + }, + "require": { + "amphp/hpack": "^3", + "amphp/parser": "^1.1", + "league/uri-components": "^2.4.2 | ^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "league/uri": "^6.8 | ^7.1", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.26.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/constants.php" + ], + "psr-4": { + "Amp\\Http\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Basic HTTP primitives which can be shared by servers and clients.", + "support": { + "issues": "https://github.com/amphp/http/issues", + "source": "https://github.com/amphp/http/tree/v2.1.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-11-23T14:57:26+00:00" + }, + { + "name": "amphp/http-client", + "version": "v5.3.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/http-client.git", + "reference": "cf885bf00550d645a27605626528a3b2ce5563ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http-client/zipball/cf885bf00550d645a27605626528a3b2ce5563ae", + "reference": "cf885bf00550d645a27605626528a3b2ce5563ae", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/hpack": "^3", + "amphp/http": "^2", + "amphp/pipeline": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "league/uri": "^7", + "league/uri-components": "^7", + "league/uri-interfaces": "^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2", + "revolt/event-loop": "^1" + }, + "conflict": { + "amphp/file": "<3 | >=5" + }, + "require-dev": { + "amphp/file": "^3 | ^4", + "amphp/http-server": "^3", + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "ext-json": "*", + "kelunik/link-header-rfc5988": "^1", + "laminas/laminas-diactoros": "^2.3", + "phpunit/phpunit": "^9", + "psalm/phar": "~5.23" + }, + "suggest": { + "amphp/file": "Required for file request bodies and HTTP archive logging", + "ext-json": "Required for logging HTTP archives", + "ext-zlib": "Allows using compression for response bodies." + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\Http\\Client\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "An advanced async HTTP client library for PHP, enabling efficient, non-blocking, and concurrent requests and responses.", + "homepage": "https://amphp.org/http-client", + "keywords": [ + "async", + "client", + "concurrent", + "http", + "non-blocking", + "rest" + ], + "support": { + "issues": "https://github.com/amphp/http-client/issues", + "source": "https://github.com/amphp/http-client/tree/v5.3.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-05-18T18:27:44+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "7b52598c2e9105ebcddf247fc523161581930367" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367", + "reference": "7b52598c2e9105ebcddf247fc523161581930367", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T16:33:53+00:00" + }, + { + "name": "amphp/process", + "version": "v2.0.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.0.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:13:44+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/socket", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^6.5 | ^7", + "league/uri-interfaces": "^2.3 | ^7", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" + ], + "psr-4": { + "Amp\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "support": { + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-21T14:33:03+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-03T19:31:26+00:00" + }, + { + "name": "api-platform/core", + "version": "v3.4.17", + "source": { + "type": "git", + "url": "https://github.com/api-platform/core.git", + "reference": "c5fb664d17ed9ae919394514ea69a5039d2ad9ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/api-platform/core/zipball/c5fb664d17ed9ae919394514ea69a5039d2ad9ab", + "reference": "c5fb664d17ed9ae919394514ea69a5039d2ad9ab", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^1.0 || ^2.0", + "php": ">=8.1", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^3.1", + "symfony/http-foundation": "^6.4 || ^7.1", + "symfony/http-kernel": "^6.4 || ^7.1", + "symfony/property-access": "^6.4 || ^7.1", + "symfony/property-info": "^6.4 || ^7.1", + "symfony/serializer": "^6.4 || ^7.1", + "symfony/translation-contracts": "^3.3", + "symfony/web-link": "^6.4 || ^7.1", + "willdurand/negotiation": "^3.0" + }, + "conflict": { + "doctrine/common": "<3.2.2", + "doctrine/dbal": "<2.10", + "doctrine/mongodb-odm": "<2.4", + "doctrine/orm": "<2.14.0", + "doctrine/persistence": "<1.3", + "elasticsearch/elasticsearch": ">=8.0,<8.4", + "phpspec/prophecy": "<1.15", + "phpunit/phpunit": "<9.5", + "symfony/framework-bundle": "6.4.6 || 7.0.6", + "symfony/var-exporter": "<6.1.1" + }, + "replace": { + "api-platform/doctrine-common": "self.version", + "api-platform/doctrine-odm": "self.version", + "api-platform/doctrine-orm": "self.version", + "api-platform/documentation": "self.version", + "api-platform/elasticsearch": "self.version", + "api-platform/graphql": "self.version", + "api-platform/http-cache": "self.version", + "api-platform/hydra": "self.version", + "api-platform/json-api": "self.version", + "api-platform/json-hal": "self.version", + "api-platform/json-schema": "self.version", + "api-platform/jsonld": "self.version", + "api-platform/laravel": "self.version", + "api-platform/metadata": "self.version", + "api-platform/openapi": "self.version", + "api-platform/parameter-validator": "self.version", + "api-platform/ramsey-uuid": "self.version", + "api-platform/serializer": "self.version", + "api-platform/state": "self.version", + "api-platform/symfony": "self.version", + "api-platform/validator": "self.version" + }, + "require-dev": { + "api-platform/doctrine-common": "^3.4 || ^4.0", + "api-platform/doctrine-odm": "^3.4 || ^4.0", + "api-platform/doctrine-orm": "^3.4 || ^4.0", + "api-platform/documentation": "^3.4 || ^4.0", + "api-platform/elasticsearch": "^3.4 || ^4.0", + "api-platform/graphql": "^3.4 || ^4.0", + "api-platform/http-cache": "^3.4 || ^4.0", + "api-platform/hydra": "^3.4 || ^4.0", + "api-platform/json-api": "^3.3 || ^4.0", + "api-platform/json-schema": "^3.4 || ^4.0", + "api-platform/jsonld": "^3.4 || ^4.0", + "api-platform/metadata": "^3.4 || ^4.0", + "api-platform/openapi": "^3.4 || ^4.0", + "api-platform/parameter-validator": "^3.4", + "api-platform/ramsey-uuid": "^3.4 || ^4.0", + "api-platform/serializer": "^3.4 || ^4.0", + "api-platform/state": "^3.4 || ^4.0", + "api-platform/validator": "^3.4 || ^4.0", + "behat/behat": "^3.11", + "behat/mink": "^1.9", + "doctrine/cache": "^1.11 || ^2.1", + "doctrine/common": "^3.2.2", + "doctrine/dbal": "^3.4.0 || ^4.0", + "doctrine/doctrine-bundle": "^1.12 || ^2.0", + "doctrine/mongodb-odm": "^2.2", + "doctrine/mongodb-odm-bundle": "^4.0 || ^5.0", + "doctrine/orm": "^2.14 || ^3.0", + "elasticsearch/elasticsearch": "^7.11 || ^8.4", + "friends-of-behat/mink-browserkit-driver": "^1.3.1", + "friends-of-behat/mink-extension": "^2.2", + "friends-of-behat/symfony-extension": "^2.1", + "guzzlehttp/guzzle": "^6.0 || ^7.1", + "jangregor/phpstan-prophecy": "^1.0", + "justinrainbow/json-schema": "^5.2.1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpdoc-parser": "^1.13|^2.0", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-doctrine": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-symfony": "^1.0", + "phpunit/phpunit": "^9.6", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "ramsey/uuid": "^3.9.7 || ^4.0", + "ramsey/uuid-doctrine": "^1.4 || ^2.0 || ^3.0", + "sebastian/comparator": "<5.0", + "soyuka/contexts": "v3.3.9", + "soyuka/pmu": "^0.0.12", + "soyuka/stubs-mongodb": "^1.0", + "symfony/asset": "^6.4 || ^7.1", + "symfony/browser-kit": "^6.4 || ^7.1", + "symfony/cache": "^6.4 || ^7.1", + "symfony/config": "^6.4 || ^7.1", + "symfony/console": "^6.4 || ^7.1", + "symfony/css-selector": "^6.4 || ^7.1", + "symfony/dependency-injection": "^6.4 || ^7.1", + "symfony/doctrine-bridge": "^6.4 || ^7.1", + "symfony/dom-crawler": "^6.4 || ^7.1", + "symfony/error-handler": "^6.4 || ^7.1", + "symfony/event-dispatcher": "^6.4 || ^7.1", + "symfony/expression-language": "^6.4 || ^7.1", + "symfony/finder": "^6.4 || ^7.1", + "symfony/form": "^6.4 || ^7.1", + "symfony/framework-bundle": "^6.4 || ^7.1", + "symfony/http-client": "^6.4 || ^7.1", + "symfony/intl": "^6.4 || ^7.1", + "symfony/maker-bundle": "^1.24", + "symfony/mercure-bundle": "*", + "symfony/messenger": "^6.4 || ^7.1", + "symfony/phpunit-bridge": "^6.4.1 || ^7.1", + "symfony/routing": "^6.4 || ^7.1", + "symfony/security-bundle": "^6.4 || ^7.1", + "symfony/security-core": "^6.4 || ^7.1", + "symfony/stopwatch": "^6.4 || ^7.1", + "symfony/string": "^6.4 || ^7.1", + "symfony/twig-bundle": "^6.4 || ^7.1", + "symfony/uid": "^6.4 || ^7.1", + "symfony/validator": "^6.4 || ^7.1", + "symfony/web-profiler-bundle": "^6.4 || ^7.1", + "symfony/yaml": "^6.4 || ^7.1", + "twig/twig": "^1.42.3 || ^2.12 || ^3.0", + "webonyx/graphql-php": "^14.0 || ^15.0" + }, + "suggest": { + "doctrine/mongodb-odm-bundle": "To support MongoDB. Only versions 4.0 and later are supported.", + "elasticsearch/elasticsearch": "To support Elasticsearch.", + "ocramius/package-versions": "To display the API Platform's version in the debug bar.", + "phpstan/phpdoc-parser": "To support extracting metadata from PHPDoc.", + "psr/cache-implementation": "To use metadata caching.", + "ramsey/uuid": "To support Ramsey's UUID identifiers.", + "symfony/cache": "To have metadata caching when using Symfony integration.", + "symfony/config": "To load XML configuration files.", + "symfony/expression-language": "To use authorization features.", + "symfony/http-client": "To use the HTTP cache invalidation system.", + "symfony/messenger": "To support messenger integration.", + "symfony/security": "To use authorization features.", + "symfony/twig-bundle": "To use the Swagger UI integration.", + "symfony/uid": "To support Symfony UUID/ULID identifiers.", + "symfony/web-profiler-bundle": "To use the data collector.", + "webonyx/graphql-php": "To support GraphQL." + }, + "type": "library", + "extra": { + "pmu": { + "projects": [ + "./src/*/composer.json", + "src/Doctrine/*/composer.json" + ] + }, + "thanks": { + "url": "https://github.com/api-platform/api-platform", + "name": "api-platform/api-platform" + }, + "symfony": { + "require": "^6.4 || ^7.1" + }, + "branch-alias": { + "dev-3.4": "3.4.x-dev", + "dev-main": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "ApiPlatform\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "kevin@dunglas.fr", + "homepage": "https://dunglas.fr" + } + ], + "description": "Build a fully-featured hypermedia or GraphQL API in minutes!", + "homepage": "https://api-platform.com", + "keywords": [ + "Hydra", + "JSON-LD", + "api", + "graphql", + "hal", + "jsonapi", + "openapi", + "rest", + "swagger" + ], + "support": { + "issues": "https://github.com/api-platform/core/issues", + "source": "https://github.com/api-platform/core/tree/v3.4.17" + }, + "time": "2025-04-07T08:40:57+00:00" + }, + { + "name": "beberlei/assert", + "version": "v3.3.3", + "source": { + "type": "git", + "url": "https://github.com/beberlei/assert.git", + "reference": "b5fd8eacd8915a1b627b8bfc027803f1939734dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beberlei/assert/zipball/b5fd8eacd8915a1b627b8bfc027803f1939734dd", + "reference": "b5fd8eacd8915a1b627b8bfc027803f1939734dd", "shasum": "" }, "require": { @@ -25,7 +1206,7 @@ "ext-json": "*", "ext-mbstring": "*", "ext-simplexml": "*", - "php": "^7.0 || ^8.0" + "php": "^7.1 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", @@ -69,33 +1250,38 @@ ], "support": { "issues": "https://github.com/beberlei/assert/issues", - "source": "https://github.com/beberlei/assert/tree/v3.3.2" + "source": "https://github.com/beberlei/assert/tree/v3.3.3" }, - "time": "2021-12-16T21:41:27+00:00" + "time": "2024-07-15T13:18:35+00:00" }, { "name": "beberlei/doctrineextensions", - "version": "v1.3.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/beberlei/DoctrineExtensions.git", - "reference": "008f162f191584a6c37c03a803f718802ba9dd9a" + "reference": "281f1650641c2f438b0a54d8eaa7ba50ac7e3eb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/DoctrineExtensions/zipball/008f162f191584a6c37c03a803f718802ba9dd9a", - "reference": "008f162f191584a6c37c03a803f718802ba9dd9a", + "url": "https://api.github.com/repos/beberlei/DoctrineExtensions/zipball/281f1650641c2f438b0a54d8eaa7ba50ac7e3eb6", + "reference": "281f1650641c2f438b0a54d8eaa7ba50ac7e3eb6", "shasum": "" }, "require": { - "doctrine/orm": "^2.7", + "doctrine/orm": "^2.19 || ^3.0", "php": "^7.2 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.14", - "nesbot/carbon": "*", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", - "symfony/yaml": "^4.2 || ^5.0", + "doctrine/annotations": "^1.14 || ^2", + "doctrine/coding-standard": "^9.0.2 || ^12.0", + "nesbot/carbon": "^2.72 || ^3", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5 || ^9.6", + "squizlabs/php_codesniffer": "^3.8", + "symfony/cache": "^5.4 || ^6.4 || ^7.0", + "symfony/yaml": "^5.4 || ^6.4 || ^7.0", + "vimeo/psalm": "^3.18 || ^5.22", "zf1/zend-date": "^1.12", "zf1/zend-registry": "^1.12" }, @@ -126,32 +1312,31 @@ "orm" ], "support": { - "source": "https://github.com/beberlei/DoctrineExtensions/tree/v1.3.0" + "source": "https://github.com/beberlei/DoctrineExtensions/tree/v1.5.0" }, - "time": "2020-11-29T07:37:23+00:00" + "time": "2024-03-03T17:55:15+00:00" }, { "name": "brick/math", - "version": "0.8.17", + "version": "0.12.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "e6f8e7d04346a95be89580f8c2c22d6c3fa65556" + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/e6f8e7d04346a95be89580f8c2c22d6c3fa65556", - "reference": "e6f8e7d04346a95be89580f8c2c22d6c3fa65556", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", "shasum": "" }, "require": { - "ext-json": "*", - "php": "^7.1|^8.0" + "php": "^8.1" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^7.5.15|^8.5", - "vimeo/psalm": "^3.5" + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" }, "type": "library", "autoload": { @@ -171,45 +1356,50 @@ "arithmetic", "bigdecimal", "bignum", + "bignumber", "brick", - "math" + "decimal", + "integer", + "math", + "mathematics", + "rational" ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/v0.8" + "source": "https://github.com/brick/math/tree/0.12.1" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/brick/math", - "type": "tidelift" + "url": "https://github.com/BenMorel", + "type": "github" } ], - "time": "2020-08-18T23:41:20+00:00" + "time": "2023-11-29T23:19:16+00:00" }, { "name": "composer/ca-bundle", - "version": "1.3.5", + "version": "1.5.6", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd" + "reference": "f65c239c970e7f072f067ab78646e9f0b2935175" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/74780ccf8c19d6acb8d65c5f39cd72110e132bbd", - "reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/f65c239c970e7f072f067ab78646e9f0b2935175", + "reference": "f65c239c970e7f072f067ab78646e9f0b2935175", "shasum": "" }, "require": { "ext-openssl": "*", "ext-pcre": "*", - "php": "^5.3.2 || ^7.0 || ^8.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.55", - "psr/log": "^1.0", - "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8 || ^9", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "type": "library", "extra": { @@ -244,7 +1434,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.3.5" + "source": "https://github.com/composer/ca-bundle/tree/1.5.6" }, "funding": [ { @@ -260,20 +1450,20 @@ "type": "tidelift" } ], - "time": "2023-01-11T08:27:00+00:00" + "time": "2025-03-06T14:30:56+00:00" }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.4", + "version": "1.11.99.5", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b174585d1fe49ceed21928a945138948cb394600" + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", - "reference": "b174585d1fe49ceed21928a945138948cb394600", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", "shasum": "" }, "require": { @@ -317,7 +1507,7 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" }, "funding": [ { @@ -333,205 +1523,82 @@ "type": "tidelift" } ], - "time": "2021-09-13T08:41:34+00:00" + "time": "2022-01-17T14:14:24+00:00" }, { - "name": "doctrine/annotations", - "version": "1.14.3", + "name": "daverandom/libdns", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af" + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af", - "reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", "shasum": "" }, "require": { - "doctrine/lexer": "^1 || ^2", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "vimeo/psalm": "^4.10" + "ext-ctype": "*", + "php": ">=7.1" }, "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + "ext-intl": "Required for IDN support" }, "type": "library", "autoload": { + "files": [ + "src/functions.php" + ], "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + "LibDNS\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "description": "DNS protocol implementation written in pure PHP", "keywords": [ - "annotations", - "docblock", - "parser" + "dns" ], "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.14.3" + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" }, - "time": "2023-02-01T09:20:38+00:00" - }, - { - "name": "doctrine/cache", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", - "shasum": "" - }, - "require": { - "php": "~7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": ">2.2,<2.4" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", - "homepage": "https://www.doctrine-project.org/projects/cache.html", - "keywords": [ - "abstraction", - "apcu", - "cache", - "caching", - "couchdb", - "memcached", - "php", - "redis", - "xcache" - ], - "support": { - "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/2.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", - "type": "tidelift" - } - ], - "time": "2022-05-20T20:07:39+00:00" + "time": "2024-04-12T12:12:48+00:00" }, { "name": "doctrine/collections", - "version": "1.8.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "2b44dd4cbca8b5744327de78bafef5945c7e7b5e" + "reference": "2eb07e5953eed811ce1b309a7478a3b236f2273d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/2b44dd4cbca8b5744327de78bafef5945c7e7b5e", - "reference": "2b44dd4cbca8b5744327de78bafef5945c7e7b5e", + "url": "https://api.github.com/repos/doctrine/collections/zipball/2eb07e5953eed811ce1b309a7478a3b236f2273d", + "reference": "2eb07e5953eed811ce1b309a7478a3b236f2273d", "shasum": "" }, "require": { - "doctrine/deprecations": "^0.5.3 || ^1", - "php": "^7.1.3 || ^8.0" + "doctrine/deprecations": "^1", + "php": "^8.1", + "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "doctrine/coding-standard": "^9.0 || ^10.0", - "phpstan/phpstan": "^1.4.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.1.5", - "vimeo/psalm": "^4.22" + "doctrine/coding-standard": "^12", + "ext-json": "*", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^10.5" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections" + "Doctrine\\Common\\Collections\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -570,84 +1637,7 @@ ], "support": { "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/1.8.0" - }, - "time": "2022-09-01T20:12:10+00:00" - }, - { - "name": "doctrine/common", - "version": "3.4.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/common.git", - "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/8b5e5650391f851ed58910b3e3d48a71062eeced", - "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced", - "shasum": "" - }, - "require": { - "doctrine/persistence": "^2.0 || ^3.0", - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0 || ^10.0", - "doctrine/collections": "^1", - "phpstan/phpstan": "^1.4.1", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0", - "squizlabs/php_codesniffer": "^3.0", - "symfony/phpunit-bridge": "^6.1", - "vimeo/psalm": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.", - "homepage": "https://www.doctrine-project.org/projects/common.html", - "keywords": [ - "common", - "doctrine", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/common/issues", - "source": "https://github.com/doctrine/common/tree/3.4.3" + "source": "https://github.com/doctrine/collections/tree/2.3.0" }, "funding": [ { @@ -659,54 +1649,131 @@ "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcommon", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcollections", "type": "tidelift" } ], - "time": "2022-10-09T11:47:59+00:00" + "time": "2025-03-22T10:17:19+00:00" }, { - "name": "doctrine/dbal", - "version": "3.6.0", + "name": "doctrine/data-fixtures", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/doctrine/dbal.git", - "reference": "85b98cb23c8af471a67abfe14485da696bcabc2e" + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "f7f1e12d6bceb58c204b3e77210a103c1c57601e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/85b98cb23c8af471a67abfe14485da696bcabc2e", - "reference": "85b98cb23c8af471a67abfe14485da696bcabc2e", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/f7f1e12d6bceb58c204b3e77210a103c1c57601e", + "reference": "f7f1e12d6bceb58c204b3e77210a103c1c57601e", + "shasum": "" + }, + "require": { + "doctrine/persistence": "^3.1 || ^4.0", + "php": "^8.1", + "psr/log": "^1.1 || ^2 || ^3" + }, + "conflict": { + "doctrine/dbal": "<3.5 || >=5", + "doctrine/orm": "<2.14 || >=4", + "doctrine/phpcr-odm": "<1.3.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "doctrine/dbal": "^3.5 || ^4", + "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", + "doctrine/orm": "^2.14 || ^3", + "ext-sqlite3": "*", + "fig/log-test": "^1", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5.3", + "symfony/cache": "^6.4 || ^7", + "symfony/var-exporter": "^6.4 || ^7" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\DataFixtures\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "database" + ], + "support": { + "issues": "https://github.com/doctrine/data-fixtures/issues", + "source": "https://github.com/doctrine/data-fixtures/tree/2.0.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures", + "type": "tidelift" + } + ], + "time": "2025-01-21T13:21:31+00:00" + }, + { + "name": "doctrine/dbal", + "version": "4.2.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "33d2d7fe1269b2301640c44cf2896ea607b30e3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/33d2d7fe1269b2301640c44cf2896ea607b30e3e", + "reference": "33d2d7fe1269b2301640c44cf2896ea607b30e3e", "shasum": "" }, "require": { - "composer-runtime-api": "^2", - "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1|^2", - "php": "^7.4 || ^8.0", + "php": "^8.1", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "11.1.0", + "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2022.3", - "phpstan/phpstan": "1.9.14", - "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "9.6.3", - "psalm/plugin-phpunit": "0.18.4", - "squizlabs/php_codesniffer": "3.7.1", - "symfony/cache": "^5.4|^6.0", - "symfony/console": "^4.4|^5.4|^6.0", - "vimeo/psalm": "4.30.0" + "jetbrains/phpstorm-stubs": "2023.2", + "phpstan/phpstan": "2.1.1", + "phpstan/phpstan-phpunit": "2.0.3", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "10.5.39", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.10.2", + "symfony/cache": "^6.3.8|^7.0", + "symfony/console": "^5.4|^6.3|^7.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "bin": [ - "bin/doctrine-dbal" - ], "type": "library", "autoload": { "psr-4": { @@ -759,7 +1826,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.6.0" + "source": "https://github.com/doctrine/dbal/tree/4.2.3" }, "funding": [ { @@ -775,29 +1842,34 @@ "type": "tidelift" } ], - "time": "2023-02-07T22:52:03+00:00" + "time": "2025-03-07T18:29:05+00:00" }, { "name": "doctrine/deprecations", - "version": "v1.0.0", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" }, "require-dev": { - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -805,7 +1877,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + "Doctrine\\Deprecations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -816,63 +1888,70 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" }, - "time": "2022-05-02T15:47:09+00:00" + "time": "2025-04-07T20:06:18+00:00" }, { "name": "doctrine/doctrine-bundle", - "version": "2.8.3", + "version": "2.14.0", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "fd67ba64db3c806f626a33dcab15a4db0c77652e" + "reference": "ca6a7350b421baf7fbdefbf9f4993292ed18effb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/fd67ba64db3c806f626a33dcab15a4db0c77652e", - "reference": "fd67ba64db3c806f626a33dcab15a4db0c77652e", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/ca6a7350b421baf7fbdefbf9f4993292ed18effb", + "reference": "ca6a7350b421baf7fbdefbf9f4993292ed18effb", "shasum": "" }, "require": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/dbal": "^3.4.0", - "doctrine/persistence": "^2.2 || ^3", + "doctrine/dbal": "^3.7.0 || ^4.0", + "doctrine/persistence": "^3.1 || ^4", "doctrine/sql-formatter": "^1.0.1", - "php": "^7.4 || ^8.0", - "symfony/cache": "^5.4 || ^6.0", - "symfony/config": "^5.4 || ^6.0", - "symfony/console": "^5.4 || ^6.0", - "symfony/dependency-injection": "^5.4 || ^6.0", + "php": "^8.1", + "symfony/cache": "^6.4 || ^7.0", + "symfony/config": "^6.4 || ^7.0", + "symfony/console": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", "symfony/deprecation-contracts": "^2.1 || ^3", - "symfony/doctrine-bridge": "^5.4.19 || ^6.0.7", - "symfony/framework-bundle": "^5.4 || ^6.0", - "symfony/service-contracts": "^1.1.1 || ^2.0 || ^3" + "symfony/doctrine-bridge": "^6.4.3 || ^7.0.3", + "symfony/framework-bundle": "^6.4 || ^7.0", + "symfony/service-contracts": "^2.5 || ^3" }, "conflict": { "doctrine/annotations": ">=3.0", - "doctrine/orm": "<2.11 || >=3.0", - "twig/twig": "<1.34 || >=2.0,<2.4" + "doctrine/cache": "< 1.11", + "doctrine/orm": "<2.17 || >=4.0", + "symfony/var-exporter": "< 6.4.1 || 7.0.0", + "twig/twig": "<2.13 || >=3.0 <3.0.4" }, "require-dev": { "doctrine/annotations": "^1 || ^2", - "doctrine/coding-standard": "^9.0", - "doctrine/orm": "^2.11 || ^3.0", + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^12", + "doctrine/deprecations": "^1.0", + "doctrine/orm": "^2.17 || ^3.0", "friendsofphp/proxy-manager-lts": "^1.0", - "phpunit/phpunit": "^9.5.26 || ^10.0", - "psalm/plugin-phpunit": "^0.18.4", - "psalm/plugin-symfony": "^4", + "phpstan/phpstan": "2.1.1", + "phpstan/phpstan-phpunit": "2.0.3", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9.6.22", "psr/log": "^1.1.4 || ^2.0 || ^3.0", - "symfony/phpunit-bridge": "^6.1", - "symfony/property-info": "^5.4 || ^6.0", - "symfony/proxy-manager-bridge": "^5.4 || ^6.0", - "symfony/security-bundle": "^5.4 || ^6.0", - "symfony/twig-bridge": "^5.4 || ^6.0", - "symfony/validator": "^5.4 || ^6.0", - "symfony/web-profiler-bundle": "^5.4 || ^6.0", - "symfony/yaml": "^5.4 || ^6.0", - "twig/twig": "^1.34 || ^2.12 || ^3.0", - "vimeo/psalm": "^4.30" + "symfony/doctrine-messenger": "^6.4 || ^7.0", + "symfony/messenger": "^6.4 || ^7.0", + "symfony/phpunit-bridge": "^7.2", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/security-bundle": "^6.4 || ^7.0", + "symfony/stopwatch": "^6.4 || ^7.0", + "symfony/string": "^6.4 || ^7.0", + "symfony/twig-bridge": "^6.4 || ^7.0", + "symfony/validator": "^6.4 || ^7.0", + "symfony/var-exporter": "^6.4.1 || ^7.0.1", + "symfony/web-profiler-bundle": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0", + "twig/twig": "^2.13 || ^3.0.4" }, "suggest": { "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", @@ -882,7 +1961,7 @@ "type": "symfony-bundle", "autoload": { "psr-4": { - "Doctrine\\Bundle\\DoctrineBundle\\": "" + "Doctrine\\Bundle\\DoctrineBundle\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -917,7 +1996,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineBundle/issues", - "source": "https://github.com/doctrine/DoctrineBundle/tree/2.8.3" + "source": "https://github.com/doctrine/DoctrineBundle/tree/2.14.0" }, "funding": [ { @@ -933,47 +2012,47 @@ "type": "tidelift" } ], - "time": "2023-02-03T09:32:42+00:00" + "time": "2025-03-22T17:28:21+00:00" }, { "name": "doctrine/doctrine-migrations-bundle", - "version": "3.2.2", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", - "reference": "3393f411ba25ade21969c33f2053220044854d01" + "reference": "5a6ac7120c2924c4c070a869d08b11ccf9e277b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/3393f411ba25ade21969c33f2053220044854d01", - "reference": "3393f411ba25ade21969c33f2053220044854d01", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/5a6ac7120c2924c4c070a869d08b11ccf9e277b9", + "reference": "5a6ac7120c2924c4c070a869d08b11ccf9e277b9", "shasum": "" }, "require": { - "doctrine/doctrine-bundle": "~1.0|~2.0", + "doctrine/doctrine-bundle": "^2.4", "doctrine/migrations": "^3.2", - "php": "^7.2|^8.0", - "symfony/framework-bundle": "~3.4|~4.0|~5.0|~6.0" + "php": "^7.2 || ^8.0", + "symfony/deprecation-contracts": "^2.1 || ^3", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { - "doctrine/coding-standard": "^8.0", - "doctrine/orm": "^2.6", - "doctrine/persistence": "^1.3||^2.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^8.0|^9.0", - "vimeo/psalm": "^4.11" + "composer/semver": "^3.0", + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.6 || ^3", + "phpstan/phpstan": "^1.4 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpstan/phpstan-symfony": "^1.3 || ^2", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/phpunit-bridge": "^6.3 || ^7", + "symfony/var-exporter": "^5.4 || ^6 || ^7" }, "type": "symfony-bundle", "autoload": { "psr-4": { - "Doctrine\\Bundle\\MigrationsBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Doctrine\\Bundle\\MigrationsBundle\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1002,7 +2081,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", - "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.2.2" + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.4.2" }, "funding": [ { @@ -1018,34 +2097,33 @@ "type": "tidelift" } ], - "time": "2022-02-01T18:08:07+00:00" + "time": "2025-03-11T17:36:26+00:00" }, { "name": "doctrine/event-manager", - "version": "1.2.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", "shasum": "" }, "require": { - "doctrine/deprecations": "^0.5.3 || ^1", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "conflict": { "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.24" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^5.24" }, "type": "library", "autoload": { @@ -1094,7 +2172,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.2.0" + "source": "https://github.com/doctrine/event-manager/tree/2.0.1" }, "funding": [ { @@ -1110,32 +2188,32 @@ "type": "tidelift" } ], - "time": "2022-10-12T20:51:15+00:00" + "time": "2024-05-22T20:47:39+00:00" }, { "name": "doctrine/inflector", - "version": "2.0.6", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024" + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^10", + "doctrine/coding-standard": "^11.0", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-strict-rules": "^1.3", "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25" + "vimeo/psalm": "^4.25 || ^5.4" }, "type": "library", "autoload": { @@ -1185,7 +2263,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.6" + "source": "https://github.com/doctrine/inflector/tree/2.0.10" }, "funding": [ { @@ -1201,34 +2279,34 @@ "type": "tidelift" } ], - "time": "2022-10-20T09:10:12+00:00" + "time": "2024-02-18T20:23:39+00:00" }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -1255,7 +2333,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -1271,32 +2349,31 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "doctrine/lexer", - "version": "2.1.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.0" + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { @@ -1333,7 +2410,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.0" + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { @@ -1349,51 +2426,52 @@ "type": "tidelift" } ], - "time": "2022-12-14T08:49:07+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { "name": "doctrine/migrations", - "version": "3.5.5", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "4b1e2b6ba71d21d0c5be22ed03b6fc954d20b204" + "reference": "325b61e41d032f5f7d7e2d11cbefff656eadc9ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/4b1e2b6ba71d21d0c5be22ed03b6fc954d20b204", - "reference": "4b1e2b6ba71d21d0c5be22ed03b6fc954d20b204", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/325b61e41d032f5f7d7e2d11cbefff656eadc9ab", + "reference": "325b61e41d032f5f7d7e2d11cbefff656eadc9ab", "shasum": "" }, "require": { "composer-runtime-api": "^2", - "doctrine/dbal": "^3.5.1", + "doctrine/dbal": "^3.6 || ^4", "doctrine/deprecations": "^0.5.3 || ^1", "doctrine/event-manager": "^1.2 || ^2.0", - "friendsofphp/proxy-manager-lts": "^1.0", - "php": "^7.4 || ^8.0", + "php": "^8.1", "psr/log": "^1.1.3 || ^2 || ^3", - "symfony/console": "^4.4.16 || ^5.4 || ^6.0", - "symfony/stopwatch": "^4.4 || ^5.4 || ^6.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.2 || ^7.0" }, "conflict": { - "doctrine/orm": "<2.12" + "doctrine/orm": "<2.12 || >=4" }, "require-dev": { - "doctrine/coding-standard": "^9", - "doctrine/orm": "^2.13", - "doctrine/persistence": "^2 || ^3", + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.13 || ^3", + "doctrine/persistence": "^2 || ^3 || ^4", "doctrine/sql-formatter": "^1.0", "ext-pdo_sqlite": "*", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-deprecation-rules": "^1", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.1", - "phpstan/phpstan-symfony": "^1.1", - "phpunit/phpunit": "^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "symfony/process": "^4.4 || ^5.4 || ^6.0", - "symfony/yaml": "^4.4 || ^5.4 || ^6.0" + "fig/log-test": "^1", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-strict-rules": "^1.4", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^10.3", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" }, "suggest": { "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", @@ -1405,7 +2483,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\Migrations\\": "lib/Doctrine/Migrations" + "Doctrine\\Migrations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1435,7 +2513,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.5.5" + "source": "https://github.com/doctrine/migrations/tree/3.9.0" }, "funding": [ { @@ -1451,69 +2529,58 @@ "type": "tidelift" } ], - "time": "2023-01-18T12:44:30+00:00" + "time": "2025-03-26T06:48:45+00:00" }, { "name": "doctrine/orm", - "version": "2.14.1", + "version": "3.3.3", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "de7eee5ed7b1b35c99b118f26f210a8281e6db8e" + "reference": "1f1891d3e20ef9881e81c2f32c53e9dc88dfc9a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/de7eee5ed7b1b35c99b118f26f210a8281e6db8e", - "reference": "de7eee5ed7b1b35c99b118f26f210a8281e6db8e", + "url": "https://api.github.com/repos/doctrine/orm/zipball/1f1891d3e20ef9881e81c2f32c53e9dc88dfc9a7", + "reference": "1f1891d3e20ef9881e81c2f32c53e9dc88dfc9a7", "shasum": "" }, "require": { "composer-runtime-api": "^2", - "doctrine/cache": "^1.12.1 || ^2.1.1", - "doctrine/collections": "^1.5 || ^2.0", - "doctrine/common": "^3.0.3", - "doctrine/dbal": "^2.13.1 || ^3.2", + "doctrine/collections": "^2.2", + "doctrine/dbal": "^3.8.2 || ^4", "doctrine/deprecations": "^0.5.3 || ^1", "doctrine/event-manager": "^1.2 || ^2", "doctrine/inflector": "^1.4 || ^2.0", - "doctrine/instantiator": "^1.3", - "doctrine/lexer": "^1.2.3 || ^2", - "doctrine/persistence": "^2.4 || ^3", + "doctrine/instantiator": "^1.3 || ^2", + "doctrine/lexer": "^3", + "doctrine/persistence": "^3.3.1 || ^4", "ext-ctype": "*", - "php": "^7.1 || ^8.0", + "php": "^8.1", "psr/cache": "^1 || ^2 || ^3", - "symfony/console": "^4.2 || ^5.0 || ^6.0", - "symfony/polyfill-php72": "^1.23", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.13 || >= 3.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.3.9 || ^7.0" }, "require-dev": { - "doctrine/annotations": "^1.13 || ^2", - "doctrine/coding-standard": "^9.0.2 || ^11.0", - "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.9.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "doctrine/coding-standard": "^13.0", + "phpbench/phpbench": "^1.0", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "2.0.3", + "phpstan/phpstan-deprecation-rules": "^2", + "phpunit/phpunit": "^10.4.0", "psr/log": "^1 || ^2 || ^3", - "squizlabs/php_codesniffer": "3.7.1", - "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "vimeo/psalm": "4.30.0 || 5.4.0" + "squizlabs/php_codesniffer": "3.12.0", + "symfony/cache": "^5.4 || ^6.2 || ^7.0" }, "suggest": { "ext-dom": "Provides support for XSD validation for XML mapping files", - "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0", - "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0" }, - "bin": [ - "bin/doctrine" - ], "type": "library", "autoload": { "psr-4": { - "Doctrine\\ORM\\": "lib/Doctrine/ORM" + "Doctrine\\ORM\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1550,42 +2617,39 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.14.1" + "source": "https://github.com/doctrine/orm/tree/3.3.3" }, - "time": "2023-01-16T18:36:59+00:00" + "time": "2025-05-02T17:42:51+00:00" }, { "name": "doctrine/persistence", - "version": "3.1.4", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/persistence.git", - "reference": "8bf8ab15960787f1a49d405f6eb8c787b4841119" + "reference": "45004aca79189474f113cbe3a53847c2115a55fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/8bf8ab15960787f1a49d405f6eb8c787b4841119", - "reference": "8bf8ab15960787f1a49d405f6eb8c787b4841119", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/45004aca79189474f113cbe3a53847c2115a55fa", + "reference": "45004aca79189474f113cbe3a53847c2115a55fa", "shasum": "" }, "require": { "doctrine/event-manager": "^1 || ^2", - "php": "^7.2 || ^8.0", + "php": "^8.1", "psr/cache": "^1.0 || ^2.0 || ^3.0" }, "conflict": { "doctrine/common": "<2.10" }, "require-dev": { - "composer/package-versions-deprecated": "^1.11", - "doctrine/coding-standard": "^11", - "doctrine/common": "^3.0", - "phpstan/phpstan": "1.9.4", + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "1.12.7", "phpstan/phpstan-phpunit": "^1", "phpstan/phpstan-strict-rules": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "vimeo/psalm": "4.30.0 || 5.3.0" + "phpunit/phpunit": "^9.6", + "symfony/cache": "^4.4 || ^5.4 || ^6.0 || ^7.0" }, "type": "library", "autoload": { @@ -1634,7 +2698,7 @@ ], "support": { "issues": "https://github.com/doctrine/persistence/issues", - "source": "https://github.com/doctrine/persistence/tree/3.1.4" + "source": "https://github.com/doctrine/persistence/tree/4.0.0" }, "funding": [ { @@ -1650,27 +2714,30 @@ "type": "tidelift" } ], - "time": "2023-02-03T11:13:07+00:00" + "time": "2024-11-01T21:49:07+00:00" }, { "name": "doctrine/sql-formatter", - "version": "1.1.3", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5" + "reference": "d6d00aba6fd2957fe5216fe2b7673e9985db20c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/25a06c7bf4c6b8218f47928654252863ffc890a5", - "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/d6d00aba6fd2957fe5216fe2b7673e9985db20c8", + "reference": "d6d00aba6fd2957fe5216fe2b7673e9985db20c8", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4" + "doctrine/coding-standard": "^12", + "ergebnis/phpunit-slow-test-detector": "^2.14", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5" }, "bin": [ "bin/sql-formatter" @@ -1700,38 +2767,40 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.1.3" + "source": "https://github.com/doctrine/sql-formatter/tree/1.5.2" }, - "time": "2022-05-23T21:33:49+00:00" + "time": "2025-01-24T11:45:48+00:00" }, { "name": "dompdf/dompdf", - "version": "v2.0.3", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/dompdf/dompdf.git", - "reference": "e8d2d5e37e8b0b30f0732a011295ab80680d7e85" + "reference": "a51bd7a063a65499446919286fb18b518177155a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/e8d2d5e37e8b0b30f0732a011295ab80680d7e85", - "reference": "e8d2d5e37e8b0b30f0732a011295ab80680d7e85", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/a51bd7a063a65499446919286fb18b518177155a", + "reference": "a51bd7a063a65499446919286fb18b518177155a", "shasum": "" }, "require": { + "dompdf/php-font-lib": "^1.0.0", + "dompdf/php-svg-lib": "^1.0.0", "ext-dom": "*", "ext-mbstring": "*", "masterminds/html5": "^2.0", - "phenx/php-font-lib": ">=0.5.4 <1.0.0", - "phenx/php-svg-lib": ">=0.3.3 <1.0.0", "php": "^7.1 || ^8.0" }, "require-dev": { + "ext-gd": "*", "ext-json": "*", "ext-zip": "*", "mockery/mockery": "^1.3", - "phpunit/phpunit": "^7.5 || ^8 || ^9", - "squizlabs/php_codesniffer": "^3.5" + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" }, "suggest": { "ext-gd": "Needed to process images", @@ -1762,32 +2831,123 @@ "homepage": "https://github.com/dompdf/dompdf", "support": { "issues": "https://github.com/dompdf/dompdf/issues", - "source": "https://github.com/dompdf/dompdf/tree/v2.0.3" + "source": "https://github.com/dompdf/dompdf/tree/v3.1.0" }, - "time": "2023-02-07T12:51:48+00:00" + "time": "2025-01-15T14:09:04+00:00" }, { - "name": "egulias/email-validator", - "version": "3.2.5", + "name": "dompdf/php-font-lib", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/egulias/EmailValidator.git", - "reference": "b531a2311709443320c786feb4519cfaf94af796" + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b531a2311709443320c786feb4519cfaf94af796", - "reference": "b531a2311709443320c786feb4519cfaf94af796", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", "shasum": "" }, "require": { - "doctrine/lexer": "^1.2|^2", - "php": ">=7.2", - "symfony/polyfill-intl-idn": "^1.15" + "ext-mbstring": "*", + "php": "^7.1 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5.8|^9.3.3", - "vimeo/psalm": "^4" + "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "The FontLib Community", + "homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/dompdf/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.1" + }, + "time": "2024-12-02T14:37:59+00:00" + }, + { + "name": "dompdf/php-svg-lib", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The SvgLib Community", + "homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/dompdf/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0" + }, + "time": "2024-04-29T13:26:35+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -1795,7 +2955,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -1823,7 +2983,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/3.2.5" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" }, "funding": [ { @@ -1831,7 +2991,7 @@ "type": "github" } ], - "time": "2023-01-02T17:26:14+00:00" + "time": "2025-03-06T22:45:56+00:00" }, { "name": "erusev/parsedown", @@ -1883,94 +3043,18 @@ }, "time": "2019-12-30T22:54:17+00:00" }, - { - "name": "fgrosse/phpasn1", - "version": "v2.5.0", - "source": { - "type": "git", - "url": "https://github.com/fgrosse/PHPASN1.git", - "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/42060ed45344789fb9f21f9f1864fc47b9e3507b", - "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "php-coveralls/php-coveralls": "~2.0", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" - }, - "suggest": { - "ext-bcmath": "BCmath is the fallback extension for big integer calculations", - "ext-curl": "For loading OID information from the web if they have not bee defined statically", - "ext-gmp": "GMP is the preferred extension for big integer calculations", - "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "FG\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Friedrich Große", - "email": "friedrich.grosse@gmail.com", - "homepage": "https://github.com/FGrosse", - "role": "Author" - }, - { - "name": "All contributors", - "homepage": "https://github.com/FGrosse/PHPASN1/contributors" - } - ], - "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", - "homepage": "https://github.com/FGrosse/PHPASN1", - "keywords": [ - "DER", - "asn.1", - "asn1", - "ber", - "binary", - "decoding", - "encoding", - "x.509", - "x.690", - "x509", - "x690" - ], - "support": { - "issues": "https://github.com/fgrosse/PHPASN1/issues", - "source": "https://github.com/fgrosse/PHPASN1/tree/v2.5.0" - }, - "abandoned": true, - "time": "2022-12-19T11:08:26+00:00" - }, { "name": "florianv/exchanger", - "version": "2.8.0", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/florianv/exchanger.git", - "reference": "be3e4b316a0fd90bac186cc8b8206e995df161ba" + "reference": "9214f51665fb907e7aa2397e21a90c456eb0c448" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/florianv/exchanger/zipball/be3e4b316a0fd90bac186cc8b8206e995df161ba", - "reference": "be3e4b316a0fd90bac186cc8b8206e995df161ba", + "url": "https://api.github.com/repos/florianv/exchanger/zipball/9214f51665fb907e7aa2397e21a90c456eb0c448", + "reference": "9214f51665fb907e7aa2397e21a90c456eb0c448", "shasum": "" }, "require": { @@ -1981,7 +3065,7 @@ "php-http/client-implementation": "^1.0", "php-http/discovery": "^1.6", "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0.2", + "psr/http-factory": "^1.0.2", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { @@ -2027,9 +3111,9 @@ ], "support": { "issues": "https://github.com/florianv/exchanger/issues", - "source": "https://github.com/florianv/exchanger/tree/2.8.0" + "source": "https://github.com/florianv/exchanger/tree/2.8.1" }, - "time": "2022-12-11T05:52:51+00:00" + "time": "2023-11-03T17:11:52+00:00" }, { "name": "florianv/swap", @@ -2097,25 +3181,25 @@ "source": { "type": "git", "url": "https://github.com/florianv/symfony-swap.git", - "reference": "fc6154976533e386ac6783c02ff19ab65aed4029" + "reference": "c8cd268ad6e2f636f10b91df9850e3941d7f5807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/florianv/symfony-swap/zipball/fc6154976533e386ac6783c02ff19ab65aed4029", - "reference": "fc6154976533e386ac6783c02ff19ab65aed4029", + "url": "https://api.github.com/repos/florianv/symfony-swap/zipball/c8cd268ad6e2f636f10b91df9850e3941d7f5807", + "reference": "c8cd268ad6e2f636f10b91df9850e3941d7f5807", "shasum": "" }, "require": { "florianv/swap": "^4.0", "php": "^7.1.3|^8.0", - "symfony/framework-bundle": "~3.0|~4.0|~5.0|~6.0" + "symfony/framework-bundle": "~3.0|~4.0|~5.0|~6.0|~7.0" }, "require-dev": { "nyholm/psr7": "^1.1", "php-http/guzzle6-adapter": "^1.0", "php-http/message": "^1.7", "phpunit/phpunit": "~5.7|~6.0|~7.0|~8.0|~9.0", - "symfony/cache": "~3.0|~4.0|~5.0|~6.0" + "symfony/cache": "~3.0|~4.0|~5.0|~6.0|~7.0" }, "suggest": { "symfony/cache": "For caching" @@ -2158,102 +3242,20 @@ "issues": "https://github.com/florianv/symfony-swap/issues", "source": "https://github.com/florianv/symfony-swap/tree/master" }, - "time": "2023-01-12T08:17:02+00:00" - }, - { - "name": "friendsofphp/proxy-manager-lts", - "version": "v1.0.14", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/proxy-manager-lts.git", - "reference": "a527c9d9d5348e012bd24482d83a5cd643bcbc9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/proxy-manager-lts/zipball/a527c9d9d5348e012bd24482d83a5cd643bcbc9e", - "reference": "a527c9d9d5348e012bd24482d83a5cd643bcbc9e", - "shasum": "" - }, - "require": { - "laminas/laminas-code": "~3.4.1|^4.0", - "php": ">=7.1", - "symfony/filesystem": "^4.4.17|^5.0|^6.0" - }, - "conflict": { - "laminas/laminas-stdlib": "<3.2.1", - "zendframework/zend-stdlib": "<3.2.1" - }, - "replace": { - "ocramius/proxy-manager": "^2.1" - }, - "require-dev": { - "ext-phar": "*", - "symfony/phpunit-bridge": "^5.4|^6.0" - }, - "type": "library", - "extra": { - "thanks": { - "name": "ocramius/proxy-manager", - "url": "https://github.com/Ocramius/ProxyManager" - } - }, - "autoload": { - "psr-4": { - "ProxyManager\\": "src/ProxyManager" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - } - ], - "description": "Adding support for a wider range of PHP versions to ocramius/proxy-manager", - "homepage": "https://github.com/FriendsOfPHP/proxy-manager-lts", - "keywords": [ - "aop", - "lazy loading", - "proxy", - "proxy pattern", - "service proxies" - ], - "support": { - "issues": "https://github.com/FriendsOfPHP/proxy-manager-lts/issues", - "source": "https://github.com/FriendsOfPHP/proxy-manager-lts/tree/v1.0.14" - }, - "funding": [ - { - "url": "https://github.com/Ocramius", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager", - "type": "tidelift" - } - ], - "time": "2023-01-30T10:40:19+00:00" + "time": "2024-07-09T13:51:01+00:00" }, { "name": "gregwar/captcha", - "version": "v1.1.9", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/Gregwar/Captcha.git", - "reference": "4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5" + "reference": "229d3cdfe33d6f1349e0aec94a26e9205a6db08e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Gregwar/Captcha/zipball/4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5", - "reference": "4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5", + "url": "https://api.github.com/repos/Gregwar/Captcha/zipball/229d3cdfe33d6f1349e0aec94a26e9205a6db08e", + "reference": "229d3cdfe33d6f1349e0aec94a26e9205a6db08e", "shasum": "" }, "require": { @@ -2265,7 +3267,7 @@ "require-dev": { "phpunit/phpunit": "^6.4" }, - "type": "captcha", + "type": "library", "autoload": { "psr-4": { "Gregwar\\": "src/Gregwar" @@ -2295,36 +3297,37 @@ ], "support": { "issues": "https://github.com/Gregwar/Captcha/issues", - "source": "https://github.com/Gregwar/Captcha/tree/master" + "source": "https://github.com/Gregwar/Captcha/tree/v1.2.1" }, - "time": "2020-03-24T14:39:05+00:00" + "time": "2023-09-26T13:45:37+00:00" }, { "name": "gregwar/captcha-bundle", - "version": "v2.2.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/Gregwar/CaptchaBundle.git", - "reference": "2b55ba41fd890f1a94d30e53a530c344bf12d6a5" + "reference": "8eb95c0911a1db9e3b2f368f6319e0945b959a6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Gregwar/CaptchaBundle/zipball/2b55ba41fd890f1a94d30e53a530c344bf12d6a5", - "reference": "2b55ba41fd890f1a94d30e53a530c344bf12d6a5", + "url": "https://api.github.com/repos/Gregwar/CaptchaBundle/zipball/8eb95c0911a1db9e3b2f368f6319e0945b959a6c", + "reference": "8eb95c0911a1db9e3b2f368f6319e0945b959a6c", "shasum": "" }, "require": { "ext-gd": "*", - "gregwar/captcha": "^1.1.9", - "php": ">=7.1.3", - "symfony/form": "~5.0|~6.0", - "symfony/framework-bundle": "~5.0|~6.0", - "symfony/translation": "~5.0|^6.0", - "twig/twig": "^2.10|^3.0" + "gregwar/captcha": "^1.2.1", + "php": ">=8.0.2", + "symfony/form": "~6.0|~7.0", + "symfony/framework-bundle": "~6.0|~7.0", + "symfony/translation": "~6.0|^7.0", + "twig/twig": "^3.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.66", - "symplify/easy-coding-standard": "^6.1" + "friendsofphp/php-cs-fixer": "^3.45", + "phpstan/phpstan": "^1.10", + "symplify/easy-coding-standard": "^12" }, "type": "symfony-bundle", "autoload": { @@ -2340,7 +3343,7 @@ { "name": "Grégoire Passault", "email": "g.passault@gmail.com", - "homepage": "http://www.gregwar.com/" + "homepage": "https://www.gregwar.com/" }, { "name": "Jeremy Livingston", @@ -2361,26 +3364,413 @@ ], "support": { "issues": "https://github.com/Gregwar/CaptchaBundle/issues", - "source": "https://github.com/Gregwar/CaptchaBundle/tree/v2.2.0" + "source": "https://github.com/Gregwar/CaptchaBundle/tree/v2.3.0" }, - "time": "2022-01-11T08:28:06+00:00" + "time": "2024-06-06T13:14:57+00:00" }, { - "name": "imagine/imagine", - "version": "1.3.3", + "name": "guzzlehttp/guzzle", + "version": "7.9.3", "source": { "type": "git", - "url": "https://github.com/php-imagine/Imagine.git", - "reference": "a6e6da93ea0f76aba33b0e8ed1325523c0413da2" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-imagine/Imagine/zipball/a6e6da93ea0f76aba33b0e8ed1325523c0413da2", - "reference": "a6e6da93ea0f76aba33b0e8ed1325523c0413da2", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", "shasum": "" }, "require": { - "php": ">=5.5" + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-03-27T13:37:11+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-03-27T13:27:01+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-03-27T12:30:47+00:00" + }, + { + "name": "hshn/base64-encoded-file", + "version": "v5.0.1", + "source": { + "type": "git", + "url": "https://github.com/hshn/base64-encoded-file.git", + "reference": "54fa81461ba4fbf5b67ed71d22b43ea5cc8c8748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hshn/base64-encoded-file/zipball/54fa81461ba4fbf5b67ed71d22b43ea5cc8c8748", + "reference": "54fa81461ba4fbf5b67ed71d22b43ea5cc8c8748", + "shasum": "" + }, + "require": { + "php": "^8.1.0", + "symfony/http-foundation": "^5.4 || ^6.0 || ^7.0", + "symfony/mime": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0.0", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/form": "^5.4 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", + "symfony/serializer": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "symfony/config": "to use the bundle in a Symfony project", + "symfony/dependency-injection": "to use the bundle in a Symfony project", + "symfony/form": "to use base64_encoded_file type", + "symfony/http-kernel": "to use the bundle in a Symfony project", + "symfony/serializer": "to convert a base64 string to a Base64EncodedFile object" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hshn\\Base64EncodedFile\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Shota Hoshino", + "email": "sht.hshn@gmail.com" + } + ], + "description": "Provides handling base64 encoded files, and the integration of symfony/form", + "support": { + "issues": "https://github.com/hshn/base64-encoded-file/issues", + "source": "https://github.com/hshn/base64-encoded-file/tree/v5.0.1" + }, + "time": "2023-12-24T07:23:07+00:00" + }, + { + "name": "imagine/imagine", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/php-imagine/Imagine.git", + "reference": "80ab21434890dee9ba54969d31c51ac8d4d551e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-imagine/Imagine/zipball/80ab21434890dee9ba54969d31c51ac8d4d551e0", + "reference": "80ab21434890dee9ba54969d31c51ac8d4d551e0", + "shasum": "" + }, + "require": { + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4 || ^9.3" @@ -2413,7 +3803,7 @@ "homepage": "http://avalanche123.com" } ], - "description": "Image processing for PHP 5.3", + "description": "Image processing for PHP", "homepage": "http://imagine.readthedocs.org/", "keywords": [ "drawing", @@ -2423,35 +3813,38 @@ ], "support": { "issues": "https://github.com/php-imagine/Imagine/issues", - "source": "https://github.com/php-imagine/Imagine/tree/1.3.3" + "source": "https://github.com/php-imagine/Imagine/tree/1.5.0" }, - "time": "2022-11-16T13:09:11+00:00" + "time": "2024-12-03T14:37:55+00:00" }, { "name": "jbtronics/2fa-webauthn", - "version": "v1.0.0", + "version": "v2.2.3", "source": { "type": "git", "url": "https://github.com/jbtronics/2fa-webauthn.git", - "reference": "c4108d16ba7a3061d977fc92f577c69067e1d003" + "reference": "fda6f39e70784cbf1f93cf758bf798563219d451" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jbtronics/2fa-webauthn/zipball/c4108d16ba7a3061d977fc92f577c69067e1d003", - "reference": "c4108d16ba7a3061d977fc92f577c69067e1d003", + "url": "https://api.github.com/repos/jbtronics/2fa-webauthn/zipball/fda6f39e70784cbf1f93cf758bf798563219d451", + "reference": "fda6f39e70784cbf1f93cf758bf798563219d451", "shasum": "" }, "require": { "ext-json": "*", "nyholm/psr7": "^1.5", - "php": "^7.4.0|^8.0", - "scheb/2fa-bundle": "^5.0.0|^6.0.0", - "symfony/framework-bundle": "^5.0|^6.0", - "symfony/psr-http-message-bridge": "^2.1", - "web-auth/webauthn-lib": "^3.3" + "php": "^8.1", + "psr/log": "^3.0.0|^2.0.0", + "scheb/2fa-bundle": "^6.0.0|^7.0.0", + "symfony/framework-bundle": "^6.0|^7.0", + "symfony/psr-http-message-bridge": "^2.1|^6.1|^7.0", + "symfony/uid": "^6.0|^7.0", + "web-auth/webauthn-lib": "^4.7" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.5", + "roave/security-advisories": "dev-latest" }, "type": "symfony-bundle", "autoload": { @@ -2480,103 +3873,449 @@ ], "support": { "issues": "https://github.com/jbtronics/2fa-webauthn/issues", - "source": "https://github.com/jbtronics/2fa-webauthn/tree/v1.0.0" + "source": "https://github.com/jbtronics/2fa-webauthn/tree/v2.2.3" }, - "time": "2022-10-03T22:29:32+00:00" + "time": "2025-03-27T19:23:40+00:00" }, { - "name": "laminas/laminas-code", - "version": "4.7.1", + "name": "jbtronics/dompdf-font-loader-bundle", + "version": "v1.1.3", "source": { "type": "git", - "url": "https://github.com/laminas/laminas-code.git", - "reference": "91aabc066d5620428120800c0eafc0411e441a62" + "url": "https://github.com/jbtronics/dompdf-font-loader-bundle.git", + "reference": "da01d9655826105d53f9d0e8ba4f9d838201dcb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-code/zipball/91aabc066d5620428120800c0eafc0411e441a62", - "reference": "91aabc066d5620428120800c0eafc0411e441a62", + "url": "https://api.github.com/repos/jbtronics/dompdf-font-loader-bundle/zipball/da01d9655826105d53f9d0e8ba4f9d838201dcb2", + "reference": "da01d9655826105d53f9d0e8ba4f9d838201dcb2", "shasum": "" }, "require": { - "php": ">=7.4, <8.2" + "dompdf/dompdf": "^1.0.0|^2.0.0|^3.0.0", + "ext-json": "*", + "php": "^8.1", + "symfony/finder": "^6.0|^7.0", + "symfony/framework-bundle": "^6.0|^7.0" }, "require-dev": { - "doctrine/annotations": "^1.13.2", - "ext-phar": "*", - "laminas/laminas-coding-standard": "^2.3.0", - "laminas/laminas-stdlib": "^3.6.1", - "phpunit/phpunit": "^9.5.10", - "psalm/plugin-phpunit": "^0.17.0", - "vimeo/psalm": "^4.13.1" + "phpunit/phpunit": "^9.5", + "roave/security-advisories": "dev-latest", + "symfony/phpunit-bridge": "^6.0" }, - "suggest": { - "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "laminas/laminas-stdlib": "Laminas\\Stdlib component" + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Jbtronics\\DompdfFontLoaderBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Böhmer", + "email": "mail@jan-boehmer.de" + } + ], + "description": "A symfony bundle to easily load custom fonts for dompdf (on cache warming)", + "keywords": [ + "dompdf", + "fonts", + "symfony", + "symfony-bundle" + ], + "support": { + "issues": "https://github.com/jbtronics/dompdf-font-loader-bundle/issues", + "source": "https://github.com/jbtronics/dompdf-font-loader-bundle/tree/v1.1.3" + }, + "time": "2025-02-07T23:21:03+00:00" + }, + { + "name": "jfcherng/php-color-output", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/jfcherng/php-color-output.git", + "reference": "6c7bf16686cc6a291647fcb87491640a2d5edd20" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jfcherng/php-color-output/zipball/6c7bf16686cc6a291647fcb87491640a2d5edd20", + "reference": "6c7bf16686cc6a291647fcb87491640a2d5edd20", + "shasum": "" + }, + "require": { + "php": ">=7.1.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.19", + "liip/rmt": "^1.6", + "phan/phan": "^2 || ^3 || ^4", + "phpunit/phpunit": ">=7 <10", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", "autoload": { - "files": [ - "polyfill/ReflectionEnumPolyfill.php" - ], "psr-4": { - "Laminas\\Code\\": "src/" + "Jfcherng\\Utility\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jack Cherng", + "email": "jfcherng@gmail.com" + } + ], + "description": "Make your PHP command-line application colorful.", + "keywords": [ + "ansi-colors", + "color", + "command-line", + "str-color" + ], + "support": { + "issues": "https://github.com/jfcherng/php-color-output/issues", + "source": "https://github.com/jfcherng/php-color-output/tree/3.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.me/jfcherng/5usd", + "type": "custom" + } + ], + "time": "2021-05-27T02:45:54+00:00" + }, + { + "name": "jfcherng/php-diff", + "version": "6.16.2", + "source": { + "type": "git", + "url": "https://github.com/jfcherng/php-diff.git", + "reference": "7f46bcfc582e81769237d0b3f6b8a548efe8799d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jfcherng/php-diff/zipball/7f46bcfc582e81769237d0b3f6b8a548efe8799d", + "reference": "7f46bcfc582e81769237d0b3f6b8a548efe8799d", + "shasum": "" + }, + "require": { + "jfcherng/php-color-output": "^3", + "jfcherng/php-mb-string": "^1.4.6 || ^2", + "jfcherng/php-sequence-matcher": "^3.2.10 || ^4", + "php": ">=7.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.51", + "liip/rmt": "^1.6", + "phan/phan": "^5", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jfcherng\\Diff\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", - "homepage": "https://laminas.dev", + "authors": [ + { + "name": "Jack Cherng", + "email": "jfcherng@gmail.com" + }, + { + "name": "Chris Boulton", + "email": "chris.boulton@interspire.com" + } + ], + "description": "A comprehensive library for generating differences between two strings in multiple formats (unified, side by side HTML etc).", "keywords": [ - "code", - "laminas", - "laminasframework" + "diff", + "udiff", + "unidiff", + "unified diff" ], "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-code/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-code/issues", - "rss": "https://github.com/laminas/laminas-code/releases.atom", - "source": "https://github.com/laminas/laminas-code" + "issues": "https://github.com/jfcherng/php-diff/issues", + "source": "https://github.com/jfcherng/php-diff/tree/6.16.2" }, "funding": [ { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" + "url": "https://www.paypal.me/jfcherng/5usd", + "type": "custom" } ], - "time": "2022-11-21T01:32:31+00:00" + "time": "2024-03-10T17:40:29+00:00" }, { - "name": "lcobucci/clock", - "version": "2.0.0", + "name": "jfcherng/php-mb-string", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/lcobucci/clock.git", - "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3" + "url": "https://github.com/jfcherng/php-mb-string.git", + "reference": "8407bfefde47849c9e7c9594e6de2ac85a0f845d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/353d83fe2e6ae95745b16b3d911813df6a05bfb3", - "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3", + "url": "https://api.github.com/repos/jfcherng/php-mb-string/zipball/8407bfefde47849c9e7c9594e6de2ac85a0f845d", + "reference": "8407bfefde47849c9e7c9594e6de2ac85a0f845d", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0" + "ext-iconv": "*", + "php": ">=8.1" }, "require-dev": { - "infection/infection": "^0.17", - "lcobucci/coding-standard": "^6.0", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/php-code-coverage": "9.1.4", - "phpunit/phpunit": "9.3.7" + "friendsofphp/php-cs-fixer": "^3", + "phan/phan": "^5", + "phpunit/phpunit": "^9 || ^10" + }, + "suggest": { + "ext-iconv": "Either \"ext-iconv\" or \"ext-mbstring\" is requried.", + "ext-mbstring": "Either \"ext-iconv\" or \"ext-mbstring\" is requried." + }, + "type": "library", + "autoload": { + "psr-4": { + "Jfcherng\\Utility\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jack Cherng", + "email": "jfcherng@gmail.com" + } + ], + "description": "A high performance multibytes sting implementation for frequently reading/writing operations.", + "support": { + "issues": "https://github.com/jfcherng/php-mb-string/issues", + "source": "https://github.com/jfcherng/php-mb-string/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/jfcherng/5usd", + "type": "custom" + } + ], + "time": "2023-04-17T14:23:16+00:00" + }, + { + "name": "jfcherng/php-sequence-matcher", + "version": "4.0.3", + "source": { + "type": "git", + "url": "https://github.com/jfcherng/php-sequence-matcher.git", + "reference": "d2038ac29627340a7458609072a8ba355e80ec5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jfcherng/php-sequence-matcher/zipball/d2038ac29627340a7458609072a8ba355e80ec5b", + "reference": "d2038ac29627340a7458609072a8ba355e80ec5b", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3", + "phan/phan": "^5", + "phpunit/phpunit": "^9 || ^10", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jfcherng\\Diff\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jack Cherng", + "email": "jfcherng@gmail.com" + }, + { + "name": "Chris Boulton", + "email": "chris.boulton@interspire.com" + } + ], + "description": "A longest sequence matcher. The logic is primarily based on the Python difflib package.", + "support": { + "issues": "https://github.com/jfcherng/php-sequence-matcher/issues", + "source": "https://github.com/jfcherng/php-sequence-matcher/tree/4.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/jfcherng/5usd", + "type": "custom" + } + ], + "time": "2023-05-21T07:57:08+00:00" + }, + { + "name": "kelunik/certificate", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.0" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, + { + "name": "knpuniversity/oauth2-client-bundle", + "version": "v2.18.3", + "source": { + "type": "git", + "url": "https://github.com/knpuniversity/oauth2-client-bundle.git", + "reference": "c38ca88a70aae3694ca346a41b13b9a8f6e33ed4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/knpuniversity/oauth2-client-bundle/zipball/c38ca88a70aae3694ca346a41b13b9a8f6e33ed4", + "reference": "c38ca88a70aae3694ca346a41b13b9a8f6e33ed4", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^2.0", + "php": ">=8.1", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "league/oauth2-facebook": "^1.1|^2.0", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", + "symfony/security-guard": "^5.4", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "suggest": { + "symfony/security-guard": "For integration with Symfony's Guard Security layer" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "KnpU\\OAuth2ClientBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ryan Weaver", + "email": "ryan@symfonycasts.com" + } + ], + "description": "Integration with league/oauth2-client to provide services", + "homepage": "https://symfonycasts.com", + "keywords": [ + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/knpuniversity/oauth2-client-bundle/issues", + "source": "https://github.com/knpuniversity/oauth2-client-bundle/tree/v2.18.3" + }, + "time": "2024-10-02T14:26:09+00:00" + }, + { + "name": "lcobucci/clock", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/039ef98c6b57b101d10bd11d8fdfda12cbd996dc", + "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc", + "shasum": "" + }, + "require": { + "php": "~8.1.0 || ~8.2.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.26", + "lcobucci/coding-standard": "^9.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-deprecation-rules": "^1.1.1", + "phpstan/phpstan-phpunit": "^1.3.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.27" }, "type": "library", "autoload": { @@ -2597,7 +4336,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/2.0.x" + "source": "https://github.com/lcobucci/clock/tree/3.0.0" }, "funding": [ { @@ -2609,43 +4348,42 @@ "type": "patreon" } ], - "time": "2020-08-27T18:56:02+00:00" + "time": "2022-12-19T15:00:24+00:00" }, { "name": "lcobucci/jwt", - "version": "4.3.0", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4" + "reference": "08071d8d2c7f4b00222cc4b1fb6aa46990a80f83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4", - "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/08071d8d2c7f4b00222cc4b1fb6aa46990a80f83", + "reference": "08071d8d2c7f4b00222cc4b1fb6aa46990a80f83", "shasum": "" }, "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mbstring": "*", "ext-openssl": "*", "ext-sodium": "*", - "lcobucci/clock": "^2.0 || ^3.0", - "php": "^7.4 || ^8.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "psr/clock": "^1.0" }, "require-dev": { - "infection/infection": "^0.21", - "lcobucci/coding-standard": "^6.0", - "mikey179/vfsstream": "^1.6.7", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/php-invoker": "^3.1", - "phpunit/phpunit": "^9.5" + "infection/infection": "^0.27.0", + "lcobucci/clock": "^3.0", + "lcobucci/coding-standard": "^11.0", + "phpbench/phpbench": "^1.2.9", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.10", + "phpstan/phpstan-strict-rules": "^1.5.0", + "phpunit/phpunit": "^10.2.6" + }, + "suggest": { + "lcobucci/clock": ">= 3.0" }, "type": "library", "autoload": { @@ -2671,7 +4409,7 @@ ], "support": { "issues": "https://github.com/lcobucci/jwt/issues", - "source": "https://github.com/lcobucci/jwt/tree/4.3.0" + "source": "https://github.com/lcobucci/jwt/tree/5.3.0" }, "funding": [ { @@ -2683,20 +4421,104 @@ "type": "patreon" } ], - "time": "2023-01-02T13:28:00+00:00" + "time": "2024-04-11T23:07:54+00:00" }, { - "name": "league/html-to-markdown", - "version": "5.1.0", + "name": "league/csv", + "version": "9.8.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/html-to-markdown.git", - "reference": "e0fc8cf07bdabbcd3765341ecb50c34c271d64e1" + "url": "https://github.com/thephpleague/csv.git", + "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/e0fc8cf07bdabbcd3765341ecb50c34c271d64e1", - "reference": "e0fc8cf07bdabbcd3765341ecb50c34c271d64e1", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/9d2e0265c5d90f5dd601bc65ff717e05cec19b47", + "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^v3.4.0", + "phpstan/phpstan": "^1.3.0", + "phpstan/phpstan-phpunit": "^1.0.0", + "phpstan/phpstan-strict-rules": "^1.1.0", + "phpunit/phpunit": "^9.5.11" + }, + "suggest": { + "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "League\\Csv\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "CSV data manipulation made easy in PHP", + "homepage": "https://csv.thephpleague.com", + "keywords": [ + "convert", + "csv", + "export", + "filter", + "import", + "read", + "transform", + "write" + ], + "support": { + "docs": "https://csv.thephpleague.com", + "issues": "https://github.com/thephpleague/csv/issues", + "rss": "https://github.com/thephpleague/csv/releases.atom", + "source": "https://github.com/thephpleague/csv" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2022-01-04T00:13:07+00:00" + }, + { + "name": "league/html-to-markdown", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/html-to-markdown.git", + "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0b4066eede55c48f38bcee4fb8f0aa85654390fd", + "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd", "shasum": "" }, "require": { @@ -2706,11 +4528,11 @@ }, "require-dev": { "mikehaertl/php-shellcommand": "^1.1.0", - "phpstan/phpstan": "^0.12.99", + "phpstan/phpstan": "^1.8.8", "phpunit/phpunit": "^8.5 || ^9.2", "scrutinizer/ocular": "^1.6", - "unleashedtech/php-coding-standard": "^2.7", - "vimeo/psalm": "^4.22" + "unleashedtech/php-coding-standard": "^2.7 || ^3.0", + "vimeo/psalm": "^4.22 || ^5.0" }, "bin": [ "bin/html-to-markdown" @@ -2752,7 +4574,7 @@ ], "support": { "issues": "https://github.com/thephpleague/html-to-markdown/issues", - "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.0" + "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.1" }, "funding": [ { @@ -2772,57 +4594,113 @@ "type": "tidelift" } ], - "time": "2022-03-02T17:24:08+00:00" + "time": "2023-07-12T21:21:09+00:00" }, { - "name": "league/uri", - "version": "6.7.2", + "name": "league/oauth2-client", + "version": "2.8.1", "source": { "type": "git", - "url": "https://github.com/thephpleague/uri.git", - "reference": "d3b50812dd51f3fbf176344cc2981db03d10fe06" + "url": "https://github.com/thephpleague/oauth2-client.git", + "reference": "9df2924ca644736c835fc60466a3a60390d334f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/d3b50812dd51f3fbf176344cc2981db03d10fe06", - "reference": "d3b50812dd51f3fbf176344cc2981db03d10fe06", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9", + "reference": "9df2924ca644736c835fc60466a3a60390d334f9", "shasum": "" }, "require": { "ext-json": "*", - "league/uri-interfaces": "^2.3", - "php": "^7.4 || ^8.0", - "psr/http-message": "^1.0" + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "php": "^7.1 || >=8.0.0 <8.5.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.5", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + }, + { + "name": "Woody Gilk", + "homepage": "https://github.com/shadowhand", + "role": "Contributor" + } + ], + "description": "OAuth 2.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "identity", + "idp", + "oauth", + "oauth2", + "single sign on" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-client/issues", + "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1" + }, + "time": "2025-02-26T04:37:30+00:00" + }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" }, "conflict": { "league/uri-schemes": "^1.0" }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^v3.3.2", - "nyholm/psr7": "^1.5", - "php-http/psr7-integration-tests": "^1.1", - "phpstan/phpstan": "^1.2.0", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.0.0", - "phpstan/phpstan-strict-rules": "^1.1.0", - "phpunit/phpunit": "^9.5.10", - "psr/http-factory": "^1.0" - }, "suggest": { - "ext-fileinfo": "Needed to create Data URI from a filepath", - "ext-intl": "Needed to improve host validation", - "league/uri-components": "Needed to easily manipulate URI objects", - "psr/http-factory": "Needed to use the URI factory" + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.x-dev" + "dev-master": "7.x-dev" } }, "autoload": { "psr-4": { - "League\\Uri\\": "src" + "League\\Uri\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2862,8 +4740,8 @@ "support": { "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", - "issues": "https://github.com/thephpleague/uri/issues", - "source": "https://github.com/thephpleague/uri/tree/6.7.2" + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.5.1" }, "funding": [ { @@ -2871,46 +4749,45 @@ "type": "github" } ], - "time": "2022-09-13T19:50:42+00:00" + "time": "2024-12-08T08:40:02+00:00" }, { - "name": "league/uri-interfaces", - "version": "2.3.0", + "name": "league/uri-components", + "version": "7.5.1", "source": { "type": "git", - "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "00e7e2943f76d8cb50c7dfdc2f6dee356e15e383" + "url": "https://github.com/thephpleague/uri-components.git", + "reference": "4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/00e7e2943f76d8cb50c7dfdc2f6dee356e15e383", - "reference": "00e7e2943f76d8cb50c7dfdc2f6dee356e15e383", + "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f", + "reference": "4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f", "shasum": "" }, "require": { - "ext-json": "*", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.19", - "phpstan/phpstan": "^0.12.90", - "phpstan/phpstan-phpunit": "^0.12.19", - "phpstan/phpstan-strict-rules": "^0.12.9", - "phpunit/phpunit": "^8.5.15 || ^9.5" + "league/uri": "^7.5", + "php": "^8.1" }, "suggest": { - "ext-intl": "to use the IDNA feature", - "symfony/intl": "to use the IDNA feature via Symfony Polyfill" + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-mbstring": "to use the sorting algorithm of URLSearchParams", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "7.x-dev" } }, "autoload": { "psr-4": { - "League\\Uri\\": "src/" + "League\\Uri\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2924,17 +4801,113 @@ "homepage": "https://nyamsprod.com" } ], - "description": "Common interface for URI representation", - "homepage": "http://github.com/thephpleague/uri-interfaces", + "description": "URI components manipulation library", + "homepage": "http://uri.thephpleague.com", "keywords": [ + "authority", + "components", + "fragment", + "host", + "middleware", + "modifier", + "path", + "port", + "query", "rfc3986", - "rfc3987", + "scheme", "uri", - "url" + "url", + "userinfo" ], "support": { - "issues": "https://github.com/thephpleague/uri-interfaces/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/2.3.0" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-components/tree/7.5.1" + }, + "funding": [ + { + "url": "https://github.com/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" }, "funding": [ { @@ -2942,55 +4915,56 @@ "type": "github" } ], - "time": "2021-06-28T04:27:21+00:00" + "time": "2024-12-08T08:18:47+00:00" }, { "name": "liip/imagine-bundle", - "version": "2.10.0", + "version": "2.13.3", "source": { "type": "git", "url": "https://github.com/liip/LiipImagineBundle.git", - "reference": "93bfc6dde87f135f9c3e63330cff23122448f4ee" + "reference": "3faccde327f91368e51d05ecad49a9cd915abd81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/liip/LiipImagineBundle/zipball/93bfc6dde87f135f9c3e63330cff23122448f4ee", - "reference": "93bfc6dde87f135f9c3e63330cff23122448f4ee", + "url": "https://api.github.com/repos/liip/LiipImagineBundle/zipball/3faccde327f91368e51d05ecad49a9cd915abd81", + "reference": "3faccde327f91368e51d05ecad49a9cd915abd81", "shasum": "" }, "require": { "ext-mbstring": "*", "imagine/imagine": "^1.3.2", - "php": "^7.1|^8.0", - "symfony/filesystem": "^3.4|^4.4|^5.3|^6.0", - "symfony/finder": "^3.4|^4.4|^5.3|^6.0", - "symfony/framework-bundle": "^3.4.23|^4.4|^5.3|^6.0", - "symfony/mime": "^4.4|^5.3|^6.0", - "symfony/options-resolver": "^3.4|^4.4|^5.3|^6.0", - "symfony/process": "^3.4|^4.4|^5.3|^6.0", + "php": "^7.2|^8.0", + "symfony/filesystem": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/finder": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/framework-bundle": "^3.4.23|^4.4|^5.3|^6.0|^7.0", + "symfony/mime": "^4.4|^5.3|^6.0|^7.0", + "symfony/options-resolver": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/process": "^3.4|^4.4|^5.3|^6.0|^7.0", "twig/twig": "^1.44|^2.9|^3.0" }, "require-dev": { "amazonwebservices/aws-sdk-for-php": "^1.0", - "aws/aws-sdk-php": "^2.4", + "aws/aws-sdk-php": "^2.4|^3.0", "doctrine/cache": "^1.11|^2.0", "doctrine/persistence": "^1.3|^2.0", "enqueue/enqueue-bundle": "^0.9|^0.10", "ext-gd": "*", "league/flysystem": "^1.0|^2.0|^3.0", - "phpstan/phpstan": "^0.12.64", + "phpstan/phpstan": "^1.10.0", "psr/cache": "^1.0|^2.0|^3.0", "psr/log": "^1.0", - "symfony/browser-kit": "^3.4|^4.4|^5.3|^6.0", - "symfony/cache": "^3.4|^4.4|^5.3|^6.0", - "symfony/console": "^3.4|^4.4|^5.3|^6.0", - "symfony/dependency-injection": "^3.4|^4.4|^5.3|^6.0", - "symfony/form": "^3.4|^4.4|^5.3|^6.0", - "symfony/messenger": "^4.4|^5.3|^6.0", - "symfony/phpunit-bridge": "^5.3", + "symfony/asset": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/browser-kit": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/cache": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/console": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/dependency-injection": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/form": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/messenger": "^4.4|^5.3|^6.0|^7.0", + "symfony/phpunit-bridge": "^7.0.2", "symfony/templating": "^3.4|^4.4|^5.3|^6.0", - "symfony/validator": "^3.4|^4.4|^5.3|^6.0", - "symfony/yaml": "^3.4|^4.4|^5.3|^6.0" + "symfony/validator": "^3.4|^4.4|^5.3|^6.0|^7.0", + "symfony/yaml": "^3.4|^4.4|^5.3|^6.0|^7.0" }, "suggest": { "alcaeus/mongo-php-adapter": "required for mongodb components", @@ -3002,10 +4976,12 @@ "ext-gd": "required to use gd driver", "ext-gmagick": "required to use gmagick driver", "ext-imagick": "required to use imagick driver", + "ext-json": "required to read JSON manifest versioning", "ext-mongodb": "required for mongodb components", "league/flysystem": "required to use FlySystem data loader or cache resolver", "monolog/monolog": "A psr/log compatible logger is required to enable logging", "rokka/imagine-vips": "required to use 'vips' driver", + "symfony/asset": "If you want to use asset versioning", "symfony/messenger": "If you like to process images in background", "symfony/templating": "required to use deprecated Templating component instead of Twig" }, @@ -3029,7 +5005,7 @@ } ], "description": "This bundle provides an image manipulation abstraction toolkit for Symfony-based projects.", - "homepage": "http://liip.ch", + "homepage": "https://www.liip.ch", "keywords": [ "bundle", "image", @@ -3043,22 +5019,22 @@ ], "support": { "issues": "https://github.com/liip/LiipImagineBundle/issues", - "source": "https://github.com/liip/LiipImagineBundle/tree/2.10.0" + "source": "https://github.com/liip/LiipImagineBundle/tree/2.13.3" }, - "time": "2022-12-01T13:19:59+00:00" + "time": "2024-12-12T09:38:23+00:00" }, { "name": "lorenzo/pinky", - "version": "1.0.9", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/lorenzo/pinky.git", - "reference": "f890472e4a25f89591f176aa03d9588a9d3332a7" + "reference": "e1b1bdb2c132b8a7ba32bca64d2443f646ddbd17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lorenzo/pinky/zipball/f890472e4a25f89591f176aa03d9588a9d3332a7", - "reference": "f890472e4a25f89591f176aa03d9588a9d3332a7", + "url": "https://api.github.com/repos/lorenzo/pinky/zipball/e1b1bdb2c132b8a7ba32bca64d2443f646ddbd17", + "reference": "e1b1bdb2c132b8a7ba32bca64d2443f646ddbd17", "shasum": "" }, "require": { @@ -3096,32 +5072,30 @@ ], "support": { "issues": "https://github.com/lorenzo/pinky/issues", - "source": "https://github.com/lorenzo/pinky/tree/1.0.9" + "source": "https://github.com/lorenzo/pinky/tree/1.1.0" }, - "time": "2023-01-12T16:15:52+00:00" + "time": "2023-07-31T13:36:50+00:00" }, { "name": "masterminds/html5", - "version": "2.7.6", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "897eb517a343a2281f11bc5556d6548db7d93947" + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/897eb517a343a2281f11bc5556d6548db7d93947", - "reference": "897eb517a343a2281f11bc5556d6548db7d93947", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-dom": "*", - "ext-libxml": "*", "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7" + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" }, "type": "library", "extra": { @@ -3165,48 +5139,49 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.7.6" + "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" }, - "time": "2022-08-18T16:18:26+00:00" + "time": "2024-03-31T07:05:07+00:00" }, { "name": "monolog/monolog", - "version": "2.9.1", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1" + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f259e2b15fb95494c83f52d3caad003bbf5ffaa1", - "reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", "shasum": "" }, "require": { - "php": ">=7.2", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" }, "provide": { - "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + "psr/log-implementation": "3.0.0" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "aws/aws-sdk-php": "^3.0", "doctrine/couchdb": "~1.0@dev", "elasticsearch/elasticsearch": "^7 || ^8", "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2@dev", - "guzzlehttp/guzzle": "^7.4", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpspec/prophecy": "^1.15", - "phpstan/phpstan": "^0.12.91", - "phpunit/phpunit": "^8.5.14", - "predis/predis": "^1.1 || ^2.0", - "rollbar/rollbar": "^1.3 || ^2 || ^3", - "ruflin/elastica": "^7", - "swiftmailer/swiftmailer": "^5.3|^6.0", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", "symfony/mailer": "^5.4 || ^6", "symfony/mime": "^5.4 || ^6" }, @@ -3229,7 +5204,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -3257,7 +5232,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.9.1" + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" }, "funding": [ { @@ -3269,7 +5244,70 @@ "type": "tidelift" } ], - "time": "2023-02-06T13:44:46+00:00" + "time": "2025-03-24T10:02:05+00:00" + }, + { + "name": "nbgrp/onelogin-saml-bundle", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/nbgrp/onelogin-saml-bundle.git", + "reference": "3341544e72b699ab69357ab38cee9c80941ce1c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nbgrp/onelogin-saml-bundle/zipball/3341544e72b699ab69357ab38cee9c80941ce1c6", + "reference": "3341544e72b699ab69357ab38cee9c80941ce1c6", + "shasum": "" + }, + "require": { + "onelogin/php-saml": "^4", + "php": "^8.1", + "psr/log": "^1 || ^2 || ^3", + "symfony/config": "^6.4", + "symfony/dependency-injection": "^6.4", + "symfony/deprecation-contracts": "^3", + "symfony/event-dispatcher-contracts": "^3", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.4", + "symfony/routing": "^6.4", + "symfony/security-bundle": "^6.4", + "symfony/security-core": "^6.4", + "symfony/security-http": "^6.4" + }, + "require-dev": { + "doctrine/orm": "^2.3 || ^3", + "symfony/event-dispatcher": "^6.4", + "symfony/phpunit-bridge": "^6.4" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Nbgrp\\OneloginSamlBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Alexander Menshchikov", + "email": "alexander.menshchikov@yandex.ru" + } + ], + "description": "OneLogin SAML Symfony Bundle", + "keywords": [ + "SSO", + "multiple IdP", + "onelogin", + "saml" + ], + "support": { + "issues": "https://github.com/nbgrp/onelogin-saml-bundle/issues", + "source": "https://github.com/nbgrp/onelogin-saml-bundle/tree/v1.4.0" + }, + "time": "2023-11-29T12:22:32+00:00" }, { "name": "nelexa/zip", @@ -3345,27 +5383,90 @@ "time": "2022-06-17T11:17:46+00:00" }, { - "name": "nelmio/security-bundle", - "version": "v3.0.0", + "name": "nelmio/cors-bundle", + "version": "2.5.0", "source": { "type": "git", - "url": "https://github.com/nelmio/NelmioSecurityBundle.git", - "reference": "34699d40d81b58b6bd256e34489c799620dff2a4" + "url": "https://github.com/nelmio/NelmioCorsBundle.git", + "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nelmio/NelmioSecurityBundle/zipball/34699d40d81b58b6bd256e34489c799620dff2a4", - "reference": "34699d40d81b58b6bd256e34489c799620dff2a4", + "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/3a526fe025cd20e04a6a11370cf5ab28dbb5a544", + "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544", + "shasum": "" + }, + "require": { + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.6", + "symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Nelmio\\CorsBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nelmio", + "homepage": "http://nelm.io" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/nelmio/NelmioCorsBundle/contributors" + } + ], + "description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application", + "keywords": [ + "api", + "cors", + "crossdomain" + ], + "support": { + "issues": "https://github.com/nelmio/NelmioCorsBundle/issues", + "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.5.0" + }, + "time": "2024-06-24T21:25:28+00:00" + }, + { + "name": "nelmio/security-bundle", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/nelmio/NelmioSecurityBundle.git", + "reference": "b1c5e323d71152bc1a61a4f8fbf7d88c6fa3e2e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nelmio/NelmioSecurityBundle/zipball/b1c5e323d71152bc1a61a4f8fbf7d88c6fa3e2e7", + "reference": "b1c5e323d71152bc1a61a4f8fbf7d88c6fa3e2e7", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "symfony/framework-bundle": "^4.4 || ^5.4 || ^6.0", - "symfony/http-kernel": "^4.4 || ^5.4 || ^6.0", - "symfony/security-core": "^4.4 || ^5.4 || ^6.0", - "symfony/security-csrf": "^4.4 || ^5.4 || ^6.0", - "symfony/security-http": "^4.4 || ^5.4 || ^6.0", - "symfony/yaml": "^4.4 || ^5.4 || ^6.0", + "symfony/deprecation-contracts": "^2.5 || ^3", + "symfony/framework-bundle": "^5.4 || ^6.3 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.3 || ^7.0", + "symfony/security-core": "^5.4 || ^6.3 || ^7.0", + "symfony/security-csrf": "^5.4 || ^6.3 || ^7.0", + "symfony/security-http": "^5.4 || ^6.3 || ^7.0", + "symfony/yaml": "^5.4 || ^6.3 || ^7.0", "ua-parser/uap-php": "^3.4.4" }, "require-dev": { @@ -3376,10 +5477,10 @@ "phpstan/phpstan-symfony": "^1.1", "phpunit/phpunit": "^9.5", "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/browser-kit": "^4.4 || ^5.4 || ^6.0", - "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "symfony/phpunit-bridge": "^6.0", - "symfony/twig-bundle": "^4.4 || ^5.4 || ^6.0", + "symfony/browser-kit": "^5.4 || ^6.3 || ^7.0", + "symfony/cache": "^5.4 || ^6.3 || ^7.0", + "symfony/phpunit-bridge": "^6.3 || ^7.0", + "symfony/twig-bundle": "^5.4 || ^6.3 || ^7.0", "twig/twig": "^2.10 || ^3.0" }, "type": "symfony-bundle", @@ -3413,95 +5514,39 @@ ], "support": { "issues": "https://github.com/nelmio/NelmioSecurityBundle/issues", - "source": "https://github.com/nelmio/NelmioSecurityBundle/tree/v3.0.0" + "source": "https://github.com/nelmio/NelmioSecurityBundle/tree/v3.5.1" }, - "time": "2022-03-17T07:30:15+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.15.3", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" - }, - "time": "2023-01-16T22:05:37+00:00" + "time": "2025-03-13T09:17:16+00:00" }, { "name": "nikolaposa/version", - "version": "4.1.0", + "version": "4.2.1", "source": { "type": "git", "url": "https://github.com/nikolaposa/version.git", - "reference": "0aada6b801962c084ae465f7569397dc2186b6a7" + "reference": "2b9ee2f0b09333b6ce00bd6b63132cdf1d7a1428" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikolaposa/version/zipball/0aada6b801962c084ae465f7569397dc2186b6a7", - "reference": "0aada6b801962c084ae465f7569397dc2186b6a7", + "url": "https://api.github.com/repos/nikolaposa/version/zipball/2b9ee2f0b09333b6ce00bd6b63132cdf1d7a1428", + "reference": "2b9ee2f0b09333b6ce00bd6b63132cdf1d7a1428", "shasum": "" }, "require": { "beberlei/assert": "^3.2", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", - "phpstan/phpstan": "^0.12.10", - "phpstan/phpstan-beberlei-assert": "^0.12.2", - "phpstan/phpstan-phpunit": "^0.12.6", - "phpunit/phpunit": "^8.0" + "friendsofphp/php-cs-fixer": "^3.44", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-beberlei-assert": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "4.2.x-dev" } }, "autoload": { @@ -3530,101 +5575,45 @@ ], "support": { "issues": "https://github.com/nikolaposa/version/issues", - "source": "https://github.com/nikolaposa/version/tree/4.1.0" + "source": "https://github.com/nikolaposa/version/tree/4.2.1" }, - "time": "2020-12-12T10:47:10+00:00" - }, - { - "name": "nyholm/nsa", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/Nyholm/NSA.git", - "reference": "c264c17ed2aa8251c64ad289442ed53f64cdb283" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Nyholm/NSA/zipball/c264c17ed2aa8251c64ad289442ed53f64cdb283", - "reference": "c264c17ed2aa8251c64ad289442ed53f64cdb283", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "webmozart/assert": "^1.1.0" - }, - "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Nyholm\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - } - ], - "description": "See everything and do whatever you want. No privacy rule will stop us. Used in tests, debugging and fixtures to access properties and methods.", - "homepage": "https://tnyholm.se", - "keywords": [ - "Fixture", - "debug", - "reflection", - "test" - ], - "support": { - "issues": "https://github.com/Nyholm/NSA/issues", - "source": "https://github.com/Nyholm/NSA/tree/1.3.0" - }, - "funding": [ - { - "url": "https://github.com/nyholm", - "type": "github" - } - ], - "time": "2021-07-15T18:25:37+00:00" + "time": "2025-03-24T19:12:02+00:00" }, { "name": "nyholm/psr7", - "version": "1.5.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "f734364e38a876a23be4d906a2a089e1315be18a" + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/f734364e38a876a23be4d906a2a089e1315be18a", - "reference": "f734364e38a876a23be4d906a2a089e1315be18a", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", "shasum": "" }, "require": { - "php": ">=7.1", - "php-http/message-factory": "^1.0", + "php": ">=7.2", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.1 || ^2.0" }, "provide": { + "php-http/message-factory-implementation": "1.0", "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", "php-http/psr7-integration-tests": "^1.0", - "phpunit/phpunit": "^7.5 || 8.5 || 9.4", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", "symfony/error-handler": "^4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -3654,7 +5643,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.5.1" + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" }, "funding": [ { @@ -3666,58 +5655,73 @@ "type": "github" } ], - "time": "2022-06-22T07:13:36+00:00" + "time": "2024-09-09T07:06:30+00:00" }, { "name": "omines/datatables-bundle", - "version": "0.5.5", + "version": "0.9.2", "source": { "type": "git", "url": "https://github.com/omines/datatables-bundle.git", - "reference": "b8a16c365f9d8e97d1e890e8783249f979f0d7ca" + "reference": "15974fc7dde750f8a3eff32d9ad4d9de6028583f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/omines/datatables-bundle/zipball/b8a16c365f9d8e97d1e890e8783249f979f0d7ca", - "reference": "b8a16c365f9d8e97d1e890e8783249f979f0d7ca", + "url": "https://api.github.com/repos/omines/datatables-bundle/zipball/15974fc7dde750f8a3eff32d9ad4d9de6028583f", + "reference": "15974fc7dde750f8a3eff32d9ad4d9de6028583f", "shasum": "" }, "require": { - "php": ">=7.2", - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/framework-bundle": "^4.4|^5.0", - "symfony/options-resolver": "^4.4|^5.0", - "symfony/property-access": "^4.4|^5.0", - "symfony/translation": "^4.4|^5.0" + "php": ">=8.1", + "symfony/event-dispatcher": "^6.4|^7.1", + "symfony/framework-bundle": "^6.4|^7.1", + "symfony/options-resolver": "^6.4|^7.1", + "symfony/polyfill-mbstring": "^1.31.0", + "symfony/property-access": "^6.4|^7.1", + "symfony/translation": "^6.4|^7.1" + }, + "conflict": { + "doctrine/orm": "^3.0 <3.3" }, "require-dev": { - "doctrine/common": "^2.6|^3.0", - "doctrine/doctrine-bundle": "^2.3|^3.0", - "doctrine/orm": "^2.6.3", - "doctrine/persistence": "^1.3.4|^2.0", + "doctrine/common": "^3.4.5", + "doctrine/doctrine-bundle": "^2.13.1", + "doctrine/orm": "^2.19.3|^3.3.0", + "doctrine/persistence": "^3.4.0", "ext-curl": "*", "ext-json": "*", + "ext-mbstring": "*", + "ext-mongodb": "*", "ext-pdo_sqlite": "*", "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^2.7", - "mongodb/mongodb": "^1.2", - "ocramius/package-versions": "^1.4", - "phpoffice/phpspreadsheet": "^1.6", - "ruflin/elastica": "^6.0", - "symfony/browser-kit": "^4.4|^5.0", - "symfony/css-selector": "^4.4|^5.0", - "symfony/dom-crawler": "^4.4|^5.0", - "symfony/intl": "^4.4|^5.0", - "symfony/mime": "^4.4|^5.0", - "symfony/phpunit-bridge": "^4.4|^5.0", - "symfony/twig-bundle": "^4.4|^5.0", - "symfony/var-dumper": "^4.4|^5.0", - "symfony/yaml": "^4.4|^5.0" + "friendsofphp/php-cs-fixer": "^3.65.0", + "mongodb/mongodb": "^1.20.0", + "ocramius/package-versions": "^2.9", + "openspout/openspout": "^4.23", + "phpoffice/phpspreadsheet": "^2.3.3|^3.5", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.0.3", + "phpstan/phpstan-doctrine": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.1", + "phpstan/phpstan-symfony": "^2.0.0", + "phpunit/phpunit": "^10.5.38|^11.4.4", + "ruflin/elastica": "^6.2|^7.3.2", + "symfony/browser-kit": "^6.4.13|^7.1", + "symfony/css-selector": "^6.4.13|^7.1", + "symfony/doctrine-bridge": "^6.4.13|^7.1", + "symfony/dom-crawler": "^6.4.13|^7.1", + "symfony/intl": "^6.4.13|^7.1", + "symfony/mime": "^6.4.13|^7.1", + "symfony/phpunit-bridge": "^7.2", + "symfony/twig-bundle": "^6.4|^7.1", + "symfony/var-dumper": "^6.4.13|^7.1", + "symfony/yaml": "^6.4.13|^7.1" }, "suggest": { "doctrine/doctrine-bundle": "For integrated access to Doctrine object managers", "doctrine/orm": "For full automated integration with Doctrine entities", "mongodb/mongodb": "For integration with MongoDB collections", + "openspout/openspout": "To use the OpenSpout Excel exporter", "phpoffice/phpspreadsheet": "To export the data from DataTables to Excel", "ruflin/elastica": "For integration with Elasticsearch indexes", "symfony/twig-bundle": "To use default Twig based rendering and TwigColumn" @@ -3725,7 +5729,7 @@ "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "0.5-dev" + "dev-master": "0.9-dev" } }, "autoload": { @@ -3741,12 +5745,12 @@ { "name": "Robbert Beesems", "email": "robbert.beesems@omines.com", - "homepage": "https://omines.nl/" + "homepage": "https://www.omines.nl/" }, { "name": "Niels Keurentjes", "email": "niels.keurentjes@omines.com", - "homepage": "https://omines.nl/" + "homepage": "https://www.omines.nl/" } ], "description": "Symfony DataTables Bundle with native Doctrine ORM, Elastica and MongoDB support", @@ -3763,22 +5767,92 @@ ], "support": { "issues": "https://github.com/omines/datatables-bundle/issues", - "source": "https://github.com/omines/datatables-bundle/tree/0.5.5" + "source": "https://github.com/omines/datatables-bundle/tree/0.9.2" }, - "time": "2021-06-29T08:12:37+00:00" + "funding": [ + { + "url": "https://github.com/curry684", + "type": "github" + } + ], + "time": "2025-01-23T14:53:20+00:00" }, { - "name": "paragonie/constant_time_encoding", - "version": "v2.6.3", + "name": "onelogin/php-saml", + "version": "4.2.0", "source": { "type": "git", - "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "58c3f47f650c94ec05a151692652a868995d2938" + "url": "https://github.com/SAML-Toolkits/php-saml.git", + "reference": "d3b5172f137db2f412239432d77253ceaaa1e939" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", - "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "url": "https://api.github.com/repos/SAML-Toolkits/php-saml/zipball/d3b5172f137db2f412239432d77253ceaaa1e939", + "reference": "d3b5172f137db2f412239432d77253ceaaa1e939", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "robrichards/xmlseclibs": "^3.1" + }, + "require-dev": { + "pdepend/pdepend": "^2.8.0", + "php-coveralls/php-coveralls": "^2.0", + "phploc/phploc": "^4.0 || ^5.0 || ^6.0 || ^7.0", + "phpunit/phpunit": "^9.5", + "sebastian/phpcpd": "^4.0 || ^5.0 || ^6.0 ", + "squizlabs/php_codesniffer": "^3.5.8" + }, + "suggest": { + "ext-curl": "Install curl lib to be able to use the IdPMetadataParser for parsing remote XMLs", + "ext-dom": "Install xml lib", + "ext-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)", + "ext-zlib": "Install zlib" + }, + "type": "library", + "autoload": { + "psr-4": { + "OneLogin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP SAML Toolkit", + "homepage": "https://github.com/SAML-Toolkits/php-saml", + "keywords": [ + "Federation", + "SAML2", + "SSO", + "identity", + "saml" + ], + "support": { + "email": "sixto.martin.garcia@gmail.com", + "issues": "https://github.com/onelogin/SAML-Toolkits/issues", + "source": "https://github.com/onelogin/SAML-Toolkits/" + }, + "funding": [ + { + "url": "https://github.com/SAML-Toolkits", + "type": "github" + } + ], + "time": "2024-05-30T15:10:40+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.7.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105", + "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105", "shasum": "" }, "require": { @@ -3832,110 +5906,194 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2022-06-14T06:56:20+00:00" + "time": "2024-05-08T12:18:48+00:00" }, { - "name": "phenx/php-font-lib", - "version": "0.5.4", + "name": "paragonie/random_compat", + "version": "v9.99.100", "source": { "type": "git", - "url": "https://github.com/dompdf/php-font-lib.git", - "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/dd448ad1ce34c63d09baccd05415e361300c35b4", - "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", "shasum": "" }, "require": { - "ext-mbstring": "*" + "php": ">= 7" }, "require-dev": { - "symfony/phpunit-bridge": "^3 || ^4 || ^5" + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", - "autoload": { - "psr-4": { - "FontLib\\": "src/FontLib" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0" + "MIT" ], "authors": [ { - "name": "Fabien Ménager", - "email": "fabien.menager@gmail.com" + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" } ], - "description": "A library to read, parse, export and make subsets of different types of font files.", - "homepage": "https://github.com/PhenX/php-font-lib", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], "support": { - "issues": "https://github.com/dompdf/php-font-lib/issues", - "source": "https://github.com/dompdf/php-font-lib/tree/0.5.4" + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" }, - "time": "2021-12-17T19:44:54+00:00" + "time": "2020-10-15T08:29:30+00:00" }, { - "name": "phenx/php-svg-lib", - "version": "0.5.0", + "name": "paragonie/sodium_compat", + "version": "v1.21.1", "source": { "type": "git", - "url": "https://github.com/dompdf/php-svg-lib.git", - "reference": "76876c6cf3080bcb6f249d7d59705108166a6685" + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "bb312875dcdd20680419564fe42ba1d9564b9e37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/76876c6cf3080bcb6f249d7d59705108166a6685", - "reference": "76876c6cf3080bcb6f249d7d59705108166a6685", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bb312875dcdd20680419564fe42ba1d9564b9e37", + "reference": "bb312875dcdd20680419564fe42ba1d9564b9e37", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": "^7.1 || ^8.0", - "sabberworm/php-css-parser": "^8.4" + "paragonie/random_compat": ">=1", + "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + "phpunit/phpunit": "^3|^4|^5|^6|^7|^8|^9" + }, + "suggest": { + "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", + "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." }, "type": "library", "autoload": { - "psr-4": { - "Svg\\": "src/Svg" - } + "files": [ + "autoload.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0" + "ISC" ], "authors": [ { - "name": "Fabien Ménager", - "email": "fabien.menager@gmail.com" + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" } ], - "description": "A library to read, parse and export to PDF SVG files.", - "homepage": "https://github.com/PhenX/php-svg-lib", + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "keywords": [ + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" + ], "support": { - "issues": "https://github.com/dompdf/php-svg-lib/issues", - "source": "https://github.com/dompdf/php-svg-lib/tree/0.5.0" + "issues": "https://github.com/paragonie/sodium_compat/issues", + "source": "https://github.com/paragonie/sodium_compat/tree/v1.21.1" }, - "time": "2022-09-06T12:16:56+00:00" + "time": "2024-04-22T22:05:04+00:00" + }, + { + "name": "part-db/label-fonts", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Part-DB/label-fonts.git", + "reference": "77c84b70ed3bb005df15f30ff835ddec490394b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Part-DB/label-fonts/zipball/77c84b70ed3bb005df15f30ff835ddec490394b9", + "reference": "77c84b70ed3bb005df15f30ff835ddec490394b9", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "proprietary" + ], + "authors": [ + { + "name": "The Part-DB Team", + "homepage": "https://github.com/Part-DB/Part-DB-server" + } + ], + "description": "This library bundles the fonts used in Part-DB for label generators. Fonts are work of others.", + "homepage": "https://github.com/Part-DB/Part-DB-server", + "keywords": [ + "font", + "fonts", + "part-db" + ], + "support": { + "issues": "https://github.com/Part-DB/label-fonts/issues", + "source": "https://github.com/Part-DB/label-fonts/tree/v1.1.0" + }, + "time": "2024-02-08T21:44:38+00:00" }, { "name": "php-http/discovery", - "version": "1.15.2", + "version": "1.20.0", "source": { "type": "git", "url": "https://github.com/php-http/discovery.git", - "reference": "5cc428320191ac1d0b6520034c2dc0698628ced5" + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/5cc428320191ac1d0b6520034c2dc0698628ced5", - "reference": "5cc428320191ac1d0b6520034c2dc0698628ced5", + "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", "shasum": "" }, "require": { @@ -3943,7 +6101,8 @@ "php": "^7.1 || ^8.0" }, "conflict": { - "nyholm/psr7": "<1.0" + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" }, "provide": { "php-http/async-client-implementation": "*", @@ -3958,7 +6117,8 @@ "php-http/httplug": "^1.0 || ^2.0", "php-http/message-factory": "^1.0", "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", - "symfony/phpunit-bridge": "^6.2" + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" }, "type": "composer-plugin", "extra": { @@ -3968,7 +6128,10 @@ "autoload": { "psr-4": { "Http\\Discovery\\": "src/" - } + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3994,40 +6157,35 @@ ], "support": { "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.15.2" + "source": "https://github.com/php-http/discovery/tree/1.20.0" }, - "time": "2023-02-11T08:28:41+00:00" + "time": "2024-10-02T11:20:13+00:00" }, { "name": "php-http/httplug", - "version": "2.3.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/php-http/httplug.git", - "reference": "f640739f80dfa1152533976e3c112477f69274eb" + "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/httplug/zipball/f640739f80dfa1152533976e3c112477f69274eb", - "reference": "f640739f80dfa1152533976e3c112477f69274eb", + "url": "https://api.github.com/repos/php-http/httplug/zipball/5cad731844891a4c282f3f3e1b582c46839d22f4", + "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", "php-http/promise": "^1.1", "psr/http-client": "^1.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.1", - "phpspec/phpspec": "^5.1 || ^6.0" + "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, "autoload": { "psr-4": { "Http\\Client\\": "src/" @@ -4056,91 +6214,32 @@ ], "support": { "issues": "https://github.com/php-http/httplug/issues", - "source": "https://github.com/php-http/httplug/tree/2.3.0" + "source": "https://github.com/php-http/httplug/tree/2.4.1" }, - "time": "2022-02-21T09:52:22+00:00" - }, - { - "name": "php-http/message-factory", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-http/message-factory.git", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Factory interfaces for PSR-7 HTTP Message", - "homepage": "http://php-http.org", - "keywords": [ - "factory", - "http", - "message", - "stream", - "uri" - ], - "support": { - "issues": "https://github.com/php-http/message-factory/issues", - "source": "https://github.com/php-http/message-factory/tree/master" - }, - "time": "2015-12-19T14:08:53+00:00" + "time": "2024-09-23T11:39:58+00:00" }, { "name": "php-http/promise", - "version": "1.1.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/php-http/promise.git", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88" + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", + "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.3.2", - "phpspec/phpspec": "^5.1.2 || ^6.2" + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3", + "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, "autoload": { "psr-4": { "Http\\Promise\\": "src/" @@ -4167,238 +6266,9 @@ ], "support": { "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/1.1.0" + "source": "https://github.com/php-http/promise/tree/1.3.1" }, - "time": "2020-07-07T09:29:14+00:00" - }, - { - "name": "php-translation/common", - "version": "3.2.0", - "source": { - "type": "git", - "url": "https://github.com/php-translation/common.git", - "reference": "986ddf4e3b2b3458d2a7353658bd40764d8ca1d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-translation/common/zipball/986ddf4e3b2b3458d2a7353658bd40764d8ca1d1", - "reference": "986ddf4e3b2b3458d2a7353658bd40764d8ca1d1", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "symfony/translation": " ^3.4 || ^4.3 || ^5.0 || ^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^8.4", - "symfony/phpunit-bridge": "^4.3 || ^5.0 || ^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Translation\\Common\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - } - ], - "description": "Common translation stuff", - "support": { - "issues": "https://github.com/php-translation/common/issues", - "source": "https://github.com/php-translation/common/tree/3.2.0" - }, - "time": "2022-02-04T11:49:38+00:00" - }, - { - "name": "php-translation/extractor", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-translation/extractor.git", - "reference": "d95f3fd89af6b2015819936c191bc11410fc7314" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-translation/extractor/zipball/d95f3fd89af6b2015819936c191bc11410fc7314", - "reference": "d95f3fd89af6b2015819936c191bc11410fc7314", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.7", - "nikic/php-parser": "^3.0 || ^4.0", - "php": "^7.2 || ^8.0", - "symfony/finder": "^3.4 || ^4.4 || ^5.0 || ^6.0", - "twig/twig": "^2.0 || ^3.0" - }, - "require-dev": { - "knplabs/knp-menu": "^3.1", - "symfony/phpunit-bridge": "^5.0 || ^6.0", - "symfony/translation": "^3.4 || ^4.4 || ^5.0 || ^6.0", - "symfony/twig-bridge": "^3.4 || ^4.4 || ^5.0 || ^6.0", - "symfony/validator": "^3.4 || ^4.4 || ^5.0 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Translation\\Extractor\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - } - ], - "description": "Extract translations form the source code", - "support": { - "issues": "https://github.com/php-translation/extractor/issues", - "source": "https://github.com/php-translation/extractor/tree/2.1.0" - }, - "time": "2022-02-14T11:34:24+00:00" - }, - { - "name": "php-translation/symfony-bundle", - "version": "0.12.8", - "source": { - "type": "git", - "url": "https://github.com/php-translation/symfony-bundle.git", - "reference": "9bd3ecace0a4019a7a4327ca9ea8df1c23ff0da3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-translation/symfony-bundle/zipball/9bd3ecace0a4019a7a4327ca9ea8df1c23ff0da3", - "reference": "9bd3ecace0a4019a7a4327ca9ea8df1c23ff0da3", - "shasum": "" - }, - "require": { - "nyholm/nsa": "^1.1", - "php": "^7.2 || ^8.0", - "php-translation/extractor": "^2.0", - "php-translation/symfony-storage": "^2.1", - "symfony/asset": "^4.4.20 || ^5.2.4 || ^6.0", - "symfony/finder": "^4.4.20 || ^5.2.4 || ^6.0", - "symfony/framework-bundle": "^4.4.20 || ^5.2.5 || ^6.0", - "symfony/intl": "^4.4.20 || ^5.2.4 || ^6.0", - "symfony/translation": "^4.4.20 || ^5.2.5 || ^6.0", - "symfony/twig-bundle": "^4.4.20 || ^5.2.4 || ^6.0", - "symfony/validator": "^4.4.20 || ^5.2.5 || ^6.0", - "twig/twig": "^2.14.4 || ^3.3" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.3", - "matthiasnoback/symfony-config-test": "^4.1", - "matthiasnoback/symfony-dependency-injection-test": "^4.1", - "nyholm/psr7": "^1.1", - "nyholm/symfony-bundle-test": "^2.0", - "php-http/curl-client": "^1.7 || ^2.0", - "php-http/message": "^1.11", - "php-http/message-factory": "^1.0.2", - "php-translation/translator": "^1.0", - "symfony/console": "^4.4.20 || ^5.2.5 || ^6.0", - "symfony/dependency-injection": "^4.4.20 || ^5.2.5 || ^6.0", - "symfony/phpunit-bridge": "^5.2 || ^6.0", - "symfony/twig-bridge": "^4.4.20 || ^5.2.5 || ^6.0", - "symfony/web-profiler-bundle": "^4.4.20 || ^5.2.4 || ^6.0" - }, - "suggest": { - "php-http/httplug-bundle": "To easier configure your httplug clients." - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "0.12-dev" - } - }, - "autoload": { - "psr-4": { - "Translation\\Bundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - } - ], - "support": { - "issues": "https://github.com/php-translation/symfony-bundle/issues", - "source": "https://github.com/php-translation/symfony-bundle/tree/0.12.8" - }, - "time": "2022-12-19T12:38:39+00:00" - }, - { - "name": "php-translation/symfony-storage", - "version": "2.3.1", - "source": { - "type": "git", - "url": "https://github.com/php-translation/symfony-storage.git", - "reference": "95d52dd86d41fe0ec2c75e1469b5003956044cc8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-translation/symfony-storage/zipball/95d52dd86d41fe0ec2c75e1469b5003956044cc8", - "reference": "95d52dd86d41fe0ec2c75e1469b5003956044cc8", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "php-translation/common": "^3.0", - "symfony/translation": "^3.4 || ^4.2 || ^5.0 || ^6.0" - }, - "require-dev": { - "phpunit/phpunit": ">=8.5.20", - "symfony/framework-bundle": " ^3.4 || ^4.2 || ^5.0 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-4": { - "Translation\\SymfonyStorage\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - } - ], - "description": "A translation file storage using Symfony translation component.", - "support": { - "issues": "https://github.com/php-translation/symfony-storage/issues", - "source": "https://github.com/php-translation/symfony-storage/tree/2.3.1" - }, - "time": "2022-02-14T11:36:15+00:00" + "time": "2024-03-15T13:55:21+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -4455,28 +6325,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "version": "5.6.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.1", "ext-filter": "*", - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -4500,36 +6377,39 @@ }, { "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "email": "opensource@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" }, - "time": "2021-10-19T17:43:47+00:00" + "time": "2025-04-13T19:20:35+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.2", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d" + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", - "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", @@ -4561,26 +6441,73 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" }, - "time": "2022-10-14T12:47:21+00:00" + "time": "2024-11-09T15:12:26+00:00" }, { - "name": "psr/cache", - "version": "1.0.1", + "name": "phpstan/phpdoc-parser", + "version": "2.1.0", "source": { "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + }, + "time": "2025-02-19T13:28:12+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" }, "type": "library", "extra": { @@ -4600,7 +6527,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for caching libraries", @@ -4610,28 +6537,81 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/master" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "time": "2016-08-06T20:24:11+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { - "name": "psr/container", - "version": "1.1.2", + "name": "psr/clock", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -4658,9 +6638,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/event-dispatcher", @@ -4714,21 +6694,21 @@ }, { "name": "psr/http-client", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -4748,7 +6728,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", @@ -4760,27 +6740,27 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/master" + "source": "https://github.com/php-fig/http-client" }, - "time": "2020-06-29T06:28:15+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -4800,10 +6780,10 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -4815,31 +6795,31 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2019-04-30T12:38:16+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "2.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -4854,7 +6834,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -4868,31 +6848,34 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "psr/link", - "version": "1.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/link.git", - "reference": "eea8e8662d5cd3ae4517c9b864493f59fca95562" + "reference": "84b159194ecfd7eaa472280213976e96415433f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/link/zipball/eea8e8662d5cd3ae4517c9b864493f59fca95562", - "reference": "eea8e8662d5cd3ae4517c9b864493f59fca95562", + "url": "https://api.github.com/repos/php-fig/link/zipball/84b159194ecfd7eaa472280213976e96415433f7", + "reference": "84b159194ecfd7eaa472280213976e96415433f7", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" + }, + "suggest": { + "fig/link-util": "Provides some useful PSR-13 utilities" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -4911,6 +6894,7 @@ } ], "description": "Common interfaces for HTTP links", + "homepage": "https://github.com/php-fig/link", "keywords": [ "http", "http-link", @@ -4920,36 +6904,36 @@ "rest" ], "support": { - "source": "https://github.com/php-fig/link/tree/master" + "source": "https://github.com/php-fig/link/tree/2.0.1" }, - "time": "2016-10-28T16:06:13+00:00" + "time": "2021-03-11T23:00:27+00:00" }, { "name": "psr/log", - "version": "1.1.4", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -4970,31 +6954,31 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "psr/simple-cache", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -5009,7 +6993,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for simple caching", @@ -5021,62 +7005,86 @@ "simple-cache" ], "support": { - "source": "https://github.com/php-fig/simple-cache/tree/master" + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "time": "2017-10-23T01:57:42+00:00" + "time": "2021-10-29T13:26:27+00:00" }, { - "name": "ramsey/collection", - "version": "1.3.0", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/ramsey/collection.git", - "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4", - "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0", - "symfony/polyfill-php81": "^1.23" + "php": ">=5.6" }, "require-dev": { - "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", - "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", - "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "revolt/event-loop", + "version": "v1.0.7", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/09bf1bf7f7f574453efe43044b06fafe12216eb3", + "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" }, "type": "library", "extra": { - "captainhook": { - "force-install": true - }, - "ramsey/conventional-commits": { - "configFile": "conventional-commits.json" + "branch-alias": { + "dev-main": "1.x-dev" } }, "autoload": { "psr-4": { - "Ramsey\\Collection\\": "src/" + "Revolt\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -5085,133 +7093,176 @@ ], "authors": [ { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } - ], - "description": "A PHP library for representing and manipulating collections.", - "keywords": [ - "array", - "collection", - "hash", - "map", - "queue", - "set" - ], - "support": { - "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/1.3.0" - }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" }, { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "time": "2022-12-27T19:12:24+00:00" + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.7" + }, + "time": "2025-01-25T19:27:39+00:00" }, { - "name": "ramsey/uuid", - "version": "4.2.3", + "name": "rhukster/dom-sanitizer", + "version": "1.0.7", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df" + "url": "https://github.com/rhukster/dom-sanitizer.git", + "reference": "c2a98f27ad742668b254282ccc5581871d0fb601" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", - "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", + "url": "https://api.github.com/repos/rhukster/dom-sanitizer/zipball/c2a98f27ad742668b254282ccc5581871d0fb601", + "reference": "c2a98f27ad742668b254282ccc5581871d0fb601", "shasum": "" }, "require": { - "brick/math": "^0.8 || ^0.9", - "ext-json": "*", - "php": "^7.2 || ^8.0", - "ramsey/collection": "^1.0", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php80": "^1.14" - }, - "replace": { - "rhumsaa/uuid": "self.version" + "ext-dom": "*", + "ext-libxml": "*", + "php": ">=7.3" }, "require-dev": { - "captainhook/captainhook": "^5.10", - "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", - "moontoast/math": "^1.1", - "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-mockery": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^8.5 || ^9", - "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" - }, - "suggest": { - "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-ctype": "Enables faster processing of character classification using ctype functions.", - "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", - "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", - "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "phpunit/phpunit": "^9" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.x-dev" - }, - "captainhook": { - "force-install": true - } - }, "autoload": { - "files": [ - "src/functions.php" - ], "psr-4": { - "Ramsey\\Uuid\\": "src/" + "Rhukster\\DomSanitizer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "authors": [ + { + "name": "Andy Miller", + "email": "rhuk@rhuk.net" + } + ], + "description": "A simple but effective DOM/SVG/MathML Sanitizer for PHP 7.4+", + "support": { + "issues": "https://github.com/rhukster/dom-sanitizer/issues", + "source": "https://github.com/rhukster/dom-sanitizer/tree/1.0.7" + }, + "time": "2023-11-06T16:46:48+00:00" + }, + { + "name": "robrichards/xmlseclibs", + "version": "3.1.3", + "source": { + "type": "git", + "url": "https://github.com/robrichards/xmlseclibs.git", + "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/2bdfd742624d739dfadbd415f00181b4a77aaf07", + "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">= 5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "RobRichards\\XMLSecLibs\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "A PHP library for XML Security", + "homepage": "https://github.com/robrichards/xmlseclibs", "keywords": [ - "guid", - "identifier", - "uuid" + "security", + "signature", + "xml", + "xmldsig" ], "support": { - "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.2.3" + "issues": "https://github.com/robrichards/xmlseclibs/issues", + "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.3" + }, + "time": "2024-11-20T21:13:56+00:00" + }, + { + "name": "runtime/frankenphp-symfony", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-runtime/frankenphp-symfony.git", + "reference": "56822c3631d9522a3136a4c33082d006bdfe4bad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-runtime/frankenphp-symfony/zipball/56822c3631d9522a3136a4c33082d006bdfe4bad", + "reference": "56822c3631d9522a3136a4c33082d006bdfe4bad", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", + "symfony/runtime": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Runtime\\FrankenPhpSymfony\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "kevin@dunglas.dev" + } + ], + "description": "FrankenPHP runtime for Symfony", + "support": { + "issues": "https://github.com/php-runtime/frankenphp-symfony/issues", + "source": "https://github.com/php-runtime/frankenphp-symfony/tree/0.2.0" }, "funding": [ { - "url": "https://github.com/ramsey", + "url": "https://github.com/nyholm", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" } ], - "time": "2021-09-25T23:10:38+00:00" + "time": "2023-12-12T12:06:11+00:00" }, { "name": "s9e/regexp-builder", @@ -5257,24 +7308,26 @@ }, { "name": "s9e/sweetdom", - "version": "2.1.0", + "version": "3.4.1", "source": { "type": "git", "url": "https://github.com/s9e/SweetDOM.git", - "reference": "9e34ff8f353234daed102274012c840bda56aff2" + "reference": "ef3a7d2745b30b4ad0d1d3d60be391a3604c69dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/s9e/SweetDOM/zipball/9e34ff8f353234daed102274012c840bda56aff2", - "reference": "9e34ff8f353234daed102274012c840bda56aff2", + "url": "https://api.github.com/repos/s9e/SweetDOM/zipball/ef3a7d2745b30b4ad0d1d3d60be391a3604c69dd", + "reference": "ef3a7d2745b30b4ad0d1d3d60be391a3604c69dd", "shasum": "" }, "require": { "ext-dom": "*", - "php": ">=7.1" + "php": "^8.1" }, "require-dev": { - "phpunit/phpunit": "*" + "friendsofphp/php-cs-fixer": "^3.52", + "phpunit/phpunit": "^10.0", + "s9e/repdoc": "dev-wip" }, "type": "library", "autoload": { @@ -5295,34 +7348,35 @@ ], "support": { "issues": "https://github.com/s9e/SweetDOM/issues", - "source": "https://github.com/s9e/SweetDOM/tree/2.1.0" + "source": "https://github.com/s9e/SweetDOM/tree/3.4.1" }, - "time": "2021-05-24T21:06:33+00:00" + "time": "2024-03-23T14:03:01+00:00" }, { "name": "s9e/text-formatter", - "version": "2.13.1", + "version": "2.19.0", "source": { "type": "git", "url": "https://github.com/s9e/TextFormatter.git", - "reference": "bbd9e34e9c30d5daeb780f115fe69cd81dd9c352" + "reference": "d65a4f61cbe494937afb3150dc73b6e757d400d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/s9e/TextFormatter/zipball/bbd9e34e9c30d5daeb780f115fe69cd81dd9c352", - "reference": "bbd9e34e9c30d5daeb780f115fe69cd81dd9c352", + "url": "https://api.github.com/repos/s9e/TextFormatter/zipball/d65a4f61cbe494937afb3150dc73b6e757d400d3", + "reference": "d65a4f61cbe494937afb3150dc73b6e757d400d3", "shasum": "" }, "require": { "ext-dom": "*", "ext-filter": "*", "lib-pcre": ">=8.13", - "php": ">=7.4", + "php": "^8.1", "s9e/regexp-builder": "^1.4", - "s9e/sweetdom": "^2.0" + "s9e/sweetdom": "^3.4" }, "require-dev": { "code-lts/doctum": "*", + "friendsofphp/php-cs-fixer": "^3.52", "matthiasmullie/minify": "*", "phpunit/phpunit": "^9.5" }, @@ -5337,7 +7391,7 @@ }, "type": "library", "extra": { - "version": "2.13.1" + "version": "2.19.0" }, "autoload": { "psr-4": { @@ -5369,36 +7423,40 @@ ], "support": { "issues": "https://github.com/s9e/TextFormatter/issues", - "source": "https://github.com/s9e/TextFormatter/tree/2.13.1" + "source": "https://github.com/s9e/TextFormatter/tree/2.19.0" }, - "time": "2023-02-11T00:18:05+00:00" + "time": "2025-04-26T09:27:34+00:00" }, { "name": "sabberworm/php-css-parser", - "version": "8.4.0", + "version": "v8.8.0", "source": { "type": "git", - "url": "https://github.com/sabberworm/PHP-CSS-Parser.git", - "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30" + "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", + "reference": "3de493bdddfd1f051249af725c7e0d2c38fed740" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30", - "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/3de493bdddfd1f051249af725c7e0d2c38fed740", + "reference": "3de493bdddfd1f051249af725c7e0d2c38fed740", "shasum": "" }, "require": { "ext-iconv": "*", - "php": ">=5.6.20" + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { - "codacy/coverage": "^1.4", - "phpunit/phpunit": "^4.8.36" + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.0.x-dev" + } + }, "autoload": { "psr-4": { "Sabberworm\\CSS\\": "src/" @@ -5411,6 +7469,14 @@ "authors": [ { "name": "Raphael Schweikert" + }, + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Jake Hotson", + "email": "jake.github@qzdesign.co.uk" } ], "description": "Parser for CSS Files written in PHP", @@ -5421,26 +7487,27 @@ "stylesheet" ], "support": { - "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues", - "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0" + "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.8.0" }, - "time": "2021-12-11T13:40:54+00:00" + "time": "2025-03-23T17:59:05+00:00" }, { "name": "scheb/2fa-backup-code", - "version": "v5.13.2", + "version": "v6.13.1", "source": { "type": "git", "url": "https://github.com/scheb/2fa-backup-code.git", - "reference": "5584eb7a2c3deb80635c7173ad77858e51129c35" + "reference": "6dceeb5be0f6339d76f8e380ee09631c8bbebc7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/2fa-backup-code/zipball/5584eb7a2c3deb80635c7173ad77858e51129c35", - "reference": "5584eb7a2c3deb80635c7173ad77858e51129c35", + "url": "https://api.github.com/repos/scheb/2fa-backup-code/zipball/6dceeb5be0f6339d76f8e380ee09631c8bbebc7e", + "reference": "6dceeb5be0f6339d76f8e380ee09631c8bbebc7e", "shasum": "" }, "require": { + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "scheb/2fa-bundle": "self.version" }, "type": "library", @@ -5470,45 +7537,45 @@ "two-step" ], "support": { - "source": "https://github.com/scheb/2fa-backup-code/tree/v5.13.2" + "source": "https://github.com/scheb/2fa-backup-code/tree/v6.13.1" }, - "time": "2022-01-03T10:21:24+00:00" + "time": "2024-11-29T19:22:48+00:00" }, { "name": "scheb/2fa-bundle", - "version": "v5.13.2", + "version": "v6.13.1", "source": { "type": "git", "url": "https://github.com/scheb/2fa-bundle.git", - "reference": "dc575cc7bc94fa3a52b547698086f2ef015d2e81" + "reference": "8eadd57ebc2078ef273dca72b1ac4bd283812346" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/2fa-bundle/zipball/dc575cc7bc94fa3a52b547698086f2ef015d2e81", - "reference": "dc575cc7bc94fa3a52b547698086f2ef015d2e81", + "url": "https://api.github.com/repos/scheb/2fa-bundle/zipball/8eadd57ebc2078ef273dca72b1ac4bd283812346", + "reference": "8eadd57ebc2078ef273dca72b1ac4bd283812346", "shasum": "" }, "require": { "ext-json": "*", - "php": ">=7.2.5", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/framework-bundle": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", - "symfony/http-kernel": "^4.4|^5.0", - "symfony/property-access": "^4.4|^5.0", - "symfony/security-bundle": "^4.4.1|^5.0", - "symfony/twig-bundle": "^4.4|^5.0" + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "symfony/config": "^5.4 || ^6.0", + "symfony/dependency-injection": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/framework-bundle": "^5.4 || ^6.0", + "symfony/http-foundation": "^5.4 || ^6.0", + "symfony/http-kernel": "^5.4 || ^6.0", + "symfony/property-access": "^5.4 || ^6.0", + "symfony/security-bundle": "^5.4 || ^6.0", + "symfony/twig-bundle": "^5.4 || ^6.0" }, "conflict": { - "scheb/two-factor-bundle": "*" + "scheb/two-factor-bundle": "*", + "symfony/security-core": "^7" }, "suggest": { "scheb/2fa-backup-code": "Emergency codes when you have no access to other methods", "scheb/2fa-email": "Send codes by email", "scheb/2fa-google-authenticator": "Google Authenticator support", - "scheb/2fa-qr-code": "Generate QR codes for Google Authenticator / TOTP", "scheb/2fa-totp": "Temporary one-time password (TOTP) support (Google Authenticator compatible)", "scheb/2fa-trusted-device": "Trusted devices support" }, @@ -5538,28 +7605,29 @@ "two-step" ], "support": { - "source": "https://github.com/scheb/2fa-bundle/tree/v5.13.2" + "source": "https://github.com/scheb/2fa-bundle/tree/v6.13.1" }, - "time": "2022-04-16T10:18:34+00:00" + "time": "2024-11-29T19:29:49+00:00" }, { "name": "scheb/2fa-google-authenticator", - "version": "v5.13.2", + "version": "v6.13.1", "source": { "type": "git", "url": "https://github.com/scheb/2fa-google-authenticator.git", - "reference": "9477bfc47b5927fb165022dd75700aefdd45a8cc" + "reference": "2c960a5cb32edb4c37f719f10180df378a44fd6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/2fa-google-authenticator/zipball/9477bfc47b5927fb165022dd75700aefdd45a8cc", - "reference": "9477bfc47b5927fb165022dd75700aefdd45a8cc", + "url": "https://api.github.com/repos/scheb/2fa-google-authenticator/zipball/2c960a5cb32edb4c37f719f10180df378a44fd6f", + "reference": "2c960a5cb32edb4c37f719f10180df378a44fd6f", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^2.2", + "paragonie/constant_time_encoding": "^2.4", + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "scheb/2fa-bundle": "self.version", - "spomky-labs/otphp": "^9.1|^10.0" + "spomky-labs/otphp": "^10.0 || ^11.0" }, "type": "library", "autoload": { @@ -5588,26 +7656,28 @@ "two-step" ], "support": { - "source": "https://github.com/scheb/2fa-google-authenticator/tree/v5.13.2" + "source": "https://github.com/scheb/2fa-google-authenticator/tree/v6.13.1" }, - "time": "2022-01-03T10:21:24+00:00" + "time": "2024-11-29T19:22:48+00:00" }, { "name": "scheb/2fa-trusted-device", - "version": "v5.13.2", + "version": "v6.13.1", "source": { "type": "git", "url": "https://github.com/scheb/2fa-trusted-device.git", - "reference": "acf5a1526eb2111fb7a82b9b52eb34b1ddfdc526" + "reference": "38e690325232a4037ff4aec8de926c938906942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/2fa-trusted-device/zipball/acf5a1526eb2111fb7a82b9b52eb34b1ddfdc526", - "reference": "acf5a1526eb2111fb7a82b9b52eb34b1ddfdc526", + "url": "https://api.github.com/repos/scheb/2fa-trusted-device/zipball/38e690325232a4037ff4aec8de926c938906942c", + "reference": "38e690325232a4037ff4aec8de926c938906942c", "shasum": "" }, "require": { - "lcobucci/jwt": "^3.4|^4.0", + "lcobucci/clock": "^2.0 || ^3.0", + "lcobucci/jwt": "^4.1 || ^5.0", + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "scheb/2fa-bundle": "self.version" }, "type": "library", @@ -5637,115 +7707,36 @@ "two-step" ], "support": { - "source": "https://github.com/scheb/2fa-trusted-device/tree/v5.13.2" + "source": "https://github.com/scheb/2fa-trusted-device/tree/v6.13.1" }, - "time": "2022-01-03T10:21:24+00:00" - }, - { - "name": "sensio/framework-extra-bundle", - "version": "v6.2.9", - "source": { - "type": "git", - "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", - "reference": "dcfac94d6bdcf95c126e8ccac2104917c7c8f135" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/dcfac94d6bdcf95c126e8ccac2104917c7c8f135", - "reference": "dcfac94d6bdcf95c126e8ccac2104917c7c8f135", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.0", - "php": ">=7.2.5", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/framework-bundle": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0" - }, - "conflict": { - "doctrine/doctrine-cache-bundle": "<1.3.1", - "doctrine/persistence": "<1.3" - }, - "require-dev": { - "doctrine/dbal": "^2.10|^3.0", - "doctrine/doctrine-bundle": "^1.11|^2.0", - "doctrine/orm": "^2.5", - "symfony/browser-kit": "^4.4|^5.0|^6.0", - "symfony/doctrine-bridge": "^4.4|^5.0|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/monolog-bridge": "^4.0|^5.0|^6.0", - "symfony/monolog-bundle": "^3.2", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0", - "symfony/security-bundle": "^4.4|^5.0|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0", - "twig/twig": "^1.34|^2.4|^3.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "6.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Sensio\\Bundle\\FrameworkExtraBundle\\": "src/" - }, - "exclude-from-classmap": [ - "/tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "This bundle provides a way to configure your controllers with annotations", - "keywords": [ - "annotations", - "controllers" - ], - "support": { - "issues": "https://github.com/sensiolabs/SensioFrameworkExtraBundle/issues", - "source": "https://github.com/sensiolabs/SensioFrameworkExtraBundle/tree/v6.2.9" - }, - "abandoned": "Symfony", - "time": "2022-11-01T17:17:13+00:00" + "time": "2024-11-29T19:22:48+00:00" }, { "name": "shivas/versioning-bundle", - "version": "4.0.2", + "version": "4.1.1", "source": { "type": "git", "url": "https://github.com/shivas/versioning-bundle.git", - "reference": "427969bce2f139e1f7234d14efcd8cd0083d896c" + "reference": "fd89e3501ff1b0d3e6abe61eb7a878d1d4746868" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/shivas/versioning-bundle/zipball/427969bce2f139e1f7234d14efcd8cd0083d896c", - "reference": "427969bce2f139e1f7234d14efcd8cd0083d896c", + "url": "https://api.github.com/repos/shivas/versioning-bundle/zipball/fd89e3501ff1b0d3e6abe61eb7a878d1d4746868", + "reference": "fd89e3501ff1b0d3e6abe61eb7a878d1d4746868", "shasum": "" }, "require": { "nikolaposa/version": "^4", - "php": "^7.2 || ^8", - "symfony/console": "^3.4 || ^4 || ^5 || ^6", - "symfony/framework-bundle": "^3.4 || ^4 || ^5 || ^6", - "symfony/process": "^3.4 || ^4 || ^5 || ^6" + "php": "^7.2.5 || ^8", + "symfony/console": "^5.4 || ^6 || ^7", + "symfony/framework-bundle": "^5.4 || ^6 || ^7", + "symfony/process": "^5.4 || ^6 || ^7" }, "require-dev": { "mikey179/vfsstream": "^2", - "nyholm/symfony-bundle-test": "1.x-dev", + "nyholm/symfony-bundle-test": "^3.0", "phpunit/phpunit": "^8.5.27", - "symfony/phpunit-bridge": "^5 || ^6", + "symfony/phpunit-bridge": "^5.4 || ^6 || ^7", "twig/twig": "^2 || ^3" }, "type": "symfony-bundle", @@ -5775,31 +7766,31 @@ ], "support": { "issues": "https://github.com/shivas/versioning-bundle/issues", - "source": "https://github.com/shivas/versioning-bundle/tree/4.0.2", + "source": "https://github.com/shivas/versioning-bundle/tree/4.1.1", "wiki": "https://github.com/shivas/versioning-bundle/wiki" }, - "time": "2022-07-10T17:39:01+00:00" + "time": "2024-08-14T19:33:15+00:00" }, { "name": "spatie/db-dumper", - "version": "2.21.1", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/spatie/db-dumper.git", - "reference": "05e5955fb882008a8947c5a45146d86cfafa10d1" + "reference": "91e1fd4dc000aefc9753cda2da37069fc996baee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/db-dumper/zipball/05e5955fb882008a8947c5a45146d86cfafa10d1", - "reference": "05e5955fb882008a8947c5a45146d86cfafa10d1", + "url": "https://api.github.com/repos/spatie/db-dumper/zipball/91e1fd4dc000aefc9753cda2da37069fc996baee", + "reference": "91e1fd4dc000aefc9753cda2da37069fc996baee", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "symfony/process": "^4.2|^5.0" + "php": "^8.0", + "symfony/process": "^5.0|^6.0|^7.0" }, "require-dev": { - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "pestphp/pest": "^1.22" }, "type": "library", "autoload": { @@ -5829,183 +7820,56 @@ "spatie" ], "support": { - "issues": "https://github.com/spatie/db-dumper/issues", - "source": "https://github.com/spatie/db-dumper/tree/2.21.1" + "source": "https://github.com/spatie/db-dumper/tree/3.8.0" }, "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, { "url": "https://github.com/spatie", "type": "github" } ], - "time": "2021-02-24T14:56:42+00:00" - }, - { - "name": "spomky-labs/base64url", - "version": "v2.0.4", - "source": { - "type": "git", - "url": "https://github.com/Spomky-Labs/base64url.git", - "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/base64url/zipball/7752ce931ec285da4ed1f4c5aa27e45e097be61d", - "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.11|^0.12", - "phpstan/phpstan-beberlei-assert": "^0.11|^0.12", - "phpstan/phpstan-deprecation-rules": "^0.11|^0.12", - "phpstan/phpstan-phpunit": "^0.11|^0.12", - "phpstan/phpstan-strict-rules": "^0.11|^0.12" - }, - "type": "library", - "autoload": { - "psr-4": { - "Base64Url\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Florent Morselli", - "homepage": "https://github.com/Spomky-Labs/base64url/contributors" - } - ], - "description": "Base 64 URL Safe Encoding/Decoding PHP Library", - "homepage": "https://github.com/Spomky-Labs/base64url", - "keywords": [ - "base64", - "rfc4648", - "safe", - "url" - ], - "support": { - "issues": "https://github.com/Spomky-Labs/base64url/issues", - "source": "https://github.com/Spomky-Labs/base64url/tree/v2.0.4" - }, - "funding": [ - { - "url": "https://github.com/Spomky", - "type": "github" - }, - { - "url": "https://www.patreon.com/FlorentMorselli", - "type": "patreon" - } - ], - "time": "2020-11-03T09:10:25+00:00" - }, - { - "name": "spomky-labs/cbor-bundle", - "version": "v2.0.3", - "source": { - "type": "git", - "url": "https://github.com/Spomky-Labs/cbor-bundle.git", - "reference": "65a5a65e7fc20eca383a0be8f3ed287a4fe80b1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/cbor-bundle/zipball/65a5a65e7fc20eca383a0be8f3ed287a4fe80b1f", - "reference": "65a5a65e7fc20eca383a0be8f3ed287a4fe80b1f", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "spomky-labs/cbor-php": "^1.0|^2.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/http-kernel": "^4.4|^5.0" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-beberlei-assert": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^9.0", - "symfony/framework-bundle": "^4.4|^5.0", - "symfony/phpunit-bridge": "^4.4|^5.0", - "thecodingmachine/phpstan-safe-rule": "^1.0@beta" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "SpomkyLabs\\CborBundle\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Florent Morselli", - "homepage": "https://github.com/Spomky" - }, - { - "name": "All contributors", - "homepage": "https://github.com/spomky-labs/cbor-bundle/contributors" - } - ], - "description": "CBOR Encoder/Decoder Bundle for Symfony.", - "homepage": "https://github.com/spomky-labs", - "keywords": [ - "Concise Binary Object Representation", - "RFC7049", - "bundle", - "cbor", - "symfony" - ], - "support": { - "issues": "https://github.com/Spomky-Labs/cbor-bundle/issues", - "source": "https://github.com/Spomky-Labs/cbor-bundle/tree/v2.0.3" - }, - "time": "2020-07-12T22:47:45+00:00" + "time": "2025-02-14T15:04:22+00:00" }, { "name": "spomky-labs/cbor-php", - "version": "v2.1.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/cbor-php.git", - "reference": "28e2712cfc0b48fae661a48ffc6896d7abe83684" + "reference": "499d9bff0a6d59c4f1b813cc617fc3fd56d6dca4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/cbor-php/zipball/28e2712cfc0b48fae661a48ffc6896d7abe83684", - "reference": "28e2712cfc0b48fae661a48ffc6896d7abe83684", + "url": "https://api.github.com/repos/Spomky-Labs/cbor-php/zipball/499d9bff0a6d59c4f1b813cc617fc3fd56d6dca4", + "reference": "499d9bff0a6d59c4f1b813cc617fc3fd56d6dca4", "shasum": "" }, "require": { - "brick/math": "^0.8.15|^0.9.0", + "brick/math": "^0.9|^0.10|^0.11|^0.12", "ext-mbstring": "*", - "php": ">=7.3" + "php": ">=8.0" }, "require-dev": { "ekino/phpstan-banned-code": "^1.0", "ext-json": "*", - "infection/infection": "^0.18|^0.25", + "infection/infection": "^0.29", + "php-parallel-lint/php-parallel-lint": "^1.3", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.0", "phpstan/phpstan-beberlei-assert": "^1.0", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.12", + "phpunit/phpunit": "^10.1|^11.0", + "qossmic/deptrac": "^2.0", + "rector/rector": "^1.0", "roave/security-advisories": "dev-latest", - "symplify/easy-coding-standard": "^10.0" + "symfony/var-dumper": "^6.0|^7.0", + "symplify/easy-coding-standard": "^12.0" }, "suggest": { "ext-bcmath": "GMP or BCMath extensions will drastically improve the library performance. BCMath extension needed to handle the Big Float and Decimal Fraction Tags", @@ -6039,7 +7903,7 @@ ], "support": { "issues": "https://github.com/Spomky-Labs/cbor-php/issues", - "source": "https://github.com/Spomky-Labs/cbor-php/tree/v2.1.0" + "source": "https://github.com/Spomky-Labs/cbor-php/tree/3.1.0" }, "funding": [ { @@ -6051,47 +7915,44 @@ "type": "patreon" } ], - "time": "2021-12-13T12:46:26+00:00" + "time": "2024-07-18T08:37:03+00:00" }, { "name": "spomky-labs/otphp", - "version": "v10.0.3", + "version": "11.3.0", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", - "reference": "9784d9f7c790eed26e102d6c78f12c754036c366" + "reference": "2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/9784d9f7c790eed26e102d6c78f12c754036c366", - "reference": "9784d9f7c790eed26e102d6c78f12c754036c366", + "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33", + "reference": "2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33", "shasum": "" }, "require": { - "beberlei/assert": "^3.0", "ext-mbstring": "*", - "paragonie/constant_time_encoding": "^2.0", - "php": "^7.2|^8.0", - "thecodingmachine/safe": "^0.1.14|^1.0|^2.0" + "paragonie/constant_time_encoding": "^2.0 || ^3.0", + "php": ">=8.1", + "psr/clock": "^1.0", + "symfony/deprecation-contracts": "^3.2" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-beberlei-assert": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^8.0", - "thecodingmachine/phpstan-safe-rule": "^1.0 || ^2.0" + "ekino/phpstan-banned-code": "^1.0", + "infection/infection": "^0.26|^0.27|^0.28|^0.29", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5.26|^10.0|^11.0", + "qossmic/deptrac-shim": "^1.0", + "rector/rector": "^1.0", + "symfony/phpunit-bridge": "^6.1|^7.0", + "symplify/easy-coding-standard": "^12.0" }, "type": "library", - "extra": { - "branch-alias": { - "v10.0": "10.0.x-dev", - "v9.0": "9.0.x-dev", - "v8.3": "8.3.x-dev" - } - }, "autoload": { "psr-4": { "OTPHP\\": "src/" @@ -6124,9 +7985,128 @@ ], "support": { "issues": "https://github.com/Spomky-Labs/otphp/issues", - "source": "https://github.com/Spomky-Labs/otphp/tree/v10.0.3" + "source": "https://github.com/Spomky-Labs/otphp/tree/11.3.0" }, - "time": "2022-03-17T08:00:35+00:00" + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2024-06-12T11:22:32+00:00" + }, + { + "name": "spomky-labs/pki-framework", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/pki-framework.git", + "reference": "5ff1dcc21e961b60149a80e77f744fc047800b31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/5ff1dcc21e961b60149a80e77f744fc047800b31", + "reference": "5ff1dcc21e961b60149a80e77f744fc047800b31", + "shasum": "" + }, + "require": { + "brick/math": "^0.10|^0.11|^0.12|^0.13", + "ext-mbstring": "*", + "php": ">=8.1" + }, + "require-dev": { + "ekino/phpstan-banned-code": "^1.0|^2.0|^3.0", + "ext-gmp": "*", + "ext-openssl": "*", + "infection/infection": "^0.28|^0.29", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.3|^2.0", + "phpstan/phpstan": "^1.8|^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", + "phpstan/phpstan-phpunit": "^1.1|^2.0", + "phpstan/phpstan-strict-rules": "^1.3|^2.0", + "phpunit/phpunit": "^10.1|^11.0|^12.0", + "rector/rector": "^1.0|^2.0", + "roave/security-advisories": "dev-latest", + "symfony/string": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symplify/easy-coding-standard": "^12.0" + }, + "suggest": { + "ext-bcmath": "For better performance (or GMP)", + "ext-gmp": "For better performance (or BCMath)", + "ext-openssl": "For OpenSSL based cyphering" + }, + "type": "library", + "autoload": { + "psr-4": { + "SpomkyLabs\\Pki\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joni Eskelinen", + "email": "jonieske@gmail.com", + "role": "Original developer" + }, + { + "name": "Florent Morselli", + "email": "florent.morselli@spomky-labs.com", + "role": "Spomky-Labs PKI Framework developer" + } + ], + "description": "A PHP framework for managing Public Key Infrastructures. It comprises X.509 public key certificates, attribute certificates, certification requests and certification path validation.", + "homepage": "https://github.com/spomky-labs/pki-framework", + "keywords": [ + "DER", + "Private Key", + "ac", + "algorithm identifier", + "asn.1", + "asn1", + "attribute certificate", + "certificate", + "certification request", + "cryptography", + "csr", + "decrypt", + "ec", + "encrypt", + "pem", + "pkcs", + "public key", + "rsa", + "sign", + "signature", + "verify", + "x.509", + "x.690", + "x509", + "x690" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/pki-framework/issues", + "source": "https://github.com/Spomky-Labs/pki-framework/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2025-04-25T15:57:13+00:00" }, { "name": "symfony/apache-pack", @@ -6156,33 +8136,28 @@ }, { "name": "symfony/asset", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/asset.git", - "reference": "64738384c91e25ad59a66e42b3b0dc0967ca03e3" + "reference": "2466c17d61d14539cddf77e57ebb9cc971185302" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset/zipball/64738384c91e25ad59a66e42b3b0dc0967ca03e3", - "reference": "64738384c91e25ad59a66e42b3b0dc0967ca03e3", + "url": "https://api.github.com/repos/symfony/asset/zipball/2466c17d61d14539cddf77e57ebb9cc971185302", + "reference": "2466c17d61d14539cddf77e57ebb9cc971185302", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, "conflict": { - "symfony/http-foundation": "<5.3" + "symfony/http-foundation": "<5.4" }, "require-dev": { - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/http-foundation": "" + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6210,7 +8185,7 @@ "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/asset/tree/v5.4.19" + "source": "https://github.com/symfony/asset/tree/v6.4.13" }, "funding": [ { @@ -6226,62 +8201,61 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/cache", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "e9147c89fdfdc5d5ef798bb7193f23726ad609f5" + "reference": "d1abcf763a7414f2e572f676f22da7a06c8cd9ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/e9147c89fdfdc5d5ef798bb7193f23726ad609f5", - "reference": "e9147c89fdfdc5d5ef798bb7193f23726ad609f5", + "url": "https://api.github.com/repos/symfony/cache/zipball/d1abcf763a7414f2e572f676f22da7a06c8cd9ee", + "reference": "d1abcf763a7414f2e572f676f22da7a06c8cd9ee", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/cache": "^1.0|^2.0", + "php": ">=8.1", + "psr/cache": "^2.0|^3.0", "psr/log": "^1.1|^2|^3", - "symfony/cache-contracts": "^1.1.7|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/cache-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.3.6|^7.0" }, "conflict": { "doctrine/dbal": "<2.13.1", - "symfony/dependency-injection": "<4.4", - "symfony/http-kernel": "<4.4", - "symfony/var-dumper": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/var-dumper": "<5.4" }, "provide": { - "psr/cache-implementation": "1.0|2.0", - "psr/simple-cache-implementation": "1.0|2.0", - "symfony/cache-implementation": "1.0|2.0" + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/cache": "^1.6|^2.0", - "doctrine/dbal": "^2.13.1|^3.0", - "predis/predis": "^1.1", - "psr/simple-cache": "^1.0|^2.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Cache\\": "" }, + "classmap": [ + "Traits/ValueWrapper.php" + ], "exclude-from-classmap": [ "/Tests/" ] @@ -6307,7 +8281,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v5.4.19" + "source": "https://github.com/symfony/cache/tree/v6.4.21" }, "funding": [ { @@ -6323,37 +8297,34 @@ "type": "tidelift" } ], - "time": "2023-01-19T09:49:58+00:00" + "time": "2025-04-08T08:21:20+00:00" }, { "name": "symfony/cache-contracts", - "version": "v2.5.2", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc" + "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/64be4a7acb83b6f2bf6de9a02cee6dad41277ebc", - "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b", + "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/cache": "^1.0|^2.0|^3.0" - }, - "suggest": { - "symfony/cache-implementation": "" + "php": ">=8.1", + "psr/cache": "^3.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -6386,7 +8357,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/cache-contracts/tree/v3.5.1" }, "funding": [ { @@ -6402,42 +8373,112 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { - "name": "symfony/config", - "version": "v5.4.19", + "name": "symfony/clock", + "version": "v6.4.13", "source": { "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "9bd60843443cda9638efdca7c41eb82ed0026179" + "url": "https://github.com/symfony/clock.git", + "reference": "b2bf55c4dd115003309eafa87ee7df9ed3dde81b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/9bd60843443cda9638efdca7c41eb82ed0026179", - "reference": "9bd60843443cda9638efdca7c41eb82ed0026179", + "url": "https://api.github.com/repos/symfony/clock/zipball/b2bf55c4dd115003309eafa87ee7df9ed3dde81b", + "reference": "b2bf55c4dd115003309eafa87ee7df9ed3dde81b", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22" + "php": ">=8.1", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:18:03+00:00" + }, + { + "name": "symfony/config", + "version": "v6.4.14", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/4e55e7e4ffddd343671ea972216d4509f46c22ef", + "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<4.4" + "symfony/finder": "<5.4", + "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6465,7 +8506,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v5.4.19" + "source": "https://github.com/symfony/config/tree/v6.4.14" }, "funding": [ { @@ -6481,56 +8522,51 @@ "type": "tidelift" } ], - "time": "2023-01-08T13:23:55+00:00" + "time": "2024-11-04T11:33:53+00:00" }, { "name": "symfony/console", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740" + "reference": "a3011c7b7adb58d89f6c0d822abb641d7a5f9719" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/dccb8d251a9017d5994c988b034d3e18aaabf740", - "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740", + "url": "https://api.github.com/repos/symfony/console/zipball/a3011c7b7adb58d89f6c0d822abb641d7a5f9719", + "reference": "a3011c7b7adb58d89f6c0d822abb641d7a5f9719", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6559,12 +8595,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.19" + "source": "https://github.com/symfony/console/tree/v6.4.21" }, "funding": [ { @@ -6580,25 +8616,24 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-04-07T15:42:41+00:00" }, { "name": "symfony/css-selector", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "f4a7d150f5b9e8f974f6f127d8167e420d11fc62" + "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/f4a7d150f5b9e8f974f6f127d8167e420d11fc62", - "reference": "f4a7d150f5b9e8f974f6f127d8167e420d11fc62", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/cb23e97813c5837a041b73a6d63a9ddff0778f5e", + "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -6630,7 +8665,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.4.19" + "source": "https://github.com/symfony/css-selector/tree/v6.4.13" }, "funding": [ { @@ -6646,52 +8681,44 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/dependency-injection", - "version": "v5.4.20", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "8185ed0df129005a26715902f1a53bad0fe67102" + "reference": "c49796a9184a532843e78e50df9e55708b92543a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8185ed0df129005a26715902f1a53bad0fe67102", - "reference": "8185ed0df129005a26715902f1a53bad0fe67102", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/c49796a9184a532843e78e50df9e55708b92543a", + "reference": "c49796a9184a532843e78e50df9e55708b92543a", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/service-contracts": "^1.1.6|^2" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4.20|^7.2.5" }, "conflict": { "ext-psr": "<1.1|>=2", - "symfony/config": "<5.3", - "symfony/finder": "<4.4", - "symfony/proxy-manager-bridge": "<4.4", - "symfony/yaml": "<4.4.26" + "symfony/config": "<6.1", + "symfony/finder": "<5.4", + "symfony/proxy-manager-bridge": "<6.3", + "symfony/yaml": "<5.4" }, "provide": { - "psr/container-implementation": "1.0", - "symfony/service-implementation": "1.0|2.0" + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^5.3|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4.26|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" + "symfony/config": "^6.1|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6719,7 +8746,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v5.4.20" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.20" }, "funding": [ { @@ -6735,33 +8762,33 @@ "type": "tidelift" } ], - "time": "2023-01-27T11:08:11+00:00" + "time": "2025-03-13T09:55:08+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.2", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -6786,7 +8813,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -6802,80 +8829,71 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/doctrine-bridge", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "359ba96a1e494f1fc08b36d51a74d2fe01d68e68" + "reference": "fcce66ede41ca56100b91fd4a00131ba6cf89aba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/359ba96a1e494f1fc08b36d51a74d2fe01d68e68", - "reference": "359ba96a1e494f1fc08b36d51a74d2fe01d68e68", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/fcce66ede41ca56100b91fd4a00131ba6cf89aba", + "reference": "fcce66ede41ca56100b91fd4a00131ba6cf89aba", "shasum": "" }, "require": { - "doctrine/event-manager": "~1.0", - "doctrine/persistence": "^2|^3", - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "doctrine/event-manager": "^1.2|^2", + "doctrine/persistence": "^2.5|^3.1|^4", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3" + "symfony/service-contracts": "^2.5|^3" }, "conflict": { "doctrine/dbal": "<2.13.1", "doctrine/lexer": "<1.1", - "doctrine/orm": "<2.7.4", - "phpunit/phpunit": "<5.4.3", + "doctrine/orm": "<2.15", "symfony/cache": "<5.4", - "symfony/dependency-injection": "<4.4", - "symfony/form": "<5.1", - "symfony/http-kernel": "<5", - "symfony/messenger": "<4.4", - "symfony/property-info": "<5", - "symfony/proxy-manager-bridge": "<4.4.19", - "symfony/security-bundle": "<5", - "symfony/security-core": "<5.3", - "symfony/validator": "<5.2" + "symfony/dependency-injection": "<6.2", + "symfony/form": "<5.4.38|>=6,<6.4.6|>=7,<7.0.6", + "symfony/http-foundation": "<6.3", + "symfony/http-kernel": "<6.2", + "symfony/lock": "<6.3", + "symfony/messenger": "<5.4", + "symfony/property-info": "<5.4", + "symfony/security-bundle": "<5.4", + "symfony/security-core": "<6.4", + "symfony/validator": "<6.4" }, "require-dev": { - "doctrine/annotations": "^1.10.4|^2", "doctrine/collections": "^1.0|^2.0", - "doctrine/data-fixtures": "^1.1", - "doctrine/dbal": "^2.13.1|^3.0", - "doctrine/orm": "^2.7.4", + "doctrine/data-fixtures": "^1.1|^2", + "doctrine/dbal": "^2.13.1|^3|^4", + "doctrine/orm": "^2.15|^3", "psr/log": "^1|^2|^3", - "symfony/cache": "^5.4|^6.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/doctrine-messenger": "^5.1|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/form": "^5.4.9|^6.0.9", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/property-info": "^5.0|^6.0", - "symfony/proxy-manager-bridge": "^4.4|^5.0|^6.0", - "symfony/security-core": "^5.3|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "symfony/validator": "^5.2|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "doctrine/data-fixtures": "", - "doctrine/dbal": "", - "doctrine/orm": "", - "symfony/form": "", - "symfony/property-info": "", - "symfony/validator": "" + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.2|^7.0", + "symfony/doctrine-messenger": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4.38|^6.4.6|^7.0.6", + "symfony/http-kernel": "^6.3|^7.0", + "symfony/lock": "^6.3|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/proxy-manager-bridge": "^6.4", + "symfony/security-core": "^6.4|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "symfony-bridge", "autoload": { @@ -6903,7 +8921,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v5.4.19" + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.21" }, "funding": [ { @@ -6919,29 +8937,99 @@ "type": "tidelift" } ], - "time": "2023-01-06T12:33:31+00:00" + "time": "2025-04-27T15:22:02+00:00" }, { - "name": "symfony/dotenv", - "version": "v5.4.19", + "name": "symfony/dom-crawler", + "version": "v6.4.19", "source": { "type": "git", - "url": "https://github.com/symfony/dotenv.git", - "reference": "38190ba62566afa26ca723a795d0a004e061bd2a" + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "19073e3e0bb50cbc1cb286077069b3107085206f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/38190ba62566afa26ca723a795d0a004e061bd2a", - "reference": "38190ba62566afa26ca723a795d0a004e061bd2a", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/19073e3e0bb50cbc1cb286077069b3107085206f", + "reference": "19073e3e0bb50cbc1cb286077069b3107085206f", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3" + "masterminds/html5": "^2.6", + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" + "symfony/css-selector": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v6.4.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-02-14T17:58:34+00:00" + }, + { + "name": "symfony/dotenv", + "version": "v6.4.16", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "1ac5e7e7e862d4d574258daf08bd569ba926e4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/1ac5e7e7e862d4d574258daf08bd569ba926e4a5", + "reference": "1ac5e7e7e862d4d574258daf08bd569ba926e4a5", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "conflict": { + "symfony/console": "<5.4", + "symfony/process": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6974,7 +9062,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v5.4.19" + "source": "https://github.com/symfony/dotenv/tree/v6.4.16" }, "funding": [ { @@ -6990,31 +9078,35 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-11-27T11:08:19+00:00" }, { "name": "symfony/error-handler", - "version": "v5.4.19", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "438ef3e5e6481244785da3ce8cf8f4e74e7f2822" + "reference": "aa3bcf4f7674719df078e61cc8062e5b7f752031" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/438ef3e5e6481244785da3ce8cf8f4e74e7f2822", - "reference": "438ef3e5e6481244785da3ce8cf8f4e74e7f2822", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/aa3bcf4f7674719df078e61cc8062e5b7f752031", + "reference": "aa3bcf4f7674719df078e61cc8062e5b7f752031", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^4.4|^5.0|^6.0" + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -7045,7 +9137,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.4.19" + "source": "https://github.com/symfony/error-handler/tree/v6.4.20" }, "funding": [ { @@ -7061,48 +9153,43 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-03-01T13:00:38+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "abf49cc084c087d94b4cb939c3f3672971784e0c" + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abf49cc084c087d94b4cb939c3f3672971784e0c", - "reference": "abf49cc084c087d94b4cb939c3f3672971784e0c", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" }, "provide": { "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -7130,7 +9217,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.19" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" }, "funding": [ { @@ -7146,37 +9233,34 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.2", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1" + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f98b54df6ad059855739db6fcbc2d36995283fe1", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -7209,7 +9293,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" }, "funding": [ { @@ -7225,26 +9309,27 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/expression-language", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "5db17a4a1c41e2d43d9b84c2eb98a5f63b11c646" + "reference": "3524904fb026356a5230cd197f9a4e6a61e0e7df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/5db17a4a1c41e2d43d9b84c2eb98a5f63b11c646", - "reference": "5db17a4a1c41e2d43d9b84c2eb98a5f63b11c646", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/3524904fb026356a5230cd197f9a4e6a61e0e7df", + "reference": "3524904fb026356a5230cd197f9a4e6a61e0e7df", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3" + "php": ">=8.1", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -7272,7 +9357,7 @@ "description": "Provides an engine that can compile and evaluate expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/expression-language/tree/v5.4.19" + "source": "https://github.com/symfony/expression-language/tree/v6.4.13" }, "funding": [ { @@ -7288,27 +9373,29 @@ "type": "tidelift" } ], - "time": "2023-01-14T19:14:44+00:00" + "time": "2024-10-09T08:40:40+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "648bfaca6a494f3e22378123bcee2894045dc9d8" + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/648bfaca6a494f3e22378123bcee2894045dc9d8", - "reference": "648bfaca6a494f3e22378123bcee2894045dc9d8", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", "autoload": { @@ -7336,7 +9423,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.19" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -7352,26 +9439,27 @@ "type": "tidelift" } ], - "time": "2023-01-14T19:14:44+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/finder", - "version": "v5.4.19", + "version": "v6.4.17", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f" + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/6071aebf810ad13fe8200c224f36103abb37cf1f", - "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f", + "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -7399,7 +9487,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.19" + "source": "https://github.com/symfony/finder/tree/v6.4.17" }, "funding": [ { @@ -7415,32 +9503,35 @@ "type": "tidelift" } ], - "time": "2023-01-14T19:14:44+00:00" + "time": "2024-12-29T13:51:37+00:00" }, { "name": "symfony/flex", - "version": "v1.19.4", + "version": "v2.5.1", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "c82477240111bfe41a1067c9f0ab91d40bafa5b6" + "reference": "62d5c38c7af6280d8605b725364680838b475641" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/c82477240111bfe41a1067c9f0ab91d40bafa5b6", - "reference": "c82477240111bfe41a1067c9f0ab91d40bafa5b6", + "url": "https://api.github.com/repos/symfony/flex/zipball/62d5c38c7af6280d8605b725364680838b475641", + "reference": "62d5c38c7af6280d8605b725364680838b475641", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": ">=7.1" + "composer-plugin-api": "^2.1", + "php": ">=8.0" + }, + "conflict": { + "composer/semver": "<1.7.2" }, "require-dev": { - "composer/composer": "^1.0.2|^2.0", - "symfony/dotenv": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/phpunit-bridge": "^4.4.12|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" + "composer/composer": "^2.1", + "symfony/dotenv": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/phpunit-bridge": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" }, "type": "composer-plugin", "extra": { @@ -7464,7 +9555,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v1.19.4" + "source": "https://github.com/symfony/flex/tree/v2.5.1" }, "funding": [ { @@ -7480,66 +9571,60 @@ "type": "tidelift" } ], - "time": "2022-12-20T07:19:24+00:00" + "time": "2025-05-10T14:05:03+00:00" }, { "name": "symfony/form", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "c29e6cccee469ca93db2dbc02a39c29312c5e362" + "reference": "44a0e253c16a3187299f07b8f80e23ecb000d360" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/c29e6cccee469ca93db2dbc02a39c29312c5e362", - "reference": "c29e6cccee469ca93db2dbc02a39c29312c5e362", + "url": "https://api.github.com/repos/symfony/form/zipball/44a0e253c16a3187299f07b8f80e23ecb000d360", + "reference": "44a0e253c16a3187299f07b8f80e23ecb000d360", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/options-resolver": "^5.1|^6.0", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/options-resolver": "^5.4|^6.0|^7.0", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-icu": "^1.21", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.23", - "symfony/property-access": "^5.0.8|^6.0", - "symfony/service-contracts": "^1.1|^2|^3" + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4", - "symfony/dependency-injection": "<4.4", - "symfony/doctrine-bridge": "<4.4", - "symfony/error-handler": "<4.4.5", - "symfony/framework-bundle": "<4.4", - "symfony/http-kernel": "<4.4", - "symfony/translation": "<4.4", - "symfony/translation-contracts": "<1.1.7", - "symfony/twig-bridge": "<4.4" + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/doctrine-bridge": "<5.4.21|>=6,<6.2.7", + "symfony/error-handler": "<5.4", + "symfony/framework-bundle": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3|>=7.0,<7.0.3", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.3" }, "require-dev": { "doctrine/collections": "^1.0|^2.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", - "symfony/security-csrf": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "symfony/validator": "^4.4.17|^5.1.9|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/security-csrf": "For protecting forms against CSRF attacks.", - "symfony/twig-bridge": "For templating with Twig.", - "symfony/validator": "For form validation." + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/html-sanitizer": "^6.1|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/security-core": "^6.2|^7.0", + "symfony/security-csrf": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4.35|~6.3.12|^6.4.3|^7.0.3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -7567,7 +9652,7 @@ "description": "Allows to easily create, process and reuse HTML forms", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/form/tree/v5.4.19" + "source": "https://github.com/symfony/form/tree/v6.4.21" }, "funding": [ { @@ -7583,114 +9668,112 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-04-27T15:22:02+00:00" }, { "name": "symfony/framework-bundle", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "a208ee578000f9dedcb50a9784ec7ff8706a7bf1" + "reference": "d0b06133b00e4dd3df7f47a3188fb7baabcc6b2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/a208ee578000f9dedcb50a9784ec7ff8706a7bf1", - "reference": "a208ee578000f9dedcb50a9784ec7ff8706a7bf1", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/d0b06133b00e4dd3df7f47a3188fb7baabcc6b2a", + "reference": "d0b06133b00e4dd3df7f47a3188fb7baabcc6b2a", "shasum": "" }, "require": { + "composer-runtime-api": ">=2.1", "ext-xml": "*", - "php": ">=7.2.5", - "symfony/cache": "^5.2|^6.0", - "symfony/config": "^5.3|^6.0", - "symfony/dependency-injection": "^5.4.5|^6.0.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^4.4.1|^5.0.1|^6.0", - "symfony/event-dispatcher": "^5.1|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^5.4|^6.0", + "php": ">=8.1", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/dependency-injection": "^6.4.12|^7.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.1|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/routing": "^5.3|^6.0" + "symfony/routing": "^6.4|^7.0" }, "conflict": { "doctrine/annotations": "<1.13.1", - "doctrine/cache": "<1.11", "doctrine/persistence": "<1.3", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "phpunit/phpunit": "<5.4.3", - "symfony/asset": "<5.3", - "symfony/console": "<5.2.5", - "symfony/dom-crawler": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/form": "<5.2", - "symfony/http-client": "<4.4", - "symfony/lock": "<4.4", - "symfony/mailer": "<5.2", - "symfony/messenger": "<5.4", - "symfony/mime": "<4.4", - "symfony/property-access": "<5.3", - "symfony/property-info": "<4.4", - "symfony/security-csrf": "<5.3", - "symfony/serializer": "<5.2", - "symfony/service-contracts": ">=3.0", - "symfony/stopwatch": "<4.4", - "symfony/translation": "<5.3", - "symfony/twig-bridge": "<4.4", - "symfony/twig-bundle": "<4.4", - "symfony/validator": "<5.2", - "symfony/web-profiler-bundle": "<4.4", - "symfony/workflow": "<5.2" + "symfony/asset": "<5.4", + "symfony/asset-mapper": "<6.4", + "symfony/clock": "<6.3", + "symfony/console": "<5.4|>=7.0", + "symfony/dom-crawler": "<6.4", + "symfony/dotenv": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<6.3", + "symfony/lock": "<5.4", + "symfony/mailer": "<5.4", + "symfony/messenger": "<6.3", + "symfony/mime": "<6.4", + "symfony/property-access": "<5.4", + "symfony/property-info": "<5.4", + "symfony/runtime": "<5.4.45|>=6.0,<6.4.13|>=7.0,<7.1.6", + "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", + "symfony/security-core": "<5.4", + "symfony/security-csrf": "<5.4", + "symfony/serializer": "<6.4", + "symfony/stopwatch": "<5.4", + "symfony/translation": "<6.4", + "symfony/twig-bridge": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/validator": "<6.4", + "symfony/web-profiler-bundle": "<6.4", + "symfony/workflow": "<6.4" }, "require-dev": { "doctrine/annotations": "^1.13.1|^2", - "doctrine/cache": "^1.11|^2.0", "doctrine/persistence": "^1.3|^2|^3", + "dragonmantank/cron-expression": "^3.1", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^5.3|^6.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/console": "^5.4.9|^6.0.9", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dom-crawler": "^4.4.30|^5.3.7|^6.0", - "symfony/dotenv": "^5.1|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/form": "^5.2|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/mailer": "^5.2|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/notifier": "^5.4|^6.0", + "seld/jsonlint": "^1.10", + "symfony/asset": "^5.4|^6.0|^7.0", + "symfony/asset-mapper": "^6.4|^7.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/console": "^5.4.9|^6.0.9|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/dotenv": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/html-sanitizer": "^6.1|^7.0", + "symfony/http-client": "^6.3|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/mailer": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.3|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/notifier": "^5.4|^6.0|^7.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/property-info": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0", - "symfony/security-bundle": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/string": "^5.0|^6.0", - "symfony/translation": "^5.3|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "symfony/validator": "^5.2|^6.0", - "symfony/web-link": "^4.4|^5.0|^6.0", - "symfony/workflow": "^5.2|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0", - "twig/twig": "^2.10|^3.0" - }, - "suggest": { - "ext-apcu": "For best performance of the system caches", - "symfony/console": "For using the console commands", - "symfony/form": "For using forms", - "symfony/property-info": "For using the property_info service", - "symfony/serializer": "For using the serializer service", - "symfony/validator": "For using validation", - "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", - "symfony/yaml": "For using the debug:config and lint:yaml commands" + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0", + "symfony/scheduler": "^6.4.4|^7.0.4", + "symfony/security-bundle": "^5.4|^6.0|^7.0", + "symfony/semaphore": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/string": "^5.4|^6.0|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/web-link": "^5.4|^6.0|^7.0", + "symfony/workflow": "^6.4|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0", + "twig/twig": "^2.10|^3.0.4" }, "type": "symfony-bundle", "autoload": { @@ -7718,7 +9801,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v5.4.19" + "source": "https://github.com/symfony/framework-bundle/tree/v6.4.21" }, "funding": [ { @@ -7734,50 +9817,53 @@ "type": "tidelift" } ], - "time": "2023-01-10T17:40:25+00:00" + "time": "2025-04-27T13:27:38+00:00" }, { "name": "symfony/http-client", - "version": "v5.4.20", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "b4d936b657c7952a41e89efd0ddcea51f8c90f34" + "reference": "3294a433fc9d12ae58128174896b5b1822c28dad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/b4d936b657c7952a41e89efd0ddcea51f8c90f34", - "reference": "b4d936b657c7952a41e89efd0ddcea51f8c90f34", + "url": "https://api.github.com/repos/symfony/http-client/zipball/3294a433fc9d12ae58128174896b5b1822c28dad", + "reference": "3294a433fc9d12ae58128174896b5b1822c28dad", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-client-contracts": "^2.4", - "symfony/polyfill-php73": "^1.11", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.0|^2|^3" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" }, "provide": { "php-http/async-client-implementation": "*", "php-http/client-implementation": "*", "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "2.4" + "symfony/http-client-implementation": "3.0" }, "require-dev": { "amphp/amp": "^2.5", "amphp/http-client": "^4.2.1", "amphp/http-tunnel": "^1.0", "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", + "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0" + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -7804,8 +9890,11 @@ ], "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", + "keywords": [ + "http" + ], "support": { - "source": "https://github.com/symfony/http-client/tree/v5.4.20" + "source": "https://github.com/symfony/http-client/tree/v6.4.19" }, "funding": [ { @@ -7821,42 +9910,42 @@ "type": "tidelift" } ], - "time": "2023-01-25T18:32:18+00:00" + "time": "2025-02-13T09:55:13+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v2.5.2", + "version": "v3.5.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70" + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", - "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", "shasum": "" }, "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/http-client-implementation": "" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\HttpClient\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7883,7 +9972,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" }, "funding": [ { @@ -7899,39 +9988,40 @@ "type": "tidelift" } ], - "time": "2022-04-12T15:48:08+00:00" + "time": "2024-12-07T08:49:48+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.4.20", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "d0435363362a47c14e9cf50663cb8ffbf491875a" + "reference": "3f0c7ea41db479383b81d436b836d37168fd5b99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0435363362a47c14e9cf50663cb8ffbf491875a", - "reference": "d0435363362a47c14e9cf50663cb8ffbf491875a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3f0c7ea41db479383b81d436b836d37168fd5b99", + "reference": "3f0c7ea41db479383b81d436b836d37168fd5b99", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" }, "require-dev": { - "predis/predis": "~1.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" - }, - "suggest": { - "symfony/mime": "To use the file extension guesser" + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -7959,7 +10049,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.20" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.21" }, "funding": [ { @@ -7975,76 +10065,78 @@ "type": "tidelift" } ], - "time": "2023-01-29T11:11:52+00:00" + "time": "2025-04-27T13:27:38+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.4.20", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e" + "reference": "983ca05eec6623920d24ec0f1005f487d3734a0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/aaeec341582d3c160cc9ecfa8b2419ba6c69954e", - "reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/983ca05eec6623920d24ec0f1005f487d3734a0c", + "reference": "983ca05eec6623920d24ec0f1005f487d3734a0c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^5.0|^6.0", - "symfony/http-foundation": "^5.3.7|^6.0", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/browser-kit": "<5.4", - "symfony/cache": "<5.0", - "symfony/config": "<5.0", - "symfony/console": "<4.4", - "symfony/dependency-injection": "<5.3", - "symfony/doctrine-bridge": "<5.0", - "symfony/form": "<5.0", - "symfony/http-client": "<5.0", - "symfony/mailer": "<5.0", - "symfony/messenger": "<5.0", - "symfony/translation": "<5.0", - "symfony/twig-bridge": "<5.0", - "symfony/validator": "<5.0", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.3", "twig/twig": "<2.13" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/config": "^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2|^3", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", "twig/twig": "^2.13|^3.0.4" }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" - }, "type": "library", "autoload": { "psr-4": { @@ -8071,7 +10163,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.4.20" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.21" }, "funding": [ { @@ -8087,43 +10179,38 @@ "type": "tidelift" } ], - "time": "2023-02-01T08:18:48+00:00" + "time": "2025-05-02T08:46:38+00:00" }, { "name": "symfony/intl", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "f378eb62448dfea67071f9f43529d3a6ad7e0bc8" + "reference": "b248d227fa10fd6345efd4c1c74efaa1c1de6f76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/f378eb62448dfea67071f9f43529d3a6ad7e0bc8", - "reference": "f378eb62448dfea67071f9f43529d3a6ad7e0bc8", + "url": "https://api.github.com/repos/symfony/intl/zipball/b248d227fa10fd6345efd4c1c74efaa1c1de6f76", + "reference": "b248d227fa10fd6345efd4c1c74efaa1c1de6f76", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, "require-dev": { - "symfony/filesystem": "^4.4|^5.0|^6.0" + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { - "files": [ - "Resources/functions.php" - ], "psr-4": { "Symfony\\Component\\Intl\\": "" }, - "classmap": [ - "Resources/stubs" - ], "exclude-from-classmap": [ - "/Tests/" + "/Tests/", + "/Resources/data/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -8148,7 +10235,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a PHP replacement layer for the C intl extension that includes additional data from the ICU library", + "description": "Provides access to the localization data of the ICU library", "homepage": "https://symfony.com", "keywords": [ "i18n", @@ -8159,7 +10246,7 @@ "localization" ], "support": { - "source": "https://github.com/symfony/intl/tree/v5.4.19" + "source": "https://github.com/symfony/intl/tree/v6.4.21" }, "funding": [ { @@ -8175,118 +10262,43 @@ "type": "tidelift" } ], - "time": "2023-01-11T13:51:47+00:00" - }, - { - "name": "symfony/lock", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/lock.git", - "reference": "14b8dfe4df7c3e59e2d3e6a0dd50bb2101eced08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/lock/zipball/14b8dfe4df7c3e59e2d3e6a0dd50bb2101eced08", - "reference": "14b8dfe4df7c3e59e2d3e6a0dd50bb2101eced08", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/dbal": "<2.13" - }, - "require-dev": { - "doctrine/dbal": "^2.13|^3.0", - "predis/predis": "~1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Lock\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jérémy Derussé", - "email": "jeremy@derusse.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Creates and manages locks, a mechanism to provide exclusive access to a shared resource", - "homepage": "https://symfony.com", - "keywords": [ - "cas", - "flock", - "locking", - "mutex", - "redlock", - "semaphore" - ], - "support": { - "source": "https://github.com/symfony/lock/tree/v5.4.19" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-04-07T19:02:30+00:00" }, { "name": "symfony/mailer", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "66081dc01cfc04fdea08bbd253f44627ec5591dd" + "reference": "ada2809ccd4ec27aba9fc344e3efdaec624c6438" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/66081dc01cfc04fdea08bbd253f44627ec5591dd", - "reference": "66081dc01cfc04fdea08bbd253f44627ec5591dd", + "url": "https://api.github.com/repos/symfony/mailer/zipball/ada2809ccd4ec27aba9fc344e3efdaec624c6438", + "reference": "ada2809ccd4ec27aba9fc344e3efdaec624c6438", "shasum": "" }, "require": { "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=7.2.5", + "php": ">=8.1", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/mime": "^5.2.6|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3" + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", + "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/http-kernel": "<4.4" + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" }, "require-dev": { - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/messenger": "^4.4|^5.0|^6.0" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" }, "type": "library", "autoload": { @@ -8314,7 +10326,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v5.4.19" + "source": "https://github.com/symfony/mailer/tree/v6.4.21" }, "funding": [ { @@ -8330,43 +10342,44 @@ "type": "tidelift" } ], - "time": "2023-01-09T05:43:46+00:00" + "time": "2025-04-26T23:47:35+00:00" }, { "name": "symfony/mime", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "a858429a9c704edc53fe057228cf9ca282ba48eb" + "reference": "fec8aa5231f3904754955fad33c2db50594d22d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/a858429a9c704edc53fe057228cf9ca282ba48eb", - "reference": "a858429a9c704edc53fe057228cf9ca282ba48eb", + "url": "https://api.github.com/repos/symfony/mime/zipball/fec8aa5231f3904754955fad33c2db50594d22d1", + "reference": "fec8aa5231f3904754955fad33c2db50594d22d1", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<4.4", - "symfony/serializer": "<5.4.14|>=6.0,<6.0.14|>=6.1,<6.1.6" + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.1|^6.0", - "symfony/property-info": "^4.4|^5.1|^6.0", - "symfony/serializer": "^5.4.14|~6.0.14|^6.1.6" + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.4|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" }, "type": "library", "autoload": { @@ -8398,7 +10411,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.4.19" + "source": "https://github.com/symfony/mime/tree/v6.4.21" }, "funding": [ { @@ -8414,47 +10427,42 @@ "type": "tidelift" } ], - "time": "2023-01-09T05:43:46+00:00" + "time": "2025-04-27T13:27:38+00:00" }, { "name": "symfony/monolog-bridge", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "6b2732feb1335d588a902ca744cae9812cc0e7d3" + "reference": "9d14621e59f22c2b6d030d92d37ffe5ae1e60452" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/6b2732feb1335d588a902ca744cae9812cc0e7d3", - "reference": "6b2732feb1335d588a902ca744cae9812cc0e7d3", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/9d14621e59f22c2b6d030d92d37ffe5ae1e60452", + "reference": "9d14621e59f22c2b6d030d92d37ffe5ae1e60452", "shasum": "" }, "require": { - "monolog/monolog": "^1.25.1|^2", - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^5.3|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3" + "monolog/monolog": "^1.25.1|^2|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/console": "<4.4", - "symfony/http-foundation": "<5.3" + "symfony/console": "<5.4", + "symfony/http-foundation": "<5.4", + "symfony/security-core": "<5.4" }, "require-dev": { - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/mailer": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/security-core": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings.", - "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", - "symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler." + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/mailer": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/security-core": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "symfony-bridge", "autoload": { @@ -8482,7 +10490,7 @@ "description": "Provides integration for Monolog with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/monolog-bridge/tree/v5.4.19" + "source": "https://github.com/symfony/monolog-bridge/tree/v6.4.13" }, "funding": [ { @@ -8498,34 +10506,34 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-10-14T08:49:08+00:00" }, { "name": "symfony/monolog-bundle", - "version": "v3.8.0", + "version": "v3.10.0", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "a41bbcdc1105603b6d73a7d9a43a3788f8e0fb7d" + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/a41bbcdc1105603b6d73a7d9a43a3788f8e0fb7d", - "reference": "a41bbcdc1105603b6d73a7d9a43a3788f8e0fb7d", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", "shasum": "" }, "require": { - "monolog/monolog": "^1.22 || ^2.0 || ^3.0", - "php": ">=7.1.3", - "symfony/config": "~4.4 || ^5.0 || ^6.0", - "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0", - "symfony/http-kernel": "~4.4 || ^5.0 || ^6.0", - "symfony/monolog-bridge": "~4.4 || ^5.0 || ^6.0" + "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", + "php": ">=7.2.5", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", + "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { - "symfony/console": "~4.4 || ^5.0 || ^6.0", - "symfony/phpunit-bridge": "^5.2 || ^6.0", - "symfony/yaml": "~4.4 || ^5.0 || ^6.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/phpunit-bridge": "^6.3 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" }, "type": "symfony-bundle", "extra": { @@ -8563,7 +10571,7 @@ ], "support": { "issues": "https://github.com/symfony/monolog-bundle/issues", - "source": "https://github.com/symfony/monolog-bundle/tree/v3.8.0" + "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0" }, "funding": [ { @@ -8579,27 +10587,25 @@ "type": "tidelift" } ], - "time": "2022-05-10T14:24:36+00:00" + "time": "2023-11-06T17:08:13+00:00" }, { "name": "symfony/options-resolver", - "version": "v5.4.19", + "version": "v6.4.16", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "b03c99236445492f20c61666e8f7e5d388b078e5" + "reference": "368128ad168f20e22c32159b9f761e456cec0c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b03c99236445492f20c61666e8f7e5d388b078e5", - "reference": "b03c99236445492f20c61666e8f7e5d388b078e5", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/368128ad168f20e22c32159b9f761e456cec0c78", + "reference": "368128ad168f20e22c32159b9f761e456cec0c78", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -8632,7 +10638,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.4.19" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.16" }, "funding": [ { @@ -8648,32 +10654,31 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-11-20T10:57:02+00:00" }, { "name": "symfony/password-hasher", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/password-hasher.git", - "reference": "51b4b4d9e368fa6e31daa24866499781848c77d3" + "reference": "e97a1b31f60b8bdfc1fdedab4398538da9441d47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/51b4b4d9e368fa6e31daa24866499781848c77d3", - "reference": "51b4b4d9e368fa6e31daa24866499781848c77d3", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/e97a1b31f60b8bdfc1fdedab4398538da9441d47", + "reference": "e97a1b31f60b8bdfc1fdedab4398538da9441d47", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.15" + "php": ">=8.1" }, "conflict": { - "symfony/security-core": "<5.3" + "symfony/security-core": "<5.4" }, "require-dev": { - "symfony/console": "^5.3|^6.0", - "symfony/security-core": "^5.3|^6.0" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/security-core": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -8705,7 +10710,7 @@ "password" ], "support": { - "source": "https://github.com/symfony/password-hasher/tree/v5.4.19" + "source": "https://github.com/symfony/password-hasher/tree/v6.4.13" }, "funding": [ { @@ -8721,24 +10726,24 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -8748,12 +10753,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -8787,7 +10789,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -8803,36 +10805,33 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -8868,7 +10867,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" }, "funding": [ { @@ -8884,36 +10883,33 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-icu", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c" + "reference": "763d2a91fea5681509ca01acbc1c5e450d127811" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/a3d9148e2c363588e05abbdd4ee4f971f0a5330c", - "reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/763d2a91fea5681509ca01acbc1c5e450d127811", + "reference": "763d2a91fea5681509ca01acbc1c5e450d127811", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance and support of other locales than \"en\"" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -8955,7 +10951,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.32.0" }, "funding": [ { @@ -8971,38 +10967,34 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-12-21T18:38:29+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -9042,7 +11034,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" }, "funding": [ { @@ -9058,36 +11050,33 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-10T14:38:51+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -9126,7 +11115,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" }, "funding": [ { @@ -9142,24 +11131,25 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -9169,12 +11159,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -9209,7 +11196,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -9225,33 +11212,30 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { - "name": "symfony/polyfill-php72", - "version": "v1.27.0", + "name": "symfony/polyfill-php82", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + "url": "https://github.com/symfony/polyfill-php82.git", + "reference": "5d2ed36f7734637dacc025f179698031951b1692" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/5d2ed36f7734637dacc025f179698031951b1692", + "reference": "5d2ed36f7734637dacc025f179698031951b1692", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -9259,83 +11243,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" + "Symfony\\Polyfill\\Php82\\": "" }, "classmap": [ "Resources/stubs" @@ -9355,7 +11263,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -9364,7 +11272,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php82/tree/v1.32.0" }, "funding": [ { @@ -9380,33 +11288,30 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "name": "symfony/polyfill-php83", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -9414,90 +11319,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-php81", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", - "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Polyfill\\Php83\\": "" }, "classmap": [ "Resources/stubs" @@ -9517,7 +11339,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -9526,7 +11348,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" }, "funding": [ { @@ -9542,25 +11364,179 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "000df7860439609837bbe28670b0be15783b7fbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf", + "reference": "000df7860439609837bbe28670b0be15783b7fbf", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-02-20T12:04:08+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", - "version": "v5.4.19", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1" + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c5ba874c9b636dbccf761e22ce750e88ec3f55e1", - "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1", + "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -9588,7 +11564,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.19" + "source": "https://github.com/symfony/process/tree/v6.4.20" }, "funding": [ { @@ -9604,33 +11580,29 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-03-10T17:11:00+00:00" }, { "name": "symfony/property-access", - "version": "v5.4.19", + "version": "v6.4.18", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "20fcf370aed6b2b4a2d8170fa23d2d07250e94ab" + "reference": "80e0378f2f058b60d87dedc3c760caec882e992c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/20fcf370aed6b2b4a2d8170fa23d2d07250e94ab", - "reference": "20fcf370aed6b2b4a2d8170fa23d2d07250e94ab", + "url": "https://api.github.com/repos/symfony/property-access/zipball/80e0378f2f058b60d87dedc3c760caec882e992c", + "reference": "80e0378f2f058b60d87dedc3c760caec882e992c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/property-info": "^5.2|^6.0" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/property-info": "^5.4|^6.0|^7.0" }, "require-dev": { - "symfony/cache": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/cache-implementation": "To cache access methods." + "symfony/cache": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -9665,11 +11637,11 @@ "injection", "object", "property", - "property path", + "property-path", "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v5.4.19" + "source": "https://github.com/symfony/property-access/tree/v6.4.18" }, "funding": [ { @@ -9685,46 +11657,41 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-12-16T14:42:05+00:00" }, { "name": "symfony/property-info", - "version": "v5.4.19", + "version": "v6.4.18", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "8ccf54bce2e2edbface1e99cb5a2560a290c9e2d" + "reference": "94d18e5cc11a37fd92856d38b61d9cdf72536a1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/8ccf54bce2e2edbface1e99cb5a2560a290c9e2d", - "reference": "8ccf54bce2e2edbface1e99cb5a2560a290c9e2d", + "url": "https://api.github.com/repos/symfony/property-info/zipball/94d18e5cc11a37fd92856d38b61d9cdf72536a1e", + "reference": "94d18e5cc11a37fd92856d38b61d9cdf72536a1e", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/string": "^5.1|^6.0" + "php": ">=8.1", + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<4.4" + "doctrine/annotations": "<1.12", + "phpdocumentor/reflection-docblock": "<5.2", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/cache": "<5.4", + "symfony/dependency-injection": "<5.4|>=6.0,<6.4", + "symfony/serializer": "<5.4" }, "require-dev": { - "doctrine/annotations": "^1.10.4|^2", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "phpstan/phpdoc-parser": "^1.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" - }, - "suggest": { - "phpdocumentor/reflection-docblock": "To use the PHPDoc", - "psr/cache-implementation": "To cache results", - "symfony/doctrine-bridge": "To use Doctrine metadata", - "symfony/serializer": "To use Serializer metadata" + "doctrine/annotations": "^1.12|^2", + "phpdocumentor/reflection-docblock": "^5.2", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/serializer": "^5.4|^6.4|^7.0" }, "type": "library", "autoload": { @@ -9760,7 +11727,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v5.4.19" + "source": "https://github.com/symfony/property-info/tree/v6.4.18" }, "funding": [ { @@ -9776,113 +11743,42 @@ "type": "tidelift" } ], - "time": "2023-01-14T11:26:56+00:00" - }, - { - "name": "symfony/proxy-manager-bridge", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/proxy-manager-bridge.git", - "reference": "02dea4937f05236483ebc3ff97b91bb414111344" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/proxy-manager-bridge/zipball/02dea4937f05236483ebc3ff97b91bb414111344", - "reference": "02dea4937f05236483ebc3ff97b91bb414111344", - "shasum": "" - }, - "require": { - "friendsofphp/proxy-manager-lts": "^1.0.2", - "php": ">=7.2.5", - "symfony/dependency-injection": "^5.0|^6.0", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/config": "^4.4|^5.0|^6.0" - }, - "type": "symfony-bridge", - "autoload": { - "psr-4": { - "Symfony\\Bridge\\ProxyManager\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides integration for ProxyManager with various Symfony components", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/proxy-manager-bridge/tree/v5.4.19" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-01-21T10:52:27+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v2.1.4", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "a125b93ef378c492e274f217874906fb9babdebb" + "reference": "c9cf83326a1074f83a738fc5320945abf7fb7fec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/a125b93ef378c492e274f217874906fb9babdebb", - "reference": "a125b93ef378c492e274f217874906fb9babdebb", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/c9cf83326a1074f83a738fc5320945abf7fb7fec", + "reference": "c9cf83326a1074f83a738fc5320945abf7fb7fec", "shasum": "" }, "require": { - "php": ">=7.1", - "psr/http-message": "^1.0", - "symfony/http-foundation": "^4.4 || ^5.0 || ^6.0" + "php": ">=8.1", + "psr/http-message": "^1.0|^2.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-kernel": "<6.2" }, "require-dev": { "nyholm/psr7": "^1.1", - "psr/log": "^1.1 || ^2 || ^3", - "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0", - "symfony/config": "^4.4 || ^5.0 || ^6.0", - "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", - "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0", - "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0", - "symfony/phpunit-bridge": "^5.4@dev || ^6.0" - }, - "suggest": { - "nyholm/psr7": "For a super lightweight PSR-7/17 implementation" + "php-http/discovery": "^1.15", + "psr/log": "^1.1.4|^2|^3", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^6.2|^7.0", + "symfony/http-kernel": "^6.2|^7.0" }, "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-main": "2.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Bridge\\PsrHttpMessage\\": "" @@ -9902,11 +11798,11 @@ }, { "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" + "homepage": "https://symfony.com/contributors" } ], "description": "PSR HTTP message bridge", - "homepage": "http://symfony.com", + "homepage": "https://symfony.com", "keywords": [ "http", "http-message", @@ -9914,8 +11810,7 @@ "psr-7" ], "support": { - "issues": "https://github.com/symfony/psr-http-message-bridge/issues", - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.1.4" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v6.4.13" }, "funding": [ { @@ -9931,29 +11826,30 @@ "type": "tidelift" } ], - "time": "2022-11-28T22:46:34+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/rate-limiter", - "version": "v5.4.19", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/rate-limiter.git", - "reference": "0518d49441e74c89d30d1a40ab3af850533cd775" + "reference": "e250d82fc17b277b97cbce94efef5414aff29bf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/0518d49441e74c89d30d1a40ab3af850533cd775", - "reference": "0518d49441e74c89d30d1a40ab3af850533cd775", + "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/e250d82fc17b277b97cbce94efef5414aff29bf9", + "reference": "e250d82fc17b277b97cbce94efef5414aff29bf9", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/lock": "^5.2|^6.0", - "symfony/options-resolver": "^5.1|^6.0" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/options-resolver": "^5.4|^6.0|^7.0" }, "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0" + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/lock": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -9985,7 +11881,7 @@ "rate-limiter" ], "support": { - "source": "https://github.com/symfony/rate-limiter/tree/v5.4.19" + "source": "https://github.com/symfony/rate-limiter/tree/v6.4.15" }, "funding": [ { @@ -10001,47 +11897,40 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-11-09T07:19:24+00:00" }, { "name": "symfony/routing", - "version": "v5.4.19", + "version": "v6.4.18", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "df1b28f37c8e78912213c58ef6ab2f2037bbfdc5" + "reference": "e9bfc94953019089acdfb9be51c1b9142c4afa68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/df1b28f37c8e78912213c58ef6ab2f2037bbfdc5", - "reference": "df1b28f37c8e78912213c58ef6ab2f2037bbfdc5", + "url": "https://api.github.com/repos/symfony/routing/zipball/e9bfc94953019089acdfb9be51c1b9142c4afa68", + "reference": "e9bfc94953019089acdfb9be51c1b9142c4afa68", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.12", - "symfony/config": "<5.3", - "symfony/dependency-injection": "<4.4", - "symfony/yaml": "<4.4" + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" }, "require-dev": { "doctrine/annotations": "^1.12|^2", "psr/log": "^1|^2|^3", - "symfony/config": "^5.3|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -10075,7 +11964,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.19" + "source": "https://github.com/symfony/routing/tree/v6.4.18" }, "funding": [ { @@ -10091,36 +11980,35 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-01-09T08:51:02+00:00" }, { "name": "symfony/runtime", - "version": "v5.4.19", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "a33478ef6d8e6ea642449001949aafd330ca0486" + "reference": "4facd4174f45cd37c65860403412b67c7381136a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/a33478ef6d8e6ea642449001949aafd330ca0486", - "reference": "a33478ef6d8e6ea642449001949aafd330ca0486", + "url": "https://api.github.com/repos/symfony/runtime/zipball/4facd4174f45cd37c65860403412b67c7381136a", + "reference": "4facd4174f45cd37c65860403412b67c7381136a", "shasum": "" }, "require": { "composer-plugin-api": "^1.0|^2.0", - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.15" + "php": ">=8.1" }, "conflict": { - "symfony/dotenv": "<5.1" + "symfony/dotenv": "<5.4" }, "require-dev": { "composer/composer": "^1.0.2|^2.0", - "symfony/console": "^4.4.30|^5.3.7|^6.0", - "symfony/dotenv": "^5.1|^6.0", - "symfony/http-foundation": "^4.4.30|^5.3.7|^6.0", - "symfony/http-kernel": "^4.4.30|^5.3.7|^6.0" + "symfony/console": "^5.4.9|^6.0.9|^7.0", + "symfony/dotenv": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0" }, "type": "composer-plugin", "extra": { @@ -10151,8 +12039,11 @@ ], "description": "Enables decoupling PHP applications from global state", "homepage": "https://symfony.com", + "keywords": [ + "runtime" + ], "support": { - "source": "https://github.com/symfony/runtime/tree/v5.4.19" + "source": "https://github.com/symfony/runtime/tree/v6.4.14" }, "funding": [ { @@ -10168,65 +12059,75 @@ "type": "tidelift" } ], - "time": "2023-01-20T16:47:48+00:00" + "time": "2024-11-05T16:39:55+00:00" }, { "name": "symfony/security-bundle", - "version": "v5.4.20", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "1a049b77e70e890c5d5d2105d96ce8b35890197e" + "reference": "99b656ff6046ef217d4e3f852940de7e22489849" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/1a049b77e70e890c5d5d2105d96ce8b35890197e", - "reference": "1a049b77e70e890c5d5d2105d96ce8b35890197e", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/99b656ff6046ef217d4e3f852940de7e22489849", + "reference": "99b656ff6046ef217d4e3f852940de7e22489849", "shasum": "" }, "require": { + "composer-runtime-api": ">=2.1", "ext-xml": "*", - "php": ">=7.2.5", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher": "^5.1|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^5.3|^6.0", - "symfony/password-hasher": "^5.3|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/security-core": "^5.4|^6.0", - "symfony/security-csrf": "^4.4|^5.0|^6.0", - "symfony/security-guard": "^5.3", - "symfony/security-http": "^5.4.20|~6.0.20|~6.1.12|^6.2.6" + "php": ">=8.1", + "symfony/clock": "^6.3|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/dependency-injection": "^6.4.11|^7.1.4", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.2|^7.0", + "symfony/http-kernel": "^6.2", + "symfony/password-hasher": "^5.4|^6.0|^7.0", + "symfony/security-core": "^6.2|^7.0", + "symfony/security-csrf": "^5.4|^6.0|^7.0", + "symfony/security-http": "^6.3.6|^7.0", + "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/browser-kit": "<4.4", - "symfony/console": "<4.4", - "symfony/framework-bundle": "<4.4", - "symfony/ldap": "<5.1", - "symfony/twig-bundle": "<4.4" + "symfony/browser-kit": "<5.4", + "symfony/console": "<5.4", + "symfony/framework-bundle": "<6.4", + "symfony/http-client": "<5.4", + "symfony/ldap": "<5.4", + "symfony/serializer": "<6.4", + "symfony/twig-bundle": "<5.4", + "symfony/validator": "<6.4" }, "require-dev": { - "doctrine/annotations": "^1.10.4|^2", - "symfony/asset": "^4.4|^5.0|^6.0", - "symfony/browser-kit": "^4.4|^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/form": "^4.4|^5.0|^6.0", - "symfony/framework-bundle": "^5.3|^6.0", - "symfony/ldap": "^5.3|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/twig-bridge": "^4.4|^5.0|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "symfony/validator": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0", - "twig/twig": "^2.13|^3.0.4" + "symfony/asset": "^5.4|^6.0|^7.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/ldap": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/twig-bridge": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0", + "twig/twig": "^2.13|^3.0.4", + "web-token/jwt-checker": "^3.1", + "web-token/jwt-signature-algorithm-ecdsa": "^3.1", + "web-token/jwt-signature-algorithm-eddsa": "^3.1", + "web-token/jwt-signature-algorithm-hmac": "^3.1", + "web-token/jwt-signature-algorithm-none": "^3.1", + "web-token/jwt-signature-algorithm-rsa": "^3.1" }, "type": "symfony-bundle", "autoload": { @@ -10254,7 +12155,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v5.4.20" + "source": "https://github.com/symfony/security-bundle/tree/v6.4.21" }, "funding": [ { @@ -10270,56 +12171,49 @@ "type": "tidelift" } ], - "time": "2023-01-30T09:35:58+00:00" + "time": "2025-04-27T13:27:38+00:00" }, { "name": "symfony/security-core", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "76fe5a7c62a3f23a5d7b72a55529e94ae2c1ae07" + "reference": "c6e70da38436a9a49ed39d9cbead1ecf760f0fbd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/76fe5a7c62a3f23a5d7b72a55529e94ae2c1ae07", - "reference": "76fe5a7c62a3f23a5d7b72a55529e94ae2c1ae07", + "url": "https://api.github.com/repos/symfony/security-core/zipball/c6e70da38436a9a49ed39d9cbead1ecf760f0fbd", + "reference": "c6e70da38436a9a49ed39d9cbead1ecf760f0fbd", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^1.1|^2|^3", - "symfony/password-hasher": "^5.3|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1.6|^2|^3" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher-contracts": "^2.5|^3", + "symfony/password-hasher": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/event-dispatcher": "<4.4", - "symfony/http-foundation": "<5.3", - "symfony/ldap": "<4.4", - "symfony/security-guard": "<4.4", - "symfony/validator": "<5.2" + "symfony/event-dispatcher": "<5.4", + "symfony/http-foundation": "<5.4", + "symfony/ldap": "<5.4", + "symfony/security-guard": "<5.4", + "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3|>=7.0,<7.0.3", + "symfony/validator": "<5.4" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "psr/container": "^1.0|^2.0", + "psr/container": "^1.1|^2.0", "psr/log": "^1|^2|^3", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/ldap": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/validator": "^5.2|^6.0" - }, - "suggest": { - "psr/container-implementation": "To instantiate the Security class", - "symfony/event-dispatcher": "", - "symfony/expression-language": "For using the expression voter", - "symfony/http-foundation": "", - "symfony/ldap": "For using LDAP integration", - "symfony/validator": "For using the user password constraint" + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/ldap": "^5.4|^6.0|^7.0", + "symfony/string": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4.35|~6.3.12|^6.4.3|^7.0.3", + "symfony/validator": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -10347,7 +12241,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v5.4.19" + "source": "https://github.com/symfony/security-core/tree/v6.4.21" }, "funding": [ { @@ -10363,35 +12257,31 @@ "type": "tidelift" } ], - "time": "2023-01-24T10:56:59+00:00" + "time": "2025-04-17T07:43:34+00:00" }, { "name": "symfony/security-csrf", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/security-csrf.git", - "reference": "892dc11b003c0d3da377264bb3d5f178cb894944" + "reference": "c34421b7d34efbaef5d611ab2e646a0ec464ffe3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/892dc11b003c0d3da377264bb3d5f178cb894944", - "reference": "892dc11b003c0d3da377264bb3d5f178cb894944", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/c34421b7d34efbaef5d611ab2e646a0ec464ffe3", + "reference": "c34421b7d34efbaef5d611ab2e646a0ec464ffe3", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16", - "symfony/security-core": "^4.4|^5.0|^6.0" + "php": ">=8.1", + "symfony/security-core": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/http-foundation": "<5.3" + "symfony/http-foundation": "<5.4" }, "require-dev": { - "symfony/http-foundation": "^5.3|^6.0" - }, - "suggest": { - "symfony/http-foundation": "For using the class SessionTokenStorage." + "symfony/http-foundation": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -10419,7 +12309,7 @@ "description": "Symfony Security Component - CSRF Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-csrf/tree/v5.4.19" + "source": "https://github.com/symfony/security-csrf/tree/v6.4.13" }, "funding": [ { @@ -10435,115 +12325,51 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" - }, - { - "name": "symfony/security-guard", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/security-guard.git", - "reference": "6f9d69b1ef4adaae02ac99af09bbe5de151e824c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/security-guard/zipball/6f9d69b1ef4adaae02ac99af09bbe5de151e824c", - "reference": "6f9d69b1ef4adaae02ac99af09bbe5de151e824c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.15", - "symfony/security-core": "^5.0", - "symfony/security-http": "^5.3" - }, - "require-dev": { - "psr/log": "^1|^2|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Security\\Guard\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Security Component - Guard", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/security-guard/tree/v5.4.19" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/security-http", - "version": "v5.4.20", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "0236efe37462df3204e758e3a55661a43285d948" + "reference": "67d0edaf6702c3192f27ad483df9a875c9a1f1a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/0236efe37462df3204e758e3a55661a43285d948", - "reference": "0236efe37462df3204e758e3a55661a43285d948", + "url": "https://api.github.com/repos/symfony/security-http/zipball/67d0edaf6702c3192f27ad483df9a875c9a1f1a2", + "reference": "67d0edaf6702c3192f27ad483df9a875c9a1f1a2", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^5.3|^6.0", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-foundation": "^6.2|^7.0", + "symfony/http-kernel": "^6.3|^7.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/security-core": "^5.4.19|~6.0.19|~6.1.11|^6.2.5" + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/event-dispatcher": "<4.3", - "symfony/security-bundle": "<5.3", - "symfony/security-csrf": "<4.4" + "symfony/clock": "<6.3", + "symfony/event-dispatcher": "<5.4.9|>=6,<6.0.9", + "symfony/http-client-contracts": "<3.0", + "symfony/security-bundle": "<5.4", + "symfony/security-csrf": "<5.4" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/security-csrf": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs", - "symfony/security-csrf": "For using tokens to protect authentication/logout attempts" + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.3|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^3.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/security-csrf": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "web-token/jwt-checker": "^3.1", + "web-token/jwt-signature-algorithm-ecdsa": "^3.1" }, "type": "library", "autoload": { @@ -10571,7 +12397,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v5.4.20" + "source": "https://github.com/symfony/security-http/tree/v6.4.21" }, "funding": [ { @@ -10587,66 +12413,61 @@ "type": "tidelift" } ], - "time": "2023-01-30T09:35:58+00:00" + "time": "2025-04-27T13:58:34+00:00" }, { "name": "symfony/serializer", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "2139fa01c19a764af81191d635b2b9302f4bafd8" + "reference": "c45f8f7763afb11e85772c0c1debb8f272c17f51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/2139fa01c19a764af81191d635b2b9302f4bafd8", - "reference": "2139fa01c19a764af81191d635b2b9302f4bafd8", + "url": "https://api.github.com/repos/symfony/serializer/zipball/c45f8f7763afb11e85772c0c1debb8f272c17f51", + "reference": "c45f8f7763afb11e85772c0c1debb8f272c17f51", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "doctrine/annotations": "<1.12", "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0|>=1.7.0", - "symfony/dependency-injection": "<4.4", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/dependency-injection": "<5.4", "symfony/property-access": "<5.4", - "symfony/property-info": "<5.3.13", - "symfony/uid": "<5.3", - "symfony/yaml": "<4.4" + "symfony/property-info": "<5.4.24|>=6,<6.2.11", + "symfony/uid": "<5.4", + "symfony/validator": "<6.4", + "symfony/yaml": "<5.4" }, "require-dev": { "doctrine/annotations": "^1.12|^2", "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/form": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/property-access": "^5.4|^6.0", - "symfony/property-info": "^5.3.13|^6.0", - "symfony/uid": "^5.3|^6.0", - "symfony/validator": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0", - "symfony/var-exporter": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/cache-implementation": "For using the metadata cache.", - "symfony/config": "For using the XML mapping loader.", - "symfony/mime": "For using a MIME type guesser within the DataUriNormalizer.", - "symfony/property-access": "For using the ObjectNormalizer.", - "symfony/property-info": "To deserialize relations.", - "symfony/var-exporter": "For using the metadata compiler.", - "symfony/yaml": "For using the default YAML mapping loader." + "seld/jsonlint": "^1.10", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.26|^6.3|^7.0", + "symfony/property-info": "^5.4.24|^6.2.11|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -10674,7 +12495,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v5.4.19" + "source": "https://github.com/symfony/serializer/tree/v6.4.21" }, "funding": [ { @@ -10690,47 +12511,47 @@ "type": "tidelift" } ], - "time": "2023-01-14T08:18:46+00:00" + "time": "2025-04-27T13:27:38+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.2", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -10757,7 +12578,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -10773,25 +12594,94 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:17:29+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { - "name": "symfony/stopwatch", - "version": "v5.4.19", + "name": "symfony/stimulus-bundle", + "version": "v2.24.0", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "bd2b066090fd6a67039371098fa25a84cb2679ec" + "url": "https://github.com/symfony/stimulus-bundle.git", + "reference": "e09840304467cda3324cc116c7f4ee23c8ff227c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/bd2b066090fd6a67039371098fa25a84cb2679ec", - "reference": "bd2b066090fd6a67039371098fa25a84cb2679ec", + "url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/e09840304467cda3324cc116c7f4ee23c8ff227c", + "reference": "e09840304467cda3324cc116c7f4ee23c8ff227c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/service-contracts": "^1|^2|^3" + "php": ">=8.1", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/deprecation-contracts": "^2.0|^3.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "twig/twig": "^2.15.3|^3.8" + }, + "require-dev": { + "symfony/asset-mapper": "^6.3|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "zenstruck/browser": "^1.4" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\UX\\StimulusBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Integration with your Symfony app & Stimulus!", + "keywords": [ + "symfony-ux" + ], + "support": { + "source": "https://github.com/symfony/stimulus-bundle/tree/v2.24.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-09T21:10:04+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -10819,7 +12709,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.4.19" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.19" }, "funding": [ { @@ -10835,38 +12725,38 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-02-21T10:06:30+00:00" }, { "name": "symfony/string", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb" + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/0a01071610fd861cc160dfb7e2682ceec66064cb", - "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb", + "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -10905,7 +12795,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.19" + "source": "https://github.com/symfony/string/tree/v6.4.21" }, "funding": [ { @@ -10921,57 +12811,55 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-04-18T15:23:29+00:00" }, { "name": "symfony/translation", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695" + "reference": "bb92ea5588396b319ba43283a5a3087a034cb29c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695", - "reference": "83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695", + "url": "https://api.github.com/repos/symfony/translation/zipball/bb92ea5588396b319ba43283a5a3087a034cb29c", + "reference": "bb92ea5588396b319ba43283a5a3087a034cb29c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^2.3" + "symfony/translation-contracts": "^2.5|^3.0" }, "conflict": { - "symfony/config": "<4.4", - "symfony/console": "<5.3", - "symfony/dependency-injection": "<5.0", - "symfony/http-kernel": "<5.0", - "symfony/twig-bundle": "<5.0", - "symfony/yaml": "<4.4" + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" }, "provide": { - "symfony/translation-implementation": "2.3" + "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { + "nikic/php-parser": "^4.18|^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/service-contracts": "^1.1.2|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -11002,7 +12890,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.4.19" + "source": "https://github.com/symfony/translation/tree/v6.4.21" }, "funding": [ { @@ -11018,42 +12906,42 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-04-07T19:02:30+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.5.2", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", "shasum": "" }, "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/translation-implementation": "" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -11080,7 +12968,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" }, "funding": [ { @@ -11096,85 +12984,73 @@ "type": "tidelift" } ], - "time": "2022-06-27T16:58:25+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/twig-bridge", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "0526188cb886be64351454826fecc6d35356eaf1" + "reference": "0457b7944bf1cc9c846c98d4923b5379ec6afc09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/0526188cb886be64351454826fecc6d35356eaf1", - "reference": "0526188cb886be64351454826fecc6d35356eaf1", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/0457b7944bf1cc9c846c98d4923b5379ec6afc09", + "reference": "0457b7944bf1cc9c846c98d4923b5379ec6afc09", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^1.1|^2|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/translation-contracts": "^2.5|^3", "twig/twig": "^2.13|^3.0.4" }, "conflict": { "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "symfony/console": "<5.3", - "symfony/form": "<5.3", - "symfony/http-foundation": "<5.3", - "symfony/http-kernel": "<4.4", - "symfony/translation": "<5.2", - "symfony/workflow": "<5.2" + "symfony/console": "<5.4", + "symfony/form": "<6.3", + "symfony/http-foundation": "<5.4", + "symfony/http-kernel": "<6.4", + "symfony/mime": "<6.2", + "symfony/serializer": "<6.4", + "symfony/translation": "<5.4", + "symfony/workflow": "<5.4" }, "require-dev": { - "doctrine/annotations": "^1.12|^2", "egulias/email-validator": "^2.1.10|^3|^4", + "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^4.4|^5.0|^6.0", - "symfony/console": "^5.3|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/form": "^5.3|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", - "symfony/mime": "^5.2|^6.0", + "symfony/asset": "^5.4|^6.0|^7.0", + "symfony/asset-mapper": "^6.3|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/form": "^6.4.20|^7.2.5", + "symfony/html-sanitizer": "^6.1|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/property-info": "^4.4|^5.1|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", "symfony/security-acl": "^2.8|^3.0", - "symfony/security-core": "^4.4|^5.0|^6.0", - "symfony/security-csrf": "^4.4|^5.0|^6.0", - "symfony/security-http": "^4.4|^5.0|^6.0", - "symfony/serializer": "^5.2|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^5.2|^6.0", - "symfony/web-link": "^4.4|^5.0|^6.0", - "symfony/workflow": "^5.2|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0", + "symfony/security-core": "^5.4|^6.0|^7.0", + "symfony/security-csrf": "^5.4|^6.0|^7.0", + "symfony/security-http": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^6.1|^7.0", + "symfony/web-link": "^5.4|^6.0|^7.0", + "symfony/workflow": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0", "twig/cssinliner-extra": "^2.12|^3", "twig/inky-extra": "^2.12|^3", "twig/markdown-extra": "^2.12|^3" }, - "suggest": { - "symfony/asset": "For using the AssetExtension", - "symfony/expression-language": "For using the ExpressionExtension", - "symfony/finder": "", - "symfony/form": "For using the FormExtension", - "symfony/http-kernel": "For using the HttpKernelExtension", - "symfony/routing": "For using the RoutingExtension", - "symfony/security-core": "For using the SecurityExtension", - "symfony/security-csrf": "For using the CsrfExtension", - "symfony/security-http": "For using the LogoutUrlExtension", - "symfony/stopwatch": "For using the StopwatchExtension", - "symfony/translation": "For using the TranslationExtension", - "symfony/var-dumper": "For using the DumpExtension", - "symfony/web-link": "For using the WebLinkExtension", - "symfony/yaml": "For using the YamlExtension" - }, "type": "symfony-bridge", "autoload": { "psr-4": { @@ -11201,7 +13077,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v5.4.19" + "source": "https://github.com/symfony/twig-bridge/tree/v6.4.21" }, "funding": [ { @@ -11217,52 +13093,47 @@ "type": "tidelift" } ], - "time": "2023-01-09T05:43:46+00:00" + "time": "2025-04-27T13:27:38+00:00" }, { "name": "symfony/twig-bundle", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "286bd9e38b9bcb142f1eda0a75b0bbeb49ff34bd" + "reference": "c3beeb5336aba1ea03c37e526968c2fde3ef25c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/286bd9e38b9bcb142f1eda0a75b0bbeb49ff34bd", - "reference": "286bd9e38b9bcb142f1eda0a75b0bbeb49ff34bd", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/c3beeb5336aba1ea03c37e526968c2fde3ef25c4", + "reference": "c3beeb5336aba1ea03c37e526968c2fde3ef25c4", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/twig-bridge": "^5.3|^6.0", + "composer-runtime-api": ">=2.1", + "php": ">=8.1", + "symfony/config": "^6.1|^7.0", + "symfony/dependency-injection": "^6.1|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^6.2", + "symfony/twig-bridge": "^6.4", "twig/twig": "^2.13|^3.0.4" }, "conflict": { - "symfony/dependency-injection": "<5.3", - "symfony/framework-bundle": "<5.0", - "symfony/service-contracts": ">=3.0", - "symfony/translation": "<5.0" + "symfony/framework-bundle": "<5.4", + "symfony/translation": "<5.4" }, "require-dev": { - "doctrine/annotations": "^1.10.4|^2", - "doctrine/cache": "^1.0|^2.0", - "symfony/asset": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/form": "^4.4|^5.0|^6.0", - "symfony/framework-bundle": "^5.0|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^5.0|^6.0", - "symfony/web-link": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" + "symfony/asset": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/web-link": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "symfony-bundle", "autoload": { @@ -11290,7 +13161,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v5.4.19" + "source": "https://github.com/symfony/twig-bundle/tree/v6.4.13" }, "funding": [ { @@ -11306,53 +13177,206 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { - "name": "symfony/ux-turbo", - "version": "v2.7.1", + "name": "symfony/uid", + "version": "v6.4.13", "source": { "type": "git", - "url": "https://github.com/symfony/ux-turbo.git", - "reference": "2c82aad06d7c1493615d09188b70a3a86f6cc6ab" + "url": "https://github.com/symfony/uid.git", + "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/2c82aad06d7c1493615d09188b70a3a86f6cc6ab", - "reference": "2c82aad06d7c1493615d09188b70a3a86f6cc6ab", + "url": "https://api.github.com/repos/symfony/uid/zipball/18eb207f0436a993fffbdd811b5b8fa35fa5e007", + "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/webpack-encore-bundle": "^1.11" + "php": ">=8.1", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:18:03+00:00" + }, + { + "name": "symfony/ux-translator", + "version": "v2.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/ux-translator.git", + "reference": "a829b5c83ed676a8e848dce90dd6d42f12e90be6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ux-translator/zipball/a829b5c83ed676a8e848dce90dd6d42f12e90be6", + "reference": "a829b5c83ed676a8e848dce90dd6d42f12e90be6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/string": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^5.2|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "symfony-bundle", + "extra": { + "thanks": { + "url": "https://github.com/symfony/ux", + "name": "symfony/ux" + } + }, + "autoload": { + "psr-4": { + "Symfony\\UX\\Translator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Hugo Alliaume", + "email": "hugo@alliau.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Exposes Symfony Translations directly to JavaScript.", + "homepage": "https://symfony.com", + "keywords": [ + "symfony-ux" + ], + "support": { + "source": "https://github.com/symfony/ux-translator/tree/v2.24.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-09T21:10:04+00:00" + }, + { + "name": "symfony/ux-turbo", + "version": "v2.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/ux-turbo.git", + "reference": "22954300bd0b01ca46f17c7890ea15138d9cf67f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/22954300bd0b01ca46f17c7890ea15138d9cf67f", + "reference": "22954300bd0b01ca46f17c7890ea15138d9cf67f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/stimulus-bundle": "^2.9.1" }, "conflict": { "symfony/flex": "<1.13" }, "require-dev": { - "doctrine/annotations": "^1.12", - "doctrine/doctrine-bundle": "^2.2", + "dbrekelmans/bdi": "dev-main", + "doctrine/doctrine-bundle": "^2.4.3", "doctrine/orm": "^2.8 | 3.0", - "phpstan/phpstan": "^0.12", - "symfony/debug-bundle": "^5.2|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/form": "^5.2|^6.0", - "symfony/framework-bundle": "^5.2|^6.0", - "symfony/mercure-bundle": "^0.3", - "symfony/messenger": "^5.2|^6.0", - "symfony/panther": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.2.1|^6.0", - "symfony/property-access": "^5.2|^6.0", - "symfony/security-core": "^5.2|^6.0", - "symfony/stopwatch": "^5.2|^6.0", - "symfony/twig-bundle": "^5.2|^6.0", - "symfony/web-profiler-bundle": "^5.2|^6.0" + "phpstan/phpstan": "^1.10", + "symfony/asset-mapper": "^6.4|^7.0", + "symfony/debug-bundle": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/mercure-bundle": "^0.3.7", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/panther": "^2.1", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|6.3.*|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/security-core": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^6.4|^7.0", + "symfony/ux-twig-component": "^2.21", + "symfony/web-profiler-bundle": "^5.4|^6.0|^7.0" }, "type": "symfony-bundle", "extra": { "thanks": { - "name": "symfony/ux", - "url": "https://github.com/symfony/ux" + "url": "https://github.com/symfony/ux", + "name": "symfony/ux" } }, "autoload": { @@ -11385,7 +13409,7 @@ "turbo-stream" ], "support": { - "source": "https://github.com/symfony/ux-turbo/tree/v2.7.1" + "source": "https://github.com/symfony/ux-turbo/tree/v2.24.0" }, "funding": [ { @@ -11401,76 +13425,59 @@ "type": "tidelift" } ], - "time": "2023-01-24T15:40:19+00:00" + "time": "2025-04-04T17:29:20+00:00" }, { "name": "symfony/validator", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "98582557a107e2db3a4e95e6dea0df8016dc246c" + "reference": "47610116f476595b90c368ff2a22514050712785" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/98582557a107e2db3a4e95e6dea0df8016dc246c", - "reference": "98582557a107e2db3a4e95e6dea0df8016dc246c", + "url": "https://api.github.com/repos/symfony/validator/zipball/47610116f476595b90c368ff2a22514050712785", + "reference": "47610116f476595b90c368ff2a22514050712785", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/translation-contracts": "^1.1|^2|^3" + "symfony/polyfill-php83": "^1.27", + "symfony/translation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.13", - "doctrine/cache": "<1.11", "doctrine/lexer": "<1.1", - "phpunit/phpunit": "<5.4.3", - "symfony/dependency-injection": "<4.4", - "symfony/expression-language": "<5.1", - "symfony/http-kernel": "<4.4", - "symfony/intl": "<4.4", - "symfony/property-info": "<5.3", - "symfony/translation": "<4.4", - "symfony/yaml": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/expression-language": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/intl": "<5.4", + "symfony/property-info": "<5.4", + "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3|>=7.0,<7.0.3", + "symfony/yaml": "<5.4" }, "require-dev": { "doctrine/annotations": "^1.13|^2", - "doctrine/cache": "^1.11|^2.0", "egulias/email-validator": "^2.1.10|^3|^4", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^5.1|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/property-info": "^5.3|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "egulias/email-validator": "Strict (RFC compliant) email validation", - "psr/cache-implementation": "For using the mapping cache.", - "symfony/config": "", - "symfony/expression-language": "For using the Expression validator and the ExpressionLanguageSyntax constraints", - "symfony/http-foundation": "", - "symfony/intl": "", - "symfony/property-access": "For accessing properties within comparison constraints", - "symfony/property-info": "To automatically add NotNull and Type constraints", - "symfony/translation": "For translating validation errors.", - "symfony/yaml": "" + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4.35|~6.3.12|^6.4.3|^7.0.3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -11478,7 +13485,8 @@ "Symfony\\Component\\Validator\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Tests/", + "/Resources/bin/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -11498,7 +13506,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v5.4.19" + "source": "https://github.com/symfony/validator/tree/v6.4.21" }, "funding": [ { @@ -11514,43 +13522,39 @@ "type": "tidelift" } ], - "time": "2023-01-15T15:23:36+00:00" + "time": "2025-04-30T18:50:04+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b" + "reference": "22560f80c0c5cd58cc0bcaf73455ffd81eb380d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b", - "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/22560f80c0c5cd58cc0bcaf73455ffd81eb380d5", + "reference": "22560f80c0c5cd58cc0bcaf73455ffd81eb380d5", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4" + "symfony/console": "<5.4" }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", "twig/twig": "^2.13|^3.0.4" }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, "bin": [ "Resources/bin/var-dump-server" ], @@ -11587,7 +13591,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.19" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.21" }, "funding": [ { @@ -11603,28 +13607,30 @@ "type": "tidelift" } ], - "time": "2023-01-16T10:52:33+00:00" + "time": "2025-04-09T07:34:50+00:00" }, { "name": "symfony/var-exporter", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6" + "reference": "717e7544aa99752c54ecba5c0e17459c48317472" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6", - "reference": "2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/717e7544aa99752c54ecba5c0e17459c48317472", + "reference": "717e7544aa99752c54ecba5c0e17459c48317472", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { - "symfony/var-dumper": "^4.4.9|^5.0.9|^6.0" + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -11657,10 +13663,12 @@ "export", "hydrate", "instantiate", + "lazy-loading", + "proxy", "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v5.4.19" + "source": "https://github.com/symfony/var-exporter/tree/v6.4.21" }, "funding": [ { @@ -11676,38 +13684,34 @@ "type": "tidelift" } ], - "time": "2023-01-12T16:39:29+00:00" + "time": "2025-04-27T21:06:26+00:00" }, { "name": "symfony/web-link", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/web-link.git", - "reference": "c5b5d4a84249959d0df65c0e804d7f57f6873ecd" + "reference": "4d188b64bb9a9c5e2e4d20c8d5fdce6bbbb32c94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-link/zipball/c5b5d4a84249959d0df65c0e804d7f57f6873ecd", - "reference": "c5b5d4a84249959d0df65c0e804d7f57f6873ecd", + "url": "https://api.github.com/repos/symfony/web-link/zipball/4d188b64bb9a9c5e2e4d20c8d5fdce6bbbb32c94", + "reference": "4d188b64bb9a9c5e2e4d20c8d5fdce6bbbb32c94", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/link": "^1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "psr/link": "^1.1|^2.0" }, "conflict": { - "symfony/http-kernel": "<5.3" + "symfony/http-kernel": "<5.4" }, "provide": { - "psr/link-implementation": "1.0" + "psr/link-implementation": "1.0|2.0" }, "require-dev": { - "symfony/http-kernel": "^5.3|^6.0" - }, - "suggest": { - "symfony/http-kernel": "" + "symfony/http-kernel": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -11747,7 +13751,7 @@ "push" ], "support": { - "source": "https://github.com/symfony/web-link/tree/v5.4.19" + "source": "https://github.com/symfony/web-link/tree/v6.4.13" }, "funding": [ { @@ -11763,43 +13767,42 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/webpack-encore-bundle", - "version": "v1.16.1", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/symfony/webpack-encore-bundle.git", - "reference": "1862d71e483769b40278548a30e756ce13ef9d4c" + "reference": "e335394b68a775a9b2bd173a8ba4fd2001f3870c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/1862d71e483769b40278548a30e756ce13ef9d4c", - "reference": "1862d71e483769b40278548a30e756ce13ef9d4c", + "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/e335394b68a775a9b2bd173a8ba4fd2001f3870c", + "reference": "e335394b68a775a9b2bd173a8ba4fd2001f3870c", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/asset": "^4.4 || ^5.0 || ^6.0", - "symfony/config": "^4.4 || ^5.0 || ^6.0", - "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0", - "symfony/deprecation-contracts": "^2.1 || ^3.0", - "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0", - "symfony/polyfill-php80": "^1.25.0", - "symfony/service-contracts": "^1.0 || ^2.0 || ^3.0" + "php": ">=8.1.0", + "symfony/asset": "^5.4 || ^6.2 || ^7.0", + "symfony/config": "^5.4 || ^6.2 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.2 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.2 || ^7.0", + "symfony/service-contracts": "^1.1.9 || ^2.1.3 || ^3.0" }, "require-dev": { - "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0", - "symfony/phpunit-bridge": "^5.3 || ^6.0", - "symfony/twig-bundle": "^4.4 || ^5.0 || ^6.0", - "symfony/web-link": "^4.4 || ^5.0 || ^6.0" + "symfony/framework-bundle": "^5.4 || ^6.2 || ^7.0", + "symfony/http-client": "^5.4 || ^6.2 || ^7.0", + "symfony/phpunit-bridge": "^5.4 || ^6.2 || ^7.0", + "symfony/twig-bundle": "^5.4 || ^6.2 || ^7.0", + "symfony/web-link": "^5.4 || ^6.2 || ^7.0" }, "type": "symfony-bundle", "extra": { "thanks": { - "name": "symfony/webpack-encore", - "url": "https://github.com/symfony/webpack-encore" + "url": "https://github.com/symfony/webpack-encore", + "name": "symfony/webpack-encore" } }, "autoload": { @@ -11817,10 +13820,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Integration with your Symfony app & Webpack Encore!", + "description": "Integration of your Symfony app with Webpack Encore", "support": { "issues": "https://github.com/symfony/webpack-encore-bundle/issues", - "source": "https://github.com/symfony/webpack-encore-bundle/tree/v1.16.1" + "source": "https://github.com/symfony/webpack-encore-bundle/tree/v2.2.0" }, "funding": [ { @@ -11836,35 +13839,32 @@ "type": "tidelift" } ], - "time": "2023-01-18T19:37:55+00:00" + "time": "2024-10-02T07:27:19+00:00" }, { "name": "symfony/yaml", - "version": "v5.4.19", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5" + "reference": "f01987f45676778b474468aa266fe2eda1f2bc7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/71c05db20cb9b54d381a28255f17580e2b7e36a5", - "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f01987f45676778b474468aa266fe2eda1f2bc7e", + "reference": "f01987f45676778b474468aa266fe2eda1f2bc7e", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.3" + "symfony/console": "<5.4" }, "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -11895,7 +13895,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.19" + "source": "https://github.com/symfony/yaml/tree/v6.4.21" }, "funding": [ { @@ -11911,20 +13911,20 @@ "type": "tidelift" } ], - "time": "2023-01-10T18:51:14+00:00" + "time": "2025-04-04T09:48:44+00:00" }, { "name": "tecnickcom/tc-lib-barcode", - "version": "1.17.19", + "version": "2.4.6", "source": { "type": "git", "url": "https://github.com/tecnickcom/tc-lib-barcode.git", - "reference": "8dbed267c44cb95a903d1149b81752ec4401dab1" + "reference": "c6130222fdc02a2d7c90682b5c2ca24a059c16f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/tc-lib-barcode/zipball/8dbed267c44cb95a903d1149b81752ec4401dab1", - "reference": "8dbed267c44cb95a903d1149b81752ec4401dab1", + "url": "https://api.github.com/repos/tecnickcom/tc-lib-barcode/zipball/c6130222fdc02a2d7c90682b5c2ca24a059c16f4", + "reference": "c6130222fdc02a2d7c90682b5c2ca24a059c16f4", "shasum": "" }, "require": { @@ -11932,16 +13932,14 @@ "ext-date": "*", "ext-gd": "*", "ext-pcre": "*", - "php": ">=5.4", - "tecnickcom/tc-lib-color": "^1.14" + "php": ">=8.1", + "tecnickcom/tc-lib-color": "^2.2" }, "require-dev": { - "pdepend/pdepend": "2.12.1", - "phploc/phploc": "7.0.2 || 6.0.2 || 5.0.0 || 4.0.1 || 3.0.1 || 2.1.5", - "phpmd/phpmd": "2.13.0", - "phpunit/phpunit": "9.5.27 || 8.5.31 || 7.5.20 || 6.5.14 || 5.7.27 || 4.8.36", - "sebastian/phpcpd": "6.0.3 || 5.0.2 || 4.1.0 || 3.0.1 || 2.0.4", - "squizlabs/php_codesniffer": "3.7.1 || 2.9.2" + "pdepend/pdepend": "2.16.2", + "phpmd/phpmd": "2.15.0", + "phpunit/phpunit": "12.1.3 || 11.5.7 || 10.5.40", + "squizlabs/php_codesniffer": "3.12.2" }, "type": "library", "autoload": { @@ -11951,7 +13949,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0" + "LGPL-3.0-or-later" ], "authors": [ { @@ -11974,6 +13972,9 @@ "EAN 13", "EAN 8", "ECC200", + "ISO IEC 15438 2006", + "ISO IEC 16022", + "ISO IEC 24778 2008", "Intelligent Mail Barcode", "Interleaved 2 of 5", "KIX", @@ -11990,6 +13991,7 @@ "USD-3", "USPS-B-3200", "USS-93", + "aztec", "barcode", "datamatrix", "pdf417", @@ -12001,41 +14003,39 @@ ], "support": { "issues": "https://github.com/tecnickcom/tc-lib-barcode/issues", - "source": "https://github.com/tecnickcom/tc-lib-barcode/tree/1.17.19" + "source": "https://github.com/tecnickcom/tc-lib-barcode/tree/2.4.6" }, "funding": [ { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tc-lib-barcode%20project", + "url": "https://www.paypal.com/donate/?hosted_button_id=NZUEC5XS8MFBJ", "type": "custom" } ], - "time": "2022-12-21T16:26:37+00:00" + "time": "2025-05-13T05:49:21+00:00" }, { "name": "tecnickcom/tc-lib-color", - "version": "1.14.18", + "version": "2.2.11", "source": { "type": "git", "url": "https://github.com/tecnickcom/tc-lib-color.git", - "reference": "c430e0b8a8847935a72bc5fcc334d1e4d029e23b" + "reference": "ead45d76932d936e065062194032bf87f7ea45f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/tc-lib-color/zipball/c430e0b8a8847935a72bc5fcc334d1e4d029e23b", - "reference": "c430e0b8a8847935a72bc5fcc334d1e4d029e23b", + "url": "https://api.github.com/repos/tecnickcom/tc-lib-color/zipball/ead45d76932d936e065062194032bf87f7ea45f8", + "reference": "ead45d76932d936e065062194032bf87f7ea45f8", "shasum": "" }, "require": { "ext-pcre": "*", - "php": ">=5.3" + "php": ">=8.1" }, "require-dev": { - "pdepend/pdepend": "2.12.1", - "phploc/phploc": "7.0.2 || 6.0.2 || 5.0.0 || 4.0.1 || 3.0.1 || 2.1.5", - "phpmd/phpmd": "2.13.0", - "phpunit/phpunit": "9.5.27 || 8.5.31 || 7.5.20 || 6.5.14 || 5.7.27 || 4.8.36", - "sebastian/phpcpd": "6.0.3 || 5.0.2 || 4.1.0 || 3.0.1 || 2.0.4", - "squizlabs/php_codesniffer": "3.7.1 || 2.9.2" + "pdepend/pdepend": "2.16.2", + "phpmd/phpmd": "2.15.0", + "phpunit/phpunit": "12.1.3 || 11.5.7 || 10.5.40", + "squizlabs/php_codesniffer": "3.12.2" }, "type": "library", "autoload": { @@ -12045,7 +14045,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0" + "LGPL-3.0-or-later" ], "authors": [ { @@ -12072,182 +14072,45 @@ ], "support": { "issues": "https://github.com/tecnickcom/tc-lib-color/issues", - "source": "https://github.com/tecnickcom/tc-lib-color/tree/1.14.18" + "source": "https://github.com/tecnickcom/tc-lib-color/tree/2.2.11" }, "funding": [ { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tc-lib-color%20project", + "url": "https://www.paypal.com/donate/?hosted_button_id=NZUEC5XS8MFBJ", "type": "custom" } ], - "time": "2022-12-21T16:25:34+00:00" - }, - { - "name": "thecodingmachine/safe", - "version": "v1.3.3", - "source": { - "type": "git", - "url": "https://github.com/thecodingmachine/safe.git", - "reference": "a8ab0876305a4cdaef31b2350fcb9811b5608dbc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/a8ab0876305a4cdaef31b2350fcb9811b5608dbc", - "reference": "a8ab0876305a4cdaef31b2350fcb9811b5608dbc", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "require-dev": { - "phpstan/phpstan": "^0.12", - "squizlabs/php_codesniffer": "^3.2", - "thecodingmachine/phpstan-strict-rules": "^0.12" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.1-dev" - } - }, - "autoload": { - "files": [ - "deprecated/apc.php", - "deprecated/libevent.php", - "deprecated/mssql.php", - "deprecated/stats.php", - "lib/special_cases.php", - "generated/apache.php", - "generated/apcu.php", - "generated/array.php", - "generated/bzip2.php", - "generated/calendar.php", - "generated/classobj.php", - "generated/com.php", - "generated/cubrid.php", - "generated/curl.php", - "generated/datetime.php", - "generated/dir.php", - "generated/eio.php", - "generated/errorfunc.php", - "generated/exec.php", - "generated/fileinfo.php", - "generated/filesystem.php", - "generated/filter.php", - "generated/fpm.php", - "generated/ftp.php", - "generated/funchand.php", - "generated/gmp.php", - "generated/gnupg.php", - "generated/hash.php", - "generated/ibase.php", - "generated/ibmDb2.php", - "generated/iconv.php", - "generated/image.php", - "generated/imap.php", - "generated/info.php", - "generated/ingres-ii.php", - "generated/inotify.php", - "generated/json.php", - "generated/ldap.php", - "generated/libxml.php", - "generated/lzf.php", - "generated/mailparse.php", - "generated/mbstring.php", - "generated/misc.php", - "generated/msql.php", - "generated/mysql.php", - "generated/mysqli.php", - "generated/mysqlndMs.php", - "generated/mysqlndQc.php", - "generated/network.php", - "generated/oci8.php", - "generated/opcache.php", - "generated/openssl.php", - "generated/outcontrol.php", - "generated/password.php", - "generated/pcntl.php", - "generated/pcre.php", - "generated/pdf.php", - "generated/pgsql.php", - "generated/posix.php", - "generated/ps.php", - "generated/pspell.php", - "generated/readline.php", - "generated/rpminfo.php", - "generated/rrd.php", - "generated/sem.php", - "generated/session.php", - "generated/shmop.php", - "generated/simplexml.php", - "generated/sockets.php", - "generated/sodium.php", - "generated/solr.php", - "generated/spl.php", - "generated/sqlsrv.php", - "generated/ssdeep.php", - "generated/ssh2.php", - "generated/stream.php", - "generated/strings.php", - "generated/swoole.php", - "generated/uodbc.php", - "generated/uopz.php", - "generated/url.php", - "generated/var.php", - "generated/xdiff.php", - "generated/xml.php", - "generated/xmlrpc.php", - "generated/yaml.php", - "generated/yaz.php", - "generated/zip.php", - "generated/zlib.php" - ], - "psr-4": { - "Safe\\": [ - "lib/", - "deprecated/", - "generated/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHP core functions that throw exceptions instead of returning FALSE on error", - "support": { - "issues": "https://github.com/thecodingmachine/safe/issues", - "source": "https://github.com/thecodingmachine/safe/tree/v1.3.3" - }, - "time": "2020-10-28T17:51:34+00:00" + "time": "2025-05-13T05:47:44+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.6", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c" + "reference": "0d72ac1c00084279c1816675284073c5a337c20d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c", - "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "php": "^5.5 || ^7.0 || ^8.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -12270,39 +14133,38 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" }, - "time": "2023-01-03T09:29:04+00:00" + "time": "2024-12-21T16:25:41+00:00" }, { "name": "twig/cssinliner-extra", - "version": "v3.5.1", + "version": "v3.21.0", "source": { "type": "git", "url": "https://github.com/twigphp/cssinliner-extra.git", - "reference": "381877765d17b0178322d68b818e0c67f9c93187" + "reference": "378d29b61d6406c456e3a4afbd15bbeea0b72ea8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/381877765d17b0178322d68b818e0c67f9c93187", - "reference": "381877765d17b0178322d68b818e0c67f9c93187", + "url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/378d29b61d6406c456e3a4afbd15bbeea0b72ea8", + "reference": "378d29b61d6406c456e3a4afbd15bbeea0b72ea8", "shasum": "" }, "require": { - "php": ">=7.1.3", + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", "tijsverkoyen/css-to-inline-styles": "^2.0", - "twig/twig": "^2.7|^3.0" + "twig/twig": "^3.13|^4.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { "Twig\\Extra\\CssInliner\\": "" }, @@ -12330,7 +14192,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/cssinliner-extra/tree/v3.5.1" + "source": "https://github.com/twigphp/cssinliner-extra/tree/v3.21.0" }, "funding": [ { @@ -12342,45 +14204,40 @@ "type": "tidelift" } ], - "time": "2023-02-08T07:44:55+00:00" + "time": "2025-01-31T20:45:36+00:00" }, { "name": "twig/extra-bundle", - "version": "v3.5.1", + "version": "v3.21.0", "source": { "type": "git", "url": "https://github.com/twigphp/twig-extra-bundle.git", - "reference": "a961e553a624eebdbd423ad5ab931497ca6d87cd" + "reference": "62d1cf47a1aa009cbd07b21045b97d3d5cb79896" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/a961e553a624eebdbd423ad5ab931497ca6d87cd", - "reference": "a961e553a624eebdbd423ad5ab931497ca6d87cd", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/62d1cf47a1aa009cbd07b21045b97d3d5cb79896", + "reference": "62d1cf47a1aa009cbd07b21045b97d3d5cb79896", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/framework-bundle": "^4.4|^5.0|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "twig/twig": "^2.7|^3.0" + "php": ">=8.1.0", + "symfony/framework-bundle": "^5.4|^6.4|^7.0", + "symfony/twig-bundle": "^5.4|^6.4|^7.0", + "twig/twig": "^3.2|^4.0" }, "require-dev": { "league/commonmark": "^1.0|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0", + "symfony/phpunit-bridge": "^6.4|^7.0", "twig/cache-extra": "^3.0", - "twig/cssinliner-extra": "^2.12|^3.0", - "twig/html-extra": "^2.12|^3.0", - "twig/inky-extra": "^2.12|^3.0", - "twig/intl-extra": "^2.12|^3.0", - "twig/markdown-extra": "^2.12|^3.0", - "twig/string-extra": "^2.12|^3.0" + "twig/cssinliner-extra": "^3.0", + "twig/html-extra": "^3.0", + "twig/inky-extra": "^3.0", + "twig/intl-extra": "^3.0", + "twig/markdown-extra": "^3.0", + "twig/string-extra": "^3.0" }, "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, "autoload": { "psr-4": { "Twig\\Extra\\TwigExtraBundle\\": "" @@ -12409,7 +14266,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.5.1" + "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.21.0" }, "funding": [ { @@ -12421,37 +14278,36 @@ "type": "tidelift" } ], - "time": "2023-02-08T07:44:55+00:00" + "time": "2025-02-19T14:29:33+00:00" }, { "name": "twig/html-extra", - "version": "v3.5.1", + "version": "v3.21.0", "source": { "type": "git", "url": "https://github.com/twigphp/html-extra.git", - "reference": "fc65dafbf8e3e319a8666e5d5437cbd404ecc496" + "reference": "5442dd707601c83b8cd4233e37bb10ab8489a90f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/html-extra/zipball/fc65dafbf8e3e319a8666e5d5437cbd404ecc496", - "reference": "fc65dafbf8e3e319a8666e5d5437cbd404ecc496", + "url": "https://api.github.com/repos/twigphp/html-extra/zipball/5442dd707601c83b8cd4233e37bb10ab8489a90f", + "reference": "5442dd707601c83b8cd4233e37bb10ab8489a90f", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/mime": "^4.4|^5.0|^6.0", - "twig/twig": "^2.7|^3.0" + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/mime": "^5.4|^6.4|^7.0", + "twig/twig": "^3.13|^4.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { "Twig\\Extra\\Html\\": "" }, @@ -12478,7 +14334,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/html-extra/tree/v3.5.1" + "source": "https://github.com/twigphp/html-extra/tree/v3.21.0" }, "funding": [ { @@ -12490,37 +14346,36 @@ "type": "tidelift" } ], - "time": "2023-02-08T07:44:55+00:00" + "time": "2025-02-19T14:29:33+00:00" }, { "name": "twig/inky-extra", - "version": "v3.5.1", + "version": "v3.21.0", "source": { "type": "git", "url": "https://github.com/twigphp/inky-extra.git", - "reference": "4efde499b99942a27e206898e55208a8692c16ca" + "reference": "aacd79d94534b4a7fd6533cb5c33c4ee97239a0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/inky-extra/zipball/4efde499b99942a27e206898e55208a8692c16ca", - "reference": "4efde499b99942a27e206898e55208a8692c16ca", + "url": "https://api.github.com/repos/twigphp/inky-extra/zipball/aacd79d94534b4a7fd6533cb5c33c4ee97239a0d", + "reference": "aacd79d94534b4a7fd6533cb5c33c4ee97239a0d", "shasum": "" }, "require": { "lorenzo/pinky": "^1.0.5", - "php": ">=7.1.3", - "twig/twig": "^2.7|^3.0" + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", + "twig/twig": "^3.13|^4.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { "Twig\\Extra\\Inky\\": "" }, @@ -12549,7 +14404,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/inky-extra/tree/v3.5.1" + "source": "https://github.com/twigphp/inky-extra/tree/v3.21.0" }, "funding": [ { @@ -12561,36 +14416,31 @@ "type": "tidelift" } ], - "time": "2023-02-08T07:44:55+00:00" + "time": "2025-01-31T20:45:36+00:00" }, { "name": "twig/intl-extra", - "version": "v3.5.1", + "version": "v3.21.0", "source": { "type": "git", "url": "https://github.com/twigphp/intl-extra.git", - "reference": "c3ebfac1624228c0556de57a34af6b7d83a1a408" + "reference": "05bc5d46b9df9e62399eae53e7c0b0633298b146" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/c3ebfac1624228c0556de57a34af6b7d83a1a408", - "reference": "c3ebfac1624228c0556de57a34af6b7d83a1a408", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/05bc5d46b9df9e62399eae53e7c0b0633298b146", + "reference": "05bc5d46b9df9e62399eae53e7c0b0633298b146", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/intl": "^4.4|^5.0|^6.0", - "twig/twig": "^2.7|^3.0" + "php": ">=8.1.0", + "symfony/intl": "^5.4|^6.4|^7.0", + "twig/twig": "^3.13|^4.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, "autoload": { "psr-4": { "Twig\\Extra\\Intl\\": "" @@ -12618,7 +14468,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/intl-extra/tree/v3.5.1" + "source": "https://github.com/twigphp/intl-extra/tree/v3.21.0" }, "funding": [ { @@ -12630,40 +14480,39 @@ "type": "tidelift" } ], - "time": "2023-02-08T07:44:55+00:00" + "time": "2025-01-31T20:45:36+00:00" }, { "name": "twig/markdown-extra", - "version": "v3.5.1", + "version": "v3.21.0", "source": { "type": "git", "url": "https://github.com/twigphp/markdown-extra.git", - "reference": "9474c89fd2787187a3809e5e964dfce2395ae5f0" + "reference": "f4616e1dd375209dacf6026f846e6b537d036ce4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/9474c89fd2787187a3809e5e964dfce2395ae5f0", - "reference": "9474c89fd2787187a3809e5e964dfce2395ae5f0", + "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/f4616e1dd375209dacf6026f846e6b537d036ce4", + "reference": "f4616e1dd375209dacf6026f846e6b537d036ce4", "shasum": "" }, "require": { - "php": ">=7.1.3", - "twig/twig": "^2.7|^3.0" + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", + "twig/twig": "^3.13|^4.0" }, "require-dev": { - "erusev/parsedown": "^1.7", + "erusev/parsedown": "dev-master as 1.x-dev", "league/commonmark": "^1.0|^2.0", "league/html-to-markdown": "^4.8|^5.0", "michelf/php-markdown": "^1.8|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { "Twig\\Extra\\Markdown\\": "" }, @@ -12691,7 +14540,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/markdown-extra/tree/v3.5.1" + "source": "https://github.com/twigphp/markdown-extra/tree/v3.21.0" }, "funding": [ { @@ -12703,38 +14552,108 @@ "type": "tidelift" } ], - "time": "2023-02-08T07:44:55+00:00" + "time": "2025-01-31T20:45:36+00:00" }, { - "name": "twig/twig", - "version": "v3.5.1", + "name": "twig/string-extra", + "version": "v3.21.0", "source": { "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15" + "url": "https://github.com/twigphp/string-extra.git", + "reference": "4b3337544ac8f76c280def94e32b53acfaec0589" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15", - "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15", + "url": "https://api.github.com/repos/twigphp/string-extra/zipball/4b3337544ac8f76c280def94e32b53acfaec0589", + "reference": "4b3337544ac8f76c280def94e32b53acfaec0589", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1.0", + "symfony/string": "^5.4|^6.4|^7.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^3.13|^4.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Symfony String", + "homepage": "https://twig.symfony.com", + "keywords": [ + "html", + "string", + "twig", + "unicode" + ], + "support": { + "source": "https://github.com/twigphp/string-extra/tree/v3.21.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2025-01-31T20:45:36+00:00" + }, + { + "name": "twig/twig", + "version": "v3.21.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "shasum": "" + }, + "require": { + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "phpstan/phpstan": "^2.0", + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -12767,7 +14686,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.5.1" + "source": "https://github.com/twigphp/Twig/tree/v3.21.1" }, "funding": [ { @@ -12779,7 +14698,7 @@ "type": "tidelift" } ], - "time": "2023-02-08T07:49:20+00:00" + "time": "2025-05-03T07:21:55+00:00" }, { "name": "ua-parser/uap-php", @@ -12846,25 +14765,43 @@ }, { "name": "web-auth/cose-lib", - "version": "v3.3.12", + "version": "4.4.0", "source": { "type": "git", "url": "https://github.com/web-auth/cose-lib.git", - "reference": "efa6ec2ba4e840bc1316a493973c9916028afeeb" + "reference": "2166016e48e0214f4f63320a7758a9386d14c92a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/efa6ec2ba4e840bc1316a493973c9916028afeeb", - "reference": "efa6ec2ba4e840bc1316a493973c9916028afeeb", + "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/2166016e48e0214f4f63320a7758a9386d14c92a", + "reference": "2166016e48e0214f4f63320a7758a9386d14c92a", "shasum": "" }, "require": { - "beberlei/assert": "^3.2", + "brick/math": "^0.9|^0.10|^0.11|^0.12", "ext-json": "*", - "ext-mbstring": "*", "ext-openssl": "*", - "fgrosse/phpasn1": "^2.1", - "php": ">=7.2" + "php": ">=8.1", + "spomky-labs/pki-framework": "^1.0" + }, + "require-dev": { + "ekino/phpstan-banned-code": "^1.0", + "infection/infection": "^0.29", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "^1.7", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.2", + "phpunit/phpunit": "^10.1|^11.0", + "qossmic/deptrac": "^2.0", + "rector/rector": "^1.0", + "symfony/phpunit-bridge": "^6.4|^7.0", + "symplify/easy-coding-standard": "^12.0" + }, + "suggest": { + "ext-bcmath": "For better performance, please install either GMP (recommended) or BCMath extension", + "ext-gmp": "For better performance, please install either GMP (recommended) or BCMath extension" }, "type": "library", "autoload": { @@ -12893,7 +14830,8 @@ "RFC8152" ], "support": { - "source": "https://github.com/web-auth/cose-lib/tree/v3.3.12" + "issues": "https://github.com/web-auth/cose-lib/issues", + "source": "https://github.com/web-auth/cose-lib/tree/4.4.0" }, "funding": [ { @@ -12905,118 +14843,57 @@ "type": "patreon" } ], - "time": "2021-12-04T12:13:35+00:00" - }, - { - "name": "web-auth/metadata-service", - "version": "v3.3.12", - "source": { - "type": "git", - "url": "https://github.com/web-auth/webauthn-metadata-service.git", - "reference": "ef40d2b7b68c4964247d13fab52e2fa8dbd65246" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-metadata-service/zipball/ef40d2b7b68c4964247d13fab52e2fa8dbd65246", - "reference": "ef40d2b7b68c4964247d13fab52e2fa8dbd65246", - "shasum": "" - }, - "require": { - "beberlei/assert": "^3.2", - "ext-json": "*", - "league/uri": "^6.0", - "php": ">=7.2", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", - "psr/log": "^1.1" - }, - "suggest": { - "web-token/jwt-key-mgmt": "Mandatory for fetching Metadata Statement from distant sources", - "web-token/jwt-signature-algorithm-ecdsa": "Mandatory for fetching Metadata Statement from distant sources" - }, - "type": "library", - "autoload": { - "psr-4": { - "Webauthn\\MetadataService\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Florent Morselli", - "homepage": "https://github.com/Spomky" - }, - { - "name": "All contributors", - "homepage": "https://github.com/web-auth/metadata-service/contributors" - } - ], - "description": "Metadata Service for FIDO2/Webauthn", - "homepage": "https://github.com/web-auth", - "keywords": [ - "FIDO2", - "fido", - "webauthn" - ], - "support": { - "source": "https://github.com/web-auth/webauthn-metadata-service/tree/v3.3.12" - }, - "funding": [ - { - "url": "https://github.com/Spomky", - "type": "github" - }, - { - "url": "https://www.patreon.com/FlorentMorselli", - "type": "patreon" - } - ], - "time": "2021-11-21T11:14:31+00:00" + "time": "2024-07-18T08:47:32+00:00" }, { "name": "web-auth/webauthn-lib", - "version": "v3.3.12", + "version": "4.9.2", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-lib.git", - "reference": "5ef9b21c8e9f8a817e524ac93290d08a9f065b33" + "reference": "008b25171c27cf4813420d0de31cc059bcc71f1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/5ef9b21c8e9f8a817e524ac93290d08a9f065b33", - "reference": "5ef9b21c8e9f8a817e524ac93290d08a9f065b33", + "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/008b25171c27cf4813420d0de31cc059bcc71f1a", + "reference": "008b25171c27cf4813420d0de31cc059bcc71f1a", "shasum": "" }, "require": { - "beberlei/assert": "^3.2", "ext-json": "*", "ext-mbstring": "*", "ext-openssl": "*", - "fgrosse/phpasn1": "^2.1", - "php": ">=7.2", + "lcobucci/clock": "^2.2|^3.0", + "paragonie/constant_time_encoding": "^2.6|^3.0", + "php": ">=8.1", + "psr/clock": "^1.0", + "psr/event-dispatcher": "^1.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "psr/log": "^1.1", - "ramsey/uuid": "^3.8|^4.0", - "spomky-labs/base64url": "^2.0", - "spomky-labs/cbor-php": "^1.0|^2.0", - "symfony/process": "^3.0|^4.0|^5.0", - "thecodingmachine/safe": "^1.1", - "web-auth/cose-lib": "self.version", - "web-auth/metadata-service": "self.version" + "psr/log": "^1.0|^2.0|^3.0", + "spomky-labs/cbor-php": "^3.0", + "spomky-labs/pki-framework": "^1.0", + "symfony/deprecation-contracts": "^3.2", + "symfony/uid": "^6.1|^7.0", + "web-auth/cose-lib": "^4.2.3" }, "suggest": { + "phpdocumentor/reflection-docblock": "As of 4.5.x, the phpdocumentor/reflection-docblock component will become mandatory for converting objects such as the Metadata Statement", + "psr/clock-implementation": "As of 4.5.x, the PSR Clock implementation will replace lcobucci/clock", "psr/log-implementation": "Recommended to receive logs from the library", - "web-token/jwt-key-mgmt": "Mandatory for the AndroidSafetyNet Attestation Statement support", - "web-token/jwt-signature-algorithm-ecdsa": "Recommended for the AndroidSafetyNet Attestation Statement support", - "web-token/jwt-signature-algorithm-eddsa": "Recommended for the AndroidSafetyNet Attestation Statement support", - "web-token/jwt-signature-algorithm-rsa": "Mandatory for the AndroidSafetyNet Attestation Statement support" + "symfony/event-dispatcher": "Recommended to use dispatched events", + "symfony/property-access": "As of 4.5.x, the symfony/serializer component will become mandatory for converting objects such as the Metadata Statement", + "symfony/property-info": "As of 4.5.x, the symfony/serializer component will become mandatory for converting objects such as the Metadata Statement", + "symfony/serializer": "As of 4.5.x, the symfony/serializer component will become mandatory for converting objects such as the Metadata Statement", + "web-token/jwt-library": "Mandatory for fetching Metadata Statement from distant sources" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/web-auth/webauthn-framework", + "name": "web-auth/webauthn-framework" + } + }, "autoload": { "psr-4": { "Webauthn\\": "src/" @@ -13044,7 +14921,7 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-lib/tree/v3.3.12" + "source": "https://github.com/web-auth/webauthn-lib/tree/4.9.2" }, "funding": [ { @@ -13056,31 +14933,49 @@ "type": "patreon" } ], - "time": "2022-02-18T07:13:44+00:00" + "time": "2025-01-04T09:47:58+00:00" }, { "name": "web-auth/webauthn-symfony-bundle", - "version": "v3.3.12", + "version": "4.9.2", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-symfony-bundle.git", - "reference": "15f2091dc351f190d27a377a0dbbc117e6be5329" + "reference": "80aa16fa6f16ab8f017a4108ffcd2ecc12264c07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-symfony-bundle/zipball/15f2091dc351f190d27a377a0dbbc117e6be5329", - "reference": "15f2091dc351f190d27a377a0dbbc117e6be5329", + "url": "https://api.github.com/repos/web-auth/webauthn-symfony-bundle/zipball/80aa16fa6f16ab8f017a4108ffcd2ecc12264c07", + "reference": "80aa16fa6f16ab8f017a4108ffcd2ecc12264c07", "shasum": "" }, "require": { - "spomky-labs/cbor-bundle": "^2.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/framework-bundle": "^4.4|^5.0", + "nyholm/psr7": "^1.5", + "php": ">=8.1", + "phpdocumentor/reflection-docblock": "^5.3", + "psr/event-dispatcher": "^1.0", + "symfony/config": "^6.1|^7.0", + "symfony/dependency-injection": "^6.1|^7.0", + "symfony/framework-bundle": "^6.1|^7.0", + "symfony/http-client": "^6.1|^7.0", + "symfony/property-access": "^6.1|^7.0", + "symfony/property-info": "^6.1|^7.0", + "symfony/psr-http-message-bridge": "^2.1|^6.1|^7.0", + "symfony/security-bundle": "^6.1|^7.0", + "symfony/security-core": "^6.1|^7.0", + "symfony/security-http": "^6.1|^7.0", + "symfony/serializer": "^6.1|^7.0", + "symfony/validator": "^6.1|^7.0", "web-auth/webauthn-lib": "self.version", - "web-token/jwt-signature": "^2.0.9" + "web-token/jwt-library": "^3.3|^4.0" }, "type": "symfony-bundle", + "extra": { + "thanks": { + "url": "https://github.com/web-auth/webauthn-framework", + "name": "web-auth/webauthn-framework" + } + }, "autoload": { "psr-4": { "Webauthn\\Bundle\\": "src/" @@ -13104,11 +14999,14 @@ "homepage": "https://github.com/web-auth", "keywords": [ "FIDO2", + "bundle", "fido", + "symfony", + "symfony-bundle", "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-symfony-bundle/tree/v3.3.12" + "source": "https://github.com/web-auth/webauthn-symfony-bundle/tree/4.9.2" }, "funding": [ { @@ -13120,37 +15018,54 @@ "type": "patreon" } ], - "time": "2021-11-21T11:14:31+00:00" + "time": "2025-01-04T09:38:56+00:00" }, { - "name": "web-token/jwt-core", - "version": "v2.2.11", + "name": "web-token/jwt-library", + "version": "3.4.8", "source": { "type": "git", - "url": "https://github.com/web-token/jwt-core.git", - "reference": "53beb6f6c1eec4fa93c1c3e5d9e5701e71fa1678" + "url": "https://github.com/web-token/jwt-library.git", + "reference": "92445671cc788fa5f639898a67c06f9fd0bf491f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-token/jwt-core/zipball/53beb6f6c1eec4fa93c1c3e5d9e5701e71fa1678", - "reference": "53beb6f6c1eec4fa93c1c3e5d9e5701e71fa1678", + "url": "https://api.github.com/repos/web-token/jwt-library/zipball/92445671cc788fa5f639898a67c06f9fd0bf491f", + "reference": "92445671cc788fa5f639898a67c06f9fd0bf491f", "shasum": "" }, "require": { - "brick/math": "^0.8.17|^0.9", + "brick/math": "^0.9|^0.10|^0.11|^0.12", "ext-json": "*", "ext-mbstring": "*", - "fgrosse/phpasn1": "^2.0", - "php": ">=7.2", - "spomky-labs/base64url": "^1.0|^2.0" + "paragonie/constant_time_encoding": "^2.6|^3.0", + "paragonie/sodium_compat": "^1.20|^2.0", + "php": ">=8.1", + "psr/cache": "^2.0|^3.0", + "psr/clock": "^1.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "spomky-labs/pki-framework": "^1.2.1", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.12" }, "conflict": { "spomky-labs/jose": "*" }, + "suggest": { + "ext-bcmath": "GMP or BCMath is highly recommended to improve the library performance", + "ext-gmp": "GMP or BCMath is highly recommended to improve the library performance", + "ext-openssl": "For key management (creation, optimization, etc.) and some algorithms (AES, RSA, ECDSA, etc.)", + "ext-sodium": "Sodium is required for OKP key creation, EdDSA signature algorithm and ECDH-ES key encryption with OKP keys", + "paragonie/sodium_compat": "Sodium is required for OKP key creation, EdDSA signature algorithm and ECDH-ES key encryption with OKP keys", + "spomky-labs/aes-key-wrap": "For all Key Wrapping algorithms (A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW, A256GCMKW, PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW...)", + "symfony/http-client": "To enable JKU/X5U support." + }, "type": "library", "autoload": { "psr-4": { - "Jose\\Component\\Core\\": "" + "Jose\\Component\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -13167,7 +15082,7 @@ "homepage": "https://github.com/web-token/jwt-framework/contributors" } ], - "description": "Core component of the JWT Framework.", + "description": "JWT library", "homepage": "https://github.com/web-token", "keywords": [ "JOSE", @@ -13188,91 +15103,20 @@ "symfony" ], "support": { - "source": "https://github.com/web-token/jwt-core/tree/v2.2.11" + "issues": "https://github.com/web-token/jwt-library/issues", + "source": "https://github.com/web-token/jwt-library/tree/3.4.8" }, "funding": [ { - "url": "https://www.patreon.com/FlorentMorselli", - "type": "patreon" - } - ], - "time": "2021-03-17T14:55:52+00:00" - }, - { - "name": "web-token/jwt-signature", - "version": "v2.2.11", - "source": { - "type": "git", - "url": "https://github.com/web-token/jwt-signature.git", - "reference": "015b59aaf3b6e8fb9f5bd1338845b7464c7d8103" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/web-token/jwt-signature/zipball/015b59aaf3b6e8fb9f5bd1338845b7464c7d8103", - "reference": "015b59aaf3b6e8fb9f5bd1338845b7464c7d8103", - "shasum": "" - }, - "require": { - "web-token/jwt-core": "^2.1" - }, - "suggest": { - "web-token/jwt-signature-algorithm-ecdsa": "ECDSA Based Signature Algorithms", - "web-token/jwt-signature-algorithm-eddsa": "EdDSA Based Signature Algorithms", - "web-token/jwt-signature-algorithm-experimental": "Experimental Signature Algorithms", - "web-token/jwt-signature-algorithm-hmac": "HMAC Based Signature Algorithms", - "web-token/jwt-signature-algorithm-none": "None Signature Algorithm", - "web-token/jwt-signature-algorithm-rsa": "RSA Based Signature Algorithms" - }, - "type": "library", - "autoload": { - "psr-4": { - "Jose\\Component\\Signature\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Florent Morselli", - "homepage": "https://github.com/Spomky" + "url": "https://github.com/Spomky", + "type": "github" }, - { - "name": "All contributors", - "homepage": "https://github.com/web-token/jwt-signature/contributors" - } - ], - "description": "Signature component of the JWT Framework.", - "homepage": "https://github.com/web-token", - "keywords": [ - "JOSE", - "JWE", - "JWK", - "JWKSet", - "JWS", - "Jot", - "RFC7515", - "RFC7516", - "RFC7517", - "RFC7518", - "RFC7519", - "RFC7520", - "bundle", - "jwa", - "jwt", - "symfony" - ], - "support": { - "source": "https://github.com/web-token/jwt-signature/tree/v2.2.11" - }, - "funding": [ { "url": "https://www.patreon.com/FlorentMorselli", "type": "patreon" } ], - "time": "2021-03-01T19:55:28+00:00" + "time": "2025-05-07T09:11:18+00:00" }, { "name": "webmozart/assert", @@ -13331,429 +15175,100 @@ "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "willdurand/negotiation", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/willdurand/Negotiation.git", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Negotiation\\": "src/Negotiation" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "William Durand", + "email": "will+git@drnd.me" + } + ], + "description": "Content Negotiation tools for PHP provided as a standalone library.", + "homepage": "http://williamdurand.fr/Negotiation/", + "keywords": [ + "accept", + "content", + "format", + "header", + "negotiation" + ], + "support": { + "issues": "https://github.com/willdurand/Negotiation/issues", + "source": "https://github.com/willdurand/Negotiation/tree/3.1.0" + }, + "time": "2022-01-30T20:08:53+00:00" } ], "packages-dev": [ - { - "name": "amphp/amp", - "version": "v2.6.2", - "source": { - "type": "git", - "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^7 | ^8 | ^9", - "psalm/phar": "^3.11@dev", - "react/promise": "^2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "files": [ - "lib/functions.php", - "lib/Internal/functions.php" - ], - "psr-4": { - "Amp\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A non-blocking concurrency framework for PHP applications.", - "homepage": "https://amphp.org/amp", - "keywords": [ - "async", - "asynchronous", - "awaitable", - "concurrency", - "event", - "event-loop", - "future", - "non-blocking", - "promise" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2022-02-20T17:52:18+00:00" - }, - { - "name": "amphp/byte-stream", - "version": "v1.8.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/byte-stream.git", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.4", - "friendsofphp/php-cs-fixer": "^2.3", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^6 || ^7 || ^8", - "psalm/phar": "^3.11.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "files": [ - "lib/functions.php" - ], - "psr-4": { - "Amp\\ByteStream\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "http://amphp.org/byte-stream", - "keywords": [ - "amp", - "amphp", - "async", - "io", - "non-blocking", - "stream" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2021-03-30T17:13:30+00:00" - }, - { - "name": "composer/pcre", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-11-17T09:50:14+00:00" - }, - { - "name": "composer/semver", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-04-01T19:23:25+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", - "shasum": "" - }, - "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-02-25T21:32:43+00:00" - }, { "name": "dama/doctrine-test-bundle", - "version": "v7.2.1", + "version": "v8.2.2", "source": { "type": "git", "url": "https://github.com/dmaicher/doctrine-test-bundle.git", - "reference": "175b47153609a369117d97d36049b8a8c3b69dc1" + "reference": "eefe54fdf00d910f808efea9cfce9cc261064a0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dmaicher/doctrine-test-bundle/zipball/175b47153609a369117d97d36049b8a8c3b69dc1", - "reference": "175b47153609a369117d97d36049b8a8c3b69dc1", + "url": "https://api.github.com/repos/dmaicher/doctrine-test-bundle/zipball/eefe54fdf00d910f808efea9cfce9cc261064a0a", + "reference": "eefe54fdf00d910f808efea9cfce9cc261064a0a", "shasum": "" }, "require": { - "doctrine/dbal": "^3.3", - "doctrine/doctrine-bundle": "^2.2.2", - "ext-json": "*", - "php": "^7.3 || ^8.0", + "doctrine/dbal": "^3.3 || ^4.0", + "doctrine/doctrine-bundle": "^2.11.0", + "php": "^7.4 || ^8.0", "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^5.4 || ^6.0", - "symfony/framework-bundle": "^5.4 || ^6.0" + "symfony/cache": "^5.4 || ^6.3 || ^7.0", + "symfony/framework-bundle": "^5.4 || ^6.3 || ^7.0" }, "require-dev": { "behat/behat": "^3.0", - "doctrine/cache": "^1.12", - "phpstan/phpstan": "^1.2", - "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0", - "symfony/phpunit-bridge": "^6.0", - "symfony/process": "^5.4 || ^6.0", - "symfony/yaml": "^5.4 || ^6.0" + "friendsofphp/php-cs-fixer": "^3.27", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0 || ^11.0", + "symfony/phpunit-bridge": "^7.2", + "symfony/process": "^5.4 || ^6.3 || ^7.0", + "symfony/yaml": "^5.4 || ^6.3 || ^7.0" }, "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "7.x-dev" + "dev-master": "8.x-dev" } }, "autoload": { @@ -13777,170 +15292,55 @@ "isolation", "performance", "symfony", + "testing", "tests" ], "support": { "issues": "https://github.com/dmaicher/doctrine-test-bundle/issues", - "source": "https://github.com/dmaicher/doctrine-test-bundle/tree/v7.2.1" + "source": "https://github.com/dmaicher/doctrine-test-bundle/tree/v8.2.2" }, - "time": "2023-02-07T10:02:27+00:00" - }, - { - "name": "dnoegel/php-xdg-base-dir", - "version": "v0.1.1", - "source": { - "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "implementation of xdg base directory specification for php", - "support": { - "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", - "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" - }, - "time": "2019-12-04T15:06:13+00:00" - }, - { - "name": "doctrine/data-fixtures", - "version": "1.6.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/data-fixtures.git", - "reference": "c27821d038e64f1bfc852a94064d65d2a75ad01f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/c27821d038e64f1bfc852a94064d65d2a75ad01f", - "reference": "c27821d038e64f1bfc852a94064d65d2a75ad01f", - "shasum": "" - }, - "require": { - "doctrine/persistence": "^1.3.3|^2.0|^3.0", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "doctrine/dbal": "<2.13", - "doctrine/orm": "<2.12", - "doctrine/phpcr-odm": "<1.3.0" - }, - "require-dev": { - "doctrine/coding-standard": "^10.0", - "doctrine/dbal": "^2.13 || ^3.0", - "doctrine/deprecations": "^1.0", - "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", - "doctrine/orm": "^2.12", - "ext-sqlite3": "*", - "phpstan/phpstan": "^1.5", - "phpunit/phpunit": "^8.5 || ^9.5", - "symfony/cache": "^5.0 || ^6.0", - "vimeo/psalm": "^4.10" - }, - "suggest": { - "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", - "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", - "doctrine/orm": "For loading ORM fixtures", - "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - } - ], - "description": "Data Fixtures for all Doctrine Object Managers", - "homepage": "https://www.doctrine-project.org", - "keywords": [ - "database" - ], - "support": { - "issues": "https://github.com/doctrine/data-fixtures/issues", - "source": "https://github.com/doctrine/data-fixtures/tree/1.6.3" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures", - "type": "tidelift" - } - ], - "time": "2023-01-07T15:10:22+00:00" + "time": "2025-02-04T14:37:36+00:00" }, { "name": "doctrine/doctrine-fixtures-bundle", - "version": "3.4.2", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineFixturesBundle.git", - "reference": "601988c5b46dbd20a0f886f967210aba378a6fd5" + "reference": "a06db6b81ff20a2980bf92063d80c013bb8b4b7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/601988c5b46dbd20a0f886f967210aba378a6fd5", - "reference": "601988c5b46dbd20a0f886f967210aba378a6fd5", + "url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/a06db6b81ff20a2980bf92063d80c013bb8b4b7c", + "reference": "a06db6b81ff20a2980bf92063d80c013bb8b4b7c", "shasum": "" }, "require": { - "doctrine/data-fixtures": "^1.3", - "doctrine/doctrine-bundle": "^1.11|^2.0", - "doctrine/orm": "^2.6.0", - "doctrine/persistence": "^1.3.7|^2.0|^3.0", - "php": "^7.1 || ^8.0", - "symfony/config": "^3.4|^4.3|^5.0|^6.0", - "symfony/console": "^3.4|^4.3|^5.0|^6.0", - "symfony/dependency-injection": "^3.4.47|^4.3|^5.0|^6.0", - "symfony/doctrine-bridge": "^3.4|^4.1|^5.0|^6.0", - "symfony/http-kernel": "^3.4|^4.3|^5.0|^6.0" + "doctrine/data-fixtures": "^2.0", + "doctrine/doctrine-bundle": "^2.2", + "doctrine/orm": "^2.14.0 || ^3.0", + "doctrine/persistence": "^2.4 || ^3.0 || ^4.0", + "php": "^8.1", + "psr/log": "^2 || ^3", + "symfony/config": "^6.4 || ^7.0", + "symfony/console": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/deprecation-contracts": "^2.1 || ^3", + "symfony/doctrine-bridge": "^6.4.16 || ^7.1.9", + "symfony/http-kernel": "^6.4 || ^7.0" + }, + "conflict": { + "doctrine/dbal": "< 3" }, "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "^1.4.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", - "symfony/phpunit-bridge": "^6.0.8", - "vimeo/psalm": "^4.22" + "doctrine/coding-standard": "13.0.0", + "phpstan/phpstan": "2.1.11", + "phpunit/phpunit": "^10.5.38 || 11.4.14" }, "type": "symfony-bundle", "autoload": { "psr-4": { - "Doctrine\\Bundle\\FixturesBundle\\": "" + "Doctrine\\Bundle\\FixturesBundle\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -13969,7 +15369,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineFixturesBundle/issues", - "source": "https://github.com/doctrine/DoctrineFixturesBundle/tree/3.4.2" + "source": "https://github.com/doctrine/DoctrineFixturesBundle/tree/4.1.0" }, "funding": [ { @@ -13985,43 +15385,43 @@ "type": "tidelift" } ], - "time": "2022-04-28T17:58:29+00:00" + "time": "2025-03-26T10:56:26+00:00" }, { "name": "ekino/phpstan-banned-code", - "version": "v1.0.0", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/ekino/phpstan-banned-code.git", - "reference": "4f0d7c8a0c9f5d222ffc24234aa6c5b3b71bf4c3" + "reference": "27122aa1783d6521e500c0c397c53244cfbde26f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ekino/phpstan-banned-code/zipball/4f0d7c8a0c9f5d222ffc24234aa6c5b3b71bf4c3", - "reference": "4f0d7c8a0c9f5d222ffc24234aa6c5b3b71bf4c3", + "url": "https://api.github.com/repos/ekino/phpstan-banned-code/zipball/27122aa1783d6521e500c0c397c53244cfbde26f", + "reference": "27122aa1783d6521e500c0c397c53244cfbde26f", "shasum": "" }, "require": { - "php": "^7.3 || ^8.0", - "phpstan/phpstan": "^1.0" + "php": "^8.1", + "phpstan/phpstan": "^2.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.6", "friendsofphp/php-cs-fixer": "^3.0", "nikic/php-parser": "^4.3", - "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^9.5", "symfony/var-dumper": "^5.0" }, "type": "phpstan-extension", "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - }, "phpstan": { "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-master": "1.0-dev" } }, "autoload": { @@ -14044,147 +15444,50 @@ "homepage": "https://github.com/ekino/phpstan-banned-code", "keywords": [ "PHPStan", - "code quality" + "code quality", + "static analysis" ], "support": { "issues": "https://github.com/ekino/phpstan-banned-code/issues", - "source": "https://github.com/ekino/phpstan-banned-code/tree/v1.0.0" + "source": "https://github.com/ekino/phpstan-banned-code/tree/v3.0.0" }, - "time": "2021-11-02T08:37:34+00:00" + "time": "2024-11-13T09:57:22+00:00" }, { - "name": "felixfbecker/advanced-json-rpc", - "version": "v3.2.1", + "name": "jbtronics/translation-editor-bundle", + "version": "v1.1.1", "source": { "type": "git", - "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + "url": "https://github.com/jbtronics/translation-editor-bundle.git", + "reference": "fa003c38f3f61060a368734b91aeb40d788b0825" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "url": "https://api.github.com/repos/jbtronics/translation-editor-bundle/zipball/fa003c38f3f61060a368734b91aeb40d788b0825", + "reference": "fa003c38f3f61060a368734b91aeb40d788b0825", "shasum": "" }, "require": { - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "php": "^7.1 || ^8.0", - "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + "ext-json": "*", + "php": "^8.1", + "symfony/deprecation-contracts": "^3.4", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/translation": "^7.0|^6.4", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/twig-bundle": "^7.0|^6.4", + "symfony/web-profiler-bundle": "^7.0|^6.4" }, "require-dev": { - "phpunit/phpunit": "^7.0 || ^8.0" + "ekino/phpstan-banned-code": "^1.0", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-strict-rules": "^1.5", + "roave/security-advisories": "dev-latest" }, - "type": "library", + "type": "symfony-bundle", "autoload": { "psr-4": { - "AdvancedJsonRpc\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], - "description": "A more advanced JSONRPC implementation", - "support": { - "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", - "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" - }, - "time": "2021-06-11T22:34:44+00:00" - }, - { - "name": "felixfbecker/language-server-protocol", - "version": "v1.5.2", - "source": { - "type": "git", - "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpstan/phpstan": "*", - "squizlabs/php_codesniffer": "^3.1", - "vimeo/psalm": "^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "LanguageServerProtocol\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], - "description": "PHP classes for the Language Server Protocol", - "keywords": [ - "language", - "microsoft", - "php", - "server" - ], - "support": { - "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" - }, - "time": "2022-03-02T22:36:06+00:00" - }, - { - "name": "fidry/cpu-core-counter", - "version": "0.4.1", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/79261cc280aded96d098e1b0e0ba0c4881b432c2", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "fidry/makefile": "^0.2.0", - "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^9.5.26 || ^8.5.31", - "theofidry/php-cs-fixer-config": "^1.0", - "webmozarts/strict-phpunit": "^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" + "Jbtronics\\TranslationEditorBundle\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14193,96 +15496,287 @@ ], "authors": [ { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" + "name": "Jan Böhmer", + "email": "mail@jan-boehmer.de" } ], - "description": "Tiny utility to get the number of CPU cores.", + "description": "A symfony bundle to allow editing translations in the symfony profiler", "keywords": [ - "CPU", - "core" + "profiler", + "symfony", + "symfony-bundle", + "translations" ], "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/0.4.1" + "issues": "https://github.com/jbtronics/translation-editor-bundle/issues", + "source": "https://github.com/jbtronics/translation-editor-bundle/tree/v1.1.1" }, "funding": [ { - "url": "https://github.com/theofidry", + "url": "https://www.paypal.me/do9jhb", + "type": "custom" + }, + { + "url": "https://github.com/jbtronics", "type": "github" } ], - "time": "2022-12-16T22:01:02+00:00" + "time": "2025-03-29T15:14:31+00:00" }, { - "name": "netresearch/jsonmapper", - "version": "v4.1.0", + "name": "myclabs/deep-copy", + "version": "1.13.1", "source": { "type": "git", - "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", "shasum": "" }, "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=7.1" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", - "squizlabs/php_codesniffer": "~3.5" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { - "psr-0": { - "JsonMapper": "src/" + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "OSL-3.0" + "MIT" ], - "authors": [ + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + }, + "funding": [ { - "name": "Christian Weiske", - "email": "cweiske@cweiske.de", - "homepage": "http://github.com/cweiske/jsonmapper/", - "role": "Developer" + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" } ], - "description": "Map nested JSON structures onto PHP classes", - "support": { - "email": "cweiske@cweiske.de", - "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" - }, - "time": "2022-12-08T20:46:14+00:00" + "time": "2025-04-29T12:36:36+00:00" }, { - "name": "phpstan/extension-installer", - "version": "1.2.0", + "name": "nikic/php-parser", + "version": "v5.4.0", "source": { "type": "git", - "url": "https://github.com/phpstan/extension-installer.git", - "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", - "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + }, + "time": "2024-12-30T11:07:19+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpstan/extension-installer", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936", "shasum": "" }, "require": { "composer-plugin-api": "^2.0", "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.8.0" + "phpstan/phpstan": "^1.9.0 || ^2.0" }, "require-dev": { "composer/composer": "^2.0", @@ -14303,28 +15797,32 @@ "MIT" ], "description": "Composer plugin for automatic installation of PHPStan extensions", + "keywords": [ + "dev", + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/extension-installer/issues", - "source": "https://github.com/phpstan/extension-installer/tree/1.2.0" + "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" }, - "time": "2022-10-17T12:59:16+00:00" + "time": "2024-09-04T20:21:43+00:00" }, { "name": "phpstan/phpstan", - "version": "1.9.17", + "version": "2.1.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "204e459e7822f2c586463029f5ecec31bb45a1f2" + "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/204e459e7822f2c586463029f5ecec31bb45a1f2", - "reference": "204e459e7822f2c586463029f5ecec31bb45a1f2", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", + "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -14349,8 +15847,11 @@ "static analysis" ], "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.9.17" + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { @@ -14360,31 +15861,27 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2023-02-08T12:25:00+00:00" + "time": "2025-05-16T09:40:10+00:00" }, { "name": "phpstan/phpstan-doctrine", - "version": "1.3.32", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "4534559a8c08ab3648c6fa09289478780e190ae7" + "reference": "4497663eb17b9d29211830df5aceaa3a4d256a35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/4534559a8c08ab3648c6fa09289478780e190ae7", - "reference": "4534559a8c08ab3648c6fa09289478780e190ae7", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/4497663eb17b9d29211830df5aceaa3a4d256a35", + "reference": "4497663eb17b9d29211830df5aceaa3a4d256a35", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.9.11" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.1.13" }, "conflict": { "doctrine/collections": "<1.0", @@ -14394,24 +15891,26 @@ "doctrine/persistence": "<1.3" }, "require-dev": { + "cache/array-adapter": "^1.1", "composer/semver": "^3.3.2", - "doctrine/annotations": "^1.11.0", - "doctrine/collections": "^1.6", + "cweagans/composer-patches": "^1.7.3", + "doctrine/annotations": "^2.0", + "doctrine/collections": "^1.6 || ^2.1", "doctrine/common": "^2.7 || ^3.0", - "doctrine/dbal": "^2.13.8 || ^3.3.3", - "doctrine/lexer": "^1.2.1", - "doctrine/mongodb-odm": "^1.3 || ^2.1", - "doctrine/orm": "^2.11.0", - "doctrine/persistence": "^1.3.8 || ^2.2.1", + "doctrine/dbal": "^3.3.8", + "doctrine/lexer": "^2.0 || ^3.0", + "doctrine/mongodb-odm": "^2.4.3", + "doctrine/orm": "^2.16.0", + "doctrine/persistence": "^2.2.1 || ^3.2", "gedmo/doctrine-extensions": "^3.8", "nesbot/carbon": "^2.49", - "nikic/php-parser": "^4.13.2", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5.10", - "ramsey/uuid-doctrine": "^1.5.0", - "symfony/cache": "^4.4.35" + "phpstan/phpstan-deprecation-rules": "^2.0.2", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6.20", + "ramsey/uuid": "^4.2", + "symfony/cache": "^5.4" }, "type": "phpstan-extension", "extra": { @@ -14434,39 +15933,86 @@ "description": "Doctrine extensions for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.32" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.3" }, - "time": "2023-01-12T13:39:08+00:00" + "time": "2025-05-05T15:28:52+00:00" }, { - "name": "phpstan/phpstan-symfony", - "version": "1.2.23", + "name": "phpstan/phpstan-strict-rules", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "8a8d0538ca943b20beda7e9799e14fb3683262d4" + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/8a8d0538ca943b20beda7e9799e14fb3683262d4", - "reference": "8a8d0538ca943b20beda7e9799e14fb3683262d4", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4" + }, + "time": "2025-03-18T11:42:40+00:00" + }, + { + "name": "phpstan/phpstan-symfony", + "version": "2.0.6", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-symfony.git", + "reference": "5005288e07583546ea00b52de4a9ac412eb869d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/5005288e07583546ea00b52de4a9ac412eb869d7", + "reference": "5005288e07583546ea00b52de4a9ac412eb869d7", "shasum": "" }, "require": { "ext-simplexml": "*", - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.9.4" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.1.13" }, "conflict": { "symfony/framework-bundle": "<3.0" }, "require-dev": { - "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "psr/container": "1.0 || 1.1.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "psr/container": "1.1.2", "symfony/config": "^5.4 || ^6.1", "symfony/console": "^5.4 || ^6.1", "symfony/dependency-injection": "^5.4 || ^6.1", @@ -14475,7 +16021,8 @@ "symfony/http-foundation": "^5.4 || ^6.1", "symfony/messenger": "^5.4", "symfony/polyfill-php80": "^1.24", - "symfony/serializer": "^5.4" + "symfony/serializer": "^5.4", + "symfony/service-contracts": "^2.2.0" }, "type": "phpstan-extension", "extra": { @@ -14505,74 +16052,498 @@ "description": "Symfony Framework extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.23" + "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.6" }, - "time": "2023-02-06T10:42:02+00:00" + "time": "2025-05-14T07:00:05+00:00" }, { - "name": "psalm/plugin-symfony", - "version": "v5.0.1", + "name": "phpunit/php-code-coverage", + "version": "9.2.32", "source": { "type": "git", - "url": "https://github.com/psalm/psalm-plugin-symfony.git", - "reference": "e9900d2f0186564a30934a2ebcf60481d916ebf0" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-symfony/zipball/e9900d2f0186564a30934a2ebcf60481d916ebf0", - "reference": "e9900d2f0186564a30934a2ebcf60481d916ebf0", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { - "ext-simplexml": "*", - "php": "^7.4 || ^8.0", - "symfony/framework-bundle": "^5.0 || ^6.0", - "vimeo/psalm": "^5.1" + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "doctrine/annotations": "^1.8|^2", - "doctrine/orm": "^2.9", - "phpunit/phpunit": "~7.5 || ~9.5", - "symfony/cache-contracts": "^1.0 || ^2.0", - "symfony/console": "*", - "symfony/form": "^5.0 || ^6.0", - "symfony/messenger": "^5.0 || ^6.0", - "symfony/security-guard": "*", - "symfony/serializer": "^5.0 || ^6.0", - "symfony/validator": "*", - "twig/twig": "^2.10 || ^3.0", - "weirdan/codeception-psalm-module": "dev-master" + "phpunit/phpunit": "^9.6" }, "suggest": { - "weirdan/doctrine-psalm-plugin": "If Doctrine is used, it is recommended install this plugin" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, - "type": "psalm-plugin", + "type": "library", "extra": { - "psalm": { - "pluginClass": "Psalm\\SymfonyPsalmPlugin\\Plugin" + "branch-alias": { + "dev-main": "9.2.x-dev" } }, "autoload": { - "psr-4": { - "Psalm\\SymfonyPsalmPlugin\\": "src" + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:23:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.23", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.5.0 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-05-02T06:40:34+00:00" + }, + { + "name": "rector/rector", + "version": "2.0.16", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "f1366d1f8c7490541c8f7af6e5c7cef7cca1b5a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/f1366d1f8c7490541c8f7af6e5c7cef7cca1b5a2", + "reference": "f1366d1f8c7490541c8f7af6e5c7cef7cca1b5a2", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.14" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.0.16" + }, + "funding": [ { - "name": "Farhad Safarov", - "email": "farhad.safarov@gmail.com" + "url": "https://github.com/tomasvotruba", + "type": "github" } ], - "description": "Psalm Plugin for Symfony", - "support": { - "issues": "https://github.com/psalm/psalm-plugin-symfony/issues", - "source": "https://github.com/psalm/psalm-plugin-symfony/tree/v5.0.1" - }, - "time": "2023-01-08T18:54:02+00:00" + "time": "2025-05-12T16:37:16+00:00" }, { "name": "roave/security-advisories", @@ -14580,413 +16551,696 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "c0e29643df152bd7fbe3de189f6c2c16c7d4c2a2" + "reference": "0cc529f6cf08a858fcb7a2c5617780fcdc20d1fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/c0e29643df152bd7fbe3de189f6c2c16c7d4c2a2", - "reference": "c0e29643df152bd7fbe3de189f6c2c16c7d4c2a2", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0cc529f6cf08a858fcb7a2c5617780fcdc20d1fe", + "reference": "0cc529f6cf08a858fcb7a2c5617780fcdc20d1fe", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", - "admidio/admidio": "<4.1.9", - "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", - "aheinze/cockpit": "<=2.2.1", + "adaptcms/adaptcms": "<=1.3", + "admidio/admidio": "<4.3.12", + "adodb/adodb-php": "<=5.22.8", + "aheinze/cockpit": "<2.2", + "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", + "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", + "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", + "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1", + "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", + "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", + "airesvsg/acf-to-rest-api": "<=3.1", "akaunting/akaunting": "<2.1.13", "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", - "alextselegidis/easyappointments": "<=1.4.3", + "alextselegidis/easyappointments": "<=1.5.1", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", + "ameos/ameos_tarteaucitron": "<1.2.23", "amphp/artax": "<1.0.6|>=2,<2.0.6", - "amphp/http": "<1.0.1", + "amphp/http": "<=1.7.2|>=2,<=2.1", "amphp/http-client": ">=4,<4.4", "anchorcms/anchor-cms": "<=0.12.7", "andreapollastri/cipi": "<=3.1.15", + "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", + "aoe/restler": "<1.7.1", + "apache-solr-for-typo3/solr": "<2.8.3", "apereo/phpcas": "<1.6", - "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", - "appwrite/server-ce": "<0.11.1|>=0.12,<0.12.2", + "api-platform/core": "<3.4.17|>=4.0.0.0-alpha1,<4.0.22", + "api-platform/graphql": "<3.4.17|>=4.0.0.0-alpha1,<4.0.22", + "appwrite/server-ce": "<=1.2.1", "arc/web": "<3", "area17/twill": "<1.2.5|>=2,<2.5.3", - "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", - "automad/automad": "<1.8", + "artesaos/seotools": "<0.17.2", + "asymmetricrypt/asymmetricrypt": "<9.9.99", + "athlon1600/php-proxy": "<=5.1", + "athlon1600/php-proxy-app": "<=3", + "athlon1600/youtube-downloader": "<=4", + "austintoddj/canvas": "<=3.4.2", + "auth0/auth0-php": ">=8.0.0.0-beta1,<8.14", + "auth0/login": "<7.17", + "auth0/symfony": "<5.4", + "auth0/wordpress": "<5.3", + "automad/automad": "<2.0.0.0-alpha5", + "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", - "aws/aws-sdk-php": ">=3,<3.2.1", - "backdrop/backdrop": "<=1.23", + "aws/aws-sdk-php": "<3.288.1", + "azuracast/azuracast": "<0.18.3", + "b13/seo_basics": "<0.8.2", + "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2", + "backpack/crud": "<3.4.9", + "backpack/filemanager": "<2.0.2|>=3,<3.0.9", + "bacula-web/bacula-web": "<8.0.0.0-RC2-dev", "badaso/core": "<2.7", - "bagisto/bagisto": "<0.1.5", + "bagisto/bagisto": "<2.1", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", "barryvdh/laravel-translation-manager": "<0.6.2", "barzahlen/barzahlen-php": "<2.0.1", - "baserproject/basercms": "<4.7.2", + "baserproject/basercms": "<=5.1.1", "bassjobsen/bootstrap-3-typeahead": ">4.0.2", - "billz/raspap-webgui": "<=2.6.6", + "bbpress/bbpress": "<2.6.5", + "bcosca/fatfree": "<3.7.2", + "bedita/bedita": "<4", + "bednee/cooluri": "<1.0.30", + "bigfork/silverstripe-form-capture": ">=3,<3.1.1", + "billz/raspap-webgui": "<=3.1.4", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", + "blueimp/jquery-file-upload": "==6.4.4", "bmarshall511/wordpress_zero_spam": "<5.2.13", "bolt/bolt": "<3.7.2", "bolt/core": "<=4.2", + "born05/craft-twofactorauthentication": "<3.3.4", "bottelet/flarepoint": "<2.2.1", + "bref/bref": "<2.1.17", "brightlocal/phpwhois": "<=4.2.5", "brotkrueml/codehighlight": "<2.7", "brotkrueml/schema": "<1.13.1|>=2,<2.5.1", "brotkrueml/typo3-matomo-integration": "<1.3.2", "buddypress/buddypress": "<7.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "bvbmedia/multishop": "<2.0.39", "bytefury/crater": "<6.0.2", "cachethq/cachet": "<2.5.1", - "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10|= 1.3.7|>=4.1,<4.1.4", + "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", "cakephp/database": ">=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", "cardgate/magento2": "<2.0.33", + "cardgate/woocommerce": "<=3.1.15", "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cart2quote/module-quotation-encoded": ">=4.1.6,<=4.4.5|>=5,<5.4.4", "cartalyst/sentry": "<=2.1.6", "catfan/medoo": "<1.7.5", - "centreon/centreon": "<22.10-beta.1", + "causal/oidc": "<4", + "cecil/cecil": "<7.47.1", + "centreon/centreon": "<22.10.15", "cesnet/simplesamlphp-module-proxystatistics": "<3.1", - "cockpit-hq/cockpit": "<2.3.8", + "chriskacerguis/codeigniter-restserver": "<=2.7.1", + "civicrm/civicrm-core": ">=4.2,<4.2.9|>=4.3,<4.3.3", + "ckeditor/ckeditor": "<4.25", + "clickstorm/cs-seo": ">=6,<6.7|>=7,<7.4|>=8,<8.3|>=9,<9.2", + "co-stack/fal_sftp": "<0.2.6", + "cockpit-hq/cockpit": "<2.7|==2.7", "codeception/codeception": "<3.1.3|>=4,<4.1.22", - "codeigniter/framework": "<=3.0.6", - "codeigniter4/framework": "<4.2.11", - "codeigniter4/shield": "= 1.0.0-beta", + "codeigniter/framework": "<3.1.9", + "codeigniter4/framework": "<4.5.8", + "codeigniter4/shield": "<1.0.0.0-beta8", "codiad/codiad": "<=2.8.4", - "composer/composer": "<1.10.26|>=2-alpha.1,<2.2.12|>=2.3,<2.3.5", - "concrete5/concrete5": "<=9.1.3|>= 9.0.0RC1, < 9.1.3", + "codingms/additional-tca": ">=1.7,<1.15.17|>=1.16,<1.16.9", + "commerceteam/commerce": ">=0.9.6,<0.9.9", + "components/jquery": ">=1.0.3,<3.5", + "composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7", + "concrete5/concrete5": "<9.4.0.0-RC2-dev", "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/contao": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3", - "contao/core": ">=2,<3.5.39", - "contao/core-bundle": "<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3|= 4.10.0", - "contao/listing-bundle": ">=4,<4.4.8", + "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", + "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4", + "contao/core": "<3.5.39", + "contao/core-bundle": "<4.13.54|>=5,<5.3.30|>=5.4,<5.5.6", + "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", "contao/managed-edition": "<=1.5", - "craftcms/cms": "<3.7.55.2|>= 4.0.0-RC1, < 4.2.1", - "croogo/croogo": "<3.0.7", + "corveda/phpsandbox": "<1.3.5", + "cosenary/instagram": "<=2.3", + "craftcms/cms": "<4.15.3|>=5,<5.7.5", + "croogo/croogo": "<4", "cuyz/valinor": "<0.12", + "czim/file-handling": "<1.5|>=2,<2.3", "czproject/git-php": "<4.0.3", + "damienharper/auditor-bundle": "<5.2.6", + "dapphp/securimage": "<3.6.6", "darylldoyle/safe-svg": "<1.9.10", "datadog/dd-trace": ">=0.30,<0.30.2", + "datatables/datatables": "<1.10.10", "david-garcia/phpwhois": "<=4.3.1", "dbrisinajumi/d2files": "<1", + "dcat/laravel-admin": "<=2.1.3|==2.2.0.0-beta|==2.2.2.0-beta", "derhansen/fe_change_pwd": "<2.0.5|>=3,<3.0.3", - "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", - "directmailteam/direct-mail": "<5.2.4", - "doctrine/annotations": ">=1,<1.2.7", + "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", + "desperado/xml-bundle": "<=0.1.7", + "dev-lancer/minecraft-motd-parser": "<=1.0.5", + "devgroup/dotplant": "<2020.09.14-dev", + "digimix/wp-svg-upload": "<=1", + "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", + "dl/yag": "<3.0.1", + "dmk/webkitpdf": "<1.1.4", + "dnadesign/silverstripe-elemental": "<5.3.12", + "doctrine/annotations": "<1.2.7", "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", - "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", + "doctrine/common": "<2.4.3|>=2.5,<2.5.1", "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2|>=3,<3.1.4", "doctrine/doctrine-bundle": "<1.5.2", - "doctrine/doctrine-module": "<=0.7.1", - "doctrine/mongodb-odm": ">=1,<1.0.2", - "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", - "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<16|>=16.0.1,<16.0.3|= 12.0.5|>= 3.3.beta1, < 13.0.2", - "dompdf/dompdf": "<2.0.2|= 2.0.2", - "drupal/core": ">=7,<7.91|>=8,<9.3.19|>=9.4,<9.4.3", - "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "doctrine/doctrine-module": "<0.7.2", + "doctrine/mongodb-odm": "<1.0.2", + "doctrine/mongodb-odm-bundle": "<3.0.1", + "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", + "dolibarr/dolibarr": "<19.0.2|==21.0.0.0-beta", + "dompdf/dompdf": "<2.0.4", + "doublethreedigital/guest-entries": "<3.1.2", + "drupal/ai": "<1.0.5", + "drupal/alogin": "<2.0.6", + "drupal/cache_utility": "<1.2.1", + "drupal/config_split": "<1.10|>=2,<2.0.2", + "drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.3.14|>=10.4,<10.4.5|>=11,<11.0.13|>=11.1,<11.1.5", + "drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/formatter_suite": "<2.1", + "drupal/gdpr": "<3.0.1|>=3.1,<3.1.2", + "drupal/google_tag": "<1.8|>=2,<2.0.8", + "drupal/ignition": "<1.0.4", + "drupal/link_field_display_mode_formatter": "<1.6", + "drupal/matomo": "<1.24", + "drupal/oauth2_client": "<4.1.3", + "drupal/oauth2_server": "<2.1", + "drupal/obfuscate": "<2.0.1", + "drupal/rapidoc_elements_field_formatter": "<1.0.1", + "drupal/spamspan": "<3.2.1", + "drupal/tfa": "<1.10", + "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", + "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", "ecodev/newsletter": "<=4", "ectouch/ectouch": "<=2.7.2", - "elefant/cms": "<1.3.13", + "egroupware/egroupware": "<23.1.20240624", + "elefant/cms": "<2.0.7", "elgg/elgg": "<3.3.24|>=4,<4.0.5", + "elijaa/phpmemcacheadmin": "<=1.3", + "encore/laravel-admin": "<=1.8.19", "endroid/qr-code-bundle": "<3.4.2", + "enhavo/enhavo-app": "<=0.13.1", "enshrined/svg-sanitize": "<0.15", "erusev/parsedown": "<1.7.2", "ether/logs": "<3.0.4", + "evolutioncms/evolution": "<=3.2.3", "exceedone/exment": "<4.4.3|>=5,<5.0.3", - "exceedone/laravel-admin": "= 3.0.0|<2.2.3", - "ezsystems/demobundle": ">=5.4,<5.4.6.1", + "exceedone/laravel-admin": "<2.2.3|==3", + "ezsystems/demobundle": ">=5.4,<5.4.6.1-dev", "ezsystems/ez-support-tools": ">=2.2,<2.2.3", - "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", - "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", + "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev", + "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev", "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26|>=3.3,<3.3.39", "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", - "ezsystems/ezplatform-graphql": ">=1-rc.1,<1.0.13|>=2-beta.1,<2.3.12", - "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<1.3.26", + "ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1-dev,<1.0.13|>=2.0.0.0-beta1,<2.3.12", + "ezsystems/ezplatform-http-cache": "<2.3.16", + "ezsystems/ezplatform-kernel": "<1.2.5.1-dev|>=1.3,<1.3.35", "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", - "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", + "ezsystems/ezplatform-richtext": ">=2.3,<2.3.26|>=3.3,<3.3.40", + "ezsystems/ezplatform-solr-search-engine": ">=1.7,<1.7.12|>=2,<2.0.2|>=3.3,<3.3.15", "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.30", - "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", + "ezsystems/ezpublish-kernel": "<6.13.8.2-dev|>=7,<7.5.31", + "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.03.5.1", "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", - "ezsystems/repository-forms": ">=2.3,<2.3.2.1|>=2.5,<2.5.15", - "ezyang/htmlpurifier": "<4.1.1", + "ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15", + "ezyang/htmlpurifier": "<=4.2", "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", - "facturascripts/facturascripts": "<=2022.8", + "facturascripts/facturascripts": "<=2022.08", + "fastly/magento2": "<1.2.26", "feehi/cms": "<=2.1.1", "feehi/feehicms": "<=2.1.1", "fenom/fenom": "<=2.12.1", + "filament/actions": ">=3.2,<3.2.123", + "filament/infolists": ">=3,<3.2.115", + "filament/tables": ">=3,<3.2.115", "filegator/filegator": "<7.8", - "firebase/php-jwt": "<2", + "filp/whoops": "<2.1.13", + "fineuploader/php-traditional-server": "<=1.2.2", + "firebase/php-jwt": "<6", + "fisharebest/webtrees": "<=2.1.18", "fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2", - "fixpunkt/fp-newsletter": "<1.1.1|>=2,<2.1.2|>=2.2,<3.2.6", - "flarum/core": "<1.6.3", + "fixpunkt/fp-newsletter": "<1.1.1|>=1.2,<2.1.2|>=2.2,<3.2.6", + "flarum/core": "<1.8.10", + "flarum/flarum": "<0.1.0.0-beta8", + "flarum/framework": "<1.8.10", "flarum/mentions": "<1.6.3", - "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", - "flarum/tags": "<=0.1-beta.13", + "flarum/sticky": ">=0.1.0.0-beta14,<=0.1.0.0-beta15", + "flarum/tags": "<=0.1.0.0-beta13", + "floriangaerber/magnesium": "<0.3.1", "fluidtypo3/vhs": "<5.1.1", - "fof/byobu": ">=0.3-beta.2,<1.1.7", + "fof/byobu": ">=0.3.0.0-beta2,<1.1.7", "fof/upload": "<1.2.3", + "foodcoopshop/foodcoopshop": ">=3.2,<3.6.1", "fooman/tcpdf": "<6.2.22", "forkcms/forkcms": "<5.11.1", "fossar/tcpdf-parser": "<6.2.22", - "francoisjacquet/rosariosis": "<10.1", + "francoisjacquet/rosariosis": "<=11.5.1", + "frappant/frp-form-answers": "<3.1.2|>=4,<4.0.2", "friendsofsymfony/oauth2-php": "<1.3", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", - "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "friendsofsymfony/user-bundle": ">=1,<1.3.5", + "friendsofsymfony1/swiftmailer": ">=4,<5.4.13|>=6,<6.2.5", + "friendsofsymfony1/symfony1": ">=1.1,<1.5.19", "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", - "froala/wysiwyg-editor": "<3.2.7", - "froxlor/froxlor": "<2.0.10", + "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", + "froala/wysiwyg-editor": "<=4.3", + "froxlor/froxlor": "<=2.2.5", + "frozennode/administrator": "<=5.0.12", "fuel/core": "<1.8.1", + "funadmin/funadmin": "<=5.0.2", "gaoming13/wechat-php-sdk": "<=1.10.2", "genix/cms": "<=1.1.11", - "getgrav/grav": "<1.7.34", - "getkirby/cms": "= 3.8.0|<3.5.8.2|>=3.6,<3.6.6.2|>=3.7,<3.7.5.1", + "georgringer/news": "<1.3.3", + "geshi/geshi": "<1.0.8.11-dev", + "getformwork/formwork": "<1.13.1|>=2.0.0.0-beta1,<2.0.0.0-beta4", + "getgrav/grav": "<1.7.46", + "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", + "getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", "getkirby/panel": "<2.5.14", "getkirby/starterkit": "<=3.7.0.2", - "gilacms/gila": "<=1.11.4", + "gilacms/gila": "<=1.15.4", + "gleez/cms": "<=1.3|==2", "globalpayments/php-sdk": "<2", + "goalgorilla/open_social": "<12.3.11|>=12.4,<12.4.10|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", + "gogentooss/samlbase": "<1.2.7", "google/protobuf": "<3.15", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", - "gree/jose": "<=2.2", + "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<5.8", + "grumpydictator/firefly-iii": "<6.1.17", + "gugoan/economizzer": "<=0.9.0.0-beta1", "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5", - "guzzlehttp/psr7": "<1.8.4|>=2,<2.1.1", + "guzzlehttp/oauth-subscriber": "<0.8.1", + "guzzlehttp/psr7": "<1.9.1|>=2,<2.4.5", + "haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2", "harvesthq/chosen": "<1.8.7", - "helloxz/imgurl": "= 2.31|<=2.31", + "helloxz/imgurl": "<=2.31", + "hhxsv5/laravel-s": "<3.7.36", "hillelcoren/invoice-ninja": "<5.3.35", "himiklab/yii2-jqgrid-widget": "<1.0.8", "hjue/justwriting": "<=1", "hov/jobfair": "<1.0.13|>=2,<2.0.2", + "httpsoft/http-message": "<1.0.12", "hyn/multi-tenant": ">=5.6,<5.7.2", - "ibexa/admin-ui": ">=4.2,<4.2.3", - "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3", + "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.14", + "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2", + "ibexa/fieldtype-richtext": ">=4.6,<4.6.19", "ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3", - "ibexa/post-install": "<=1.0.4", + "ibexa/http-cache": ">=4.6,<4.6.14", + "ibexa/post-install": "<1.0.16|>=4.6,<4.6.14", + "ibexa/solr": ">=4.5,<4.5.4", + "ibexa/user": ">=4,<4.4.3", "icecoder/icecoder": "<=8.1", "idno/known": "<=1.3.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", - "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", + "ilicmiljan/secure-props": ">=1.2,<1.2.2", + "illuminate/auth": "<5.5.10", + "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<6.18.31|>=7,<7.22.4", "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", - "impresscms/impresscms": "<=1.4.3", - "in2code/femanager": "<5.5.3|>=6,<6.3.4|>=7,<7.1", + "imdbphp/imdbphp": "<=5.1.1", + "impresscms/impresscms": "<=1.4.5", + "impresspages/impresspages": "<1.0.13", + "in2code/femanager": "<5.5.3|>=6,<6.3.4|>=7,<7.2.3", + "in2code/ipandlanguageredirect": "<5.1.2", "in2code/lux": "<17.6.1|>=18,<24.0.2", + "in2code/powermail": "<7.5.1|>=8,<8.5.1|>=9,<10.9.1|>=11,<12.4.1", "innologi/typo3-appointments": "<2.0.6", - "intelliants/subrion": "<=4.2.1", + "intelliants/subrion": "<4.2.2", + "inter-mediator/inter-mediator": "==5.5", + "ipl/web": "<0.10.1", + "islandora/crayfish": "<4.1", "islandora/islandora": ">=2,<2.4.1", "ivankristianto/phpwhois": "<=4.3", "jackalope/jackalope-doctrine-dbal": "<1.7.4", + "jambagecom/div2007": "<0.10.2", "james-heinrich/getid3": "<1.9.21", + "james-heinrich/phpthumb": "<1.7.12", "jasig/phpcas": "<1.3.3", + "jbartels/wec-map": "<3.0.3", + "jcbrand/converse.js": "<3.3.3", + "joelbutcher/socialstream": "<5.6|>=6,<6.2", + "johnbillion/wp-crontrol": "<1.16.2", + "joomla/application": "<1.0.13", "joomla/archive": "<1.1.12|>=2,<2.0.1", + "joomla/database": ">=1,<2.2|>=3,<3.4", "joomla/filesystem": "<1.6.2|>=2,<2.0.1", "joomla/filter": "<1.4.4|>=2,<2.0.1", + "joomla/framework": "<1.5.7|>=2.5.4,<=3.8.12", "joomla/input": ">=2,<2.0.2", + "joomla/joomla-cms": "<3.9.12|>=4,<4.4.13|>=5,<5.2.6", + "joomla/joomla-platform": "<1.5.4", "joomla/session": "<1.3.1", "joyqi/hyper-down": "<=2.4.27", "jsdecena/laracom": "<2.0.9", "jsmitty12/phpwhois": "<5.1", + "juzaweb/cms": "<=3.4", + "jweiland/events2": "<8.3.8|>=9,<9.0.6", + "jweiland/kk-downloader": "<1.2.2", "kazist/phpwhois": "<=4.2.6", "kelvinmo/simplexrd": "<3.1.1", "kevinpapst/kimai2": "<1.16.7", - "kitodo/presentation": "<3.1.2", + "khodakhah/nodcms": "<=3", + "kimai/kimai": "<=2.20.1", + "kitodo/presentation": "<3.2.3|>=3.3,<3.3.4", "klaviyo/magento2-extension": ">=1,<3", - "krayin/laravel-crm": "<1.2.2", + "knplabs/knp-snappy": "<=1.4.2", + "kohana/core": "<3.3.3", + "koillection/koillection": "<1.6.12", + "krayin/laravel-crm": "<=1.3", "kreait/firebase-php": ">=3.2,<3.8.1", + "kumbiaphp/kumbiapp": "<=1.1.1", "la-haute-societe/tcpdf": "<6.2.22", - "laminas/laminas-diactoros": "<2.11.1", + "laminas/laminas-diactoros": "<2.18.1|==2.19|==2.20|==2.21|==2.22|==2.23|>=2.24,<2.24.2|>=2.25,<2.25.2", "laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1", "laminas/laminas-http": "<2.14.2", + "lara-zeus/artemis": ">=1,<=1.0.6", + "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1", "laravel/fortify": "<1.11.1", - "laravel/framework": "<6.20.42|>=7,<7.30.6|>=8,<8.75", - "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", + "laravel/framework": "<10.48.29|>=11,<11.44.1|>=12,<12.1.1", + "laravel/laravel": ">=5.4,<5.4.22", + "laravel/pulse": "<1.3.1", + "laravel/reverb": "<1.4", + "laravel/socialite": ">=1,<2.0.10", "latte/latte": "<2.10.8", - "lavalite/cms": "<=5.8", + "lavalite/cms": "<=9|==10.1", "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", - "league/commonmark": "<0.18.3", + "league/commonmark": "<2.7", "league/flysystem": "<1.1.4|>=2,<2.1.1", + "league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3", + "leantime/leantime": "<3.3", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", - "librenms/librenms": "<22.10", + "libreform/libreform": ">=2,<=2.0.8", + "librenms/librenms": "<2017.08.18", "liftkit/database": "<2.13.2", - "limesurvey/limesurvey": "<3.27.19", + "lightsaml/lightsaml": "<1.3.5", + "limesurvey/limesurvey": "<6.5.12", "livehelperchat/livehelperchat": "<=3.91", - "livewire/livewire": ">2.2.4,<2.2.6", + "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.5.2", + "livewire/volt": "<1.7", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", + "luracast/restler": "<3.1", "luyadev/yii-helpers": "<1.2.1", - "magento/community-edition": ">=2,<2.2.10|>=2.3,<2.3.3", - "magento/magento1ce": "<1.9.4.3", - "magento/magento1ee": ">=1,<1.14.4.3", - "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", + "macropay-solutions/laravel-crud-wizard-free": "<3.4.17", + "maestroerror/php-heic-to-jpg": "<1.0.5", + "magento/community-edition": "<2.4.5|==2.4.5|>=2.4.5.0-patch1,<2.4.5.0-patch12|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch10|>=2.4.7.0-beta1,<2.4.7.0-patch5|>=2.4.8.0-beta1,<2.4.8.0-beta2", + "magento/core": "<=1.9.4.5", + "magento/magento1ce": "<1.9.4.3-dev", + "magento/magento1ee": ">=1,<1.14.4.3-dev", + "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", + "magento/project-community-edition": "<=2.0.2", + "magneto/core": "<1.9.4.4-dev", + "maikuolan/phpmussel": ">=1,<1.6", + "mainwp/mainwp": "<=4.4.3.3", + "mantisbt/mantisbt": "<=2.26.3", "marcwillmann/turn": "<0.3.3", + "matomo/matomo": "<1.11", "matyhtf/framework": "<3.0.6", - "mautic/core": "<4.3|= 2.13.1", - "mediawiki/core": ">=1.27,<1.27.6|>=1.29,<1.29.3|>=1.30,<1.30.2|>=1.31,<1.31.9|>=1.32,<1.32.6|>=1.32.99,<1.33.3|>=1.33.99,<1.34.3|>=1.34.99,<1.35", + "mautic/core": "<5.2.3", + "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1", + "maximebf/debugbar": "<1.19", + "mdanter/ecc": "<2", + "mediawiki/abuse-filter": "<1.39.9|>=1.40,<1.41.3|>=1.42,<1.42.2", + "mediawiki/cargo": "<3.6.1", + "mediawiki/core": "<1.39.5|==1.40", + "mediawiki/data-transfer": ">=1.39,<1.39.11|>=1.41,<1.41.3|>=1.42,<1.42.2", + "mediawiki/matomo": "<2.4.3", + "mediawiki/semantic-media-wiki": "<4.0.2", + "mehrwert/phpmyadmin": "<3.2", "melisplatform/melis-asset-manager": "<5.0.1", "melisplatform/melis-cms": "<5.0.1", "melisplatform/melis-front": "<5.0.1", "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", "mgallegos/laravel-jqgrid": "<=1.3", - "microweber/microweber": "<1.3.2", + "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1", + "microsoft/microsoft-graph-beta": "<2.0.1", + "microsoft/microsoft-graph-core": "<2.0.2", + "microweber/microweber": "<=2.0.16", + "mikehaertl/php-shellcommand": "<1.6.1", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", - "modx/revolution": "<= 2.8.3-pl|<2.8", + "mobiledetect/mobiledetectlib": "<2.8.32", + "modx/revolution": "<=3.1", "mojo42/jirafeau": "<4.4", + "mongodb/mongodb": ">=1,<1.9.2", "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<4.0.5", + "moodle/moodle": "<4.3.12|>=4.4,<4.4.8|>=4.5.0.0-beta,<4.5.4", + "mos/cimage": "<0.7.19", + "movim/moxl": ">=0.8,<=0.10", + "movingbytes/social-network": "<=1.2.1", + "mpdf/mpdf": "<=7.1.7", + "munkireport/comment": "<4.1", + "munkireport/managedinstalls": "<2.6", + "munkireport/munki_facts": "<1.5", + "munkireport/munkireport": ">=2.5.3,<5.6.3", + "munkireport/reportdata": "<3.5", + "munkireport/softwareupdate": "<1.6", "mustache/mustache": ">=2,<2.14.1", + "mwdelaney/wp-enable-svg": "<=0.2", "namshi/jose": "<2.2", + "nasirkhan/laravel-starter": "<11.11", + "nategood/httpful": "<1", "neoan3-apps/template": "<1.1.1", - "neorazorx/facturascripts": "<2022.4", + "neorazorx/facturascripts": "<2022.04", "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", "neos/form": ">=1.2,<4.3.3|>=5,<5.0.9|>=5.1,<5.1.3", - "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.9.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<5.3.10|>=7,<7.0.9|>=7.1,<7.1.7|>=7.2,<7.2.6|>=7.3,<7.3.4|>=8,<8.0.2", - "neos/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", + "neos/media-browser": "<7.3.19|>=8,<8.0.16|>=8.1,<8.1.11|>=8.2,<8.2.11|>=8.3,<8.3.9", + "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<5.3.10|>=7,<7.0.9|>=7.1,<7.1.7|>=7.2,<7.2.6|>=7.3,<7.3.4|>=8,<8.0.2", + "neos/swiftmailer": "<5.4.5", + "nesbot/carbon": "<2.72.6|>=3,<3.8.4", + "netcarver/textile": "<=4.1.2", "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", - "nilsteampassnet/teampass": "<=2.1.27.36", + "nilsteampassnet/teampass": "<3.1.3.1-dev", + "nonfiction/nterchange": "<4.1.1", "notrinos/notrinos-erp": "<=0.7", "noumo/easyii": "<=0.9", - "nukeviet/nukeviet": "<4.5.2", + "novaksolutions/infusionsoft-php-sdk": "<1", + "nukeviet/nukeviet": "<4.5.02", + "nyholm/psr7": "<1.6.1", "nystudio107/craft-seomatic": "<3.4.12", + "nzedb/nzedb": "<0.8", "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", "october/backend": "<1.1.2", - "october/cms": "= 1.1.1|= 1.0.471|= 1.0.469|>=1.0.319,<1.0.469", - "october/october": ">=1.0.319,<1.0.466|>=2.1,<2.1.12", + "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", + "october/october": "<3.7.5", "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<1.0.476|>=1.1,<1.1.12|>=2,<2.2.34|>=3,<3.0.66", + "october/system": "<3.7.5", + "oliverklee/phpunit": "<3.5.15", + "omeka/omeka-s": "<4.0.3", "onelogin/php-saml": "<2.10.4", - "oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5", + "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", "open-web-analytics/open-web-analytics": "<1.7.4", - "opencart/opencart": "<=3.0.3.7", + "opencart/opencart": ">=0", "openid/php-openid": "<2.3", - "openmage/magento-lts": "<19.4.22|>=20,<20.0.19", - "orchid/platform": ">=9,<9.4.4", - "oro/commerce": ">=4.1,<5.0.6", + "openmage/magento-lts": "<20.12.3", + "opensolutions/vimbadmin": "<=3.0.15", + "opensource-workshop/connect-cms": "<1.8.7|>=2,<2.4.7", + "orchid/platform": ">=8,<14.43", + "oro/calendar-bundle": ">=4.2,<=4.2.6|>=5,<=5.0.6|>=5.1,<5.1.1", + "oro/commerce": ">=4.1,<5.0.11|>=5.1,<5.1.1", "oro/crm": ">=1.7,<1.7.4|>=3.1,<4.1.17|>=4.2,<4.2.7", - "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<4.2.8", + "oro/crm-call-bundle": ">=4.2,<=4.2.5|>=5,<5.0.4|>=5.1,<5.1.1", + "oro/customer-portal": ">=4.1,<=4.1.13|>=4.2,<=4.2.10|>=5,<=5.0.11|>=5.1,<=5.1.3", + "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<=4.2.10|>=5,<=5.0.12|>=5.1,<=5.1.3", + "oveleon/contao-cookiebar": "<1.16.3|>=2,<2.1.3", + "oxid-esales/oxideshop-ce": "<=7.0.5", + "oxid-esales/paymorrow-module": ">=1,<1.0.2|>=2,<2.0.1", "packbackbooks/lti-1-3-php-library": "<5", "padraic/humbug_get_contents": "<1.1.2", - "pagarme/pagarme-php": ">=0,<3", + "pagarme/pagarme-php": "<3", "pagekit/pagekit": "<=1.0.18", + "paragonie/ecc": "<2.0.1", "paragonie/random_compat": "<2", - "passbolt/passbolt_api": "<2.11", + "passbolt/passbolt_api": "<4.6.2", + "paypal/adaptivepayments-sdk-php": "<=3.9.2", + "paypal/invoice-sdk-php": "<=3.9", "paypal/merchant-sdk-php": "<3.12", + "paypal/permissions-sdk-php": "<=3.9.1", "pear/archive_tar": "<1.4.14", + "pear/auth": "<1.2.4", "pear/crypt_gpg": "<1.6.7", + "pear/http_request2": "<2.7", + "pear/pear": "<=1.10.1", "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", "personnummer/personnummer": "<3.0.2", "phanan/koel": "<5.1.4", + "phenx/php-svg-lib": "<0.5.2", + "php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5", "php-mod/curl": "<2.3.2", + "phpbb/phpbb": "<3.3.11", + "phpems/phpems": ">=6,<=6.1.3", "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", "phpmailer/phpmailer": "<6.5", "phpmussel/phpmussel": ">=1,<1.6", - "phpmyadmin/phpmyadmin": "<5.1.3", - "phpmyfaq/phpmyfaq": "<=3.1.7", - "phpoffice/phpexcel": "<1.8", - "phpoffice/phpspreadsheet": "<1.16", - "phpseclib/phpseclib": "<2.0.31|>=3,<3.0.7", - "phpservermon/phpservermon": "<=3.5.2", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5,<5.6.3", + "phpmyadmin/phpmyadmin": "<5.2.2", + "phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5|>=3.2.10,<=4.0.1", + "phpoffice/common": "<0.2.9", + "phpoffice/phpexcel": "<=1.8.2", + "phpoffice/phpspreadsheet": "<1.29.9|>=2,<2.1.8|>=2.2,<2.3.7|>=3,<3.9", + "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", + "phpservermon/phpservermon": "<3.6", + "phpsysinfo/phpsysinfo": "<3.4.3", + "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", "phpxmlrpc/phpxmlrpc": "<4.9.2", + "pi/pi": "<=2.5", + "pimcore/admin-ui-classic-bundle": "<1.7.6", + "pimcore/customer-management-framework-bundle": "<4.2.1", "pimcore/data-hub": "<1.2.4", - "pimcore/pimcore": "<10.5.16", + "pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3", + "pimcore/demo": "<10.3", + "pimcore/ecommerce-framework-bundle": "<1.0.10", + "pimcore/perspective-editor": "<1.5.1", + "pimcore/pimcore": "<11.5.4", + "piwik/piwik": "<1.11", + "pixelfed/pixelfed": "<0.12.5", + "plotly/plotly.js": "<2.25.2", "pocketmine/bedrock-protocol": "<8.0.2", - "pocketmine/pocketmine-mp": "<4.12.5|>= 4.0.0-BETA5, < 4.4.2", + "pocketmine/pocketmine-mp": "<5.25.2", + "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", + "prestashop/blockreassurance": "<=5.1.3", "prestashop/blockwishlist": ">=2,<2.1.1", "prestashop/contactform": ">=1.0.1,<4.3", "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": "<1.7.8.8", + "prestashop/prestashop": "<8.1.6", "prestashop/productcomments": "<5.0.2", + "prestashop/ps_contactinfo": "<=3.3.2", "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", - "privatebin/privatebin": "<1.4", - "processwire/processwire": "<=3.0.200", - "propel/propel": ">=2-alpha.1,<=2-alpha.7", + "privatebin/privatebin": "<1.4|>=1.5,<1.7.4", + "processwire/processwire": "<=3.0.229", + "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", - "pterodactyl/panel": "<1.7", + "pterodactyl/panel": "<1.11.8", + "ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2", "ptrofimov/beanstalk_console": "<1.7.14", + "pubnub/pubnub": "<6.1", + "punktde/pt_extbase": "<1.5.1", "pusher/pusher-php-server": "<2.2.1", - "pwweb/laravel-core": "<=0.3.6-beta", + "pwweb/laravel-core": "<=0.3.6.0-beta", + "pxlrbt/filament-excel": "<1.1.14|>=2.0.0.0-alpha,<2.3.3", "pyrocms/pyrocms": "<=3.9.1", + "qcubed/qcubed": "<=3.1.1", + "quickapps/cms": "<=2.0.0.0-beta2", + "rainlab/blog-plugin": "<1.4.1", "rainlab/debugbar-plugin": "<3.1", + "rainlab/user-plugin": "<=1.4.5", "rankmath/seo-by-rank-math": "<=1.0.95", - "react/http": ">=0.7,<1.7", - "remdex/livehelperchat": "<3.99", + "rap2hpoutre/laravel-log-viewer": "<0.13", + "react/http": ">=0.7,<1.9", + "really-simple-plugins/complianz-gdpr": "<6.4.2", + "redaxo/source": "<5.18.3", + "remdex/livehelperchat": "<4.29", + "reportico-web/reportico": "<=8.1", + "rhukster/dom-sanitizer": "<1.0.7", "rmccue/requests": ">=1.6,<1.8", - "robrichards/xmlseclibs": "<3.0.4", + "robrichards/xmlseclibs": ">=1,<3.0.4", "roots/soil": "<4.1", "rudloff/alltube": "<3.0.3", + "rudloff/rtmpdump-bin": "<=2.3.1", "s-cart/core": "<6.9", "s-cart/s-cart": "<6.9", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", - "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", + "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", + "samwilson/unlinked-wikibase": "<1.42", + "scheb/two-factor-bundle": "<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", - "shopware/core": "<=6.4.18", - "shopware/platform": "<=6.4.18", + "sfroemken/url_redirect": "<=1.2.1", + "sheng/yiicms": "<1.2.1", + "shopware/core": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", + "shopware/platform": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.14", - "shopware/storefront": "<=6.4.8.1", - "shopxo/shopxo": "<2.2.6", + "shopware/shopware": "<=5.7.17", + "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", + "shopxo/shopxo": "<=6.4", "showdoc/showdoc": "<2.10.4", - "silverstripe/admin": ">=1,<1.11.3", + "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", + "silverstripe-australia/advancedreports": ">=1,<=2", + "silverstripe/admin": "<1.13.19|>=2,<2.1.8", "silverstripe/assets": ">=1,<1.11.1", "silverstripe/cms": "<4.11.3", - "silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1", + "silverstripe/comments": ">=1.3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": "<4.11.14", - "silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2|= 4.0.0-alpha1", + "silverstripe/framework": "<5.3.23", + "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3", "silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1", + "silverstripe/recipe-cms": ">=4.5,<4.5.3", "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", - "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4", + "silverstripe/reports": "<5.2.3", + "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4|>=2.1,<2.1.2", "silverstripe/silverstripe-omnipay": "<2.5.2|>=3,<3.0.2|>=3.1,<3.1.4|>=3.2,<3.2.1", "silverstripe/subsites": ">=2,<2.6.1", "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", - "silverstripe/userforms": "<3", + "silverstripe/userforms": "<3|>=5,<5.4.2", "silverstripe/versioned-admin": ">=1,<1.11.1", "simple-updates/phpwhois": "<=1", - "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", + "simplesamlphp/saml2": "<=4.16.15|>=5.0.0.0-alpha1,<=5.0.0.0-alpha19", + "simplesamlphp/saml2-legacy": "<=4.16.15", "simplesamlphp/simplesamlphp": "<1.18.6", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "simplesamlphp/simplesamlphp-module-openid": "<1", "simplesamlphp/simplesamlphp-module-openidprovider": "<0.9", + "simplesamlphp/xml-common": "<1.20", + "simplesamlphp/xml-security": "==1.6.11", "simplito/elliptic-php": "<1.0.6", + "sitegeist/fluid-components": "<3.5", + "sjbr/sr-feuser-register": "<2.6.2", + "sjbr/sr-freecap": "<2.4.6|>=2.5,<2.5.3", + "sjbr/static-info-tables": "<2.3.1", + "slim/psr7": "<1.4.1|>=1.5,<1.5.1|>=1.6,<1.6.1", "slim/slim": "<2.6", - "smarty/smarty": "<3.1.47|>=4,<4.2.1", - "snipe/snipe-it": "<=6.0.14|>= 6.0.0-RC-1, <= 6.0.0-RC-5", + "slub/slub-events": "<3.0.3", + "smarty/smarty": "<4.5.3|>=5,<5.1.1", + "snipe/snipe-it": "<8.1", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", - "spatie/browsershot": "<3.57.4", - "spipu/html2pdf": "<5.2.4", + "spatie/browsershot": "<5.0.5", + "spatie/image-optimizer": "<1.7.3", + "spencer14420/sp-php-email-handler": "<1", + "spipu/html2pdf": "<5.2.8", + "spoon/library": "<1.4.1", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", - "ssddanbrown/bookstack": "<22.2.3", - "statamic/cms": "<3.2.39|>=3.3,<3.3.2", - "stormpath/sdk": ">=0,<9.9.99", - "studio-42/elfinder": "<2.1.59", - "subrion/cms": "<=4.2.1", + "ssddanbrown/bookstack": "<24.05.1", + "starcitizentools/citizen-skin": ">=2.6.3,<2.31", + "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2", + "statamic/cms": "<=5.16", + "stormpath/sdk": "<9.9.99", + "studio-42/elfinder": "<=2.1.64", + "studiomitte/friendlycaptcha": "<0.1.4", + "subhh/libconnect": "<7.0.8|>=8,<8.1", "sukohi/surpass": "<1", - "sulu/sulu": "= 2.4.0-RC1|<1.6.44|>=2,<2.2.18|>=2.3,<2.3.8", + "sulu/form-bundle": ">=2,<2.5.3", + "sulu/sulu": "<1.6.44|>=2,<2.5.25|>=2.6,<2.6.9|>=3.0.0.0-alpha1,<3.0.0.0-alpha3", "sumocoders/framework-user-bundle": "<1.4", + "superbig/craft-audit": "<3.0.2", + "svewap/a21glossary": "<=0.4.10", "swag/paypal": "<5.4.4", - "swiftmailer/swiftmailer": ">=4,<5.4.5", + "swiftmailer/swiftmailer": "<6.2.5", + "swiftyedit/swiftyedit": "<1.2", "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", "sylius/grid-bundle": "<1.10.1", - "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", - "sylius/resource-bundle": "<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.9.10|>=1.10,<1.10.11|>=1.11,<1.11.2", - "symbiote/silverstripe-multivaluefield": ">=3,<3.0.99", + "sylius/paypal-plugin": "<1.6.2|>=1.7,<1.7.2|>=2,<2.0.2", + "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", + "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4", + "symbiote/silverstripe-multivaluefield": ">=3,<3.1", "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", "symbiote/silverstripe-seed": "<6.0.3", "symbiote/silverstripe-versionedfiles": "<=2.0.3", @@ -14995,8 +17249,9 @@ "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<=5.3.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3|= 6.0.3|= 5.4.3|= 5.3.14", - "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", + "symfony/http-client": ">=4.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", + "symfony/http-foundation": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", @@ -15004,83 +17259,142 @@ "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/polyfill": ">=1,<1.10", "symfony/polyfill-php55": ">=1,<1.10", + "symfony/process": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/routing": ">=2,<2.0.19", + "symfony/runtime": ">=5.3,<5.4.46|>=6,<6.4.14|>=7,<7.1.7", "symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", - "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", + "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.4.10|>=7,<7.0.10|>=7.1,<7.1.3", "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.3.2", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", + "symfony/symfony": "<5.4.47|>=6,<6.4.15|>=7,<7.1.8", "symfony/translation": ">=2,<2.0.17", - "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", + "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", + "symfony/ux-autocomplete": "<2.11.2", + "symfony/validator": "<5.4.43|>=6,<6.4.11|>=7,<7.1.4", "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", - "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", - "t3/dce": ">=2.2,<2.6.2", + "symfony/webhook": ">=6.3,<6.3.8", + "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7|>=2.2.0.0-beta1,<2.2.0.0-beta2", + "symphonycms/symphony-2": "<2.6.4", + "t3/dce": "<0.11.5|>=2.2,<2.6.2", "t3g/svg-sanitizer": "<1.0.3", - "tastyigniter/tastyigniter": "<3.3", - "tecnickcom/tcpdf": "<6.2.22", + "t3s/content-consent": "<1.0.3|>=2,<2.0.2", + "tastyigniter/tastyigniter": "<4", + "tcg/voyager": "<=1.8", + "tecnickcom/tc-lib-pdf-font": "<2.6.4", + "tecnickcom/tcpdf": "<6.8", "terminal42/contao-tablelookupwizard": "<3.3.5", "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1-beta.1,<2.1.3", + "thelia/thelia": ">=2.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", - "thinkcmf/thinkcmf": "<=5.1.7", - "thorsten/phpmyfaq": "<3.1.10", - "tinymce/tinymce": "<5.10.7|>=6,<6.3.1", - "titon/framework": ">=0,<9.9.99", - "tobiasbg/tablepress": "<= 2.0-RC1", - "topthink/framework": "<6.0.14", - "topthink/think": "<=6.0.9", - "topthink/thinkphp": "<=3.2.3", - "tribalsystems/zenario": "<=9.3.57595", + "thinkcmf/thinkcmf": "<6.0.8", + "thorsten/phpmyfaq": "<=4.0.1", + "tikiwiki/tiki-manager": "<=17.1", + "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", + "tinymce/tinymce": "<7.2", + "tinymighty/wiki-seo": "<1.2.2", + "titon/framework": "<9.9.99", + "tltneon/lgsl": "<7", + "tobiasbg/tablepress": "<=2.0.0.0-RC1", + "topthink/framework": "<6.0.17|>=6.1,<=8.0.4", + "topthink/think": "<=6.1.1", + "topthink/thinkphp": "<=3.2.3|>=6.1.3,<=8.0.4", + "torrentpier/torrentpier": "<=2.4.3", + "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", + "tribalsystems/zenario": "<=9.7.61188", "truckersmp/phpwhois": "<=4.3.1", "ttskch/pagination-service-provider": "<1", - "twig/twig": "<1.44.7|>=2,<2.15.3|>=3,<3.4.3", - "typo3/cms": "<2.0.5|>=3,<3.0.3|>=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", - "typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", - "typo3/cms-core": "<8.7.51|>=9,<9.5.40|>=10,<10.4.36|>=11,<11.5.23|>=12,<12.2", - "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", + "twbs/bootstrap": "<=3.4.1|>=4,<=4.6.2", + "twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19", + "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", + "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<10.4.46|>=11,<11.5.40|>=12,<12.4.21|>=13,<13.3.1", + "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-beuser": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-core": "<=8.7.56|>=9,<=9.5.48|>=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-dashboard": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", + "typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-felogin": ">=4.2,<4.2.3", + "typo3/cms-fluid": "<4.3.4|>=4.4,<4.4.1", + "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-frontend": "<4.3.9|>=4.4,<4.4.5", + "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2", + "typo3/cms-lowlevel": ">=11,<=11.5.41", + "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", + "typo3/cms-scheduler": ">=11,<=11.5.41", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", - "typo3/html-sanitizer": ">=1,<1.5|>=2,<2.1.1", + "typo3/html-sanitizer": ">=1,<=1.5.2|>=2,<=2.1.3", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", "typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10", "ua-parser/uap-php": "<3.8", - "unisharp/laravel-filemanager": "<=2.5.1", + "uasoft-indonesia/badaso": "<=2.9.7", + "unisharp/laravel-filemanager": "<2.9.1", + "unopim/unopim": "<0.1.5", "userfrosting/userfrosting": ">=0.3.1,<4.6.3", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", + "uvdesk/community-skeleton": "<=1.1.1", + "uvdesk/core-framework": "<=1.1.1", "vanilla/safecurl": "<0.9.2", - "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", + "verbb/comments": "<1.5.5", + "verbb/formie": "<=2.1.43", + "verbb/image-resizer": "<2.0.9", + "verbb/knock-knock": "<1.2.8", + "verot/class.upload.php": "<=2.1.6", + "vertexvaar/falsftp": "<0.2.6", + "villagedefrance/opencart-overclocked": "<=1.11.1", "vova07/yii2-fileapi-widget": "<0.1.9", "vrana/adminer": "<4.8.1", + "vufind/vufind": ">=2,<9.1.1", + "waldhacker/hcaptcha": "<2.1.2", "wallabag/tcpdf": "<6.2.22", - "wallabag/wallabag": "<2.5.4", + "wallabag/wallabag": "<2.6.11", "wanglelecc/laracms": "<=1.0.3", - "web-auth/webauthn-framework": ">=3.3,<3.3.4", + "wapplersystems/a21glossary": "<=0.4.10", + "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9", + "web-auth/webauthn-lib": ">=4.5,<4.9", + "web-feet/coastercms": "==5.5", + "web-tp3/wec_map": "<3.0.3", + "webbuilders-group/silverstripe-kapost-bridge": "<0.4", "webcoast/deferred-image-processing": "<1.0.2", + "webklex/laravel-imap": "<5.3", + "webklex/php-imap": "<5.3", "webpa/webpa": "<3.1.2", + "wikibase/wikibase": "<=1.39.3", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", - "wintercms/winter": "<1.0.475|>=1.1,<1.1.10|>=1.2,<1.2.1", - "woocommerce/woocommerce": "<6.6", - "wp-cli/wp-cli": "<2.5", - "wp-graphql/wp-graphql": "<0.3.5", + "winter/wn-backend-module": "<1.2.4", + "winter/wn-cms-module": "<1.0.476|>=1.1,<1.1.11|>=1.2,<1.2.7", + "winter/wn-dusk-plugin": "<2.1", + "winter/wn-system-module": "<1.2.4", + "wintercms/winter": "<=1.2.3", + "wireui/wireui": "<1.19.3|>=2,<2.1.3", + "woocommerce/woocommerce": "<6.6|>=8.8,<8.8.5|>=8.9,<8.9.3", + "wp-cli/wp-cli": ">=0.12,<2.5", + "wp-graphql/wp-graphql": "<=1.14.5", + "wp-premium/gravityforms": "<2.4.21", "wpanel/wpanel4-cms": "<=4.3.1", - "wwbn/avideo": "<12.4", + "wpcloud/wp-stateless": "<3.2", + "wpglobus/wpglobus": "<=1.9.6", + "wwbn/avideo": "<14.3", "xataface/xataface": "<3", "xpressengine/xpressengine": "<3.0.15", - "yeswiki/yeswiki": "<4.1", - "yetiforce/yetiforce-crm": "<=6.4", + "yab/quarx": "<2.4.5", + "yeswiki/yeswiki": "<4.5.4", + "yetiforce/yetiforce-crm": "<6.5", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", - "yiisoft/yii": "<1.1.27", - "yiisoft/yii2": "<2.0.38", + "yiisoft/yii": "<1.1.31", + "yiisoft/yii2": "<2.0.52", + "yiisoft/yii2-authclient": "<2.2.15", "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.43", + "yiisoft/yii2-dev": "<=2.0.45", "yiisoft/yii2-elasticsearch": "<2.0.5", "yiisoft/yii2-gii": "<=2.2.4", "yiisoft/yii2-jui": "<2.0.4", @@ -15088,11 +17402,13 @@ "yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6", "yoast-seo-for-typo3/yoast_seo": "<7.2.3", "yourls/yourls": "<=1.8.2", + "yuan1994/tpadmin": "<=1.3.12", + "zencart/zencart": "<=1.5.7.0-beta", "zendesk/zendesk_api_client_php": "<2.2.11", "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", + "zendframework/zend-db": "<2.2.10|>=2.3,<2.3.5", "zendframework/zend-developer-tools": ">=1.2.2,<1.2.3", "zendframework/zend-diactoros": "<1.8.4", "zendframework/zend-feed": "<2.10.3", @@ -15100,21 +17416,30 @@ "zendframework/zend-http": "<2.8.1", "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", - "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", + "zendframework/zend-mail": "<2.4.11|>=2.5,<2.7.2", "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", + "zendframework/zend-session": ">=2,<2.2.9|>=2.3,<2.3.4", "zendframework/zend-validator": ">=2.3,<2.3.6", "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", "zendframework/zendframework": "<=3", "zendframework/zendframework1": "<1.12.20", - "zendframework/zendopenid": ">=2,<2.0.2", + "zendframework/zendopenid": "<2.0.2", + "zendframework/zendrest": "<2.0.2", + "zendframework/zendservice-amazon": "<2.0.3", + "zendframework/zendservice-api": "<1", + "zendframework/zendservice-audioscrobbler": "<2.0.2", + "zendframework/zendservice-nirvanix": "<2.0.2", + "zendframework/zendservice-slideshare": "<2.0.2", + "zendframework/zendservice-technorati": "<2.0.2", + "zendframework/zendservice-windowsazure": "<2.0.2", "zendframework/zendxml": ">=1,<1.0.1", + "zenstruck/collection": "<0.2.1", "zetacomponents/mail": "<1.8.2", "zf-commons/zfc-user": "<1.2.2", "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", "zfr/zfr-oauth2-server-module": "<0.1.2", - "zoujingli/thinkadmin": "<6.0.22" + "zoujingli/thinkadmin": "<=6.1.53" }, "default-branch": true, "type": "metapackage", @@ -15135,6 +17460,9 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "keywords": [ + "dev" + ], "support": { "issues": "https://github.com/Roave/SecurityAdvisories/issues", "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" @@ -15149,20 +17477,318 @@ "type": "tidelift" } ], - "time": "2023-02-09T20:04:23+00:00" + "time": "2025-05-17T16:05:20+00:00" }, { - "name": "sebastian/diff", - "version": "4.0.4", + "name": "sebastian/cli-parser", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -15207,7 +17833,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -15215,99 +17841,630 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { - "name": "spatie/array-to-xml", - "version": "2.17.1", + "name": "sebastian/environment", + "version": "5.1.5", "source": { "type": "git", - "url": "https://github.com/spatie/array-to-xml.git", - "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", - "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { - "ext-dom": "*", - "php": "^7.4|^8.0" + "php": ">=7.3" }, "require-dev": { - "mockery/mockery": "^1.2", - "pestphp/pest": "^1.21", - "phpunit/phpunit": "^9.0", - "spatie/pest-plugin-snapshots": "^1.1" + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", - "autoload": { - "psr-4": { - "Spatie\\ArrayToXml\\": "src" + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://freek.dev", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Convert an array to xml", - "homepage": "https://github.com/spatie/array-to-xml", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ - "array", - "convert", - "xml" + "Xdebug", + "environment", + "hhvm" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/2.17.1" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, - { - "url": "https://github.com/spatie", + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2022-12-26T08:22:07+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { - "name": "symfony/browser-kit", - "version": "v5.4.19", + "name": "sebastian/exporter", + "version": "4.0.6", "source": { "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "572b9e03741051b97c316f65f8c361eed08fdb14" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/572b9e03741051b97c316f65f8c361eed08fdb14", - "reference": "572b9e03741051b97c316f65f8c361eed08fdb14", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { - "symfony/process": "" + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v6.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "ce95f3e3239159e7fa3be7690c6ce95a4714637f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ce95f3e3239159e7fa3be7690c6ce95a4714637f", + "reference": "ce95f3e3239159e7fa3be7690c6ce95a4714637f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/dom-crawler": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -15335,7 +18492,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v5.4.19" + "source": "https://github.com/symfony/browser-kit/tree/v6.4.19" }, "funding": [ { @@ -15351,42 +18508,37 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-02-14T11:23:16+00:00" }, { "name": "symfony/debug-bundle", - "version": "v5.4.19", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/debug-bundle.git", - "reference": "40e9ffe230aed8518c80816da27c80433ec220c7" + "reference": "7bcfaff39e094cc09455201916d016d9b2ae08ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/40e9ffe230aed8518c80816da27c80433ec220c7", - "reference": "40e9ffe230aed8518c80816da27c80433ec220c7", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/7bcfaff39e094cc09455201916d016d9b2ae08ff", + "reference": "7bcfaff39e094cc09455201916d016d9b2ae08ff", "shasum": "" }, "require": { "ext-xml": "*", - "php": ">=7.2.5", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/twig-bridge": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" + "php": ">=8.1", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/twig-bridge": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/config": "<4.4", - "symfony/dependency-injection": "<5.2" + "symfony/config": "<5.4", + "symfony/dependency-injection": "<5.4" }, "require-dev": { - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/web-profiler-bundle": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "For service container configuration", - "symfony/dependency-injection": "For using as a service from the container" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/web-profiler-bundle": "^5.4|^6.0|^7.0" }, "type": "symfony-bundle", "autoload": { @@ -15414,7 +18566,7 @@ "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug-bundle/tree/v5.4.19" + "source": "https://github.com/symfony/debug-bundle/tree/v6.4.13" }, "funding": [ { @@ -15430,129 +18582,54 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" - }, - { - "name": "symfony/dom-crawler", - "version": "v5.4.19", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "224a1820e7669babdd85970230ed72bd6e342ad4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/224a1820e7669babdd85970230ed72bd6e342ad4", - "reference": "224a1820e7669babdd85970230ed72bd6e342ad4", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "masterminds/html5": "<2.6" - }, - "require-dev": { - "masterminds/html5": "^2.6", - "symfony/css-selector": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases DOM navigation for HTML and XML documents", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v5.4.19" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-14T19:14:44+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/maker-bundle", - "version": "v1.43.0", + "version": "v1.63.0", "source": { "type": "git", "url": "https://github.com/symfony/maker-bundle.git", - "reference": "e3f9a1d9e0f4968f68454403e820dffc7db38a59" + "reference": "69478ab39bc303abfbe3293006a78b09a8512425" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/e3f9a1d9e0f4968f68454403e820dffc7db38a59", - "reference": "e3f9a1d9e0f4968f68454403e820dffc7db38a59", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/69478ab39bc303abfbe3293006a78b09a8512425", + "reference": "69478ab39bc303abfbe3293006a78b09a8512425", "shasum": "" }, "require": { "doctrine/inflector": "^2.0", - "nikic/php-parser": "^4.11", - "php": ">=7.2.5", - "symfony/config": "^5.4.7|^6.0", - "symfony/console": "^5.4.7|^6.0", - "symfony/dependency-injection": "^5.4.7|^6.0", + "nikic/php-parser": "^5.0", + "php": ">=8.1", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", "symfony/deprecation-contracts": "^2.2|^3", - "symfony/filesystem": "^5.4.7|^6.0", - "symfony/finder": "^5.4.3|^6.0", - "symfony/framework-bundle": "^5.4.7|^6.0", - "symfony/http-kernel": "^5.4.7|^6.0" + "symfony/filesystem": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" }, "conflict": { - "doctrine/orm": "<2.10" + "doctrine/doctrine-bundle": "<2.10", + "doctrine/orm": "<2.15" }, "require-dev": { "composer/semver": "^3.0", - "doctrine/doctrine-bundle": "^2.4", - "doctrine/orm": "^2.10.0", - "symfony/http-client": "^5.4.7|^6.0", - "symfony/phpunit-bridge": "^5.4.7|^6.0", - "symfony/polyfill-php80": "^1.16.0", - "symfony/process": "^5.4.7|^6.0", - "symfony/security-core": "^5.4.7|^6.0", - "symfony/yaml": "^5.4.3|^6.0", - "twig/twig": "^2.0|^3.0" + "doctrine/doctrine-bundle": "^2.5.0", + "doctrine/orm": "^2.15|^3", + "symfony/http-client": "^6.4|^7.0", + "symfony/phpunit-bridge": "^6.4.1|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/twig": "^3.0|^4.x-dev" }, "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-main": "1.0-dev" + "dev-main": "1.x-dev" } }, "autoload": { @@ -15574,13 +18651,14 @@ "homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html", "keywords": [ "code generator", + "dev", "generator", "scaffold", "scaffolding" ], "support": { "issues": "https://github.com/symfony/maker-bundle/issues", - "source": "https://github.com/symfony/maker-bundle/tree/v1.43.0" + "source": "https://github.com/symfony/maker-bundle/tree/v1.63.0" }, "funding": [ { @@ -15596,34 +18674,32 @@ "type": "tidelift" } ], - "time": "2022-05-17T15:46:50+00:00" + "time": "2025-04-26T01:41:37+00:00" }, { "name": "symfony/phpunit-bridge", - "version": "v5.4.19", + "version": "v6.4.16", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "3b213e88832612c5eee0f8a9b764897896b23bf0" + "reference": "cebafe2f1ad2d1e745c1015b7c2519592341e4e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/3b213e88832612c5eee0f8a9b764897896b23bf0", - "reference": "3b213e88832612c5eee0f8a9b764897896b23bf0", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/cebafe2f1ad2d1e745c1015b7c2519592341e4e6", + "reference": "cebafe2f1ad2d1e745c1015b7c2519592341e4e6", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=7.1.3" }, "conflict": { "phpunit/phpunit": "<7.5|9.1.2" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/polyfill-php81": "^1.27" }, "bin": [ "bin/simple-phpunit" @@ -15631,8 +18707,8 @@ "type": "symfony-bridge", "extra": { "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" + "url": "https://github.com/sebastianbergmann/phpunit", + "name": "phpunit/phpunit" } }, "autoload": { @@ -15643,7 +18719,8 @@ "Symfony\\Bridge\\PhpUnit\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Tests/", + "/bin/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -15663,7 +18740,7 @@ "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v5.4.19" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.4.16" }, "funding": [ { @@ -15679,43 +18756,42 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-11-13T15:06:22+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v5.4.19", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "cd83822071f2bc05583af1e53c1bc46be625a56d" + "reference": "7d1026a8e950d416cb5148ae88ac23db5d264839" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/cd83822071f2bc05583af1e53c1bc46be625a56d", - "reference": "cd83822071f2bc05583af1e53c1bc46be625a56d", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/7d1026a8e950d416cb5148ae88ac23db5d264839", + "reference": "7d1026a8e950d416cb5148ae88ac23db5d264839", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/framework-bundle": "^5.3|^6.0", - "symfony/http-kernel": "^5.3|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", + "php": ">=8.1", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0", "twig/twig": "^2.13|^3.0.4" }, "conflict": { - "symfony/dependency-injection": "<5.2", - "symfony/form": "<4.4", + "symfony/form": "<5.4", "symfony/mailer": "<5.4", - "symfony/messenger": "<4.4" + "symfony/messenger": "<5.4", + "symfony/twig-bundle": ">=7.0" }, "require-dev": { - "symfony/browser-kit": "^4.4|^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0" + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "symfony-bundle", "autoload": { @@ -15742,8 +18818,11 @@ ], "description": "Provides a development tool that gives detailed information about the execution of any request", "homepage": "https://symfony.com", + "keywords": [ + "dev" + ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v5.4.19" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.19" }, "funding": [ { @@ -15759,28 +18838,32 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2025-02-14T12:21:59+00:00" }, { "name": "symplify/easy-coding-standard", - "version": "11.2.8", + "version": "12.5.18", "source": { "type": "git", "url": "https://github.com/easy-coding-standard/easy-coding-standard.git", - "reference": "012d68b5cad4a5e3a6ccd99571209142b87491f5" + "reference": "451dfeba3770f2d7476468b891a789c451ae4b34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/012d68b5cad4a5e3a6ccd99571209142b87491f5", - "reference": "012d68b5cad4a5e3a6ccd99571209142b87491f5", + "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/451dfeba3770f2d7476468b891a789c451ae4b34", + "reference": "451dfeba3770f2d7476468b891a789c451ae4b34", "shasum": "" }, "require": { "php": ">=7.2" }, "conflict": { - "friendsofphp/php-cs-fixer": "<3.0", - "squizlabs/php_codesniffer": "<3.6" + "friendsofphp/php-cs-fixer": "<3.46", + "phpcsstandards/php_codesniffer": "<3.8", + "symplify/coding-standard": "<12.1" + }, + "suggest": { + "ext-dom": "Needed to support checkstyle output format in class CheckstyleOutputFormatter" }, "bin": [ "bin/ecs" @@ -15796,9 +18879,15 @@ "MIT" ], "description": "Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer", + "keywords": [ + "Code style", + "automation", + "fixer", + "static analysis" + ], "support": { "issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues", - "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/11.2.8" + "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.5.18" }, "funding": [ { @@ -15810,114 +18899,67 @@ "type": "github" } ], - "time": "2023-02-05T15:08:01+00:00" + "time": "2025-05-14T09:38:08+00:00" }, { - "name": "vimeo/psalm", - "version": "5.6.0", + "name": "theseer/tokenizer", + "version": "1.2.3", "source": { "type": "git", - "url": "https://github.com/vimeo/psalm.git", - "reference": "e784128902dfe01d489c4123d69918a9f3c1eac5" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/e784128902dfe01d489c4123d69918a9f3c1eac5", - "reference": "e784128902dfe01d489c4123d69918a9f3c1eac5", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { - "amphp/amp": "^2.4.2", - "amphp/byte-stream": "^1.5", - "composer/package-versions-deprecated": "^1.10.0", - "composer/semver": "^1.4 || ^2.0 || ^3.0", - "composer/xdebug-handler": "^2.0 || ^3.0", - "dnoegel/php-xdg-base-dir": "^0.1.1", - "ext-ctype": "*", "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-simplexml": "*", "ext-tokenizer": "*", - "felixfbecker/advanced-json-rpc": "^3.1", - "felixfbecker/language-server-protocol": "^1.5.2", - "fidry/cpu-core-counter": "^0.4.0", - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.13", - "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", - "sebastian/diff": "^4.0 || ^5.0", - "spatie/array-to-xml": "^2.17.0", - "symfony/console": "^4.1.6 || ^5.0 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0" + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, - "provide": { - "psalm/psalm": "self.version" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4", - "brianium/paratest": "^6.0", - "ext-curl": "*", - "mockery/mockery": "^1.5", - "nunomaduro/mock-final-classes": "^1.1", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpdoc-parser": "^1.6", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.6", - "symfony/process": "^4.4 || ^5.0 || ^6.0" - }, - "suggest": { - "ext-curl": "In order to send data to shepherd", - "ext-igbinary": "^2.0.5 is required, used to serialize caching data" - }, - "bin": [ - "psalm", - "psalm-language-server", - "psalm-plugin", - "psalm-refactor", - "psalter" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev", - "dev-4.x": "4.x-dev", - "dev-3.x": "3.x-dev", - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" - } - }, "autoload": { - "psr-4": { - "Psalm\\": "src/Psalm/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Matthew Brown" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "A static analysis tool for finding errors in PHP applications", - "keywords": [ - "code", - "inspection", - "php" - ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { - "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.6.0" + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, - "time": "2023-01-23T20:32:47+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [ + { + "package": "brick/math", + "version": "0.12.1.0", + "alias": "0.11.0", + "alias_normalized": "0.11.0.0" } ], - "aliases": [], "minimum-stability": "stable", "stability-flags": { "florianv/swap-bundle": 20, @@ -15926,8 +18968,9 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.4 || ^8.0", + "php": "^8.1", "ext-ctype": "*", + "ext-dom": "*", "ext-gd": "*", "ext-iconv": "*", "ext-intl": "*", @@ -15936,7 +18979,7 @@ }, "platform-dev": [], "platform-overrides": { - "php": "7.4.0" + "php": "8.1.0" }, "plugin-api-version": "2.3.0" } diff --git a/config/bootstrap.php b/config/bootstrap.php deleted file mode 100644 index 55560fb8..00000000 --- a/config/bootstrap.php +++ /dev/null @@ -1,23 +0,0 @@ -=1.2) -if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) { - (new Dotenv(false))->populate($env); -} else { - // load all the .env files - (new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env'); -} - -$_SERVER += $_ENV; -$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; -$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; -$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; diff --git a/config/bundles.php b/config/bundles.php index 8ca67ae7..ea066084 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -2,7 +2,6 @@ return [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], - Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], @@ -19,12 +18,18 @@ return [ DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], Gregwar\CaptchaBundle\GregwarCaptchaBundle::class => ['all' => true], - Translation\Bundle\TranslationBundle::class => ['all' => true], Florianv\SwapBundle\FlorianvSwapBundle::class => ['all' => true], Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true], Symfony\UX\Turbo\TurboBundle::class => ['all' => true], Jbtronics\TFAWebauthn\TFAWebauthnBundle::class => ['all' => true], Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true], - SpomkyLabs\CborBundle\SpomkyLabsCborBundle::class => ['all' => true], Webauthn\Bundle\WebauthnBundle::class => ['all' => true], + Nbgrp\OneloginSamlBundle\NbgrpOneloginSamlBundle::class => ['all' => true], + Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true], + Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true], + Jbtronics\DompdfFontLoaderBundle\DompdfFontLoaderBundle::class => ['all' => true], + KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true], + Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], + ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], + Jbtronics\TranslationEditorBundle\JbtronicsTranslationEditorBundle::class => ['dev' => true], ]; diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml new file mode 100644 index 00000000..d55f91ea --- /dev/null +++ b/config/packages/api_platform.yaml @@ -0,0 +1,41 @@ +api_platform: + title: 'Part-DB API' + description: 'API of Part-DB' + version: '0.1.0' + + formats: + jsonld: ['application/ld+json'] + json: ['application/json'] + jsonapi: ['application/vnd.api+json'] + + docs_formats: + jsonld: ['application/ld+json'] + jsonopenapi: ['application/vnd.openapi+json'] + html: ['text/html'] + json: ['application/vnd.openapi+json'] + + swagger: + api_keys: + # overridden in OpenApiFactoryDecorator + access_token: + name: Authorization + type: header + + defaults: + # TODO: Change this to true later. In the moment it is false, because we use the session in somewhere + stateless: false + cache_headers: + vary: ['Content-Type', 'Authorization', 'Origin'] + extra_properties: + standard_put: true + rfc_7807_compliant_errors: true + + pagination_client_items_per_page: true # Allow clients to override the default items per page + + keep_legacy_inflector: false + # Need to be true, or some tests will fail + use_symfony_listeners: true + + serializer: + # Change this to false later, to remove the hydra prefix on the API + hydra_prefix: true \ No newline at end of file diff --git a/config/packages/cache.yaml b/config/packages/cache.yaml index 07ecf18a..6adea442 100644 --- a/config/packages/cache.yaml +++ b/config/packages/cache.yaml @@ -20,3 +20,6 @@ framework: tree.cache: adapter: cache.app tags: true + + info_provider.cache: + adapter: cache.app diff --git a/config/packages/dama_doctrine_test_bundle.yaml b/config/packages/dama_doctrine_test_bundle.yaml new file mode 100644 index 00000000..3482cbae --- /dev/null +++ b/config/packages/dama_doctrine_test_bundle.yaml @@ -0,0 +1,5 @@ +when@test: + dama_doctrine_test: + enable_static_connection: true + enable_static_meta_data_cache: true + enable_static_query_cache: true diff --git a/config/packages/datatables.yaml b/config/packages/datatables.yaml index 0ccd6434..6076a6c7 100644 --- a/config/packages/datatables.yaml +++ b/config/packages/datatables.yaml @@ -8,15 +8,14 @@ datatables: # Set options, as documented at https://datatables.net/reference/option/ options: - lengthMenu : [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]] - pageLength: 50 - #dom: "<'row' <'col-sm-12' tr>><'row' <'col-sm-6'l><'col-sm-6 text-right'pif>>" - dom: " <'row'<'col mb-2 input-group' B l> <'col mb-2' <'pull-end' p>>> - <'card' - rt - <'card-footer card-footer-table text-muted' i > - > - <'row'<'col mt-2 input-group' B l> <'col mt-2' <'pull-right' p>>>" + 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 + dom: " <'row' <'col mb-2 input-group flex-nowrap' B l > <'col-auto mb-2' < p >>> + <'card' + rt + <'card-footer card-footer-table text-muted' i > + > + <'row' <'col mt-2 input-group flex-nowrap' B l > <'col-auto mt-2' < p >>>" pagingType: 'simple_numbers' searching: true stateSave: true diff --git a/config/packages/dev/php_translation.yaml b/config/packages/dev/php_translation.yaml deleted file mode 100644 index 53398fbc..00000000 --- a/config/packages/dev/php_translation.yaml +++ /dev/null @@ -1,5 +0,0 @@ -translation: - symfony_profiler: - enabled: true - webui: - enabled: true diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 20c1001e..3211fbbe 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -2,16 +2,32 @@ doctrine: dbal: url: '%env(resolve:DATABASE_URL)%' + # Required for DAMA doctrine test bundle + use_savepoints: true + # IMPORTANT: You MUST configure your server version, # either here or in the DATABASE_URL env var (see .env file) types: + # UTC datetimes datetime: class: App\Doctrine\Types\UTCDateTimeType date: class: App\Doctrine\Types\UTCDateTimeType + + datetime_immutable: + class: App\Doctrine\Types\UTCDateTimeImmutableType + date_immutable: + class: App\Doctrine\Types\UTCDateTimeImmutableType + big_decimal: class: App\Doctrine\Types\BigDecimalType + tinyint: + class: App\Doctrine\Types\TinyIntType + + # This was removed in doctrine/orm 4.0 but we need it for the WebauthnKey entity + array: + class: App\Doctrine\Types\ArrayType schema_filter: ~^(?!internal)~ # Only enable this when needed @@ -19,20 +35,29 @@ doctrine: orm: auto_generate_proxy_classes: true + enable_lazy_ghost_objects: true + report_fields_where_declared: true + validate_xml_mapping: true naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware auto_mapping: true + controller_resolver: + auto_mapping: true mappings: App: + type: attribute is_bundle: false - type: annotation dir: '%kernel.project_dir%/src/Entity' prefix: 'App\Entity' alias: App dql: string_functions: - regexp: DoctrineExtensions\Query\Mysql\Regexp - ifnull: DoctrineExtensions\Query\Mysql\IfNull + regexp: App\Doctrine\Functions\Regexp + field: DoctrineExtensions\Query\Mysql\Field + field2: App\Doctrine\Functions\Field2 + natsort: App\Doctrine\Functions\Natsort + array_position: App\Doctrine\Functions\ArrayPosition + ilike: App\Doctrine\Functions\ILike when@test: doctrine: diff --git a/config/packages/dompdf_font_loader.yaml b/config/packages/dompdf_font_loader.yaml new file mode 100644 index 00000000..8e35c37b --- /dev/null +++ b/config/packages/dompdf_font_loader.yaml @@ -0,0 +1,11 @@ +dompdf_font_loader: + auto_install: true + + fonts: + unifont: + normal: "%kernel.project_dir%/vendor/part-db/label-fonts/fonts/unifont.ttf" + + # Enable autodiscovery of fonts, so that font installation is much easier + autodiscovery: + paths: + - "%kernel.project_dir%/assets/fonts/dompdf" \ No newline at end of file diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 6adac2bb..279c51f5 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -2,8 +2,13 @@ framework: secret: '%env(APP_SECRET)%' csrf_protection: true + annotations: false + handle_all_throwables: true - # Must be set to true, to enable the change of HTTP methhod via _method parameter, otherwise our delete routines does not work anymore + # We set this header by ourselves, so we can disable it here + disallow_search_engine_index: false + + # Must be set to true, to enable the change of HTTP method via _method parameter, otherwise our delete routines does not work anymore # TODO: Rework delete routines to work without _method parameter as it is not recommended anymore (see https://github.com/symfony/symfony/issues/45278) http_method_override: true @@ -22,16 +27,12 @@ framework: handler_id: null cookie_secure: auto cookie_samesite: lax - storage_factory_id: session.storage.factory.native #esi: true #fragments: true php_errors: log: true - form: - legacy_error_messages: false # Enable to use the new Form component validation messages - when@test: framework: test: true diff --git a/config/packages/http_client.yaml b/config/packages/http_client.yaml new file mode 100644 index 00000000..2e693f7f --- /dev/null +++ b/config/packages/http_client.yaml @@ -0,0 +1,5 @@ +framework: + http_client: + default_options: + headers: + 'User-Agent': 'Part-DB' \ No newline at end of file diff --git a/config/packages/http_discovery.yaml b/config/packages/http_discovery.yaml new file mode 100644 index 00000000..2a789e73 --- /dev/null +++ b/config/packages/http_discovery.yaml @@ -0,0 +1,10 @@ +services: + Psr\Http\Message\RequestFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\ResponseFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\ServerRequestFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\StreamFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\UploadedFileFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\UriFactoryInterface: '@http_discovery.psr17_factory' + + http_discovery.psr17_factory: + class: Http\Discovery\Psr17Factory diff --git a/config/packages/knpu_oauth2_client.yaml b/config/packages/knpu_oauth2_client.yaml new file mode 100644 index 00000000..7d296a8b --- /dev/null +++ b/config/packages/knpu_oauth2_client.yaml @@ -0,0 +1,38 @@ +knpu_oauth2_client: + clients: + # configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration + + ip_digikey_oauth: + type: generic + provider_class: '\League\OAuth2\Client\Provider\GenericProvider' + + client_id: '%env(PROVIDER_DIGIKEY_CLIENT_ID)%' + client_secret: '%env(PROVIDER_DIGIKEY_SECRET)%' + + redirect_route: 'oauth_client_check' + redirect_params: {name: 'ip_digikey_oauth'} + + provider_options: + urlAuthorize: 'https://api.digikey.com/v1/oauth2/authorize' + urlAccessToken: 'https://api.digikey.com/v1/oauth2/token' + urlResourceOwnerDetails: '' + + # Sandbox + #urlAuthorize: 'https://sandbox-api.digikey.com/v1/oauth2/authorize' + #urlAccessToken: 'https://sandbox-api.digikey.com/v1/oauth2/token' + #urlResourceOwnerDetails: '' + + ip_octopart_oauth: + type: generic + provider_class: '\League\OAuth2\Client\Provider\GenericProvider' + + client_id: '%env(PROVIDER_OCTOPART_CLIENT_ID)%' + client_secret: '%env(PROVIDER_OCTOPART_SECRET)%' + + redirect_route: 'oauth_client_check' + redirect_params: { name: 'ip_octopart_oauth' } + + provider_options: + urlAuthorize: 'https://identity.nexar.com/connect/authorize' + urlAccessToken: 'https://identity.nexar.com/connect/token' + urlResourceOwnerDetails: '' \ No newline at end of file diff --git a/config/packages/liip_imagine.yaml b/config/packages/liip_imagine.yaml index a2205a24..51686b58 100644 --- a/config/packages/liip_imagine.yaml +++ b/config/packages/liip_imagine.yaml @@ -3,9 +3,15 @@ liip_imagine: # valid drivers options include "gd" or "gmagick" or "imagick" driver: "gd" + twig: + mode: lazy + + default_filter_set_settings: + format: webp + filter_sets: thumbnail_sm: - quality: 90 + quality: 65 filters: thumbnail: size: [150, 150] @@ -20,7 +26,7 @@ liip_imagine: mode: inset thumbnail_xs: - quality: 90 + quality: 60 filters: thumbnail: size: [50, 50] diff --git a/config/packages/lock.yaml b/config/packages/lock.yaml deleted file mode 100644 index 574879f8..00000000 --- a/config/packages/lock.yaml +++ /dev/null @@ -1,2 +0,0 @@ -framework: - lock: '%env(LOCK_DSN)%' diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml index 8938cc13..44a078b8 100644 --- a/config/packages/monolog.yaml +++ b/config/packages/monolog.yaml @@ -50,7 +50,6 @@ when@prod: type: stream path: "%kernel.logs_dir%/%kernel.environment%.log" level: debug - formatter: monolog.formatter.json console: type: console process_psr_3_messages: false @@ -74,7 +73,6 @@ when@docker: type: stream path: "php://stderr" level: debug - formatter: monolog.formatter.json console: type: console process_psr_3_messages: false diff --git a/config/packages/nbgrp_onelogin_saml.yaml b/config/packages/nbgrp_onelogin_saml.yaml new file mode 100644 index 00000000..2b1974f3 --- /dev/null +++ b/config/packages/nbgrp_onelogin_saml.yaml @@ -0,0 +1,69 @@ +# See https://github.com/SAML-Toolkits/php-saml for more information about the SAML settings + +# Define a parameter here, so we can access it later in the default fallback +parameters: + saml.sp.privateKey: '%env(string:SAML_SP_PRIVATE_KEY)%' + +nbgrp_onelogin_saml: + use_proxy_vars: '%env(bool:SAML_BEHIND_PROXY)%' + onelogin_settings: + default: + # Basic settings + idp: + entityId: '%env(string:SAML_IDP_ENTITY_ID)%' + singleSignOnService: + url: '%env(string:SAML_IDP_SINGLE_SIGN_ON_SERVICE)%' + binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' + singleLogoutService: + url: '%env(string:SAML_IDP_SINGLE_LOGOUT_SERVICE)%' + binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' + x509cert: '%env(string:SAML_IDP_X509_CERT)%' + sp: + entityId: '%env(string:SAML_SP_ENTITY_ID)%' + assertionConsumerService: + url: '%partdb.default_uri%saml/acs' + binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' + singleLogoutService: + url: '%partdb.default_uri%logout' + binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' + x509cert: '%env(string:SAML_SP_X509_CERT)%' + # Before the env variable was wrongly named "SAMLP_SP_PRIVATE_KEY". + # For compatibility reasons we keep it and only fallback to the new name if the old one is not set. This may be removed in the future. + privateKey: '%env(string:default:saml.sp.privateKey:string:SAMLP_SP_PRIVATE_KEY)%' + + # Optional settings + baseurl: '%partdb.default_uri%saml/' + strict: true + debug: false + security: + allowRepeatAttributeName: true + # nameIdEncrypted: false + authnRequestsSigned: true + logoutRequestSigned: true + logoutResponseSigned: true + # wantMessagesSigned: false + # wantAssertionsSigned: true + # wantNameIdEncrypted: false + # requestedAuthnContext: true + # signMetadata: false + # wantXMLValidation: true + # relaxDestinationValidation: false + # destinationStrictlyMatches: true + # rejectUnsolicitedResponsesWithInResponseTo: false + # signatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' + # digestAlgorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' + #contactPerson: + # technical: + # givenName: 'Tech User' + # emailAddress: 'techuser@example.com' + # support: + # givenName: 'Support User' + # emailAddress: 'supportuser@example.com' + # administrative: + # givenName: 'Administrative User' + # emailAddress: 'administrativeuser@example.com' + #organization: + # en: + # name: 'Part-DB-name' + # displayname: 'Displayname' + # url: 'http://example.com' \ No newline at end of file diff --git a/config/packages/nelmio_cors.yaml b/config/packages/nelmio_cors.yaml new file mode 100644 index 00000000..c7665081 --- /dev/null +++ b/config/packages/nelmio_cors.yaml @@ -0,0 +1,10 @@ +nelmio_cors: + defaults: + origin_regex: true + allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] + allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] + allow_headers: ['Content-Type', 'Authorization'] + expose_headers: ['Link'] + max_age: 3600 + paths: + '^/': null diff --git a/config/packages/nelmio_security.yaml b/config/packages/nelmio_security.yaml index d97b3983..1cb74da7 100644 --- a/config/packages/nelmio_security.yaml +++ b/config/packages/nelmio_security.yaml @@ -12,6 +12,13 @@ nelmio_security: external_redirects: abort: true log: true + allow_list: + # Whitelist the domain of the SAML IDP, so we can redirect to it during the SAML login process + - '%env(string:key:host:url:SAML_IDP_SINGLE_SIGN_ON_SERVICE)%' + + # Whitelist the info provider APIs (OAuth redirects) + - 'digikey.com' + - 'nexar.com' # forces Microsoft's XSS-Protection with # its block mode @@ -44,12 +51,16 @@ nelmio_security: img-src: - '*' - 'data:' + # Required for be able to load pictures in the QR code scanner + - 'blob:' style-src: - 'self' - 'unsafe-inline' - 'data:' script-src: - 'self' + # Required for loading the Wasm for the barcode scanner: + - 'wasm-unsafe-eval' object-src: - 'self' - 'data:' diff --git a/config/packages/php_translation.yaml b/config/packages/php_translation.yaml deleted file mode 100644 index 7c4f6ad9..00000000 --- a/config/packages/php_translation.yaml +++ /dev/null @@ -1,11 +0,0 @@ -translation: - locales: ["en", "de"] - edit_in_place: - enabled: false - config_name: app - configs: - app: - dirs: ["%kernel.project_dir%/templates", "%kernel.project_dir%/src"] - output_dir: "%kernel.project_dir%/translations" - excluded_names: ["*TestCase.php", "*Test.php"] - excluded_dirs: [cache, data, logs] diff --git a/config/packages/routing.yaml b/config/packages/routing.yaml index 4b766ce5..df5d98d2 100644 --- a/config/packages/routing.yaml +++ b/config/packages/routing.yaml @@ -4,7 +4,7 @@ framework: # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands - #default_uri: http://localhost + default_uri: '%env(DEFAULT_URI)%' when@prod: framework: diff --git a/config/packages/scheb_2fa.yaml b/config/packages/scheb_2fa.yaml index f58bdacc..ad0e7a5a 100644 --- a/config/packages/scheb_2fa.yaml +++ b/config/packages/scheb_2fa.yaml @@ -1,12 +1,12 @@ -# See the configuration reference at https://symfony.com/bundles/SchebTwoFactorBundle/5.x/configuration.html +# See the configuration reference at https://symfony.com/bundles/SchebTwoFactorBundle/6.x/configuration.html scheb_two_factor: google: enabled: true # If Google Authenticator should be enabled, default false - server_name: '%partdb.title%' # Server name used in QR code - issuer: 'Part-DB' # Issuer name used in QR code + server_name: '$$DOMAIN$$' # This field is replaced by the domain name of the server in DecoratedGoogleAuthenticator + issuer: '%partdb.title%' # Issuer name used in QR code digits: 6 # Number of digits in authentication code - window: 1 # How many codes before/after the current one would be accepted as valid + leeway: 5 # Acceptable time drift in seconds template: security/2fa_form.html.twig backup_codes: @@ -23,6 +23,6 @@ scheb_two_factor: security_tokens: - Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken # If you're using guard-based authentication, you have to use this one: - # - Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken + # - Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken # If you're using authenticator-based security (introduced in Symfony 5.1), you have to use this one: - # - Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken \ No newline at end of file + - Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken diff --git a/config/packages/security.yaml b/config/packages/security.yaml index e58ab1ec..95f5c6b1 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -1,10 +1,8 @@ security: - enable_authenticator_manager: true - + # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' - # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers providers: # used to reload user from session & other features (e.g. switch_user) app_user_provider: @@ -12,6 +10,7 @@ security: class: App\Entity\UserSystem\User property: name + firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ @@ -20,6 +19,13 @@ security: provider: app_user_provider lazy: true user_checker: App\Security\UserChecker + entry_point: App\Security\AuthenticationEntryPoint + + # Enable user impersonation + switch_user: { role: CAN_SWITCH_USER } + + custom_authenticators: + - App\Security\ApiTokenAuthenticator two_factor: auth_form_path: 2fa_login @@ -29,6 +35,14 @@ security: login_throttling: max_attempts: 5 # per minute + saml: + use_referer: true + user_factory: saml_user_factory + persist_user: true + check_path: saml_acs + login_path: saml_login + failure_path: login + # https://symfony.com/doc/current/security/form_login_setup.html form_login: login_path: login @@ -55,3 +69,7 @@ security: # We get into trouble with the U2F authentication, if the calls to the trees trigger an 2FA login # This settings should not do much harm, because a read only access to show available data structures is not really critical - { path: "^/\\w{2}/tree", role: PUBLIC_ACCESS } + # Restrict access to API to users, which has the API access permission + - { path: "^/api", allow_if: 'is_granted("@api.access_api") and is_authenticated()' } + # Restrict access to KICAD to users, which has API access permission + - { path: "^/kicad-api", allow_if: 'is_granted("@api.access_api") and is_authenticated()' } diff --git a/config/packages/sensio_framework_extra.yaml b/config/packages/sensio_framework_extra.yaml deleted file mode 100644 index 1821ccc0..00000000 --- a/config/packages/sensio_framework_extra.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sensio_framework_extra: - router: - annotations: false diff --git a/config/packages/test/framework.yaml b/config/packages/test/framework.yaml index d051c840..f76cc2ef 100644 --- a/config/packages/test/framework.yaml +++ b/config/packages/test/framework.yaml @@ -1,4 +1,2 @@ framework: test: true - session: - storage_id: session.storage.mock_file diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index fe8bd4ae..5b2d64e5 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -19,6 +19,8 @@ twig: 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' when@test: twig: diff --git a/config/packages/uid.yaml b/config/packages/uid.yaml new file mode 100644 index 00000000..01520944 --- /dev/null +++ b/config/packages/uid.yaml @@ -0,0 +1,4 @@ +framework: + uid: + default_uuid_version: 7 + time_based_uuid_version: 7 diff --git a/config/packages/ux_translator.yaml b/config/packages/ux_translator.yaml new file mode 100644 index 00000000..1c1c7060 --- /dev/null +++ b/config/packages/ux_translator.yaml @@ -0,0 +1,3 @@ +ux_translator: + # The directory where the JavaScript translations are dumped + dump_directory: '%kernel.project_dir%/var/translations' diff --git a/config/packages/web_profiler.yaml b/config/packages/web_profiler.yaml index 17893da1..b9461110 100644 --- a/config/packages/web_profiler.yaml +++ b/config/packages/web_profiler.yaml @@ -4,7 +4,9 @@ when@dev: intercept_redirects: false framework: - profiler: { only_exceptions: false } + profiler: + only_exceptions: false + collect_serializer_data: true when@test: web_profiler: diff --git a/config/parameters.yaml b/config/parameters.yaml index ec80e939..b2c10893 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -11,15 +11,22 @@ parameters: 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', 'fr', 'ru', 'ja'] # The languages that are shown in user drop down menu + 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 + + partdb.db.emulate_natural_sort: '%env(bool:DATABASE_EMULATE_NATURAL_SORT)%' # If this is set to true, natural sorting is emulated on platforms that do not support it natively. This can be slow on large datasets. ###################################################################################################################### # Users and Privacy ###################################################################################################################### - partdb.gpdr_compliance: true # If this option is activated, IP addresses are anonymized to be GPDR compliant + 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,9 +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.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.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 @@ -39,6 +48,17 @@ parameters: partdb.error_pages.admin_email: '%env(trim:string:ERROR_PAGE_ADMIN_EMAIL)%' # You can set an email address here, which is shown on an error page, how to contact an administrator partdb.error_pages.show_help: '%env(trim:string:ERROR_PAGE_SHOW_HELP)%' # 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... + ###################################################################################################################### + # SAML + ###################################################################################################################### + 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 ###################################################################################################################### @@ -95,7 +115,11 @@ parameters: env(INSTANCE_NAME): 'Part-DB' env(BASE_CURRENCY): 'EUR' env(USE_GRAVATAR): '0' - env(ALLOW_ATTACHMENT_DOWNLOADS): 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 @@ -108,5 +132,20 @@ 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 + + env(DEFAULT_URI): 'https://partdb.changeme.invalid/' + + 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 diff --git a/config/permissions.yaml b/config/permissions.yaml index f9b4a1ee..b8970556 100644 --- a/config/permissions.yaml +++ b/config/permissions.yaml @@ -25,24 +25,35 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co # If a part can be read by a user, he can also see all the datastructures (except devices) alsoSet: ['storelocations.read', 'footprints.read', 'categories.read', 'suppliers.read', 'manufacturers.read', 'currencies.read', 'attachment_types.read', 'measurement_units.read'] + apiTokenRole: ROLE_API_READ_ONLY edit: label: "perm.edit" alsoSet: ['read', 'parts_stock.withdraw', 'parts_stock.add', 'parts_stock.move'] + apiTokenRole: ROLE_API_EDIT create: label: "perm.create" alsoSet: ['read', 'edit'] + apiTokenRole: ROLE_API_EDIT delete: label: "perm.delete" alsoSet: ['read', 'edit'] + apiTokenRole: ROLE_API_EDIT change_favorite: label: "perm.part.change_favorite" alsoSet: ['edit'] + apiTokenRole: ROLE_API_EDIT show_history: label: "perm.part.show_history" alsoSet: ['read'] + apiTokenRole: ROLE_API_READ_ONLY revert_element: label: "perm.revert_elements" alsoSet: ["read", "edit", "create", "delete", "show_history"] + apiTokenRole: ROLE_API_EDIT + import: + label: "perm.import" + alsoSet: ["read", "edit", "create"] + apiTokenRole: ROLE_API_EDIT parts_stock: group: "data" @@ -50,10 +61,13 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co operations: withdraw: label: "perm.parts_stock.withdraw" + apiTokenRole: ROLE_API_EDIT add: label: "perm.parts_stock.add" + apiTokenRole: ROLE_API_EDIT move: label: "perm.parts_stock.move" + apiTokenRole: ROLE_API_EDIT storelocations: &PART_CONTAINING @@ -62,20 +76,30 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co operations: read: label: "perm.read" + apiTokenRole: ROLE_API_READ_ONLY edit: label: "perm.edit" alsoSet: 'read' + apiTokenRole: ROLE_API_EDIT create: label: "perm.create" alsoSet: ['read', 'edit'] + apiTokenRole: ROLE_API_EDIT delete: label: "perm.delete" alsoSet: ['read', 'edit'] + apiTokenRole: ROLE_API_EDIT show_history: label: "perm.show_history" + apiTokenRole: ROLE_API_READ_ONLY revert_element: label: "perm.revert_elements" alsoSet: ["read", "edit", "create", "delete", "show_history"] + apiTokenRole: ROLE_API_EDIT + import: + label: "perm.import" + alsoSet: [ "read", "edit", "create" ] + apiTokenRole: ROLE_API_EDIT footprints: <<: *PART_CONTAINING @@ -133,29 +157,48 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co ic_logos: label: "perm.tools.ic_logos" + info_providers: + label: "perm.part.info_providers" + operations: + create_parts: + label: "perm.part.info_providers.create_parts" + alsoSet: ['parts.create'] + apiTokenRole: ROLE_API_EDIT + groups: label: "perm.groups" group: "system" operations: read: label: "perm.read" + apiTokenRole: ROLE_API_ADMIN edit: label: "perm.edit" alsoSet: 'read' + apiTokenRole: ROLE_API_ADMIN create: label: "perm.create" alsoSet: ['read', 'edit'] + apiTokenRole: ROLE_API_ADMIN delete: label: "perm.delete" alsoSet: ['read', 'delete'] + apiTokenRole: ROLE_API_ADMIN edit_permissions: label: "perm.edit_permissions" alsoSet: ['read', 'edit'] + apiTokenRole: ROLE_API_ADMIN show_history: label: "perm.show_history" + apiTokenRole: ROLE_API_ADMIN revert_element: label: "perm.revert_elements" alsoSet: ["read", "edit", "create", "delete", "edit_permissions", "show_history"] + apiTokenRole: ROLE_API_ADMIN + import: + label: "perm.import" + alsoSet: [ "read", "edit", "create" ] + apiTokenRole: ROLE_API_ADMIN users: label: "perm.users" @@ -163,31 +206,49 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co operations: read: label: "perm.read" + apiTokenRole: ROLE_API_ADMIN create: label: "perm.create" alsoSet: ['read', 'edit_username', 'edit_infos'] + apiTokenRole: ROLE_API_ADMIN delete: label: "perm.delete" alsoSet: ['read', 'edit_username', 'edit_infos'] + apiTokenRole: ROLE_API_ADMIN edit_username: label: "perm.users.edit_user_name" alsoSet: ['read'] + apiTokenRole: ROLE_API_ADMIN edit_infos: label: "perm.users.edit_infos" alsoSet: 'read' + apiTokenRole: ROLE_API_ADMIN edit_permissions: label: "perm.users.edit_permissions" alsoSet: 'read' + apiTokenRole: ROLE_API_ADMIN set_password: label: "perm.users.set_password" alsoSet: 'read' + apiTokenRole: ROLE_API_FULL + impersonate: + label: "perm.users.impersonate" + alsoSet: ['set_password'] + apiTokenRole: ROLE_API_FULL change_user_settings: label: "perm.users.change_user_settings" + apiTokenRole: ROLE_API_ADMIN show_history: label: "perm.show_history" + apiTokenRole: ROLE_API_ADMIN revert_element: label: "perm.revert_elements" alsoSet: ["read", "create", "delete", "edit_permissions", "show_history", "edit_infos", "edit_username"] + apiTokenRole: ROLE_API_ADMIN + import: + label: "perm.import" + alsoSet: [ "read", "create" ] + apiTokenRole: ROLE_API_ADMIN #database: # label: "perm.database" @@ -222,60 +283,94 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co operations: show_logs: label: "perm.show_logs" + apiTokenRole: ROLE_API_ADMIN delete_logs: label: "perm.delete_logs" alsoSet: 'show_logs' + apiTokenRole: ROLE_API_ADMIN server_infos: label: "perm.server_infos" + apiTokenRole: ROLE_API_ADMIN + manage_oauth_tokens: + label: "Manage OAuth tokens" + apiTokenRole: ROLE_API_ADMIN + show_updates: + label: "perm.system.show_available_updates" + apiTokenRole: ROLE_API_ADMIN + attachments: label: "perm.part.attachments" operations: show_private: label: "perm.attachments.show_private" + apiTokenRole: ROLE_API_READ_ONLY list_attachments: label: "perm.attachments.list_attachments" alsoSet: ['attachment_types.read'] + apiTokenRole: ROLE_API_READ_ONLY self: label: "perm.self" operations: edit_infos: label: "perm.self.edit_infos" + apiTokenRole: ROLE_API_FULL edit_username: label: "perm.self.edit_username" + apiTokenRole: ROLE_API_FULL show_permissions: label: "perm.self.show_permissions" + apiTokenRole: ROLE_API_READ_ONLY show_logs: label: "perm.self.show_logs" + apiTokenRole: ROLE_API_FULL labels: label: "perm.labels" operations: create_labels: label: "perm.self.create_labels" + apiTokenRole: ROLE_API_READ_ONLY edit_options: label: "perm.self.edit_options" alsoSet: ['create_labels'] + apiTokenRole: ROLE_API_READ_ONLY read_profiles: label: "perm.self.read_profiles" + apiTokenRole: ROLE_API_READ_ONLY edit_profiles: label: "perm.self.edit_profiles" alsoSet: ['read_profiles'] + apiTokenRole: ROLE_API_EDIT create_profiles: label: "perm.self.create_profiles" alsoSet: ['read_profiles', 'edit_profiles'] + apiTokenRole: ROLE_API_EDIT delete_profiles: label: "perm.self.delete_profiles" alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles'] + apiTokenRole: ROLE_API_EDIT use_twig: label: "perm.labels.use_twig" alsoSet: ['create_labels', 'edit_options'] + apiTokenRole: ROLE_API_ADMIN show_history: label: "perm.show_history" alsoSet: ['read_profiles'] + apiTokenRole: ROLE_API_READ_ONLY revert_element: label: "perm.revert_elements" alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles', 'delete_profiles'] + apiTokenRole: ROLE_API_EDIT - + api: + label: "perm.api" + operations: + access_api: + label: "perm.api.access_api" + apiTokenRole: ROLE_API_READ_ONLY + manage_tokens: + label: "perm.api.manage_tokens" + alsoSet: ['access_api'] + apiTokenRole: ROLE_API_FULL \ No newline at end of file diff --git a/config/routes.yaml b/config/routes.yaml index 7d495d6d..8b38fa71 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,12 +1,8 @@ -#index: -# path: / -# controller: App\Controller\DefaultController::index - # Redirect every url without an locale to the locale of the user/the global base locale scan_qr: path: /scan/{type}/{id} - controller: App\Controller\ScanController:scanQRCode + controller: App\Controller\ScanController::scanQRCode csp_report: path: /csp/report @@ -19,5 +15,5 @@ redirector: requirements: url: ".*" controller: App\Controller\RedirectController::addLocalePart - # Dont match localized routes (no redirection loop, if no root with that name exists) - condition: "not (request.getPathInfo() matches '/^\\\\/[a-z]{2}(_[A-Z]{2})?\\\\//')" \ No newline at end of file + # Dont match localized routes (no redirection loop, if no root with that name exists) or API prefixed routes + condition: "not (request.getPathInfo() matches '/^\\\\/([a-z]{2}(_[A-Z]{2})?|api)\\\\//')" \ No newline at end of file diff --git a/config/routes/api_platform.yaml b/config/routes/api_platform.yaml new file mode 100644 index 00000000..38f11cba --- /dev/null +++ b/config/routes/api_platform.yaml @@ -0,0 +1,4 @@ +api_platform: + resource: . + type: api_platform + prefix: /api diff --git a/config/routes/annotations.yaml b/config/routes/attributes.yaml similarity index 66% rename from config/routes/annotations.yaml rename to config/routes/attributes.yaml index bd9ea273..72d7c9bc 100644 --- a/config/routes/annotations.yaml +++ b/config/routes/attributes.yaml @@ -1,6 +1,8 @@ controllers: - resource: ../../src/Controller/ - type: annotation + resource: + path: ../../src/Controller/ + namespace: App\Controller + type: attribute prefix: '{_locale}' defaults: @@ -11,4 +13,4 @@ controllers: kernel: resource: ../../src/Kernel.php - type: annotation + type: attribute diff --git a/config/routes/dev/php_translation.yaml b/config/routes/dev/php_translation.yaml deleted file mode 100644 index 903b23fb..00000000 --- a/config/routes/dev/php_translation.yaml +++ /dev/null @@ -1,6 +0,0 @@ -_translation_webui: - resource: '@TranslationBundle/Resources/config/routing_webui.yaml' - prefix: /admin - -_translation_profiler: - resource: '@TranslationBundle/Resources/config/routing_symfony_profiler.yaml' diff --git a/config/routes/jbtronics_translation_editor.yaml b/config/routes/jbtronics_translation_editor.yaml new file mode 100644 index 00000000..31409a61 --- /dev/null +++ b/config/routes/jbtronics_translation_editor.yaml @@ -0,0 +1,3 @@ +when@dev: + translation_editor: + resource: '@JbtronicsTranslationEditorBundle/config/routes.php' \ No newline at end of file diff --git a/config/routes/nbgrp_saml.yaml b/config/routes/nbgrp_saml.yaml new file mode 100644 index 00000000..6bf45795 --- /dev/null +++ b/config/routes/nbgrp_saml.yaml @@ -0,0 +1,4 @@ +nbgrp_saml: + resource: "@NbgrpOneloginSamlBundle/Resources/config/routes.php" + # Only load the SAML routes if SAML is enabled + condition: "env('SAML_ENABLED') == '1' or env('SAML_ENABLED') == 'true'" diff --git a/config/routes/php_translation.yaml b/config/routes/php_translation.yaml deleted file mode 100644 index 96ffd76f..00000000 --- a/config/routes/php_translation.yaml +++ /dev/null @@ -1,3 +0,0 @@ -_translation_edit_in_place: - resource: '@TranslationBundle/Resources/config/routing_edit_in_place.yaml' - prefix: /admin diff --git a/config/routes/security.yaml b/config/routes/security.yaml new file mode 100644 index 00000000..f853be15 --- /dev/null +++ b/config/routes/security.yaml @@ -0,0 +1,3 @@ +_security_logout: + resource: security.route_loader.logout + type: service diff --git a/config/services.yaml b/config/services.yaml index f2200115..b2342edd 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -14,16 +14,19 @@ services: autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. bind: bool $demo_mode: '%partdb.demo_mode%' - bool $gpdr_compliance : '%partdb.gpdr_compliance%' - bool $kernel_debug: '%kernel.debug%' + 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 $default_currency: '%partdb.default_currency%' + string $base_currency: '%partdb.default_currency%' _instanceof: App\Services\LabelSystem\PlaceholderProviders\PlaceholderProviderInterface: tags: ['app.label_placeholder_provider'] + App\Services\InfoProviderSystem\Providers\InfoProviderInterface: + tags: ['app.info_provider'] + # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name App\: @@ -73,32 +76,28 @@ services: # Only the event classes specified here are saved to DB (set to []) to log all events $whitelist: [] - App\EventSubscriber\LogSystem\EventLoggerSubscriber: + 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)%' - tags: - - { name: 'doctrine.event_subscriber' } - - App\EventSubscriber\LogSystem\LogDBMigrationSubscriber: - tags: - - { name: 'doctrine.event_subscriber' } + $save_new_data: '%env(bool:HISTORY_SAVE_NEW_DATA)%' App\Form\AttachmentFormType: arguments: - $allow_attachments_downloads: '%partdb.attachments.allow_downloads%' + $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\EventSubscriber\LogSystem\LogoutLoggerListener: - tags: - - name: 'kernel.event_listener' - event: 'Symfony\Component\Security\Http\Event\LogoutEvent' - dispatcher: security.event_dispatcher.main + App\Services\LogSystem\EventCommentNeededHelper: + arguments: + $enforce_change_comments_for: '%partdb.enforce_change_comments_for%' #################################################################################################################### # Attachment system @@ -127,6 +126,28 @@ services: # Security #################################################################################################################### + saml_user_factory: + alias: App\Security\SamlUserFactory + public: true + + App\Security\SamlUserFactory: + arguments: + $saml_role_mapping: '%env(json:SAML_ROLE_MAPPING)%' + $update_group_on_login: '%env(bool:SAML_UPDATE_GROUP_ON_LOGIN)%' + + + security.access_token_extractor.header.token: + class: Symfony\Component\Security\Http\AccessToken\HeaderAccessTokenExtractor + arguments: + $tokenType: 'Token' + + security.access_token_extractor.main: + class: Symfony\Component\Security\Http\AccessToken\ChainAccessTokenExtractor + arguments: + $accessTokenExtractors: + - '@security.access_token_extractor.header' + - '@security.access_token_extractor.header.token' + #################################################################################################################### # Cache #################################################################################################################### @@ -167,7 +188,7 @@ services: App\EventSubscriber\UserSystem\SetUserTimezoneSubscriber: arguments: - $timezone: '%partdb.timezone%' + $default_timezone: '%partdb.timezone%' App\Controller\SecurityController: arguments: @@ -194,6 +215,19 @@ services: arguments: $available_themes: '%partdb.available_themes%' + App\Command\User\ConvertToSAMLUserCommand: + arguments: + $saml_enabled: '%partdb.saml.enabled%' + + #################################################################################################################### + # 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 #################################################################################################################### # Label system @@ -207,6 +241,11 @@ services: tags: - { name: 'app.label_placeholder_provider', priority: 10} + App\Services\LabelSystem\DompdfFactory: + arguments: + $fontDirectory: '%kernel.project_dir%/var/dompdf/fonts/' + $tmpDirectory: '%kernel.project_dir%/var/dompdf/tmp/' + #################################################################################################################### # Trees #################################################################################################################### @@ -215,6 +254,84 @@ services: $rootNodeExpandedByDefault: '%partdb.sidebar.root_expanded%' $rootNodeEnabled: '%partdb.sidebar.root_node_enable%' + #################################################################################################################### + # Part info provider system + #################################################################################################################### + App\Services\InfoProviderSystem\ProviderRegistry: + 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 #################################################################################################################### @@ -225,6 +342,13 @@ services: tags: - {name: serializer.normalizer, priority: -9000} + # Disable igbinary serialization for cache even when igbinary is available, as it causes issues with the doctrine + # proxy objects (see https://github.com/igbinary/igbinary/issues/377 and https://github.com/igbinary/igbinary/issues/273) + cache.default_marshaller: + class: Symfony\Component\Cache\Marshaller\DefaultMarshaller + arguments: + $useIgbinarySerialize: false + #################################################################################################################### # Miscellaneous @@ -238,7 +362,7 @@ services: tags: - { name: 'doctrine.fixtures.purger_factory', alias: 'reset_autoincrement_purger' } - # We are needing this service inside of a migration, where only the container is injected. So we need to define it as public, to access it from the container. + # We are needing this service inside a migration, where only the container is injected. So we need to define it as public, to access it from the container. App\Services\UserSystem\PermissionPresetsHelper: public: true @@ -246,6 +370,20 @@ 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: + $enabled: '%env(bool:DATABASE_MYSQL_USE_SSL_CA)%' + $verify: '%env(bool:DATABASE_MYSQL_SSL_VERIFY_CERT)%' + #################################################################################################################### # Monolog #################################################################################################################### @@ -262,3 +400,14 @@ services: autowire: true tags: - { name: monolog.processor } + +when@test: + services: + # Decorate the doctrine fixtures load command to use our custom purger by default + doctrine.fixtures_load_command.custom: + decorates: doctrine.fixtures_load_command + class: Doctrine\Bundle\FixturesBundle\Command\LoadDataFixturesDoctrineCommand + arguments: + - '@doctrine.fixtures.loader' + - '@doctrine' + - { default: '@App\Doctrine\Purger\DoNotUsePurgerFactory' } diff --git a/docs/api/authentication.md b/docs/api/authentication.md new file mode 100644 index 00000000..b386c0cd --- /dev/null +++ b/docs/api/authentication.md @@ -0,0 +1,77 @@ +--- +title: Authentication +layout: default +parent: API +nav_order: 2 +--- + +# Authentication + +To use API endpoints, the external application has to authenticate itself, so that Part-DB knows which user is accessing +the data and which permissions +the application should have during the access. Authentication is always bound to a specific user, so the external +applications is acting on behalf of a +specific user. This user limits the permissions of the application so that it can only access data, which the user is +allowed to access. + +The only method currently available for authentication is to use API tokens: + +## API tokens + +An API token is a long alphanumeric string, which is bound to a specific user and can be used to authenticate as this user when accessing the API. +The API token is passed via the `Authorization` HTTP header during the API request, like the +following: `Authorization: Bearer tcp_sdjfks....`. + +{: .important } +> Everybody who knows the API token can access the API as the user, which is bound to the token. So you should treat the +> API token like a password +> and keep it secret. Only share it with trusted applications. + +API tokens can be created and managed on the user settings page in the API token section. You can create as many API +tokens as you want and also delete them again. +When deleting a token, it is immediately invalidated and can not be used anymore, which means that the application can +not access the API anymore with this token. + +### Token permissions and scopes + +API tokens are ultimately limited by the permissions of the user, which belongs to the token. That means that the token +can only access data, that the user is allowed to access, no matter the token permissions. + +But you can further limit the permissions of a token by choosing a specific scope for the token. The scope defines which +subset of permissions the token has, which can be less than the permissions of the user. For example, you can have a +user +with full read and write permissions, but create a token with only read permissions, which can only read data, but not +change anything in the database. + +{: .warning } +> In general, you should always use the least possible permissions for a token, to limit the possible damage, which can +> be done with a stolen token or a bug in the application. +> Only use the full or admin scope, if you really need it, as they could potentially be used to do a lot of damage to +> your Part-DB instance. + +The following token scopes are available: + +* **Read-Only**: The token can only read non-sensitive data (like parts, but no users or groups) from the API and can + not change anything. +* **Edit**: The token can read and write non-sensitive data via the API. This includes creating, updating and deleting + data. This should be enough for most applications. +* **Admin**: The token can read and write all data via the API, including sensitive data like users and groups. This + should only be used for trusted applications, which need to access sensitive data and perform administrative actions. +* **Full**: The token can do anything the user can do, including changing the user's password and creating new tokens. This + should only be used for highly trusted applications!! + +Please note, that in early versions of the API, there might be no endpoints yet, to really perform the actions, which +would be allowed by the token scope. + +### Expiration date + +API tokens can have an expiration date, which means that the token is only valid until the expiration date. After that +the token is automatically invalidated and can not be used anymore. The token is still listed on the user settings page, +and can be deleted there, but the code can not be used to access Part-DB anymore after the expiration date. + +### Get token information + +When authenticating with an API token, you can get information about the currently used token by accessing +the `/api/tokens/current` endpoint. +It gives you information about the token scope, expiration date and the user, which is bound to the token and the last +time the token was used. \ No newline at end of file diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 00000000..441ede9a --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,11 @@ +--- +layout: default +title: API +nav_order: 7 +has_children: true +--- + +# API + +Part-DB provides a REST API to access the data stored in the database. +In this section you can find information about the API and how to use it. diff --git a/docs/api/intro.md b/docs/api/intro.md new file mode 100644 index 00000000..78a8d2c1 --- /dev/null +++ b/docs/api/intro.md @@ -0,0 +1,229 @@ +--- +title: Introduction +layout: default +parent: API +nav_order: 1 +--- + +# Introduction + +Part-DB provides a [REST API](https://en.wikipedia.org/wiki/REST) to programmatically access the data stored in the +database. +This allows external applications to interact with Part-DB, extend it or integrate it into other applications. + +{: .warning } +> This feature is currently in beta. Please report any bugs you find. +> The API should not be considered stable yet and could change in future versions, without prior notice. +> Some features might be missing or not working yet. +> Also be aware, that there might be security issues in the API, which could allow attackers to access or edit data via +> the API, which +> they normally should be able to access. So currently you should only use the API with trusted users and trusted +> applications. + +Part-DB uses [API Platform](https://api-platform.com/) to provide the API, which allows for easy creation of REST APIs +with Symfony and gives you a lot of features out of the box. +See the [API Platform documentation](https://api-platform.com/docs/core/) for more details about the API Platform +features and how to use them. + +## Enable the API + +The API is available under the `/api` path, but not reachable without proper permissions. +You have to give the users, which should be able to access the API the proper permissions (Miscellaneous -> API). +Please note that there are two relevant permissions, the first one allows users to access the `/api/` path at all and show the documentation, +and the second one allows them to create API tokens which are needed for the authentication of external applications. + +## Authentication + +To use API endpoints, the external application has to authenticate itself, so that Part-DB knows which user is accessing +the data and +which permissions the application should have. Basically, this is done by creating an API token for a user and then +passing it on every request +with the `Authorization` header as bearer token, so you add a header `Authorization: Bearer `. + +See [Authentication chapter]({% link api/authentication.md %}) for more details. + +## API endpoints + +The API is split into different endpoints, which are reachable under the `/api/` path of your Part-DB instance ( +e.g. `https://your-part-db.local/api/`). +There are various endpoints for each entity type (like `part`, `manufacturer`, etc.), which allow you to read and write data, and some special endpoints like `search` or `statistics`. + +For example, all API endpoints for managing categories are available under `/api/categories/`. Depending on the exact +path and the HTTP method used, you can read, create, update or delete categories. +For most entities, there are endpoints like this: + +* **GET**: `/api/categories/` - List all categories in the database (with pagination of the results) +* **POST**: `/api/categories/` - Create a new category +* **GET**: `/api/categories/{id}` - Get a specific category by its ID +* **DELETE**: `/api/categories/{id}` - Delete a specific category by its ID +* **UPDATE**: `/api/categories/{id}` - Update a specific category by its ID. Only the fields which are sent in the + request are updated, all other fields are left unchanged. + Be aware that you have to set the [JSON Merge Patch](https://datatracker.ietf.org/doc/html/rfc7386) content type + header (`Content-Type: application/merge-patch+json`) for this to work. + +A full (interactive) list of endpoints can be displayed when visiting the `/api/` path in your browser, when you are +logged in with a user, which is allowed to access the API. +There is also a link to this page, on the user settings page in the API token section. +This documentation also lists all available fields for each entity type and the allowed operations. + +## Formats + +The API supports different formats for the request and response data, which you can control via the `Accept` +and `Content-Type` headers. +You should use [JSON-LD](https://json-ld.org/) as format, which is basically JSON with some additional metadata, which +allows you to describe the data in a more structured way and also allows to link between different entities. You can achieve this +by setting `Accept: application/ld+json` header to the API requests. + +To get plain JSON without any metadata or links, use the `Accept: application/json` header. + +Without an `Accept` header (e.g. when you call the endpoint in a browser), the API will return an HTML page with the +documentation, so be sure to include the desired `Accept` header in your API requests. +If you can not control the `Accept` header, you can add a `.json` or `.jsonld` suffix to the URL to enforce a JSON or +JSON-LD response (e.g. `/api/parts.jsonld`). + +## OpenAPI schema + +Part-DB provides a [OpenAPI](https://swagger.io/specification/) (formally Swagger) schema for the API +under `/api/docs.json` (so `https://your-part-db.local/api/docs.json`). +This schema is a machine-readable description of the API, which can be imported into software to test the API or even +automatically generate client libraries for the API. + +API generators which can generate a client library for the API from the schema are available for many programming +languages, like [OpenAPI Generator](https://openapi-generator.tech/). + +An JSONLD/Hydra version of the schema is also available under `/api/docs.jsonld` ( +so `https://your-part-db.local/api/docs.jsonld`). + +## Interactive documentation + +Part-DB provides an interactive documentation for the API, which is available under `/api/docs` ( +so `https://your-part-db.local/api/docs`). +You can pass your API token in the form on the top of the page, to authenticate yourself, and then you can try out the +API directly in the browser. +This is a great way to test the API and see how it works, without having to write any code. + +## Pagination + +By default, all list endpoints are paginated, which means only a certain number of results is returned per request. +To get another page of the results, you have to use the `page` query parameter, which contains the page number you want +to get (e.g. `/api/categoues/?page=2`). +When using JSONLD, the links to the next page are also included in the `hydra:view` property of the response. + +To change the size of the pages (the number of items in a single page) use the `itemsPerPage` query parameter ( +e.g. `/api/categoues/?itemsPerPage=50`). + +See [API Platform docs](https://api-platform.com/docs/core/pagination) for more infos. + +## Filtering results / Searching + +When retrieving a list of entities, you can restrict the results by various filters. Almost all entities have a search +filter, which allows you to only include entities, which (text) fields match the given search term: For example, if you only want +to get parts, with the Name "BC547", you can use `/api/parts.jsonld?name=BC547`. You can use `%` as a wildcard for multiple +characters in the search term (Be sure to properly encode the search term, if you use special characters). For example, if you want +to get all parts, whose name starts with "BC", you can use `/api/parts.jsonld?name=BC%25` (the `%25` is the url encoded version of `%`). + +There are other filters available for some entities, allowing you to search on other fields, or restricting the results +by numeric values or dates. See the endpoint documentation for the available filters. + +## Filter by associated entities + +To get all parts with a certain category, manufacturer, etc. you can use the `category`, `manufacturer`, etc. query +parameters of the `/api/parts` endpoint. +They are so-called entity filters and accept a comma-separated list of IDs of the entities you want to filter by. +For example, if you want to get all parts with the category "Resistor" (Category ID 1) and "Capacitor" (Category ID 2), +you can use `/api/parts.jsonld?category=1,2`. + +Suffix an id with `+` to suffix, to include all direct children categories of the given category. Use the `++` suffix to +include all children categories recursively. +To get all parts with the category "Resistor" (Category ID 1) and all children categories of "Capacitor" (Category ID +2), you can use `/api/parts.jsonld?category=1,2++`. + +See the endpoint documentation for the available entity filters. + +## Ordering results + +When retrieving a list of entities, you can order the results by various fields using the `order` query parameter. +For example, if you want to get all parts ordered by their name, you can use `/api/parts/?order[name]=asc`. You can use +this parameter multiple times to order by multiple fields. + +See the endpoint documentation for the available fields to order by. + +## Property filter + +Sometimes you only want to get a subset of the properties of an entity, for example when you only need the name of a +part, but not all the other properties. +You can achieve this using the `properties[]` query parameter with the name of the field you want to get. You can use +this parameter multiple times to get multiple fields. +For example, if you only want to get the name and the description of a part, you can +use `/api/parts/123?properties[]=name&properties[]=description`. +It is also possible to use these filters on list endpoints (get collection), to only get a subset of the properties of +all entities in the collection. + +See [API Platform docs](https://api-platform.com/docs/core/filters/#property-filter) for more info. + +## Change comment + +Similar to the changes using Part-DB web interface, you can add a change comment to every change you make via the API, +which will be +visible in the log of the entity. + +You can pass the text for this via the `_comment` query parameter (beware of the proper encoding). For +example `/api/parts/123?_comment=This%20is%20a%20change%20comment`. + +## Creating attachments and parameters + +To create attachments and parameters, use the POST endpoint. Internally there are different types of attachments and +parameters, for each entity type, where the attachments or parameters are used (e.g. PartAttachment for parts, etc.). +The type of the attachment or parameter is automatically determined by the `element` property of the request data if a +IRI is passed. You can use the `_type` property to explicitly set the type of the attachment or parameter (the value must +be the value of the `@type` property of the owning entity. e.g. `Part` for parts). + +For example, to create an attachment on a part, you can use the following request: + +``` +POST /api/attachments + +{ + "name": "front68", + "attachment_type": "/api/attachment_types/1", + "url": "https://invalid.invalid/test.url", + "element": "/api/parts/123" +} +``` + +## Uploading files to attachments + +To upload files to the attachments you can use the special `upload` property of the attachment entity during write operations (POST, PUT, PATCH). +Under `data` you can pass a base64 encoded string of the file content, and under `filename` the name of the file. +Using the `private` property you can control if the file is the attachment should be stored privately or public. + +For example, to upload a file to an attachment, you can use the following request: + +``` +PATCH /api/attachments/123 + +{ + "upload": { + "data": "data:@file/octet-stream;base64,LS0gcGhwTXlB[...]", + "filename": "test.csv", + "private": false + }, + "name": "Rename attachment" +} +``` + +This also works for creating new attachments, by including the `upload` property in the request data along with the other properties. + +Using the `downloadUrl` property of `upload` you can say Part-DB to upload the file specified at the URL set on the attachment. + +``` +PATCH /api/attachments/123 + +{ + "upload": { + "downloadUrl": true + }, + "url": "https://host.invalid/myfile.pdf" +} + +``` \ No newline at end of file diff --git a/docs/assets/usage/import_export/part_import_example.csv b/docs/assets/usage/import_export/part_import_example.csv new file mode 100644 index 00000000..08701426 --- /dev/null +++ b/docs/assets/usage/import_export/part_import_example.csv @@ -0,0 +1,4 @@ +name;description;category;notes;footprint;tags;quantity;storage_location;mass;ipn;mpn;manufacturing_status;manufacturer;supplier;spn;price;favorite;needs_review;minamount;partUnit;manufacturing_status +BC547;NPN transistor;Transistors -> NPN;very important notes;TO -> TO-92;NPN,Transistor;5;Room 1 -> Shelf 1 -> Box 2;10;;;Manufacturer;;You need to fill this line, to use spn and price;BC547C;2,3;0;;;; +BC557;PNP transistor;HTML;;TO -> TO-92;PNP,Transistor;10;Room 2-> Box 3;;Internal1234;;;;;;;;1;;;active +Copper Wire;;Wire;;;;;;;;;;;;;;;;;Meter; \ No newline at end of file diff --git a/docs/assets/usage/information_provider_system/animation.gif b/docs/assets/usage/information_provider_system/animation.gif new file mode 100644 index 00000000..d16c9747 Binary files /dev/null and b/docs/assets/usage/information_provider_system/animation.gif differ diff --git a/docs/concepts.md b/docs/concepts.md index 844c50ee..ddf38633 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -5,50 +5,85 @@ nav_order: 2 --- # Concepts + This page explains the different concepts of Part-DB and what their intended use is: 1. TOC {:toc} -## Part managment +## Part management ### Part -A part is the central concept of Part-DB. A part represents a single kind (or type) of a thing, like an electronic component, an device, an book or similar (depending on what you use Part-DB for). A part entity just represents a certain type of a thing, so if you have 1000 times an BC547 transistor you would create ONE part with the name BC547 and set its quantity to 1000. The individual quantities (so a single BC547 transistor) of a part, should be indistinguishable from each other, so that it does not matter which one of your 1000 things of Part you use. -A part entity have many fields, which can be used to describe it better. Most of the fields are optional: -* **Name** (Required): The name of the part or how you wanna call it. This could be an manufacturer provided name, or a name you thought of your self. The name have to be unique in a single category. -* **Description**: A short (single-line) description of what this part is/does. For longer informations you should use the comment field or the specifications + +A part is the central concept of Part-DB. A part represents a single kind (or type) of a thing, like an electronic +component, a device, a book or similar (depending on what you use Part-DB for). A part entity just represents a certain +type of thing, so if you have 1000 times a BC547 transistor you would create ONE part with the name BC547 and set its +quantity to 1000. The individual quantities (so a single BC547 transistor) of a part, should be indistinguishable from +each other so that it does not matter which one of your 1000 things of Part you use. +A part entity has many fields, which can be used to describe it better. Most of the fields are optional: + +* **Name** (Required): The name of the part or how you want to call it. This could be a manufacturer-provided name, or a + name you thought of yourself. Each name needs to be unique and must exist in a single category. +* **Description**: A short (single-line) description of what this part is/does. For longer information, you should use + the comment field or the specifications * **Category** (Required): The category (see there) to which this part belongs to. -* **Tags**: The list of tags this part belong to. Tags can be used to group parts logically (similar to the category), but tags are much less strict and formal (they dont have to be defined forehands) and you can assign multiple tags to a part. When clicking on a tag, a list with all parts which have the same tag, is shown. -* **Min Instock**: *Not really implemented yet*. Parts where the total instock is below this value, will show up for ordering. -* **Footprint**: See there. Useful especially for electronic parts, which have one of the common electronic footprints (like DIP8, SMD0805 or similar). If a part has no explicit defined preview picture, the preview picture of its footprint will be shown instead in tables. +* **Tags**: The list of tags this part belongs to. Tags can be used to group parts logically (similar to the category), + but tags are much less strict and formal (they don't have to be defined forehands) and you can assign multiple tags to + a part. When clicking on a tag, a list with all parts which have the same tag, is shown. +* **Min Instock**: *Not really implemented yet*. Parts where the total instock is below this value, will show up for + ordering. +* **Footprint**: See there. Useful especially for electronic parts, which have one of the common electronic footprints ( + like DIP8, SMD0805 or similar). If a part has no explicitly defined preview picture, the preview picture of its + footprint will be shown instead in tables. * **Manufacturer**: The manufacturer which has manufactured (not sold) this part. See Manufacturer entity for more info. -* **Manufacturer part number** (MPN): If you have used your own name for a part, you can put the part number the manufacturer uses in this field, so that you can find a part also under its manufacturer number. -* **Link to product page**: If you want to link to the manufacturer website of a part, and it is not possible to determine it automatically from the part name, set in the manufacturer entity (or no manfacturer is set), you can set the link here for each part individually. -* **Manufacturing Status**: The manufacturing status of this part, meaning the information about where the part is in its manufacturing lifecycle. -* **Needs review**: If you think parts informations maybe are inaccurate or incomplete and needs some later review/checking, you can set this flag. A part with this flag is marked, so that users know the informations are not completly trustworthy. +* **Manufacturer part number** (MPN): If you have used your own name for a part, you can put the part number the + manufacturer uses in this field so that you can find a part also under its manufacturer number. +* **Link to product page**: If you want to link to the manufacturer website of a part, and it is not possible to + determine it automatically from the part name, set in the manufacturer entity (or no manufacturer is set), you can set + the link here for each part individually. +* **Manufacturing Status**: The manufacturing status of this part, meaning the information about where the part is in + its manufacturing lifecycle. +* **Needs review**: If you think parts information may be inaccurate or incomplete and needs some later + review/checking, you can set this flag. A part with this flag is marked, so that users know the information is not + completely trustworthy. * **Favorite**: Parts with this flag are highlighted in parts lists * **Mass**: The mass of a single piece of this part (so of a single transistor). Given in grams. -* **Internal Part number** (IPN): Each part is automatically assigned an numerical ID which identifies a part in the database. This ID depends on when a part was created and can not be changed. If you want to assign your own unique identifiers, or sync parts identifiers with the identifiers of another database you can use this field. +* **Internal Part number** (IPN): Each part is automatically assigned a numerical ID that identifies a part in the + database. This ID depends on when a part was created and can not be changed. If you want to assign your own unique + identifiers, or sync parts identifiers with the identifiers of another database you can use this field. ### Stock / Part lot -A part can have many stock at multiple different locations. This is represented by part lots / stocks, which consists basically of a storelocation (so where are the parts of this lot are stored) and an amount (how many parts are there). -### Purchase Informations -The purchase informations describe where the part can be bought (at which vendors) and to which prices. -The first part (the order information) describes at which supplier the part can be bought and which is the name of the part under which you can order the part there. -An order information can contain multiple price informations, which describes the prices for the part at the supplier including bulk discount, etc. +A part can have many stocks at multiple different locations. This is represented by part lots/stocks, which consists +basically of a storage location (so where the parts of this lot are stored) and an amount (how many parts are there). + +### Purchase Information + +The purchase information describes where the part can be bought (at which vendors) and at which prices. +The first part (the order information) describes at which supplier the part can be bought and which is the name of the +part under which you can order the part there. +An order information can contain multiple price information, which describes the prices for the part at the supplier +including bulk discount, etc. ### Parameters -Parameters represents various specifications / parameters of a part, like the the maximum current of a diode, etc. The advantage of using parameters instead of just putting the data in the comment field or so, is that you can filter for parameters values (including ranges and more) later on. -Parameters describe can describe numeric values and/or text values for which they can be filtered. This basically allows you to define custom fields on a part. -Using the group field a parameter allows you to group parameters together in the info page later (all parameters with the same group value will be shown under the same group title). +Parameters represent various specifications/parameters of a part, like the maximum current of a diode, etc. The +advantage of using parameters instead of just putting the data in the comment field or so, is that you can filter for +parameter's values (including ranges and more) later on. +Parameters can describe numeric values and/or text values for which they can be filtered. This allows +you to define custom fields on a part. + +Using the group field as a parameter allows you to group parameters together on the info page later (all parameters with +the same group value will be shown under the same group title). ## Core data + ### Category -A category is used to group parts logically by their function (e.g. all NPN transistors would be put in a "NPN-Transistors" category). -Categories are hierarchical structures meaning that you can create logical trees to group categories together. A possible category tree could look like this: +A category is used to group parts logically by their function (e.g. all NPN transistors would be put in a " +NPN-Transistors" category). +Categories are hierarchical structures meaning that you can create logical trees to group categories together. A +possible category tree could look like this: * Active Components * Transistors @@ -60,97 +95,148 @@ Categories are hierarchical structures meaning that you can create logical trees * MCUs * Passive Components * Capacitors - * Resitors + * Resistors ### Supplier -A Supplier is a vendor / distributor where you can buy/order parts. Price informations of parts are associated with a supplier. + +A Supplier is a vendor/distributor where you can buy/order parts. Price information of parts is associated with a +supplier. ### Manufacturer -A manufacturer represents the company that manufacturer / build various parts (not necessary sell them). If the manufacturer also sell the parts, you have to create a supplier for that. -### Storelocation -A storelocation represents a place where parts can be stored. This could be a box, a shelf or other things (like the SMD feeder of a machine or so). +A manufacturer represents the company that manufacturers/builds various parts (not necessarily sell them). If the +manufacturer also sells the parts, you have to create a supplier for that. -Storelocations are hierarchical to represent storelocations contained in each other. +### Storage location + +A storage location represents a place where parts can be stored. This could be a box, a shelf, or other things (like the +SMD feeder of a machine or so). + +Storage locations are hierarchical to represent storage locations contained in each other. An example tree could look like this: + * Shelf 1 - * Box 1 - * Box 2 - * Box shelf A1 - * Box shelf A2 - * Box shelf B1 - * Box shelf B2 + * Box 1 + * Box 2 + * Box shelf A1 + * Box shelf A2 + * Box shelf B1 + * Box shelf B2 * Shelf 2 * Cupboard -Storelocations should be defined down to the smallest possible location, to make finding the part again easy. +Storage locations should be defined down to the smallest possible location, to make finding the part again easy. ### Footprint -In electronics many components have one of the common components cases / footprints. The footprint entity describes such common footprints, which can be assigned to parts. -You can assign an image (and an 3D model) as an attachment to a footprint, which will be used as preview for parts with this footprint, even if the parts do not have an explicitly assigned preview image. -Footprints are a hierachically which allows you to build logical sorted trees. An example tree could look like this: +In electronics, many components have one of the common components cases/footprints. The footprint entity describes such +common footprints, which can be assigned to parts. +You can assign an image (and a 3D model) as an attachment to a footprint, which will be used as preview for parts with +this footprint, even if the parts do not have an explicitly assigned preview image. + +Footprints are hierarchically which allows you to build logically sorted trees. An example tree could look like this: * Through-Hole components * DIP - * DIP-8 - * DIP-28 - * DIP-28W + * DIP-8 + * DIP-28 + * DIP-28W * TO - * TO-92 + * TO-92 * SMD components * SOIC - * SO-8 + * SO-8 * Resistors - * 0805 - * 0603 + * 0805 + * 0603 ### Measurement Unit -By default part instock is counted in number of individual parts, which is fine for things like electronic components, which exists only in integer quantities. However if you have things with fractional units like the length of a wire or the volume of a liquid, you have to define a measurement unit. -The measurement unit represents a physical quantity like mass, volume or length. -You can define a short unit for it (like m for Meters, or g for gramms) which will be shown, when a quantity of a part with this unit is shown. +By default, part in stock is counted in number of individual parts, which is fine for things like electronic components, +which exist only in integer quantities. However, if you have things with fractional units like the length of a wire or +the volume of a liquid, you have to define a measurement unit. +The measurement unit represents a physical quantity like mass, volume, or length. + +You can define a short unit for it (like m for Meters, or g for grams) which will be shown when a quantity of a part +with this unit is shown. + +In order to cover wider use cases and allow you to define measurement units further, it is possible to define parameters +associated to a measurement unit. These parameters are distinct from a part's parameters and are not inherited. ### Currency -By default all prices are set in the base currency configured for the instance (by default euros). If you want to use multiple currencies together (as e.g. vendors use foreign currencies for their price and you do not want to update the prices for every exchange rate change), you have to define these currencies here. -You can set an exchange rate here in terms of the base currency (or fetch it from the internet if configured). The exchange rate will be used to show users the prices in their preferred currency. +By default, all prices are set in the base currency configured for the instance (by default euros). If you want to use +multiple currencies together (e.g. vendors use foreign currencies for their price, and you do not want to update the +prices for every exchange rate change), you have to define these currencies here. + +You can set an exchange rate here in terms of the base currency (or fetch it from the internet if configured). The +exchange rate will be used to show users the prices in their preferred currency. ## Attachments + ### Attachment -An attachment is an file that can be associated with another entity (like a Part, Storelocation, User, etc.). This could for example be a datasheet in a Part, the logo of a vendor or some CAD drawing of a footprint. -An attachment has an attachment type (see below), which groups the attachments logically (and optionally restricts the allowed file types), a name describing the attachment and a file. The file can either be uploaded to the server and stored there, or given as a link to a file on another webpath. If configured in the settings, it is also possible that the webserver downloads the file from the supplied website and stores it locally on the server. +An attachment is a file that can be associated with another entity (like a Part, location, User, etc.). This could +for example be a datasheet in a Part, the logo of a vendor or some CAD drawing of a footprint. -By default all uploaded files, are accessible for everyone (even non logged in users), if the link is known. If your Part-DB instance is publicly available and you want to store private/sensitve files on it, you should mark the attachment as "Private attachment". Private attachments are only accessible to users, which has the permission to access private attachments. -Please not, that no thumbnails are generated for private attachments, which can have an performance impact. +An attachment has an attachment type (see below), which groups the attachments logically (and optionally restricts the +allowed file types), a name describing the attachment and a file. The file can either be uploaded to the server and +stored there, or given as a link to a file on another web path. If configured in the settings, it is also possible that +the web server downloads the file from the supplied website and stores it locally on the server. -Part-DB ships some preview images for various common footprints like DIP-8 and others, as internal ressources. These can be accessed/searched by typing the keyword in the URL field of a part and choosing one of the choices from the dropdown. +By default, all uploaded files, are accessible for everyone (even non-logged-in users), if the link is known. If your +Part-DB instance is publicly available, and you want to store private/sensitive files on it, you should mark the +attachment as "Private attachment". Private attachments are only accessible to users, which has permission to access +private attachments. +Please note, that no thumbnails are generated for private attachments, which can have a performance impact. -### Preview image / attachment -Most entities with attachments allow you to select one of the defined attachments as "Preview image". You can select an image attachment here, that previews the entity, this could be a picture of a Part, the logo of a manufacturer or supplier, the schematic symbol of a category or the image of an footprint. -The preview image will be shown in various locations together with the entities name. +Part-DB ships some preview images for various common footprints like DIP-8 and others, as internal resources. These can +be accessed/searched by typing the keyword in the URL field of a part and choosing one of the choices from the dropdown. -Please note that as long as the picture is not secret, it should be stored on the Part-DB instance (by upload, or letting Part-DB download the file) and *not* be marked as a private attachments, so that thumbnails can be generated for the picture (which improves performance). +### Preview image/attachment +Most entities with attachments allow you to select one of the defined attachments as "Preview image". You can select an +image attachment here, that previews the entity, this could be a picture of a Part, the logo of a manufacturer or +supplier, the schematic symbol of a category or the image of a footprint. +The preview image will be shown in various locations together with the entity's name. + +Please note that as long as the picture is not secret, it should be stored on the Part-DB instance (by uploading, or +letting Part-DB download the file) and *not* be marked as a private attachment, so that thumbnails can be generated for +the picture (which improves performance). ### Attachment types -Attachment types define logical groups of attachments. For example you could define an attachment group "Datasheets" where all datasheets of Parts, Footprints, etc. belong in, "Pictures" for preview images and more. -You can define file type restrictions, which file types and extensions are allowed for files with that attachment type. + +Attachment types define logical groups of attachments. For example, you could define an attachment group "Datasheets" +where all datasheets of Parts, Footprints, etc. belong in, "Pictures" for preview images and more. +You can define file type restrictions, and which file types and extensions are allowed for files with that attachment type. ## User System -### User -Each person which should be able to use Part-DB (by logging in) is represented by an user entity, which defines things like access rights, the password, and other things. For security reasons, every person which will use Part-DB should use its own personal account with an secret password. This allows to track activity of the users via the log. -There is a special user called `anonymous`, whose access rights are used to determine what an non-logged in user can do. Normally the anonymous user should be the most restricted user. +### User + +Each person who should be able to use Part-DB (by logging in) is represented by a user entity, which defines things +like access rights, the password, and other things. For security reasons, every person who will use Part-DB should use +their own personal account with a secret password. This allows to track activity of the users via the log. + +There is a special user called `anonymous`, whose access rights are used to determine what a non-logged-in user can do. +Normally the anonymous user should be the most restricted user. For simplification of access management users can be assigned to groups. ### Group -A group is entity, to which users can be assigned to. This can be used to logically group users by for example organisational structures and to simplify permissions managment, as you can define groups with access rights for common use cases and then just assign users to them, without the need to change every permission on the users individually. + +A group is an entity, to which users can be assigned to. This can be used to logically group users by for example +organizational structures and to simplify permissions management, as you can define groups with access rights for common +use cases and then just assign users to them, without the need to change every permission on the users individually. ## Labels -### Label profiles -A label profile represents an template for a label (for a storelocation, a part or part lot). It consists of a size, an barcode type and the content. There are various placeholders which can be inserted in the text content and which will be used replaced with data for the actual thing. -You do not have to define a label profile to generate labels (you can just set the settings on the fly in the label dialog), however if you want to generate many labels, it is recommended to save the settings as label profile, to save it for later usage. This ensures that all generated labels look the same. \ No newline at end of file +### Label profiles + +A label profile represents a template for a label (for a storage location, a part or part lot). It consists of a size, a +barcode type and the content. There are various placeholders that can be inserted in the text content and which will be +replaced with data for the actual thing. + +You do not have to define a label profile to generate labels (you can just set the settings on the fly in the label +dialog), however, if you want to generate many labels, it is recommended to save the settings as a label profile, to save +it for later usage. This ensures that all generated labels look the same. diff --git a/docs/configuration.md b/docs/configuration.md index 3c947f48..0ad30a00 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -6,74 +6,250 @@ nav_order: 5 # Configuration -Part-DBs behavior can be configured to your needs. There are different kind of configuration options: Options which are user changable (changable dynamically via frontend), options which can be configured by environment variables, and options which are only configurable via symfony config files. +Part-DBs behavior can be configured to your needs. There are different kinds of configuration options: Options, which are +user-changeable (changeable dynamically via frontend), options that can be configured by environment variables, and +options that are only configurable via Symfony config files. -## User changable -Following things can be changed for every user and a user can change it for himself (if he has the correct permission for it). Configuration is either possible via the users own setting page (where you can also change the password) or via the user admin page: -* **Language**: The language that the users prefers, and which will be used when no language is explicitly specified. Language can still always be changed via the language selector. By default the global configured language is used. -* **Timezone**: The timezone which the user resides in and in which all dates and times should be shown. By default the globally configured language. -* **Theme**: The theme to use for the frontend. Allows the user to choose the frontend design, he prefers. -* **Prefered currency**: One of the defined currencies, in which all prices should be shown, if possible. Prices with other currencies will be converted to the price selected here +## User changeable + +The following things can be changed for every user and a user can change it for himself (if he has the correct permission +for it). Configuration is either possible via the user's own settings page (where you can also change the password) or via +the user admin page: + +* **Language**: The language that the users prefer, and which will be used when no language is explicitly specified. + Language can still always be changed via the language selector. By default, the globally configured language is used. +* **Timezone**: The timezone in which the user resides and in which all dates and times should be shown. By default, the + globally configured language. +* **Theme**: The theme to use for the front end. Allows the user to choose the front end design, he prefers. +* **Preferred currency**: One of the defined currencies, in which all prices should be shown, if possible. Prices with + other currencies will be converted to the price selected here ## Environment variables (.env.local) -The following configuration options can only be changed by the server administrator, by either changing the server variables, changing the `.env.local` file or setting env for your docker container. Here are just the most important options listed, see `.env` file for full list of possible env variables. + +The following configuration options can only be changed by the server administrator, by either changing the server +variables, changing the `.env.local` file or setting env for your docker container. Here are just the most important +options listed, see `.env` file for the full list of possible env variables. ### General options -* `DATABASE_URL`: Configures the database which Part-DB uses. For mysql use a string in the form of `mysql://:@:/` here (e.g. `DATABASE_URL=mysql://user:password@127.0.0.1:3306/part-db`. For sqlite use the following format to specify the absolute path where it should be located `sqlite:///path/part/app.db`. You can use `%kernel.project_dir%` as placeholder for the Part-DB root folder (e.g. `sqlite:///%kernel.project_dir%/var/app.db`) -* `DEFAULT_LANG`: The default language to use serverwide (when no language is explictly specified by a user or via language chooser). Must be something like `en`, `de`, `fr`, etc. -* `DEFAULT_TIMEZONE`: The default timezone to use globally, when a user has not timezone specified. Must be something like `Europe/Berlin`. See [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) under TZ Database name for a list of available options. -* `BASE_CURRENCY`: The currency to use internally for monetary values and when no currency is explictly specified. When migrating from a legacy Part-DB version, this should be the same as the currency in the old Part-DB instance (normally euro). This should be the currency you use the most. **Please note that you can not really change this setting after you have created data**. The value has to be a valid [ISO4217](https://en.wikipedia.org/wiki/ISO_4217) code, like `EUR` or `USD`. -* `INSTANCE_NAME`: The name of your installation. It will be shown as a title in the navbar and other places. By default `Part-DB`, but you can customize it to something likes `ExampleCorp. Inventory`. -* `ALLOW_ATTACHMENT_DOWNLOADS` (allowed values `0` or `1`): By setting this option to 1, users can make Part-DB directly download a file specified as an URL and create it as local file. Please not that this allows users access to all ressources publicly available to the server (so full access to other servers in the same local network), which could be a security risk. -* `USE_GRAVATAR`: Set to `1` to use [gravatar.com](gravatar.com) images for user avatars (as long as they have not set their own picture). The users browsers have to download the pictures from a third-party (gravatars) server, so this might be a privacy risk. +* `DATABASE_URL`: Configures the database which Part-DB uses: + * For MySQL (or MariaDB) use a string in the form of `mysql://:@:/` here + (e.g. `DATABASE_URL=mysql://user:password@127.0.0.1:3306/part-db`). + * For SQLite use the following format to specify the + absolute path where it should be located `sqlite:///path/part/app.db`. You can use `%kernel.project_dir%` as + placeholder for the Part-DB root folder (e.g. `sqlite:///%kernel.project_dir%/var/app.db`) + * For Postgresql use a string in the form of `DATABASE_URL=postgresql://user:password@127.0.0.1:5432/part-db?serverVersion=x.y`. + + Please note that **`serverVersion=x.y`** variable is required due to dependency of Symfony framework. + +* `DATABASE_MYSQL_USE_SSL_CA`: If this value is set to `1` or `true` and a MySQL connection is used, then the connection + is encrypted by SSL/TLS and the server certificate is verified against the system CA certificates or the CA certificate +bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept all certificates. +* `DATABASE_EMULATE_NATURAL_SORT` (default 0): If set to 1, Part-DB will emulate natural sorting, even if the database + does not support it natively. However this is much slower than the native sorting, and contain bugs or quirks, so use + it only, if you have to. +* `DEFAULT_LANG`: The default language to use server-wide (when no language is explicitly specified by a user or via + language chooser). Must be something like `en`, `de`, `fr`, etc. +* `DEFAULT_TIMEZONE`: The default timezone to use globally, when a user has no timezone specified. Must be something + like `Europe/Berlin`. See [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) under TZ Database name + for a list of available options. +* `BASE_CURRENCY`: The currency to use internally for monetary values and when no currency is explicitly specified. When + migrating from a legacy Part-DB version, this should be the same as the currency in the old Part-DB instance (normally + euro). This should be the currency you use the most. **Please note that you can not really change this setting after + you have created data**. The value has to be a valid [ISO4217](https://en.wikipedia.org/wiki/ISO_4217) code, + like `EUR` or `USD`. +* `INSTANCE_NAME`: The name of your installation. It will be shown as a title in the navbar and other places. By + default `Part-DB`, but you can customize it to something likes `ExampleCorp. Inventory`. +* `ALLOW_ATTACHMENT_DOWNLOADS` (allowed values `0` or `1`): By setting this option to 1, users can make Part-DB directly + download a file specified as a URL and create it as a local file. Please note that this allows users access to all + resources publicly available to the server (so full access to other servers in the same local network), which could + be a security risk. +* `ATTACHMENT_DOWNLOAD_BY_DEFAULT`: When this is set to 1, the "download external file" checkbox is checked by default + when adding a new attachment. Otherwise, it is unchecked by default. Use this if you wanna download all attachments + locally by default. Attachment download is only possible, when `ALLOW_ATTACHMENT_DOWNLOADS` is set to 1. +* `USE_GRAVATAR`: Set to `1` to use [gravatar.com](https://gravatar.com/) images for user avatars (as long as they have + not set their own picture). The users browsers have to download the pictures from a third-party (gravatar) server, so + this might be a privacy risk. +* `MAX_ATTACHMENT_FILE_SIZE`: The maximum file size (in bytes) for attachments. You can use the suffix `K`, `M` or `G` + to specify the size in kilobytes, megabytes or gigabytes. By default `100M` (100 megabytes). Please note that this is + only the limit of Part-DB. You still need to configure the php.ini `upload_max_filesize` and `post_max_size` to allow + bigger files to be uploaded. +* `DEFAULT_URI`: The default URI base to use for the Part-DB, when no URL can be determined from the browser request. + This should be the primary URL/Domain, which is used to access Part-DB. This value is used to create correct links in + emails and other places, where the URL is needed. It is also used, when SAML is enabled.s If you are using a reverse + proxy, you should set this to the URL of the reverse proxy (e.g. `https://part-db.example.com`). **This value must end + with a slash**. +* `ENFORCE_CHANGE_COMMENTS_FOR`: With this option, you can configure, where users are enforced to give a change reason, + which will be written to the log. This is a comma-separated list of values (e.g. `part_edit,part_delete`). Leave empty + to make change comments optional everywhere. Possible values are: + * `part_edit`: Edit operation of an existing part + * `part_delete`: Delete operation of an existing part + * `part_create`: Creation of a new part + * `part_stock_operation`: Stock operation on a part (therefore withdraw, add or move stock) + * `datastructure_edit`: Edit operation of an existing datastructure (e.g. category, manufacturer, ...) + * `datastructure_delete`: Delete operation of a existing datastructure (e.g. category, manufacturer, ...) + * `datastructure_create`: Creation of a new datastructure (e.g. category, manufacturer, ...) +* `CHECK_FOR_UPDATES` (default `1`): Set this to 0, if you do not want Part-DB to connect to GitHub to check for new + versions, or if your server can not connect to the internet. +* `APP_SECRET`: This variable is a configuration parameter used for various security-related purposes, + particularly for securing and protecting various aspects of your application. It's a secret key that is used for + cryptographic operations and security measures (session management, CSRF protection, etc..). Therefore this + value should be handled as confidential data and not shared publicly. ### E-Mail settings -* `MAILER_DSN`: You can configure the mail provider which should be used for email delivery (see https://symfony.com/doc/current/components/mailer.html for full documentation). If you just want to use an SMTP mail account, you can use the following syntax `MAILER_DSN=smtp://user:password@smtp.mailserver.invalid:587` -* `EMAIL_SENDER_EMAIL`: The email address from which emails should be sent from (in most cases this has to be the same as the email address used for SMTP access) -* `EMAIL_SENDER_NAME`: Similar to `EMAIL_SENDER_EMAIL` but this allows you to specify the name from which the mails are sent from. -* `ALLOW_EMAIL_PW_RESET`: Set this value to true, if you wan to allow users to reset their password via an email notification. You have to configure the mailprovider first before via the MAILER_DSN setting. -### History/Eventlog related settings +* `MAILER_DSN`: You can configure the mail provider which should be used for email delivery ( + see https://symfony.com/doc/current/components/mailer.html for full documentation). If you just want to use an SMTP + mail account, you can use the following syntax `MAILER_DSN=smtp://user:password@smtp.mailserver.invalid:587` +* `EMAIL_SENDER_EMAIL`: The email address from which emails should be sent from (in most cases this has to be the same + as the email address used for SMTP access) +* `EMAIL_SENDER_NAME`: Similar to `EMAIL_SENDER_EMAIL`, but this allows you to specify the name from which the mails are + sent from. +* `ALLOW_EMAIL_PW_RESET`: Set this value to true, if you want to allow users to reset their password via an email + notification. You have to configure the mail provider first before via the MAILER_DSN setting. + +### Table related settings + +* `TABLE_DEFAULT_PAGE_SIZE`: The default page size for tables. This is the number of rows which are shown per page. Set + to `-1` to disable pagination and show all rows at once. +* `TABLE_PARTS_DEFAULT_COLUMNS`: The columns in parts tables, which are visible by default (when loading table for first + time). + Also specify the default order of the columns. This is a comma separated list of column names. Available columns + are: `name`, `id`, `ipn`, `description`, `category`, `footprint`, `manufacturer`, `storage_location`, `amount`, `minamount`, `partUnit`, `addedDate`, `lastModified`, `needs_review`, `favorite`, `manufacturing_status`, `manufacturer_product_number`, `mass`, `tags`, `attachments`, `edit`. + +### History/Eventlog-related settings + The following options are used to configure, which (and how much) data is written to the system log: -* `HISTORY_SAVE_CHANGED_FIELDS`: When this option is set to true, the name of the fields which are changed, are saved to the DB (so for example it is logged that a user has changed, that the user has changed the name and description of the field, but not the data/content of these changes) -* `HISTORY_SAVE_CHANGED_DATA`: When this option is set to true, the changed data is saved to log (so it is logged, that a user has changed the name of a part and what the name was before). This can increase database size, when you have a lot of changes to enties. -* `HISTORY_SAVE_REMOVED_DATA`: When this option is set to true, removed data is saved to log, meaning that you can easily undelete an entity, when it was removed accidentally. -If you wanna use want to revert changes or view older revisions of entities, then `HISTORY_SAVE_CHANGED_FIELDS`, `HISTORY_SAVE_CHANGED_DATA` and `HISTORY_SAVE_REMOVED_DATA` all have to be true. +* `HISTORY_SAVE_CHANGED_FIELDS`: When this option is set to true, the name of the fields that are changed, are saved to + the DB (so for example it is logged that a user has changed, that the user has changed the name and description of the + field, but not the data/content of these changes) +* `HISTORY_SAVE_CHANGED_DATA`: When this option is set to true, the changed data is saved to log (so it is logged, that + a user has changed the name of a part and what the name was before). This can increase database size when you have a + lot of changes to entities. +* `HISTORY_SAVE_REMOVED_DATA`: When this option is set to true, removed data is saved to log, meaning that you can + easily undelete an entity, when it was removed accidentally. +* `HISTORY_SAVE_NEW_DATA`: When this option is set to true, the new data (the data after a change) is saved to element + changed log entries. This allows you to easily see the changes between two revisions of an entity. This can increase + database size, when you have a lot of changes to entities. + +If you want to use want to revert changes or view older revisions of entities, +then `HISTORY_SAVE_CHANGED_FIELDS`, `HISTORY_SAVE_CHANGED_DATA` and `HISTORY_SAVE_REMOVED_DATA` all have to be true. ### Error pages settings -* `ERROR_PAGE_ADMIN_EMAIL`: You can set an email-address here, which is shown on the error page, who should be contacted about the issue (e.g. an IT support email of your company) -* `ERROR_PAGE_SHOW_HELP`: Set this 0, to disable the solution hints shown on an error page. These hints should not contain senstive informations, but could confuse end-users. -### Other / less used options -* `TRUSTED_PROXIES`: Set the IP addresses (or IP blocks) of trusted reverse proxies here. This is needed to get correct IP informations (see [here](https://symfony.com/doc/current/deployment/proxies.html) for more info). -* `TRUSTED_HOSTS`: To prevent `HTTP Host header attacks` you can set a regex containing all host names via which Part-DB should be accessible. If accessed via the wrong hostname, an error will be shown. -* `DEMO_MODE`: Set Part-DB into demo mode, which forbids users to change their passwords and settings. Used for the demo instance, should not be needed for normal installations. -* `NO_URL_REWRITE_AVAILABLE` (allowed values `true` or `false`): Set this value to true, if your webserver does not support rewrite. In this case, all URL pathes will contain index.php/, which is needed then. Normally this setting do not need to be changed. -* `FIXER_API_KEY`: If you want to automatically retrieve exchange rates for base currencies other than euros, you have configure an exchange rate provider API. [Fixer.io](https://fixer.io/) is preconfigured, and you just have to register there and set the retrieved API key in this environment variable. -* `APP_ENV`: This value should always be set to `prod` in normal use. Set it to `dev` to enable debug/development mode. (**You should not do this on a publicly accessible server, as it will leak sensitive informations!**) -* `BANNER`: You can configure the text that should be shown as the banner on the homepage. Useful especially for docker container. In all other applications you can just change the `config/banner.md` file. +* `ERROR_PAGE_ADMIN_EMAIL`: You can set an email address here, which is shown on the error page, who should be contacted + about the issue (e.g. an IT support email of your company) +* `ERROR_PAGE_SHOW_HELP`: Set this 0, to disable the solution hints shown on an error page. These hints should not + contain sensitive information but could confuse end-users. + +### EDA related settings + +* `EDA_KICAD_CATEGORY_DEPTH`: A number, which determines how many levels of Part-DB categories should be shown inside KiCad. + All parts in the selected category and all subcategories are shown in KiCad. + For performance reason this value should not be too high. The default is 0, which means that only the top level categories are shown in KiCad. + All parts in the selected category and all subcategories are shown in KiCad. Set this to a higher value, if you want to show more categories in KiCad. + When you set this value to -1, all parts are shown inside a single category in KiCad. + +### SAML SSO settings + +The following settings can be used to enable and configure Single-Sign on via SAML. This allows users to log in to +Part-DB without entering a username and password, but instead they are redirected to a SAML Identity Provider (IdP) and +are logged in automatically. This is especially useful when you want to use Part-DB in a company, where all users have +a SAML account (e.g. via Active Directory or LDAP). +You can find more advanced settings in the `config/packages/hslavich_onelogin_saml.yaml` file. Please note that this +file is not backed up by the backup script, so you have to back up it manually, if you want to keep your changes. If you +want to edit it on docker, you have to map the file to a volume. + +* `SAML_ENABLED`: When this is set to 1, SAML SSO is enabled and the SSO Login button is shown in the login form. You + have to configure the SAML settings below before you can use this feature. +* `SAML_BEHIND_PROXY`: Set this to 1, if Part-DB is behind a reverse proxy. See [here]({% link installation/reverse-proxy.md %}) + for more information. Otherwise, leave it to 0 (default.) +* `SAML_ROLE_MAPPING`: A [JSON](https://en.wikipedia.org/wiki/JSON)-encoded map which specifies how Part-DB should + convert the user roles given by SAML attribute `group` should be converted to a Part-DB group (specified by ID). You + can use a wildcard `*` to map all otherwise unmapped roles to a certain group. + Example: `{"*": 1, "admin": 2, "editor": 3}`. This would map all roles to the group with ID 1, except the + role `admin`, which is mapped to the group with ID 2, and the role `editor`, which is mapped to the group with ID 3. +* `SAML_UPDATE_GROUP_ON_LOGIN`: When this is enabled the group of the user is updated on every login of the user based + on the SAML role attributes. When this is disabled, the group is only assigned on the first login of the user, and a + Part-DB administrator can change the group afterward by editing the user. +* `SAML_IDP_ENTITY_ID`: The entity ID of your SAML Identity Provider (IdP). You can find this value in the metadata XML + file or configuration UI of your IdP. +* `SAML_IDP_SINGLE_SIGN_ON_SERVICE`: The URL of the SAML IdP Single Sign-On Service (SSO). You can find this value in + the metadata XML file or configuration UI of your IdP. +* `SAML_IDP_SINGLE_LOGOUT_SERVICE`: The URL of the SAML IdP Single Logout Service (SLO). You can find this value in the + metadata XML file or configuration UI of your IdP. +* `SAML_IDP_X509_CERT`: The base64 encoded X.509 public certificate of your SAML IdP. You can find this value in the + metadata XML file or configuration UI of your IdP. It should start with `MIIC` and end with `=`. +* `SAML_SP_ENTITY_ID`: The entity ID of your SAML Service Provider (SP). This is the value you have configured for the + Part-DB client in your IdP. +* `SAML_SP_X509_CERT`: The public X.509 certificate of your SAML SP (here Part-DB). This is the value you have + configured for the Part-DB client in your IdP. It should start with `MIIC` and end with `=`. IdPs like keycloak allows + you to generate a public/private key pair for the client which you can set up here and in the `SAML_SP_PRIVATE_KEY` + setting. +* `SAML_SP_PRIVATE_KEY`: The private key of your SAML SP (here Part-DB), corresponding the public key specified + in `SAML_SP_X509_CERT`. This is the value you have configured for the Part-DB client in your IdP. It should start + with `MIIE` and end with `=`. IdPs like keycloak allows you to generate a public/private key pair for the client which + you can set up here and in the `SAML_SP_X509_CERT` setting. + +### Information provider settings + +The settings prefixes with `PROVIDER_*` are used to configure the information providers. +See the [information providers]({% link usage/information_provider_system.md %}) page for more information. + +### Other / less-used options + +* `TRUSTED_PROXIES`: Set the IP addresses (or IP blocks) of trusted reverse proxies here. This is needed to get correct + IP information (see [here](https://symfony.com/doc/current/deployment/proxies.html) for more info). +* `TRUSTED_HOSTS`: To prevent `HTTP Host header attacks` you can set a regex containing all host names via which Part-DB + should be accessible. If accessed via the wrong hostname, an error will be shown. +* `DEMO_MODE`: Set Part-DB into demo mode, which forbids users to change their passwords and settings. Used for the demo + instance. This should not be needed for normal installations. +* `NO_URL_REWRITE_AVAILABLE` (allowed values `true` or `false`): Set this value to true, if your webserver does not + support rewrite. In this case, all URL paths will contain index.php/, which is needed then. Normally this setting does + not need to be changed. +* `REDIRECT_TO_HTTPS`: If this is set to true, all requests to http will be redirected to https. This is useful if your + web server does not already do this (like the one used in the demo instance). If your web server already redirects to + https, you don't need to set this. Ensure that Part-DB is accessible via HTTPS before you enable this setting. +* `FIXER_API_KEY`: If you want to automatically retrieve exchange rates for base currencies other than euros, you have to + configure an exchange rate provider API. [Fixer.io](https://fixer.io/) is preconfigured, and you just have to register + there and set the retrieved API key in this environment variable. +* `APP_ENV`: This value should always be set to `prod` in normal use. Set it to `dev` to enable debug/development + mode. (**You should not do this on a publicly accessible server, as it will leak sensitive information!**) +* `BANNER`: You can configure the text that should be shown as the banner on the homepage. Useful especially for docker + containers. In all other applications you can just change the `config/banner.md` file. +* `DISABLE_YEAR2038_BUG_CHECK`: If set to `1`, the year 2038 bug check is disabled on 32-bit systems, and dates after +2038 are no longer forbidden. However this will lead to 500 error messages when rendering dates after 2038 as all current +32-bit PHP versions can not format these dates correctly. This setting is for the case that future PHP versions will +handle this correctly on 32-bit systems. 64-bit systems are not affected by this bug, and the check is always disabled. ## Banner + To change the banner you can find on the homepage, you can either set the `BANNER` environment variable to the text you want to show, or you can edit the `config/banner.md` file. The banner is written in markdown, so you can use all markdown (and even some subset of HTML) syntax to format the text. ## parameters.yaml -You can also configure some options via the `config/parameters.yaml` file. This should normally not needed, -and you should know what you are doing, when you change something here. You should expect, that you will have to do some -manual merge, when you have changed something here and update to a newer version of Part-DB. It is possible that configuration -options here will change or completely removed in future versions of Part-DB. -If you change something here, you have to clear the cache, before the changes will take effect with the command `bin/console cache:clear`. +You can also configure some options via the `config/parameters.yaml` file. This should normally not need, +and you should know what you are doing, when you change something here. You should expect, that you will have to do some +manual merge, when you have changed something here and update to a newer version of Part-DB. It is possible that +configuration options here will change or be completely removed in future versions of Part-DB. + +If you change something here, you have to clear the cache, before the changes will take effect with the +command `bin/console cache:clear`. The following options are available: -* `partdb.global_theme`: The default theme to use, when no user specific theme is set. Should be one of the themes from the `partdb.available_themes` config option. -* `partdb.locale_menu`: The codes of the languages, which should be shown in the language chooser menu (the one with the user icon in the navbar). The first language in the list will be the default language. -* `partdb.gpdr_compliance`: When set to true (default value), IP addresses which are saved in the database will be anonymized, by removing the last byte of the IP. This is required by the GDPR (General Data Protection Regulation) in the EU. -* `partdb.sidebar.items`: The panel contents which should be shown in the sidebar by default. You can also change the number of sidebar panels by changing the number of items in this list. +* `partdb.global_theme`: The default theme to use, when no user specific theme is set. Should be one of the themes from + the `partdb.available_themes` config option. +* `partdb.locale_menu`: The codes of the languages, which should be shown in the language chooser menu (the one with the + user icon in the navbar). The first language in the list will be the default language. +* `partdb.gdpr_compliance`: When set to true (default value), IP addresses which are saved in the database will be + anonymized, by removing the last byte of the IP. This is required by the GDPR (General Data Protection Regulation) in + the EU. +* `partdb.sidebar.items`: The panel contents which should be shown in the sidebar by default. You can also change the + number of sidebar panels by changing the number of items in this list. * `partdb.sidebar.root_node_enable`: Show a root node in the sidebar trees, of which all nodes are children of * `partdb.sidebar.root_expanded`: Expand the root node in the sidebar trees by default -* `partdb.available_themes`: The list of available themes a user can choose from. \ No newline at end of file +* `partdb.available_themes`: The list of available themes a user can choose from. diff --git a/docs/index.md b/docs/index.md index c2f561cf..d732f31d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,37 +5,52 @@ nav_order: 0 --- # Part-DB + Part-DB is an Open-Source inventory management system for your electronic components. It is installed on a web server and so can be accessed with any browser without the need to install additional software. {: .important-title } > Demo -> -> If you want to test Part-DB without installing it, you can use [this](https://part-db.herokuapp.com) Heroku instance. -> (Or this link for the [German Version](https://part-db.herokuapp.com/de/)). +> +> If you want to test Part-DB without installing it, you can use [this](https://demo.part-db.de/) Heroku instance. +> (Or this link for the [German Version](https://demo.part-db.de/de/)). > > You can log in with username: **user** and password: **user**, to change/create data. > -> Every change to the master branch gets automatically deployed, so it represents the currenct development progress and is -> maybe not completly stable. Please mind, that the free Heroku instance is used, so it can take some time when loading the page +> Every change to the master branch gets automatically deployed, so it represents the current development progress and +> is +> maybe not completely stable. Please mind, that the free Heroku instance is used, so it can take some time when loading +> the page > for the first time. ## Features -* Inventory management of your electronic parts. Each part can be assigned to a category, footprint, manufacturer - and multiple store locations and price information. Parts can be grouped using tags. You can associate various files like datasheets or pictures with the parts. -* Multi-Language support (currently German, English, Russian, Japanese and French (experimental)) + +* Inventory management of your electronic parts. Each part can be assigned to a category, footprint, manufacturer, + and multiple store locations and price information. Parts can be grouped using tags. You can associate various files + like datasheets or pictures with the parts. +* Multi-language support (currently German, English, Russian, Japanese and French (experimental)) * Barcodes/Labels generator for parts and storage locations, scan barcodes via webcam using the builtin barcode scanner * User system with groups and detailed (fine granular) permissions. - Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups. Password reset via email can be setuped. -* Import/Export system (partial working) -* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build this project and directly withdraw all components needed from DB -* Event log: Track what changes happens to your inventory, track which user does what. Revert your parts to older versions. + Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups. + Password reset via email can be setup. +* Optional support for single sign-on (SSO) via SAML (using an intermediate service + like [Keycloak](https://www.keycloak.org/) you can connect Part-DB to an existing LDAP or Active Directory server) +* Import/Export system +* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build + this project and directly withdraw all components needed from DB +* Event log: Track what changes happens to your inventory, track which user does what. Revert your parts to older + versions. * Responsive design: You can use Part-DB on your PC, your tablet and your smartphone using the same interface. -* MySQL and SQLite supported as database backends +* MySQL, SQLite and PostgreSQL are supported as database backends * Support for rich text descriptions and comments in parts * Support for multiple currencies and automatic update of exchange rates supported * Powerful search and filter function, including parametric search (search for parts according to some specifications) - +* Easy migration from an existing PartKeepr instance (see [here]({%link partkeepr_migration.md %})) +* Use cloud providers (like Octopart, Digikey, Farnell or TME) to automatically get part information, datasheets and + prices for parts (see [here]({% link usage/information_provider_system.md %})) +* API to access Part-DB from other applications/scripts +* [Integration with KiCad]({%link usage/eda_integration.md %}): Use Part-DB as central datasource for your + KiCad and see available parts from Part-DB directly inside KiCad. With these features Part-DB is useful to hobbyists, who want to keep track of their private electronic parts inventory, or makerspaces, where many users have should have (controlled) access to the shared inventory. @@ -43,25 +58,31 @@ or makerspaces, where many users have should have (controlled) access to the sha Part-DB is also used by small companies and universities for managing their inventory. ## License + Part-DB is licensed under the GNU Affero General Public License v3.0 (or at your opinion any later). This mostly means that you can use Part-DB for whatever you want (even use it commercially) as long as you publish the source code for every change you make under the AGPL, too. -See [LICENSE](https://github.com/Part-DB/Part-DB-symfony/blob/master/LICENSE) for more informations. +See [LICENSE](https://github.com/Part-DB/Part-DB-symfony/blob/master/LICENSE) for more information. ## Donate for development + If you want to donate to the Part-DB developer, see the sponsor button in the top bar (next to the repo name). There you will find various methods to support development on a monthly or a one time base. ## Built with + * [Symfony 5](https://symfony.com/): The main framework used for the serverside PHP * [Bootstrap 5](https://getbootstrap.com/) and [Bootswatch](https://bootswatch.com/): Used as website theme * [Fontawesome](https://fontawesome.com/): Used as icon set -* [Hotwire Stimulus](https://stimulus.hotwired.dev/) and [Hotwire Turbo](https://turbo.hotwired.dev/): Frontend Javascript +* [Hotwire Stimulus](https://stimulus.hotwired.dev/) and [Hotwire Turbo](https://turbo.hotwired.dev/): Frontend + Javascript ## Authors -* **Jan Böhmer** - *Inital work and Maintainer* - [Github](https://github.com/jbtronics/) -See also the list of [contributors](https://github.com/Part-DB/Part-DB-symfony/graphs/contributors) who participated in this project. +* **Jan Böhmer** - *Initial work and Maintainer* - [GitHub](https://github.com/jbtronics/) + +See also the list of [contributors](https://github.com/Part-DB/Part-DB-symfony/graphs/contributors) who participated in +this project. Based on the original Part-DB by Christoph Lechner and K. Jacobs diff --git a/docs/installation/choosing_database.md b/docs/installation/choosing_database.md index b3a7a3a8..cd9657d4 100644 --- a/docs/installation/choosing_database.md +++ b/docs/installation/choosing_database.md @@ -7,24 +7,176 @@ nav_order: 1 # Choosing database: SQLite or MySQL -Part-DB saves its data in a [relational (SQL) database](https://en.wikipedia.org/wiki/Relational_database). Part-DB supports either the use of [SQLite](https://www.sqlite.org/index.html) or [MySQL](https://www.mysql.com/) / [MariaDB](https://mariadb.org/) (which are mostly the same, except for some minor differences). +Part-DB saves its data in a [relational (SQL) database](https://en.wikipedia.org/wiki/Relational_database). + +For this multiple database types are supported, currently these are: + +* [SQLite](https://www.sqlite.org/index.html) +* [MySQL](https://www.mysql.com/) / [MariaDB](https://mariadb.org/) (which are mostly the same, except for some minor + differences) +* [PostgreSQL](https://www.postgresql.org/) + +All these database types allow for the same basic functionality and allow Part-DB to run. However, there are some minor +differences between them, which might be important for you. Therefore the pros and cons of the different database types +are listed here. {: .important } -You have to choose between the database types before you start using Part-DB and **you can not change it (easily) after you have started creating data**. So you should choose the database type for your usecase (and possible future uses). +You have to choose between the database types before you start using Part-DB and **you can not change it (easily) after +you have started creating data**. So you should choose the database type for your use case (and possible future uses). ## Comparison -**SQLite** is the default database type which is configured out of the box. All data is saved in a single file (normally `var/app.db` in the Part-DB folder) and no additional installation or configuration besides Part-DB is needed. -To use **MySQL/MariaDB** as database, you have to install and configure the MySQL server, configure it and create a database and user for Part-DB, which needs some additional work. When using docker you need an additional docker container, and volume for the data +### SQLite -When using **SQLite** The database can be backuped easily by just copying the SQLite file to a safe place. Ideally the **MySQL** database has to be dumped to a SQL file (using `mysqldump`). The `console partdb:backup` command can do this automatically - -However SQLite does not support certain operations like regex search, which has to be emulated by PHP and therefore are pretty slow compared to the same operation at MySQL. In future there might be features that may only be available, when using MySQL. +#### Pros -In general MySQL might perform better for big Part-DB instances with many entries, lots of users and high activity, than SQLite. +* **Easy to use**: No additional installation or configuration is needed, just start Part-DB and it will work out of the box +* **Easy backup**: Just copy the SQLite file to a safe place, and you have a backup, which you can restore by copying it + back. No need to work with SQL dumps -## Conclusion and Suggestion +#### Cons -When you are a hobbyist and use Part-DB for your own small inventory managment with only you as user (or maybe sometimes a few other people), then the easy to use SQLite database will be fine. +* **Performance**: SQLite is not as fast as MySQL or PostgreSQL, especially when using complex queries or many users. +* **Emulated RegEx search**: SQLite does not support RegEx search natively. Part-DB can emulate it, however that is pretty slow. +* **Emualted natural sorting**: SQLite does not support natural sorting natively. Part-DB can emulate it, but it is pretty slow. +* **Limitations with Unicode**: SQLite has limitations in comparisons and sorting of Unicode characters, which might lead to + unexpected behavior when using non-ASCII characters in your data. For example `µ` (micro sign) is not seen as equal to + `μ` (greek minuscule mu), therefore searching for `µ` (micro sign) will not find parts containing `μ` (mu) and vice versa. + The other databases behave more intuitive in this case. +* **No advanced features**: SQLite do no support many of the advanced features of MySQL or PostgreSQL, which might be utilized + in future versions of Part-DB -When you are planning to have a very big database, with a lot of entries and many users which regulary (and concurrently) using Part-DB you should maybe use MySQL as this will scale better. \ No newline at end of file + +### MySQL/MariaDB + +**If possible, it is recommended to use MariaDB 10.7+ (instead of MySQL), as it supports natural sorting of columns natively.** + +#### Pros + +* **Performance**: Compared to SQLite, MySQL/MariaDB will probably perform better, especially in large databases with many + users and high activity. +* **Natural Sorting**: MariaDB 10.7+ supports natural sorting of columns. On other databases it has to be emulated, which is pretty + slow. +* **Native RegEx search**: MySQL supports RegEx search natively, which is faster than emulating it in PHP. +* **Advanced features**: MySQL/MariaDB supports many advanced features, which might be utilized in future versions of Part-DB. +* **Full Unicode support**: MySQL/MariaDB has better support for Unicode characters, which makes it more intuitive to use + non-ASCII characters in your data. + +#### Cons + +* **Additional installation and configuration**: You have to install and configure the MySQL server, create a database and + user for Part-DB, which needs some additional work compared to SQLite. +* **Backup**: The MySQL database has to be dumped to a SQL file (using `mysqldump`). The `console partdb:backup` command can automate this. + + +### PostgreSQL + +#### Pros +* **Performance**: PostgreSQL is known for its performance, especially in large databases with many users and high activity. +* **Advanced features**: PostgreSQL supports many advanced features, which might be utilized in future versions of Part-DB. +* **Full Unicode support**: PostgreSQL has better support for Unicode characters, which makes it more intuitive to use + non-ASCII characters in your data. +* **Native RegEx search**: PostgreSQL supports RegEx search natively, which is faster than emulating it in PHP. +* **Native Natural Sorting**: PostgreSQL supports natural sorting of columns natively in all versions and in general the support for it + is better than on MariaDB. +* **Support of transactional DDL**: PostgreSQL supports transactional DDL, which means that if you encounter a problem during a schema change, +the database will automatically rollback the changes. On MySQL/MariaDB you have to manually rollback the changes, by restoring from a database backup. + +#### Cons +* **New backend**: The support of postgresql is new, and it was not tested as much as the other backends. There might be some bugs caused by this. +* **Additional installation and configuration**: You have to install and configure the PostgreSQL server, create a database and + user for Part-DB, which needs some additional work compared to SQLite. +* **Backup**: The PostgreSQL database has to be dumped to a SQL file (using `pg_dump`). The `console partdb:backup` command can automate this. + + +## Recommendation + +When you are a hobbyist and use Part-DB for your own small inventory management with only you as user (or maybe sometimes +a few other people), then the easy-to-use SQLite database will be fine, as long as you can live with the limitations, stated above. +However using MariaDB (or PostgreSQL), has no disadvantages in that situation (besides the initial setup requirements), so you might +want to use it, to be prepared for future use cases. + +When you are planning to have a very big database, with a lot of entries and many users which regularly using Part-DB, then you should +use MariaDB or PostgreSQL, as they will perform better in that situation and allow for more advanced features. +If you should use MariaDB or PostgreSQL depends on your personal preference and what you already have installed on your servers and +what you are familiar with. + +## Using the different databases + +The only difference in using the different databases, is a different value in the `DATABASE_URL` environment variable in the `.env.local` file +or in the `DATABASE_URL` environment variable in your server or container configuration. It has the shape of a URL, where the scheme (the part before `://`) +is the database type, and the rest is connection information. + +**The env var format below is for the `env.local` file. It might work differently for other env configuration. E.g. in a docker-compose file you have to remove the quotes!** + +### SQLite + +```shell +DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db" +``` + +Here you just need to configure the path to the SQLite file, which is created by Part-DB when performing the database migrations. +The `%kernel.project_dir%` is a placeholder for the path to the project directory, which is replaced by the actual path by Symfony, so that you do not +need to specify the path manually. In the example the database will be created as `app.db` in the `var` directory of your Part-DB installation folder. + +### MySQL/MariaDB + +```shell +DATABASE_URL="mysql://user:password@127.0.0.1:3306/database?serverVersion=8.0.37" +``` + +Here you have to replace `user`, `password` and `database` with the credentials of the MySQL/MariaDB user and the database name you want to use. +The host (here 127.0.0.1) and port should also be specified according to your MySQL/MariaDB server configuration. + +In the `serverVersion` parameter you can specify the version of the MySQL/MariaDB server you are using, in the way the server returns it +(e.g. `8.0.37` for MySQL and `10.4.14-MariaDB`). If you do not know it, you can leave the default value. + +If you want to use a unix socket for the connection instead of a TCP connnection, you can specify the socket path in the `unix_socket` parameter. +```shell +DATABASE_URL="mysql://user:password@localhost/database?serverVersion=8.0.37&unix_socket=/var/run/mysqld/mysqld.sock" +``` + +### PostgreSQL + +```shell +DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=12.19&charset=utf8" +``` + +Here you have to replace `db_user`, `db_password` and `db_name` with the credentials of the PostgreSQL user and the database name you want to use. +The host (here 127.0.0.1) and port should also be specified according to your PostgreSQL server configuration. + +In the `serverVersion` parameter you can specify the version of the PostgreSQL server you are using, in the way the server returns it +(e.g. `12.19 (Debian 12.19-1.pgdg120+1)`). If you do not know it, you can leave the default value. + +The `charset` parameter specify the character set of the database. It should be set to `utf8` to ensure that all characters are stored correctly. + +If you want to use a unix socket for the connection instead of a TCP connnection, you can specify the socket path in the `host` parameter. +```shell +DATABASE_URL="postgresql://db_user@localhost/db_name?serverVersion=16.6&charset=utf8&host=/var/run/postgresql" +``` + + +## Natural Sorting + +Natural sorting is the sorting of strings in a way that numbers are sorted by their numerical value, not by their ASCII value. + +For example in the classical binary sorting the string `DIP-4`, `DIP-8`, `DIP-16`, `DIP-28` would be sorted as following: + +* `DIP-16` +* `DIP-28` +* `DIP-4` +* `DIP-8` + +In natural sorting, it would be sorted as: + +* `DIP-4` +* `DIP-8` +* `DIP-16` +* `DIP-28` + +Part-DB can sort names in part tables and tree views naturally. PostgreSQL and MariaDB 10.7+ support natural sorting natively, +and it is automatically used if available. + +For SQLite and MySQL < 10.7 it has to be emulated if wanted, which is pretty slow. Therefore it has to be explicity enabled by setting the +`DATABASE_EMULATE_NATURAL_SORT` environment variable to `1`. If it is 0 the classical binary sorting is used, on these databases. The emulations +might have some quirks and issues, so it is recommended to use a database which supports natural sorting natively, if you want to use it. diff --git a/docs/installation/email.md b/docs/installation/email.md index 08211616..c9feaba6 100644 --- a/docs/installation/email.md +++ b/docs/installation/email.md @@ -7,31 +7,34 @@ nav_order: 12 # Email -Part-DB can communicate with its users via email. +Part-DB can communicate with its users via email. At the moment this is only used to send password reset links, but in future this will be used for other things too. To make emails work you have to properly configure a mail provider in Part-DB. ## Configuration -Part-DB uses [Symfony Mailer](https://symfony.com/doc/current/mailer.html) to send emails, which supports multiple -automatic mail providers (like MailChimp or SendGrid). If you want to use one of these providers, check the Symfony Mailer documentation for more information. -We will only cover the configuration of a SMTP provider here, which is sufficient for most usecases. -You will need an email account, which you can use send emails from via password-bases SMTP authentication, this account +Part-DB uses [Symfony Mailer](https://symfony.com/doc/current/mailer.html) to send emails, which supports multiple +automatic mail providers (like MailChimp or SendGrid). If you want to use one of these providers, check the Symfony +Mailer documentation for more information. + +We will only cover the configuration of an SMTP provider here, which is sufficient for most use-cases. +You will need an email account, which you can use send emails from via password-bases SMTP authentication, this account should be dedicated to Part-DB. To configure the SMTP provider, you have to set the following environment variables: -`MAILER_DSN`: You have to provide the SMTP server address and the credentials for the email account here. The format is the following: -`smtp://:@:`. In most cases the username is the email address of the account, and the port is 587. +`MAILER_DSN`: You have to provide the SMTP server address and the credentials for the email account here. The format is +the following: +`smtp://:@:`. In most cases the username is the email address of the +account, and the port is 587. So the resulting DSN could look like this: `smtp://j.doe@mail.invalid:SUPER_SECRET_PA$$WORD@smtp.mail.invalid:587`. `EMAIL_SENDER_EMAIL`: This is the email address which will be used as sender address for all emails sent by Part-DB. -This should be the same email address as the one used in the `MAILER_DSN` (the email adress of your email account): +This should be the same email address as the one used in the `MAILER_DSN` (the email address of your email account): e.g. `j.doe@mail.invalid`. -`EMAIL_SENDER_NAME`: This is the name which will be used as sender name for all emails sent by Part-DB. +`EMAIL_SENDER_NAME`: This is the name which will be used as sender name for all emails sent by Part-DB. This can be anything you want, e.g. `My Part-DB Mailer`. - Now you can enable the possibility to reset password by setting the `ALLOW_EMAIL_PW_RESET` env to `1` (or `true`). \ No newline at end of file diff --git a/docs/installation/index.md b/docs/installation/index.md index e7b59104..217f702a 100644 --- a/docs/installation/index.md +++ b/docs/installation/index.md @@ -6,4 +6,6 @@ has_children: true --- # Installation -Below you can find some guides to install Part-DB. \ No newline at end of file +Below you can find some guides to install Part-DB. + +For the hobbyists without much experience, we recommend the docker installation or direct installation on debian. \ No newline at end of file diff --git a/docs/installation/installation_docker.md b/docs/installation/installation_docker.md index ddac7e42..c9b46fdb 100644 --- a/docs/installation/installation_docker.md +++ b/docs/installation/installation_docker.md @@ -7,19 +7,23 @@ nav_order: 2 # Installation of Part-DB via docker -Part-DB can be installed containerized via docker. This is the easiest way to get Part-DB up and running and works on all platforms, -where docker is available (especially recommended for Windows and MacOS). - +Part-DB can be installed containerized via docker. This is the easiest way to get Part-DB up and running and works on +all platforms, +where docker is available (especially recommended for Windows and macOS). {: .warning } -> The methods described here, configure PHP without HTTPS and therefore should only be used locally in a trusted network. +> The methods described here, configure PHP without HTTPS and therefore should only be used locally in a trusted +> network. > If you want to expose Part-DB to the internet, you have to configure a reverse proxy with an SSL certificate! +It is recommended to install Part-DB on a 64-bit system, as the 32-bit version of PHP is affected by the +[Year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) and can not handle dates after 2038 correctly. + ## Docker-compose + Docker-compose configures the needed images and automatically creates the needed containers and volumes. - -1. Install docker and docker-compose like described under https://docs.docker.com/compose/install/ +1. Install docker and docker-compose as described under https://docs.docker.com/compose/install/ 2. Create a folder where the Part-DB data should live 3. Create a file named docker-compose.yaml with the following content: @@ -43,11 +47,18 @@ services: - DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db # In docker env logs will be redirected to stderr - APP_ENV=docker + + # Uncomment this, if you want to use the automatic database migration feature. With this you have you do not have to + # run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/ + # folder (under .automigration-backup), so you can restore it, if the migration fails. + # This feature is currently experimental, so use it at your own risk! + # - DB_AUTOMIGRATE=true # You can configure Part-DB using environment variables # Below you can find the most essential ones predefined - # However you can add add any other environment configuration you want here + # However you can add any other environment configuration you want here # See .env file for all available options or https://docs.part-db.de/configuration.html + # !!! Do not use quotes around the values, as they will be interpreted as part of the value and this will lead to errors !!! # The language to use serverwide as default (en, de, ru, etc.) - DEFAULT_LANG=en @@ -64,23 +75,35 @@ services: # Use gravatars for user avatars, when user has no own avatar defined - USE_GRAVATAR=0 - # Override value if you want to show to show a given text on homepage. + # Override value if you want to show a given text on homepage. # When this is empty the content of config/banner.md is used as banner #- BANNER=This is a test banner
with a line break + + # If you use a reverse proxy in front of Part-DB, you must configure the trusted proxies IP addresses here (see reverse proxy documentation for more information): + # - TRUSTED_PROXIES=127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 ``` -4. Customize the settings by changing the environment variables (or add new ones). See [Configuration]({% link configuration.md %}) for more information. + +4. Customize the settings by changing the environment variables (or adding new ones). See [Configuration]({% link + configuration.md %}) for more information. 5. Inside the folder, run + ```bash docker-compose up -d ``` -6. Create the inital database with + +6. Create the initial database with + ```bash docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate ``` -and watch for the password output -6. Part-DB is available under `http://localhost:8080` and you can log in with username `admin` and the password shown before -The docker image uses a SQLite database and all data (database, uploads and other media) is put into folders relative to the docker-compose.yml. +and watch for the password output + +6. Part-DB is available under `http://localhost:8080` and you can log in with the username `admin` and the password shown + before + +The docker image uses a SQLite database and all data (database, uploads, and other media) is put into folders relative to +the docker-compose.yml. ### MySQL @@ -113,6 +136,12 @@ services: # In docker env logs will be redirected to stderr - APP_ENV=docker + # Uncomment this, if you want to use the automatic database migration feature. With this you have you do not have to + # run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/ + # folder (under .automigration-backup), so you can restore it, if the migration fails. + # This feature is currently experimental, so use it at your own risk! + # - DB_AUTOMIGRATE=true + # You can configure Part-DB using environment variables # Below you can find the most essential ones predefined # However you can add add any other environment configuration you want here @@ -140,7 +169,8 @@ services: database: container_name: partdb_database image: mysql:8.0 - command: --default-authentication-plugin=mysql_native_password + restart: unless-stopped + command: --default-authentication-plugin=mysql_native_password --log-bin-trust-function-creators=1 environment: # Change this Password MYSQL_ROOT_PASSWORD: SECRET_ROOT_PASSWORD @@ -156,8 +186,10 @@ services: ``` ### Update Part-DB + You can update Part-DB by pulling the latest image and restarting the container. Then you have to run the database migrations again + ```bash docker-compose pull docker-compose up -d @@ -165,19 +197,30 @@ docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate ``` ## Direct use of docker image -You can use the `jbtronics/part-db1:master` image directly. You have to expose the port 80 to a host port and configure volumes for `/var/www/html/uploads` and `/var/www/html/public/media`. -If you want to use SQLite database (which is default), you have to configure Part-DB to put the database file in a mapped volume via the `DATABASE_URL` environment variable. -For example if you set `DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db` then you will have to map the `/var/www/html/var/db/` folder to the docker container (see docker-compose.yaml for example). +You can use the `jbtronics/part-db1:master` image directly. You have to expose port 80 to a host port and configure +volumes for `/var/www/html/uploads` and `/var/www/html/public/media`. -You also have to create the database like described above in step 4. +If you want to use SQLite database (which is default), you have to configure Part-DB to put the database file in a +mapped volume via the `DATABASE_URL` environment variable. +For example, if you set `DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db` then you will have to map +the `/var/www/html/var/db/` folder to the docker container (see docker-compose.yaml for example). + +You also have to create the database as described above in step 4. ## Running console commands -You can run the console commands described in README by executing `docker exec --user=www-data -it partdb bin/console [command]` + +You can run the console commands described in README by +executing `docker exec --user=www-data -it partdb bin/console [command]` + +{: .warning } +> If you run a root console inside the container, and wanna execute commands on the webserver behalf, be sure to use `sudo -E` command (with the `-E` flag) to preserve env variables from the current shell. +> Otherwise Part-DB console might use the wrong configuration to execute commands. ## Troubleshooting -*Login not possible. Login page is just reloading and no error message is shown or something like "CSFR token invalid"*: +*Login is not possible. Login page is just reloading and no error message is shown or something like "CSFR token invalid"*: -Clear all cookies in your browser or use a inkognito tab for Part-DB. -This related to the fact that Part-DB can not set cookies via HTTP, after some webpage has set cookies before under localhost via https. This is a security mechanism of the browser and can not be bypassed by Part-DB. +Clear all cookies in your browser or use an incognito tab for Part-DB. +This is related to the fact that Part-DB can not set cookies via HTTP after some webpages have set cookies before under +localhost via HTTPS. This is a security mechanism of the browser and can not be bypassed by Part-DB. diff --git a/docs/installation/installation_guide-debian.md b/docs/installation/installation_guide-debian.md index b8365116..885eea90 100644 --- a/docs/installation/installation_guide-debian.md +++ b/docs/installation/installation_guide-debian.md @@ -6,25 +6,41 @@ nav_order: 4 --- # Part-DB installation guide for Debian 11 (Bullseye) -This guide shows you how to install Part-DB directly on Debian 11 using apache2 and SQLite. This guide should work with recent Ubuntu and other Debian based distributions with little to no changes. -Depending on what you want to do, using the prebuilt docker images may be a better choice, as you dont need to install this much dependencies. See **TODO** for more information of the docker installation. + +This guide shows you how to install Part-DB directly on Debian 11 using apache2 and SQLite. This guide should work with +recent Ubuntu and other Debian-based distributions with little to no changes. +Depending on what you want to do, using the prebuilt docker images may be a better choice, as you don't need to install +this many dependencies. See [here]({% link installation/installation_docker.md %}) for more information on the docker +installation. {: .warning } -> The methods described here, configure PHP without HTTPS and therefore should only be used locally in a trusted network. +> The methods described here, configure PHP without HTTPS and therefore should only be used locally in a trusted +> network. > If you want to expose Part-DB to the internet, you HAVE to configure an SSL connection! +It is recommended to install Part-DB on a 64-bit system, as the 32-bit version of PHP is affected by the +[Year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) and can not handle dates after 2038 correctly. + ## Installation with SQLite database ### Install prerequisites + For the installation of Part-DB, we need some prerequisites. They can be installed by running the following command: + ```bash sudo apt install git curl zip ca-certificates software-properties-common apt-transport-https lsb-release nano wget ``` ### Install PHP and apache2 -Part-DB is written in [PHP](https://php.net) and therefore needs an PHP interpreter to run. Part-DB needs PHP 7.3 or higher, however it is recommended to use the most recent version of PHP for performance reasons and future compatibility. -As Debian 11 does not ship PHP 8.1 in it's default repositories, we have to add a repository for it. You can skip this step if your distribution is shipping a recent version of PHP or you want to use the built-in PHP version. +Part-DB is written in [PHP](https://php.net) and therefore needs a PHP interpreter to run. Part-DB needs PHP 8.1 or +higher. However, it is recommended to use the most recent version of PHP for performance reasons and future +compatibility. + +As Debian 11 does not ship PHP 8.1 in its default repositories, we have to add a repository for it. You can skip this +step if your distribution is shipping a recent version of PHP or you want to use the built-in PHP version. If you are +using Debian 12, you can skip this step, as PHP 8.1 is already included in the default repositories. + ```bash # Add sury repository for PHP 8.1 sudo curl -sSL https://packages.sury.org/php/README.txt | sudo bash -x @@ -32,14 +48,21 @@ sudo curl -sSL https://packages.sury.org/php/README.txt | sudo bash -x # Update package list sudo apt update && sudo apt upgrade ``` -Now you can install PHP 8.1 and required packages (change the 8.1 in the package version according to the version you want to use): + +Now you can install PHP 8.1 and the required packages (change the 8.1 in the package version according to the version you +want to use): + ```bash sudo apt install php8.1 libapache2-mod-php8.1 php8.1-opcache php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-bcmath php8.1-intl php8.1-zip php8.1-xsl php8.1-sqlite3 php8.1-mysql ``` + The apache2 webserver should be already installed with this command and configured basically. ### Install composer -Part-DB uses [composer](https://getcomposer.org/) to install required PHP libraries. As the versions shipped in the repositories is pretty old we install it manually: + +Part-DB uses [composer](https://getcomposer.org/) to install required PHP libraries. As the version shipped in the +repositories is pretty old, we will install it manually: + ```bash # Download composer installer script wget -O /tmp/composer-setup.php https://getcomposer.org/installer @@ -50,7 +73,10 @@ chmod +x /usr/local/bin/composer ``` ### Install yarn and nodejs -To build the frontend (the user interface) Part-DB uses [yarn](https://yarnpkg.com/). As it dependens on nodejs and the shipped versions are pretty old, we install new versions from offical nodejs repository: + +To build the front end (the user interface) Part-DB uses [yarn](https://yarnpkg.com/). As it depends on Node.js and the +shipped versions are pretty old, we install new versions from the official Node.js repository: + ```bash # Add recent node repository (nodejs 18 is supported until 2025) curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - @@ -59,6 +85,7 @@ sudo apt install nodejs ``` We can install yarn with the following commands: + ```bash # Add yarn repository curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null @@ -68,35 +95,64 @@ sudo apt update && sudo apt install yarn ``` ### Create a folder for Part-DB and download it -We now have all prerequisites installed and can start to install Part-DB. We will create a folder for Part-DB in a webfolder of apache2 and download it to this folder. The downloading is done via git, which allows you to update easily later. + +We now have all prerequisites installed and can start to install Part-DB. We will create a folder for Part-DB in the +webroot of apache2 and download it to this folder. The downloading is done via git, which allows you to update easily +later. + ```bash # Download Part-DB into the new folder /var/www/partdb git clone https://github.com/Part-DB/Part-DB-symfony.git /var/www/partdb ``` +By default, you are now on the latest development version. In most cases, you want to use the latest stable version. You +can switch to the latest stable version (tagged) by running the following command: + +```bash +# This finds the latest release/tag and checks it out +git checkout $(git describe --tags $(git rev-list --tags --max-count=1)) +``` + +Alternatively, you can check out a specific version by running ( +see [GitHub Releases page](https://github.com/Part-DB/Part-DB-server/releases) for a list of available versions): + +```bash +# This checks out the version 1.5.2 +git checkout v1.5.2 +``` + Change ownership of the files to the apache user: + ```bash chown -R www-data:www-data /var/www/partdb ``` For the next steps we should be in the Part-DB folder, so move into it: + ```bash cd /var/www/partdb ``` ### Create configuration for Part-DB -The basic configuration of Part-DB is done by a `.env.local` file in the main directory. Create on by from the default configuration: + +The basic configuration of Part-DB is done by a `.env.local` file in the main directory. Create on by from the default +configuration: + ```bash cp .env .env.local ``` -In your `.env.local` you can configure Part-DB according to your wishes. A full list of configuration options can be found [here]({% link configuration.md %}. +In your `.env.local` you can configure Part-DB according to your wishes. A full list of configuration options can be +found [here](../configuration.md). Other configuration options like the default language or default currency can be found in `config/parameters.yaml`. -Please check that the `partdb.default_currency` value in `config/parameters.yaml` matches your mainly used currency, as this can not be changed after creating price informations. +Please check that the `partdb.default_currency` value in `config/parameters.yaml` matches your mainly used currency, as +this can not be changed after creating price information. ### Install dependencies for Part-DB and build frontend + Part-DB depends on several other libraries and components. Install them by running the following commands: + ```bash # Install composer dependencies (please note the sudo command, to run it under the web server user) sudo -u www-data composer install --no-dev -o @@ -110,32 +166,47 @@ sudo yarn build ### Clear cache To ensure everything is working, clear the cache: + ```bash sudo -u www-data php bin/console cache:clear ``` ### Check if everything is installed + To check if everything is installed, run the following command: + ```bash sudo -u www-data php bin/console partdb:check-requirements ``` -The most things should be green, and no red ones. Yellow messages means optional dependencies which are not important but can improve performance and functionality. + +Most things should be green, and no red ones. Yellow messages mean optional dependencies which are not important +but can improve performance and functionality. ### Create a database for Part-DB -Part-DB by default uses a file based sqlite database to store the data. Use the following command to create the database. The database will normally created at `/var/www/partdb/var/app.db`. + +Part-DB by default uses a file-based SQLite database to store the data. Use the following command to create the +database. The database will normally be created at `/var/www/partdb/var/app.db`. + ```bash sudo -u www-data php bin/console doctrine:migrations:migrate ``` + The command will warn you about schema changes and potential data loss. Continue with typing `yes`. -The command will output several lines of informations. Somewhere should be a a yellow background message like `The initial password for the "admin" user is: f502481134`. Write down this password as you will need it later for inital login. +The command will output several lines of information. Somewhere should be a yellow background message +like `The initial password for the "admin" user is: f502481134`. Write down this password as you will need it later for the initial login. ### Configure apache2 to show Part-DB -Part-DB is now configured, but we have to say apache2 to serve Part-DB as web application. This is done by creating a new apache site: + +Part-DB is now configured, but we have to say apache2 to serve Part-DB as web application. This is done by creating a +new apache site: + ```bash sudo nano /etc/apache2/sites-available/partdb.conf ``` + and add the following content (change ServerName and ServerAlias to your needs): + ``` ServerName partdb.lan @@ -152,38 +223,53 @@ and add the following content (change ServerName and ServerAlias to your needs): CustomLog /var/log/apache2/partdb_access.log combined ``` + Activate the new site by: + ```bash sudo ln -s /etc/apache2/sites-available/partdb.conf /etc/apache2/sites-enabled/partdb.conf ``` -Configure apache to show pretty URL pathes for Part-DB (`/label/dialog` instead of `/index.php/label/dialog`): +Configure apache to show pretty URL paths for Part-DB (`/label/dialog` instead of `/index.php/label/dialog`): + ```bash sudo a2enmod rewrite ``` -If you want to access Part-DB via the IP-Address of the server, instead of the domain name, you have to remove the apache2 default configuration with: +If you want to access Part-DB via the IP-Address of the server, instead of the domain name, you have to remove the +apache2 default configuration with: + ```bash sudo rm /etc/apache2/sites-enabled/000-default.conf ``` Restart the apache2 webserver with: + ```bash sudo service apache2 restart ``` -and Part-DB should now be available under `http://YourServerIP` (or `http://partdb.lan` if you configured DNS in your network to point on the server). +and Part-DB should now be available under `http://YourServerIP` (or `http://partdb.lan` if you configured DNS in your +network to point to the server). ### Login to Part-DB -Navigate to the Part-DB web interface and login via the user icon in the top right corner. You can login using the username `admin` and the password you have written down earlier. + +Navigate to the Part-DB web interface and login via the user icon in the top right corner. You can log in using the +username `admin` and the password you have written down earlier. ## Update Part-DB + If you want to update your existing Part-DB installation, you just have to run the following commands: + ```bash # Move into Part-DB folder cd /var/www/partdb # Pull latest Part-DB version from GitHub git pull + +# Checkout the latest version (or use a specific version, like described above) +git checkout $(git describe --tags $(git rev-list --tags --max-count=1)) + # Apply correct permission chown -R www-data:www-data . # Install new composer dependencies @@ -199,11 +285,12 @@ sudo nano config/parameters.yaml sudo -u www-data php bin/console doctrine:migrations:migrate # Clear Part-DB cache -sudo u www-data php bin/console cache:clear +sudo -u www-data php bin/console cache:clear ``` ## MySQL/MariaDB database -To use a MySQL database, follow the steps from above (except the creation of database, we will do this later). + +To use a MySQL database, follow the steps from above (except the creation of the database, we will do this later). Debian 11 does not ship MySQL in its repositories anymore, so we use the compatible MariaDB instead: 1. Install maria-db with: @@ -213,9 +300,11 @@ sudo apt update && sudo apt install mariadb-server ``` 2. Configure maria-db with: + ```bash sudo mysql_secure_installation ``` + When asked for the root password, just press enter, as we have not set a root password yet. In the next steps you are asked if you want to switch to unix_socket authentication, answer with `n` and press enter. Then you are asked if you want to remove anonymous users, answer with `y` and press enter. @@ -224,33 +313,42 @@ Then you are asked if you want to remove the test database and access to it, ans Then you are asked if you want to reload the privilege tables now, answer with `y` and press enter. 3. Create a new database and user for Part-DB: Run the following commands: + ```bash sudo mariadb ``` + A SQL shell will open, in which you can run the following commands to create a new database and user for Part-DB. Replace 'YOUR_SECRET_PASSWORD' with a secure password. + ```sql CREATE DATABASE partdb; GRANT ALL PRIVILEGES ON partdb.* TO 'partdb'@'localhost' IDENTIFIED BY 'YOUR_SECRET_PASSWORD'; ``` -Finally save the changes with: + +Finally, save the changes with: + ```sql FLUSH PRIVILEGES; ``` + and exit the SQL shell with: + ```sql exit ``` 4. Configure Part-DB to use the new database. Open your `.env.local` file and search the line `DATABASE_URL`. -Change it to the following (you have to replace `YOUR_SECRET_PASSWORD` with the password you have choosen in step 3): + Change it to the following (you have to replace `YOUR_SECRET_PASSWORD` with the password you have chosen in step 3): + ``` -DATABASE_URL=DATABASE_URL=mysql://partdb:YOUR_SECRET_PASSWORD@127.0.0.1:3306/partdb +DATABASE_URL=mysql://partdb:YOUR_SECRET_PASSWORD@127.0.0.1:3306/partdb ``` 5. Create the database schema with: + ```bash sudo -u www-data php bin/console doctrine:migrations:migrate ``` -6. The migration step should have shown you a password for the admin user, which you can use now to login to Part-DB. +6. The migration step should have shown you a password for the admin user, which you can use now to log in to Part-DB. diff --git a/docs/installation/kubernetes.md b/docs/installation/kubernetes.md new file mode 100644 index 00000000..86504af2 --- /dev/null +++ b/docs/installation/kubernetes.md @@ -0,0 +1,42 @@ +--- +title: Kubernetes / Helm +layout: default +parent: Installation +nav_order: 5 +--- + +# Kubernetes / Helm Charts + +If you are using Kubernetes, you can use the [helm charts](https://helm.sh/) provided in this [repository](https://github.com/Part-DB/helm-charts). + +## Usage + +[Helm](https://helm.sh) must be installed to use the charts. Please refer to +Helm's [documentation](https://helm.sh/docs) to get started. + +Once Helm has been set up correctly, add the repo as follows: + +`helm repo add part-db https://part-db.github.io/helm-charts` + +If you had already added this repo earlier, run `helm repo update` to retrieve +the latest versions of the packages. You can then run `helm search repo +part-db` to see the charts. + +To install the part-db chart: + + helm install my-part-db part-db/part-db + +To uninstall the chart: + + helm delete my-part-db + +This repository is also available at [ArtifactHUB](https://artifacthub.io/packages/search?repo=part-db). + +## Configuration + +See the README in the [chart directory](https://github.com/Part-DB/helm-charts/tree/main/charts/part-db) for more +information on the available configuration options. + +## Bugreports + +If you find issues related to the helm charts, please open an issue in the [helm-charts repository](https://github.com/Part-DB/helm-charts). \ No newline at end of file diff --git a/docs/installation/nginx.md b/docs/installation/nginx.md index 542c11eb..84305975 100644 --- a/docs/installation/nginx.md +++ b/docs/installation/nginx.md @@ -6,14 +6,18 @@ nav_order: 10 --- # Nginx + You can also use [nginx](https://www.nginx.com/) as webserver for Part-DB. Setup Part-DB with apache is a bit easier, so this is the method shown in the guides. This guide assumes that you already have a working nginx installation with PHP configured. ## Setup -1. Install composer and yarn like described in the [apache guide]({% link installation/installation_guide-debian.md %}#install-composer). + +1. Install composer and yarn as described in the [apache guide]({% link installation/installation_guide-debian.md + %}#install-composer). 2. Create a folder for Part-DB and install and configure it as described 3. Instead of creating the config for apache, add the following snippet to your nginx config: + ```nginx server { # Redirect all HTTP requests to HTTPS @@ -48,6 +52,11 @@ server { location ~ \.php$ { return 404; } + + # Set Content-Security-Policy for svg files, to block embedded javascript in there + location ~* \.svg$ { + add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';"; + } error_log /var/log/nginx/parts.error.log; access_log /var/log/nginx/parts.access.log; @@ -64,4 +73,6 @@ server { ssl_prefer_server_ciphers on; } ``` -4. Restart nginx with `sudo systemctl restart nginx` and you should be able to access Part-DB under your configured domain. \ No newline at end of file + +4. Restart nginx with `sudo systemctl restart nginx` and you should be able to access Part-DB under your configured + domain. \ No newline at end of file diff --git a/docs/installation/proxmox.md b/docs/installation/proxmox.md new file mode 100644 index 00000000..865d2bf4 --- /dev/null +++ b/docs/installation/proxmox.md @@ -0,0 +1,31 @@ +--- +title: Proxmox VE LXC +layout: default +parent: Installation +nav_order: 6 +--- + +# Proxmox VE LXC + +{: .warning } +> The proxmox VE LXC script for Part-DB is developed and maintained by [Proxmox VE Helper-Scripts](https://community-scripts.github.io/ProxmoxVE/) +> and not by the Part-DB developers. Keep in mind that the script is not officially supported by the Part-DB developers. + +If you are using Proxmox VE you can use the scripts provided by [Proxmox VE Helper-Scripts community](https://community-scripts.github.io/ProxmoxVE/scripts?id=part-db) +to easily install Part-DB in a LXC container. + +## Usage + +To create a new LXC container with Part-DB, you can use the following command in the Proxmox VE shell: + +```bash +bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/part-db.sh)" +``` + +The same command can be used to update an existing Part-DB container. + +See the [helper script website](https://community-scripts.github.io/ProxmoxVE/scripts?id=part-db) for more information. + +## Bugreports + +If you find issues related to the proxmox VE LXC script, please open an issue in the [Proxmox VE Helper-Scripts repository](https://github.com/community-scripts/ProxmoxVE). diff --git a/docs/installation/reverse-proxy.md b/docs/installation/reverse-proxy.md index 5fdfef01..605b93fa 100644 --- a/docs/installation/reverse-proxy.md +++ b/docs/installation/reverse-proxy.md @@ -9,11 +9,32 @@ nav_order: 11 If you want to put Part-DB behind a reverse proxy, you have to configure Part-DB correctly to make it work properly. -You have to set the `TRUSTED_PROXIES` environment variable to the IP address of your reverse proxy -(either in your `docker-compose.yaml` in the case of docker, or `.env.local` in case of direct installation). +You have to set the `TRUSTED_PROXIES` environment variable to the IP address of your reverse proxy +(either in your `docker-compose.yaml` in the case of docker, or `.env.local` in case of direct installation). If you have multiple reverse proxies, you can set multiple IP addresses separated by a comma (or specify a range). -For example, if your reverse proxy has the IP address `192.168.2.10`, your value should be: +For example, if your reverse proxy has the IP address `192.168.2.10`, your value should be: + ``` TRUSTED_PROXIES=192.168.2.10 ``` + +Set the `DEFAULT_URI` environment variable to the URL of your Part-DB installation, available from the outside (so via +the reverse proxy). + +## Part-DB in a subpath via reverse proxy + +If you put Part-DB into a subpath via the reverse proxy, you have to configure your webserver to include `X-Forwarded-Prefix` in the request headers. +For example if you put Part-DB behind a reverse proxy with the URL `https://example.com/partdb`, you have to set the `X-Forwarded-Prefix` header to `/partdb`. + +In apache, you can do this by adding the following line to your virtual host configuration: + +``` +RequestHeader set X-Forwarded-Prefix "/partdb" +``` + +and in nginx, you can do this by adding the following line to your server configuration: + +``` +proxy_set_header X-Forwarded-Prefix "/partdb"; +``` \ No newline at end of file diff --git a/docs/installation/saml_sso.md b/docs/installation/saml_sso.md new file mode 100644 index 00000000..d2e65e7f --- /dev/null +++ b/docs/installation/saml_sso.md @@ -0,0 +1,237 @@ +--- +title: Single Sign-On via SAML +layout: default +parent: Installation +nav_order: 12 +--- + +# Single Sign-On via SAML + +Part-DB supports Single Sign-On via SAML. This means that you can use your existing SAML identity provider to log in to +Part-DB. +Using an intermediate SAML server like [Keycloak](https://www.keycloak.org/), also allows you to connect Part-DB to an +LDAP or Active Directory server. + +{: .important } +> This feature is for advanced users only. Single Sign-On is useful for large organizations with many users, which are +> already using SAML for other services. +> If you have only one or a few users, you should use the built-in authentication system of Part-DB. +> This guide assumes that you already have an SAML identity provider set up and working, and have a basic understanding +> of how SAML works. + +{: .warning } +> This feature is currently in beta. Please report any bugs you find. +> So far it has only tested with Keycloak, but it should work with any SAML 2.0 compatible identity provider. + +This guide will show you how to configure Part-DB with [Keycloak](https://www.keycloak.org/) as the SAML identity +provider, but it should work with any SAML 2.0 compatible identity provider. + +This guide assumes that you have a working Keycloak installation with some users. If you don't, you can follow +the [Keycloak Getting Started Guide](https://www.keycloak.org/docs/latest/getting_started/index.html). + +{: .important } +> Part-DB associates local users with SAML users by their username. That means if the username of a SAML user changes, a +> new local user will be created (and the old account can not be accessed). +> You should make sure that the username of a SAML user does not change. If you use Keycloak make sure that the +> possibility to change the username is disabled (which is by default). +> If you really have to rename a SAML user, a Part-DB admin can rename the local user in the Part-DB in the admin panel, +> to match the new username of the SAML user. + +## Configure basic SAML connection + +### Create a new SAML client + +1. First, you need to configure a new SAML client in Keycloak. Login in to your Keycloak admin console and go to + the `Clients` page. +2. Click on `Create client` and select `SAML` as type from the dropdown menu. For the client ID, you can use anything + you want, but it should be unique. + *It is recommended to set this value to the domain name of your Part-DB installation, with an attached `/sp` ( + e.g. `https://partdb.yourdomain.invalid/sp`)*. + The name field should be set to something human-readable, like `Part-DB`. +3. Click on `Save` to create a new client. + +### Configure the SAML client + +1. Now you need to configure the SAML client. Go to the `Settings` tab and set the following values: + * Set `Home URL` to the homepage of your Part-DB installation (e.g. `https://partdb.yourdomain.invalid/`). + * Set `Valid redirect URIs` to your homepage with a wildcard at the end ( + e.g. `https://partdb.yourdomain.invalid/*`). + * Set `Valid post logout redirect URIs` to `+` to allow all URLs from the `Valid redirect URIs`. + * Set `Name ID format` to `username` + * Ensure `Force POST binding` is enabled. + * Ensure `Sign documents` is enabled. + * Ensure `Front channel logout` is enabled. + * Ensure `Signature Algorithm` is set to `RSA_SHA256`. + + Click on `Save` to save the changes. +2. Go to the `Advanced` tab and set the following values: + * Assertion Consumer Service POST Binding URL to your homepage with `/saml/acs` at the end ( + e.g. `https://partdb.yourdomain.invalid/saml/acs`). + * Logout Service POST Binding URL to your homepage with `/logout` at the end ( + e.g. `https://partdb.yourdomain.invalid/logout`). +3. Go to Keys tab and ensure `Client Signature Required` is enabled. +4. In the Keys tab click on `Generate new keys`. This will generate a new key pair for the SAML client. The private key + will be downloaded to your computer. + +### Configure Part-DB to use SAML + +1. Open the `.env.local` file of Part-DB (or the docker-compose.yaml) for edit +2. Set the `SAMLP_SP_PRIVATE_KEY` environment variable to the content of the private key file you downloaded in the + previous step. It should start with `MIEE` and end with `=`. +3. Set the `SAML_SP_X509_CERT` environment variable to the content of the Certificate field shown in the `Keys` tab of + the SAML client in Keycloak. It should start with `MIIC` and end with `=`. +4. Set the `SAML_SP_ENTITY_ID` environment variable to the entityID of the SAML client in Keycloak ( + e.g. `https://partdb.yourdomain.invalid/sp`). +5. In Keycloak navigate to `Realm Settings` -> `SAML 2.0 Identity Provider` (by default something + like `https://idp.yourdomain.invalid/realms/master/protocol/saml/descriptor) to show the SAML metadata. +6. Copy the `entityID` value from the metadata to the `SAML_IDP_ENTITY_ID` configuration variable of Part-DB (by default + something like `https://idp.yourdomain.invalid/realms/master`). +7. Copy the `Single Sign-On Service` value from the metadata to the `SAML_IDP_SINGLE_SIGN_ON_SERVICE` configuration + variable of Part-DB (by default something like `https://idp.yourdomain.invalid/realms/master/protocol/saml`). +8. Copy the `Single Logout Service` value from the metadata to the `SAML_IDP_SINGLE_LOGOUT_SERVICE` configuration + variable of Part-DB (by default something like `https://idp.yourdomain.invalid/realms/master/protocol/saml/logout`). +9. Copy the `X.509 Certificate` value from the metadata to the `SAML_IDP_X509_CERT` configuration variable of Part-DB ( + it should start with `MIIC` and should be pretty long). +10. Set the `DEFAULT_URI` to the homepage (on the publicly available domain) of your Part-DB installation ( + e.g. `https://partdb.yourdomain.invalid/`). It must end with a slash. +11. Set the `SAML_ENABLED` configuration in Part-DB to 1 to enable SAML authentication. + +When you access the Part-DB login form now, you should see a new button to log in via SSO. Click on it to be redirected +to the SAML identity provider and log in. + +In the following sections, you will learn how to configure that Part-DB uses the data provided by the SAML identity +provider to fill out user information. + +### Set user information based on SAML attributes + +Part-DB can set basic user information like the username, the real name and the email address based on the SAML +attributes provided by the SAML identity provider. +To do this, you need to configure your SAML identity provider to provide the following attributes: + +* `email` or `urn:oid:1.2.840.113549.1.9.1` for the email address +* `firstName` or `urn:oid:2.5.4.42` for the first name +* `lastName` or `urn:oid:2.5.4.4` for the last name +* `department` for the department field of the user + +You can omit any of these attributes, but then the corresponding field will be empty (but can be overwritten by an +administrator). +These values are written to Part-DB database, whenever the user logs in via SAML (the user is created on the first +login, and updated on every login). + +To configure Keycloak to provide these attributes, you need to go to the `Client scopes` page and select +the `sp-dedicatd` client scope (or create a new one). +In the scope configuration page, click on `Add mappers` and `From predefined mappers`. Select the following mappers: + +* `X500 email` +* `X500 givenName` +* `X500 surname` + +and click `Add`. Now Part-DB will be provided with the email, first name and last name of the user based on the Keycloak +user database. + +### Configure permissions for SAML users + +On the first login of a SAML user, Part-DB will create a new user in the database. This user will have the same username +as the SAML user, but no password set. The user will be marked as a SAML user, so he can only log in via SAML in the +future. However, in other aspects the user is a normal user, so Part-DB admins can set permissions for SAML users like +for any other user and override permissions assigned via groups. + +For large organizations, you maybe want to automatically assign permissions to SAML users based on the roles or +groups configured in the identity provider. For this purpose Part-DB allows you to map SAML roles or groups to Part-DB +groups. See the next section for details. + +### Map SAML roles to Part-DB groups + +Part-DB allows you to configure a mapping between SAML roles or groups and Part-DB groups. This allows you to +automatically assign permissions to SAML users based on the roles or groups configured in the identity provider. For +example, if a user at your SAML provider has the role `admin`, you can configure Part-DB to assign the `admin` group to +this user. This will give the user all permissions of the `admin` group. + +For this, you need first have to create the groups in Part-DB, to which you want to assign the users and configure their +permissions. You will need the IDs of the groups, which you can find on the `System->Group` page of Part-DB in the Info +tab. + +The map is provided as [JSON](https://en.wikipedia.org/wiki/JSON) encoded map between the SAML role and the group ID, +which has the form `{"saml_role": group_id, "*": group_id, ...}`. You can use the `*` key to assign a group to all +users, which are not in any other group. The map is configured via the `SAML_ROLE_MAPPING` environment variable, which +you can configure via the `.env.local` or `docker-compose.yml` file. Please note that you have to enclose the JSON +string in single quotes here, as JSON itself uses double quotes ( +e.g. `SAML_ROLE_MAPPING='{ "*": 2, "editor": 3, "admin": 1 }`). + +For example, if you want to assign the group with ID 1 (by default admin) to every SAML user which has the role `admin`, +the role with ID 3 (by default editor) to every SAML user with the role `editor` and everybody else to the group with ID +2 (by default readonly), you can configure the following map: + +``` +SAML_ROLE_MAPPING='{"admin": 1, "editor": 3, "*": 2}' +``` + +Please note that the order of the mapping is important. The first matching role will be assigned to the user. So if you +have a user with the roles `admin` and `editor`, he will be assigned to the group with ID 1 (admin) and not to the group +with ID 3 (editor), as the `admin` role comes first in the JSON map. +This mean that you should always put the most specific roles (e.g. admins) first of the map and the most general roles ( +e.g. normal users) later. + +If you want to assign users with a certain role to an empty group, provide the group ID -1 as the value. This is not a +valid group ID, so the user will not be assigned to any group. + +The SAML roles (or groups depending on your configuration), have to be supplied via a SAML attribute `group`. You have +to configure your SAML identity provider to provide this attribute. For example, in Keycloak you can configure this +attribute on the `Client scopes` page. Select the `sp-dedicated` client scope (or create a new one) and click +on `Add mappers`. Select `Role mapping` or `Group membership`, change the field name, and click `Add`. Now Part-DB will +be provided with the groups of the user based on the Keycloak user database. + +By default, the group is assigned to the user on the first login and updated on every login based on the SAML +attributes. This allows you to configure the groups in the SAML identity provider and the users will automatically stay +up to date with their permissions. However, if you want to disable this behavior (and let the Part-DB admins configure +the groups manually, after the first login), you can set the `SAML_UPDATE_GROUP_ON_LOGIN` environment variable +to `false`. If you want to disable the automatic group assignment completely (so not even on the first login of a user), +set the `SAML_ROLE_MAPPING` to `{}` (empty JSON object). + +## Overview of possible SAML attributes used by Part-DB + +The following table shows all SAML attributes, which can be used by Part-DB. If your identity provider is configured to +provide these attributes, you can use to automatically fill the corresponding fields of the user in Part-DB. + +| SAML attribute | Part-DB user field | Description | +|-------------------------------------------|--------------------|-------------------------------------------------------------------| +| `urn:oid:1.2.840.113549.1.9.1` or `email` | email | The email address of the user. | +| `urn:oid:2.5.4.42` or `firstName` | firstName | The first name of the user. | +| `urn:oid:2.5.4.4` or `lastName` | lastName | The last name of the user. | +| `department` | department | The department of the user. | +| `group` | group | The group of the user (determined by `SAML_ROLE_MAPPING` option). | + +## Use SAML Login for existing users + +Part-DB distinguishes between local users and SAML users. Local users are users, that can log in via the Part-DB login form +and use the password (hash) saved in the Part-DB database. SAML users are stored in the database too (they are +created on the first login of the user via SAML), but they use the SAML identity provider to authenticate the user and +have no password stored in the database. When you try you will get an error message. + +For security reasons, it is not possible to authenticate via SAML as a local user (and vice versa). So if you have +existing users in your Part-DB database and want them to be able to log in via SAML in the future, you can use +the `php bin/console partdb:user:convert-to-saml-user username` command to convert them to SAML users. This will remove +the password hash from the database and mark them as SAML users, so they can log in via SAML in the future. + +The reverse is also possible: If you have existing SAML users and want them to be able to log in via the Part-DB login +form, you can use the `php bin/console partdb:user:convert-to-saml-user --to-local username` command to convert them to +local users. You have to set a password for the user afterward. + +{: .important } +> It is recommended that you let the original admin user (ID: 2) be a local user, so you can still login to Part-DB if +> the SAML identity provider is not available. + +## Advanced SAML configuration + +You can find some more advanced SAML configuration options in the `config/packages/nbgrp_onelogin_saml.yaml` file. Refer +to the file for more information. +Normally you don't have to change anything here. + +Please note that this file is not saved by the Part-DB backup tool, so you have to save it manually if you want to keep +your changes. On docker containers you have to configure a volume mapping for it. + +## SAML behind a reverse proxy + +If you are running Part-DB behind a reverse proxy, configure the `TRUSTED_PROXIES` environment and other reverse proxy +settings as described in the [reverse proxy guide]({% link installation/reverse-proxy.md %}). +If you want to use SAML you also need to set `SAML_BEHIND_PROXY` to `true` to enable the SAML proxy mode. diff --git a/docs/partkeepr_migration.md b/docs/partkeepr_migration.md new file mode 100644 index 00000000..e37f8055 --- /dev/null +++ b/docs/partkeepr_migration.md @@ -0,0 +1,67 @@ +--- +layout: default +title: Migrate from PartKeepr to Part-DB +nav_order: 101 +--- + +# Migrate from PartKeepr to Part-DB + +{: .warning } +> This feature is currently in beta. Please report any bugs you find. + +This guide describes how to migrate from [PartKeepr](https://partkeepr.org/) to Part-DB. + +Part-DB has a built-in migration tool, which can be used to migrate the data from an existing PartKeepr instance to +a new Part-DB instance. Most of the data can be migrated, however, there are some limitations, that you can find below. + +## What can be imported + +* Data structures (Categories, Footprints, Storage Locations, Manufacturers, Distributors, Part Measurement Units) +* Basic part information (Name, Description, Comment, etc.) +* Attachments and images of parts, projects, footprints, manufacturers, and storage locations +* Part prices (distributor infos) +* Part parameters +* Projects (including parts and attachments) +* Users (optional): Passwords however will not be migrated, and need to be reset later + +## What can't be imported + +* Metaparts (A dummy version of the metapart will be created in Part-DB, however, it will not function as metapart) +* Multiple manufacturers per part (only the last manufacturer of a part will be migrated) +* Overage information for project parts (the overage info will be set as a comment in the project BOM, but will have no + effect) +* Batch Jobs +* Parameter Units (the units will be written into the parameters) +* Project Reports and Project Runs +* Stock History +* Any kind of PartKeepr preferences + +## How to migrate + +1. Install Part-DB as described in the installation guide. You can use any database backend you want (MySQL or + SQLite). Run the database migration, but do not create any new data yet. +2. Export your PartKeepr database as XML file using [mysqldump](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html): + When the MySQL database is running on the local computer, and you are root you can just run the + command `mysqldump --xml PARTKEEPR_DATABASE --result-file pk.xml`. + If your server is remote or your MySQL authentication is different, you need to + run `mysqldump --xml -h PARTKEEPR_HOST -u PARTKEEPR_USER -p PARTKEEPR_DATABASE`, where you replace `PARTKEEPR_HOST` + with the hostname of your MySQL database and `PARTKEEPR_USER` with the username of MySQL user which has access to the + PartKeepr database. You will be asked for the MySQL user password. +3. Go to the Part-DB main folder and run the command `php bin/console partdb:migrations:import-partkeepr path/to/pk.xml`. + This step will delete all existing data in the Part-DB database and import the contents of PartKeepr. +4. Copy the contents of `data/files/` from your PartKeepr installation to the `uploads/` folder of your Part-DB + installation and the contents of `data/images` from PartKeepr to `public/media/` of Part-DB. +5. Clear the cache of Part-DB by running: `php bin/console cache:clear` +6. Go to the Part-DB web interface. You can log in with the username `admin` and the password, which is shown during the + installation process of Part-DB (step 1). You should be able to see all the data from PartKeepr. + +## Import users + +If you want to import the users (mostly the username and email address) from PartKeepr, you can add the `--import-users` +option on the database import command (step 3): +`php bin/console partdb:migrations:import-partkeepr --import-users path/to/pk.xml`. + +All imported users of PartKeepr will be assigned to a new group "PartKeepr Users", which has normal user permissions (so +editing data, but no administrative tasks). You can change the group and permissions later in Part-DB users management. +Passwords can not be imported from PartKeepr and all imported users get marked as disabled. So to allow users to +log in, you need to enable them in the user management and assign a password. \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 6c43dae0..f20a7f22 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -4,30 +4,46 @@ title: Troubleshooting --- # Troubleshooting + Sometimes things go wrong and Part-DB shows an error message. This page should help you to solve the problem. ## Error messages -When a common, easy fixable error occurs (like a non up-to-date database), Part-DB will show you some short instructions on how to fix the problem. If you have a problem that is not listed here, please open an issue on GitHub. + +When a common, easy fixable error occurs (like a non-up-to-date database), Part-DB will show you some short instructions +on how to fix the problem. If you have a problem that is not listed here, please open an issue on GitHub. ## General procedure + If you encounter an error, try the following steps: -* Clear cache of Part-DB with the console command: + +* Clear the cache of Part-DB with the console command: + ```bash php bin/console cache:clear ``` -* Check if the database needs an update (and perform it when needed) with the console command: + +* Check if the database needs an update (and perform it when needed) with the console command: + ```bash php bin/console doctrine:migrations:migrate ``` If this does not help, please [open an issue on GitHub](https://github.com/Part-DB/Part-DB-symfony). +## Search for the user and reset the password: + +You can list all users with the following command: `php bin/console partdb:users:list` +To reset the password of a user you can use the following +command: `php bin/console partdb:users:set-password [username]` + ## Error logs + Detailed error logs can be found in the `var/log` directory. When Part-DB is installed directly, the errors are written to the `var/log/prod.log` file. -When Part-DB is installed with Docker, the errors are written directly to the console output. +When Part-DB is installed with Docker, the errors are written directly to the console output. You can see the logs with the following command, when you are in the folder with the `docker-compose.yml` file + ```bash docker-compose logs -f ``` @@ -35,4 +51,5 @@ docker-compose logs -f Please include the error logs in your issue on GitHub, if you open an issue. ## Report Issue -If an error occurs, or you found a bug, please [open an issue on GitHub](https://github.com/Part-DB/Part-DB-symfony). \ No newline at end of file + +If an error occurs, or you found a bug, please [open an issue on GitHub](https://github.com/Part-DB/Part-DB-symfony). diff --git a/docs/upgrade_legacy.md b/docs/upgrade_legacy.md index 66298a79..e1e43831 100644 --- a/docs/upgrade_legacy.md +++ b/docs/upgrade_legacy.md @@ -1,58 +1,90 @@ --- layout: default title: Upgrade from legacy Part-DB version (<1.0) +nav_order: 100 --- # Upgrade from legacy Part-DB version -Part-DB 1.0 was a complete rewrite of the old Part-DB (< 1.0.0), which you can find [here](https://github.com/Part-DB/Part-DB). A lot of things changed internally, but Part-DB was always developed with compatibility in mind, so you can migrate smoothly to the new Part-DB version, and utilize its new features and improvements. +Part-DB 1.0 was a complete rewrite of the old Part-DB (< 1.0.0), which you can +find [here](https://github.com/Part-DB/Part-DB). A lot of things changed internally, but Part-DB was always developed +with compatibility in mind, so you can migrate smoothly to the new Part-DB version, and utilize its new features and +improvements. -Some things changed however to the old version and some features are still missing, so be sure to read the following sections carefully before proceeding to upgrade. +Some things changed however to the old version and some features are still missing, so be sure to read the following +sections carefully before proceeding to upgrade. ## Changes -* PHP 7.4 or higher is required now (Part-DB 0.5 required PHP 5.4+, Part-DB 0.6 PHP 7.0). - PHP 7.4 (or newer) is shipped by all current major Linux distros now (and can be installed by third party sources on others), - Releases are available for Windows too, so almost everybody should be able to use PHP 7.4 -* **Console access highly required.** The installation of composer and frontend dependencies require console access, also more sensitive stuff like database migration work via CLI now, so you should have console access on your server. + +* PHP 8.1 or higher is required now (Part-DB 0.5 required PHP 5.4+, Part-DB 0.6 PHP 7.0). + Releases are available for Windows too, so almost everybody should be able to use PHP 8.1 +* **Console access is highly recommended.** The installation of composer and frontend dependencies require console access, + also more sensitive stuff like database migration works via CLI now, so you should have console access on your server. * Markdown/HTML is now used instead of BBCode for rich text in description and command fields. - It is possible to migrate your existing BBCode to Markdown via `php bin/console php bin/console partdb:migrations:convert-bbcode`. -* Server exceptions are not logged to Event log anymore. For security reasons (exceptions can contain sensitive informations) - exceptions are only logged to server log (by default under './var/log'), so only the server admins can access it. -* Profile labels are now saved in Database (before they were saved in a seperate JSON file). **The profiles of legacy Part-DB versions can not be imported into new Part-DB 1.0** -* Label placeholders now use the `[[PLACEHOLDER]]` format instead of `%PLACEHOLDER%`. Also some placeholders has changed. -* Configuration is now done via configuration files / environment variables instead of the WebUI (this maybe change in the future). -* Database updated are now done via console instead of the WebUI + It is possible to migrate your existing BBCode to Markdown + via `php bin/console php bin/console partdb:migrations:convert-bbcode`. +* Server exceptions are not logged into event log anymore. For security reasons (exceptions can contain sensitive + information) exceptions are only logged to server log (by default under './var/log'), so only the server admins can access it. +* Profile labels are now saved in the database (before they were saved in a separate JSON file). **The profiles of legacy + Part-DB versions can not be imported into new Part-DB 1.0** +* Label placeholders now use the `[[PLACEHOLDER]]` format instead of `%PLACEHOLDER%`. Also, some placeholders have + changed. +* Configuration is now done via configuration files/environment variables instead of the WebUI (this may change in + the future). +* Database updates are now done via console instead of the WebUI * Permission system changed: **You will have to newly set the permissions of all users and groups!** +* Import / Export file format changed. Fields must be English now (unlike in legacy Part-DB versions, where German + fields in CSV were possible) + and you may have to change the header line/field names of your CSV files. ## Missing features -* No possibility to mark parts for ordering (yet) -* No import / export possibility for parts (yet), however you can import/export other datastructures like Categories, Footprints, etc. (yet) -* No support for 3D models of footprints (yet) -* No possibility to disable footprints, manufacturers globally (or per category). This should not have a big impact, when you forbid users to edit/create them. +* No possibility of marking parts for ordering (yet) +* No support for 3D models of footprints (yet) +* No possibility to disable footprints, manufacturers globally (or per category). This should not have a big impact + when you forbid users to edit/create them. +* No resistor calculator or SMD label tools ## Upgrade process {: .warning } -> Once you have upgraded the database to the latest version, you will not be able to access the database with Part-DB 0.5.*. Doing so could lead to data corruption. So make a a backup before you proceed the upgrade, so you will be able to revert the upgrade, when you are not happy with the new version +> Once you have upgraded the database to the latest version, you will not be able to access the database with Part-DB +> 0.5.*. Doing so could lead to data corruption. So make a backup before you proceed the upgrade, so you will be able to +> revert the upgrade, when you are not happy with the new version > -> Beware that all user and group permissions will be reset, and you have to set the permissions again +> Beware that all user and group permissions will be reset, and you have to set the permissions again > the new Part-DB as many permissions changed, and automatic migration is not possible. - 1. Upgrade your existing Part-DB version the newest Part-DB 0.5.* version (in the moment Part-DB 0.5.8), like described in the old Part-DB's repository. - 2. Make a backup of your database and attachments. If somethings goes wrong during migration, you can use this backup to start over. If you have some more complex permission configuration, you maybe want to do screenshots of it, so you can redo it again later. - 3. Setup the new Part-DB like described in installation section. You will need to do the setup for a MySQL instance (either via docker or direct installation). Set the `DATABASE_URL` environment variable in your `.env.local` (or `docker-compose.yaml`) to your existing database. (e.g. `DATABASE_URL=mysql://PARTDB_USER:PASSWORD@localhost:3306/DATABASE_NAME`) - 4. Ensure that the correct base currency is configured (`BASE_CURRENCY` env), this must match the currency used in the old Part-DB version. If you used Euro, you do not need to change anything. - 5. Run `php bin/console cache:clear` and `php bin/console doctrine:migrations:migrate`. - 4. Run `php bin/console partdb:migrations:convert-bbcode` to convert the BBCode used in comments and part description to the newly used markdown. - 5. Copy the content of the `data/media` folder from the old Part-DB instance into `public/media` folder in the new version. - 6. Run `php bin/console cache:clear` - 7. You should be able to login to Part-DB now using your admin account and the old password. If you do not know the admin username, run `php bin/console partdb:users:list` and look for the user with ID 1. You can reset the password of this user using `php bin/console partdb:users:set-password [username]`. - 8. All other users besides the admin user are disabled (meaning they can not login). Go to "System->User" and "System->Group" and check the permissions of the users (and change them if needed). If you are done enable the users again, by removing the disabled checkmark in the password section. If you have a lot of users you can enable them all at once using `php bin/console partdb:users:enable --all` - -**It is not possible to access the database using the old Part-DB version. -If you do so, this could damage your database.** Therefore it is recommended to remove the old Part-DB version, after everything works. +1. Upgrade your existing Part-DB version the newest Part-DB 0.5.* version (at the moment Part-DB 0.5.8), as described + in the old Part-DB's repository. +2. Make a backup of your database and attachments. If something goes wrong during migration, you can use this backup to + start over. If you have some more complex permission configuration, you maybe want to do screenshots of it, so you + can redo it again later. +3. Set up the new Part-DB as described in the installation section. You will need to do the setup for a MySQL instance ( + either via docker or direct installation). Set the `DATABASE_URL` environment variable in your `.env.local` ( + or `docker-compose.yaml`) to your existing database. ( + e.g. `DATABASE_URL=mysql://PARTDB_USER:PASSWORD@localhost:3306/DATABASE_NAME`) +4. Ensure that the correct base currency is configured (`BASE_CURRENCY` env), this must match the currency used in the + old Part-DB version. If you used Euro, you do not need to change anything. +5. Run `php bin/console cache:clear` and `php bin/console doctrine:migrations:migrate`. +6. Run `php bin/console partdb:migrations:convert-bbcode` to convert the BBCode used in comments and part description to + the newly used markdown. +7. Copy the content of the `data/media` folder from the old Part-DB instance into `public/media` folder in the new + version. +8. Run `php bin/console cache:clear` +9. You should be able to log in to Part-DB now using your admin account and the old password. If you do not know the + admin username, run `php bin/console partdb:users:list` and look for the user with ID 1. You can reset the password + of this user using `php bin/console partdb:users:set-password [username]`. +10. All other users besides the admin user are disabled (meaning they can not log in). Go to "System->User" and "System-> + Group" and check the permissions of the users (and change them if needed). If you are done enable the users again, by + removing the disabled checkmark in the password section. If you have a lot of users you can enable them all at once + using `php bin/console partdb:users:enable --all` +**It is not possible to access the database using the old Part-DB version. +If you do so, this could damage your database.** Therefore, it is recommended to remove the old Part-DB version, after +everything works. ## Issues -If you encounter any issues (especially during the database migration) or features do not work like intended, please open an issue ticket at GitHub. \ No newline at end of file + +If you encounter any issues (especially during the database migration) or features do not work like intended, please +open an issue ticket at GitHub. diff --git a/docs/usage/backup_restore.md b/docs/usage/backup_restore.md index edcd1a87..bef3792d 100644 --- a/docs/usage/backup_restore.md +++ b/docs/usage/backup_restore.md @@ -6,48 +6,75 @@ parent: Usage # Backup and Restore Data -When working productively you should backup the data and configuration of Part-DB regularly to prevent data loss. This is also useful, if you want to migrate your Part-DB instance from one server to another. In that case you just have to backup the data on server 1, move the backup to server 2, install Part-DB on server 2 and restore the backup. +When working productively you should back up the data and configuration of Part-DB regularly to prevent data loss. This +is also useful if you want to migrate your Part-DB instance from one server to another. In that case, you just have to +back up the data on server 1, move the backup to server 2, install Part-DB on server 2, and restore the backup. ## Backup (automatic / Part-DB supported) -Part-DB includes a command `php bin/console partdb:backup` which automatically collects all the needed data (described below) and saves them to a ZIP file. + +Part-DB includes a command `php bin/console partdb:backup` which automatically collects all the needed data (described +below) and saves them to a ZIP file. If you are using a MySQL/MariaDB database you need to have `mysqldump` installed and added to your `$PATH` env. ### Usage -To backup all possible data, run the following command: `php bin/console partdb:backup --full /path/to/backup/partdb_backup.zip`. -It is possible to do only partial backups (config, attachments, or database). See `php bin/console partdb:backup --help` for more infos about these options. +To back up all possible data, run the following +command: `php bin/console partdb:backup --full /path/to/backup/partdb_backup.zip`. + +It is possible to do only partial backups (config, attachments, or database). See `php bin/console partdb:backup --help` +for more info about these options. ## Backup (manual) -There are 3 parts which have to be backup-ed: The configuration files, which contains the instance specific options, the uploaded files of attachments, and the database containing the most data of Part-DB. + +3 parts have to be backup-ed: The configuration files, which contain the instance-specific options, the +uploaded files of attachments, and the database containing the most data of Part-DB. Everything else like thumbnails and cache files, are recreated automatically when needed. ### Configuration files -You have to copy the `.env.local` file and (if you have changed it) the `config/parameters.yaml` and `config/banner.md` to your backup location. + +You have to copy the `.env.local` file and (if you have changed it) the `config/parameters.yaml` and `config/banner.md` +to your backup location. ### Attachment files + You have to recursively copy the `uploads/` folder and the `public/media` folder to your backup location. ### Database -#### Sqlite -If you are using sqlite, it is sufficient to just copy your `app.db` from your database location (normally `var/app.db`) to your backup location. + +#### SQLite + +If you are using sqlite, it is sufficient to just copy your `app.db` from your database location (normally `var/app.db`) +to your backup location. #### MySQL / MariaDB -For MySQL / MariaDB you have to dump the database to an SQL file. You can do this manually with phpmyadmin, or you use [`mysqldump`](https://mariadb.com/kb/en/mariadb-dumpmysqldump/) to dump the database to an SQL file via command line interface (`mysqldump -uBACKUP -pPASSWORD DATABASE`) + +For MySQL / MariaDB you have to dump the database to an SQL file. You can do this manually with phpmyadmin, or you +use [`mysqldump`](https://mariadb.com/kb/en/mariadb-dumpmysqldump/) to dump the database to an SQL file via command line +interface (`mysqldump -uBACKUP -pPASSWORD DATABASE`) ## Restore -Install Part-DB as usual as described in the installation section, with the exception of the database creation / migration part. You have to use the same database type (sqlite or mysql) as on the back-uped server instance. + +Install Part-DB as usual as described in the installation section, except for the database creation/migration part. You +have to use the same database type (SQLite or MySQL) as on the backuped server instance. ### Restore configuration -Copy configuration files `.env.local`, (and if existing) `config/parameters.yaml` and `config/banner.md` from the backup to your new Part-DB instance and overwrite the existing files there. + +Copy configuration files `.env.local`, (and if existing) `config/parameters.yaml` and `config/banner.md` from the backup +to your new Part-DB instance and overwrite the existing files there. ### Restore attachment files + Copy the `uploads/` and the `public/media/` folder from your backup into your new Part-DB folder. ### Restore database -#### Sqlite + +#### SQLite + Copy the backup-ed `app.db` into the database folder normally `var/app.db` in Part-DB root folder. #### MySQL / MariaDB -Recreate a database and user with the same credentials as before (or update the database credentials in the `.env.local` file). + +Recreate a database and user with the same credentials as before (or update the database credentials in the `.env.local` +file). Import the dumped SQL file from the backup into your new database. \ No newline at end of file diff --git a/docs/usage/bom_import.md b/docs/usage/bom_import.md new file mode 100644 index 00000000..94a06d55 --- /dev/null +++ b/docs/usage/bom_import.md @@ -0,0 +1,36 @@ +--- +layout: default +title: Import Bill of Material (BOM) for Projects +nav_order: 5 +parent: Usage +--- + +# Import Bill of Material (BOM) for Projects + +Part-DB supports the import of Bill of Material (BOM) files for projects. This allows you to directly import a BOM file +from your ECAD software into your Part-DB project. + +The import process is currently semi-automatic. This means Part-DB will take the BOM file and create entries for all +parts in the BOM file in your project and assign fields like +mount names (e.g. 'C1, C2, C3'), quantity and more. +However, you still have to assign the parts from Part-DB database to the entries (if applicable) after the import by +hand, +as Part-DB can not know which part you had in mind when you designed your schematic. + +## Usage + +In the project view or edit click on the "Import BOM" button, below the BOM table. This will open a dialog where you can +select the BOM file you want to import and some options for the import process: + +* **Type**: The format/type of the BOM file. See below for explanations of the different types. +* **Clear existing BOM entries before import**: If this is checked, all existing BOM entries, which are currently + associated with the project, will be deleted before the import. + +### Supported BOM file formats + +* **KiCAD Pcbnew BOM (CSV file)**: A CSV file of the Bill of Material (BOM) generated + by [KiCAD Pcbnew](https://www.kicad.org/). + Please note that you have to export the BOM from the PCB editor, the BOM generated by the schematic editor (Eeschema) + has a different format and does not work with this type. + You can generate this BOM file by going to "File" -> "Fabrication Outputs" -> "Bill of Materials" in Pcbnew and save + the file to your desired location. diff --git a/docs/usage/console_commands.md b/docs/usage/console_commands.md index ccc9a64d..e5197251 100644 --- a/docs/usage/console_commands.md +++ b/docs/usage/console_commands.md @@ -8,29 +8,72 @@ parent: Usage Part-DB provides some console commands to display various information or perform some tasks. The commands are invoked from the main directory of Part-DB with the command `php bin/console [command]` in the context -of the database user (so usually the webserver user), so you maybe have to use `sudo` or `su` to execute the commands. +of the database user (so usually the webserver user), so you maybe have to use `sudo` or `su` to execute the commands: + +```bash +sudo -u www-data php bin/console [command] +``` -You can get help for every command with the parameter `--help`. See `php bin/console` for a list of all available commands. +You can get help for every command with the parameter `--help`. See `php bin/console` for a list of all available +commands. + +If you are running Part-DB in a docker container, you must either execute the commands from a shell inside a container, +or use the `docker exec` command to execute the command directly inside the container. For example if you docker container +is named `partdb`, you can execute the command `php bin/console cache:clear` with the following command: + +```bash +docker exec --user=www-data partdb php bin/console cache:clear +``` + +{: .warning } +> If you run a root console inside the docker container, and wanna execute commands on the webserver behalf, be sure to use `sudo -E` command (with the `-E` flag) to preserve env variables from the current shell. +> Otherwise Part-DB console might use the wrong configuration to execute commands. + +## Troubleshooting + +## User management commands -## User managment commands * `php bin/console partdb:users:list`: List all users of this Part-DB instance -* `php bin/console partdb:users:set-password [username]`: Set/Changes the password of the user with the given username. This allows administrators to reset a password of a user, if he forgot it. -* `php bin/console partdb:users:enable [username]`: Enable/Disable the user with the given username (use `--disable` to disable the user, which prevents login) +* `php bin/console partdb:users:set-password [username]`: Set/Changes the password of the user with the given username. + This allows administrators to reset a password of a user, if he forgot it. +* `php bin/console partdb:users:enable [username]`: Enable/Disable the user with the given username (use `--disable` to + disable the user, which prevents login) * `php bin/console partdb:users:permissions`: View/Change the permissions of the user with the given username -* `php bin/console partdb:users:upgrade-permissions-schema`: Upgrade the permissions schema of users to the latest version (this is normally automatically done when the user visits a page) +* `php bin/console partdb:users:upgrade-permissions-schema`: Upgrade the permissions schema of users to the latest + version (this is normally automatically done when the user visits a page) * `php bin/console partdb:logs:show`: Show the most recent entries of the Part-DB event log / recent activity +* `php bin/console partdb:user:convert-to-saml-user`: Convert a local user to a SAML/SSO user. This is needed, if you + want to use SAML/SSO authentication for a user, which was created before you enabled SAML/SSO authentication. ## Currency commands -* `php bin/console partdb:currencies:update-exchange-rates`: Update the exchange rates of all currencies from the internet) + +* `php bin/console partdb:currencies:update-exchange-rates`: Update the exchange rates of all currencies from the + internet ## Installation/Maintenance commands + * `php bin/console partdb:backup`: Backup the database and the attachments * `php bin/console partdb:version`: Display the current version of Part-DB and the used PHP version -* `php bin/console partdb:check-requirements`: Check if the requirements for Part-DB are met (PHP version, PHP extensions, etc.) and make suggestions what could be improved -* `partdb:migrations:convert-bbcode`: Migrate the old BBCode markup codes used in legacy Part-DB versions (< 1.0.0) to the new markdown syntax -* `partdb:attachments:clean-unused`: Remove all attachments which are not used by any database entry (e.g. orphaned attachments) -* `partdb:cache:clear`: Clears all caches, so the next page load will be slower, but the cache will be rebuild. This can maybe fix some issues, when the cache were corrupted. This command is also needed after changing things in the `parameters.yaml` file or upgrading Part-DB. +* `php bin/console partdb:check-requirements`: Check if the requirements for Part-DB are met (PHP version, PHP + extensions, etc.) and make suggestions what could be improved +* `partdb:migrations:convert-bbcode`: Migrate the old BBCode markup codes used in legacy Part-DB versions (< 1.0.0) to + the new Markdown syntax +* `partdb:attachments:clean-unused`: Remove all attachments which are not used by any database entry (e.g. orphaned + attachments) +* `partdb:cache:clear`: Clears all caches, so the next page load will be slower, but the cache will be rebuilt. This can + maybe fix some issues, when the cache were corrupted. This command is also needed after changing things in + the `parameters.yaml` file or upgrading Part-DB. +* `partdb:migrations:import-partkeepr`: Imports a mysqldump XML dump of a PartKeepr database into Part-DB. This is only + needed for users, which want to migrate from PartKeepr to Part-DB. *All existing data in the Part-DB database is + deleted!* ## Database commands + * `php bin/console doctrine:migrations:migrate`: Migrate the database to the latest version -* `php bin/console doctrine:migrations:up-to-date`: Check if the database is up-to-date \ No newline at end of file +* `php bin/console doctrine:migrations:up-to-date`: Check if the database is up-to-date + +## Attachment commands + +* `php bin/console partdb:attachments:download`: Download all attachments, which are not already downloaded, to the + local filesystem. This is useful to create local backups of the attachments, no matter what happens on the remote and + also makes pictures thumbnails available for the frontend for them \ No newline at end of file diff --git a/docs/usage/eda_integration.md b/docs/usage/eda_integration.md new file mode 100644 index 00000000..9444e55f --- /dev/null +++ b/docs/usage/eda_integration.md @@ -0,0 +1,79 @@ +--- +layout: default +title: EDA / KiCad integration +parent: Usage +--- + +# EDA / KiCad integration + +Part-DB can function as a central database for [EDA](https://en.wikipedia.org/wiki/Electronic_design_automation) or ECAD software used to design electronic schematics and PCBs. +You can connect your EDA software and can view your available parts, with the data saved from Part-DB directly in your EDA software. +Part-DB allows to configure additional metadata for the EDA, to associate symbols and footprints for use inside the EDA software, so the part becomes +directly usable inside the EDA software. +This also allows to configure available and usable parts and their properties in a central place, which is especially useful in teams, where multiple persons design PCBs. + +**Currently only KiCad is supported!** + +## KiCad Setup + +{: .important } +> Part-DB uses the HTTP library feature of KiCad, which is experimental and not part of the stable KiCad 7 releases. If you want to use this feature, you need to install a KiCad nightly build (7.99 version). This feature will most likely also be part of KiCad 8. + +Part-DB should be accessible from the PCs with KiCAD. The URL should be stable (so no dynamically changing IP). +You require a user account in Part-DB, which has permission to access Part-DB API and create API tokens. Every user can have its own account, or you set up a shared read-only account. + +To connect KiCad with Part-DB do the following steps: + +1. Create an API token on the user settings page for the KiCAD application and copy/save it, when it is shown. Currently, KiCad can only read Part-DB database, so a token with a read-only scope is enough. +2. Add some EDA metadata to parts, categories, or footprints. Only parts with usable info will show up in KiCad. See below for more info. +3. Create a file `partd.kicad_httplib` (or similar, only the extension is important) with the following content: +``` +{ + "meta": { + "version": 1.0 + }, + "name": "Part-DB library", + "description": "This KiCAD library fetches information externally from ", + "source": { + "type": "REST_API", + "api_version": "v1", + "root_url": "http://kicad-instance.invalid/en/kicad-api/", + "token": "THE_GENERATED_API_TOKEN" + } +} +``` +4. Replace the `root_url` with the URL of your Part-DB instance plus `/en/kicad-api/`. You can find the right value for this in the Part-DB user settings page under "API endpoints" in the "API tokens" panel. +5. Replace the `token` field value with the token you have generated in step 1. +6. Open KiCad and add this created file as a library in the KiCad symbol table under (Preferences --> Manage Symbol Libraries) + +If you then place a new part, the library dialog opens, and you should be able to see the categories and parts from Part-DB. + +### How to associate footprints and symbols with parts + +Part-DB doesn't save any concrete footprints or symbols for the part. Instead, Part-DB just contains a reference string in the part metadata, which points to a symbol/footprint in KiCad's local library. + +You can define this on a per-part basis using the KiCad symbol and KiCad footprint field in the EDA tab of the part editor. Or you can define it at a category (symbol) or footprint level, to assign this value to all parts with this category and footprint. + +For example, to configure the values for a BC547 transistor you would put `Transistor_BJT:BC547` on the parts Kicad symbol to give it the right schematic symbol in EEschema and `Package_TO_SOT_THT:TO-92` to give it the right footprint in PcbNew. + +If you type in a character, you will get an autocomplete list of all symbols and footprints available in the KiCad standard library. You can also input your own value. + +### Parts and category visibility + +Only parts and their categories, on which there is any kind of EDA metadata are defined show up in KiCad. So if you want to see parts in KiCad, +you need to define at least a symbol, footprint, reference prefix, or value on a part, category or footprint. + +You can use the "Force visibility" checkbox on a part or category to override this behavior and force parts to be visible or hidden in KiCad. + +*Please note that KiCad caches the library categories. So if you change something, which would change the visible categories in KiCad, you have to reload EEschema to see the changes.* + +### Category depth in KiCad + +For performance reasons, only the most top-level categories of Part-DB are shown as categories in KiCad. All parts in the subcategories are shown in the top-level category. + +You can configure the depth of the categories shown in KiCad, via the `EDA_KICAD_CATEGORY_DEPTH` env option. The default value is 0, which means only the top-level categories are shown. +To show more levels of categories, you can set this value to a higher number. + +If you set this value to -1, all parts are shown inside a single category in KiCad, without any subcategories. + +You can view the "real" category path of a part in the part details dialog in KiCad. diff --git a/docs/usage/getting_started.md b/docs/usage/getting_started.md index bc25123f..4bb8afb9 100644 --- a/docs/usage/getting_started.md +++ b/docs/usage/getting_started.md @@ -7,111 +7,145 @@ nav_order: 4 # Getting started After Part-DB you should begin with customizing the settings, and setting up the basic structures. -Before starting its useful to read a bit about the [concepts of Part-DB]({% link concepts.md %}). +Before starting, it's useful to read a bit about the [concepts of Part-DB]({% link concepts.md %}). 1. TOC {:toc} ## Customize config files -Before you start creating data structures, you should configure Part-DB to your needs by changing possible configuration options. -This is done either via changing the `.env.local` file in a direct installation or by changing the env variables in your `docker-compose.yaml` file. -A list of possible configuration options, can be found [here]({% link configuration.md %}). +Before you start creating data structures, you should configure Part-DB to your needs by changing possible configuration +options. +This is done either via changing the `.env.local` file in a direct installation or by changing the env variables in +your `docker-compose.yaml` file. +A list of possible configuration options can be found [here]({% link configuration.md %}). ## Change password, Set up Two-Factor-Authentication & Customize User settings -If you have not already done, you should change your user password. You can do this in the user settings (available in the navigation bar drop down with the user symbol). +If you have not already done so, you should change your user password. You can do this in the user settings (available in +the navigation bar drop-down with the user symbol). ![image]({% link assets/getting_started/change_password.png %}) -There you can also find the option, to set up Two Factor Authentication methods like Google Authenticator. Using this is highly recommended (especially if you have admin permissions) to increase the security of your account. (Two Factor Authentication even can be enforced for all members of a user group) +There you can also find the option, to set up Two-Factor Authentication methods like Google Authenticator. Using this is +highly recommended (especially if you have admin permissions) to increase the security of your account. (Two-factor authentication +even can be enforced for all members of a user group) -In the user settings panel you can change account infos like your username, your first and last name (which will be shown alongside your username to identify you better), department information and your email address. The email address is used to send password reset mails, if your system is configured to use this. +In the user settings panel, you can change account info like your username, your first and last name (which will be +shown alongside your username to identify you better), department information, and your email address. The email address +is used to send password reset mails if your system is configured to use this. ![image]({% link assets/getting_started/user_settings.png %}) -In the configuration tab you can also override global settings, like your preferred UI language (which will automatically be applied after login), the timezone you are in (and in which times will be shown for you), your preferred currency (all money values will be shown converted to this to you, if possible) and the theme that should be used. +In the configuration tab you can also override global settings, like your preferred UI language (which will +automatically be applied after login), the timezone you are in (and in which times will be shown for you), your +preferred currency (all money values will be shown converted to this to you, if possible) and the theme that should be +used. ## (Optional) Customize homepage banner -The banner which is shown on the homepage, can be customized/changed by changing the `config/banner.md` file with a text editor. You can use markdown and (safe) HTML here, to style and customize the banner. -You can even use Latex style equations by wrapping the expressions into `$` (like `$E=mc^2$`, which is rendered inline: $E=mc^2$) or `$$` (like `$$E=mc^2$$`) which will be rendered as a block, like so: $$E=mc^2$$ +The banner which is shown on the homepage, can be customized/changed by changing the `config/banner.md` file with a text +editor. You can use markdown and (safe) HTML here, to style and customize the banner. +You can even use LaTeX-style equations by wrapping the expressions into `$` (like `$E=mc^2$`, which is rendered inline: +$E=mc^2$) or `$$` (like `$$E=mc^2$$`) which will be rendered as a block, like so: $$E=mc^2$$ ## Create groups, users and customize permissions ### Users -When logged in as administrator, you can open the users menu in the `Tools` section of the sidebar under `System -> Users`. -At this page you can create new users, change their passwords and settings and change their permissions. -For each user which should use Part-DB you should setup a own account, so that tracking of what user did what works properly. +When logged in as administrator, you can open the users menu in the `Tools` section of the sidebar +under `System -> Users`. +On this page you can create new users, change their passwords and settings, and change their permissions. +For each user who should use Part-DB you should set up their own account so that tracking of what user did works +properly. ![image]({% link assets/getting_started/user_admin.png %}) - -You should check the permissions for every user and ensure that they are in the intended way, and no user has more permissions than he needs. -For each capability you can choose between allow, forbid and inherit. In the last case, the permission is determined by the group a user has (if no group is chosen, it equals forbid) +You should check the permissions for every user and ensure that they are in the intended way, and no user has more +permissions than he needs. +For each capability, you can choose between allow, forbid, and inherit. In the last case, the permission is determined by +the group a user has (if no group is chosen, it equals forbid) ![image]({% link assets/getting_started/user_permissions.png %}) - ### Anonymous user -The `anonymous` user is special, as its settings and permissions are used for everybody who is not logged in. By default the anonymous user has read capabilities for your parts. If your Part-DB instance is publicly available you maybe want to restrict the permissions. +The `anonymous` user is special, as its settings and permissions are used for everybody who is not logged in. By default, +the anonymous user has read capabilities for your parts. If your Part-DB instance is publicly available you maybe want +to restrict the permissions. ### Groups -If you have many users which should share the same permissions, it is useful to define the permissions using user groups, which you can create and edit in the `System -> Groups` menu. +If you have many users who should share the same permissions, it is useful to define the permissions using user +groups, which you can create and edit in the `System -> Groups` menu. -By default 3 groups are defined: -* `readonly` which users have only have read permissions (like viewing, searching parts, attachments, etc.) +By default, 3 groups are defined: + +* `readonly` which users only have read permissions (like viewing, searching parts, attachments, etc.) * `users` which users also have rights to edit/delete/create elements -* `admin` which users can do administrative operations (like creating new users, show global system log, etc.) +* `admin` which users can do administrative operations (like creating new users, showing global system log, etc.) -Users only use the setting of a capability from a group, if the user has a group associated and the capability on the user is set to `inherit` (which is the default if creating a new user). You can override the permissions settings of a group per user by explicitly settings the permission at the user. - -Groups are organized as trees, meaning a group can have parent and child permissions and child groups can inherit permissions from their parents. -To inherit the permissions from a parent group set the capability to inherit, otherwise set it explicitly to override the parents permission. +Users only use the setting of a capability from a group, if the user has a group associated and the capability on the +user is set to `inherit` (which is the default if creating a new user). You can override the permissions settings of a +group per user by explicitly setting the permission of the user. +Groups are organized as trees, meaning a group can have parent and child permissions and child groups can inherit +permissions from their parents. +To inherit the permissions from a parent group set the capability to inherit, otherwise, set it explicitly to override +the parents' permission. ## Create Attachment types -Every attachment (that is an file associated with a part, data structure, etc.) must have an attachment type. They can be used to group attachments logically, like differentiating between datasheets, pictures and other documents. +Every attachment (that is a file associated with a part, data structure, etc.) must have an attachment type. They can +be used to group attachments logically, like differentiating between datasheets, pictures, and other documents. You can create/edit attachment types in the tools sidebar under "Edit -> Attachment types": ![image]({% link assets/getting_started/attachment_type_admin.png %}) - -Depending on your usecase different entries here make sense. For part mananagment the following (additional) entries maybe make sense: + +Depending on your use case different entries here make sense. For part management the following (additional) entries may make sense: * Datasheets (restricted to pdfs, Allowed filetypes: `application/pdf`) * Pictures (for generic pictures of components, storage locations, etc., Allowed filetypes: `image/*` -For every attachment type a list of allowed file types, which can be uploaded to an attachment with this attachment type, can be defined. You can either pass a list of allowed file extensions (e.g. `.pdf, .zip, .docx`) and/or a list of [Mime Types](https://en.wikipedia.org/wiki/Media_type) (e.g. `application/pdf, image/jpeg`) or a combination of both here. To allow all browser supported images, you can use `image/*` wildcard here. +For every attachment type a list of allowed file types, which can be uploaded to an attachment with this attachment +type, can be defined. You can either pass a list of allowed file extensions (e.g. `.pdf, .zip, .docx`) and/or a list +of [Mime Types](https://en.wikipedia.org/wiki/Media_type) (e.g. `application/pdf, image/jpeg`) or a combination of both +here. To allow all browser-supported images, you can use `image/*` wildcard here. ## (Optional) Create Currencies -If you want to save priceinformations for parts in a currency different to your global currency (by default Euro), you have to define the additional currencies you want to use under `Edit -> Currencies`: +If you want to save price information for parts in a currency different from your global currency (by default Euro), you +have to define the additional currencies you want to use under `Edit -> Currencies`: ![image]({% link assets/getting_started/currencies_admin.png %}) -You create a new currency, name it however you want (it is recommended to use the official name of the currency) and select the currency ISO code from the list and save it. The currency symbol is determined automatically from chose ISO code. -You can define a exchange rate in terms of your base currency (e.g. how much euros is one unit of your currency worth) to convert the currencies values in your preferred display currency automatically. - +You create a new currency, name it however you want (it is recommended to use the official name of the currency), +select the currency ISO code from the list, and save it. The currency symbol is determined automatically from the chosen ISO +code. +You can define an exchange rate in terms of your base currency (e.g. how many euros is one unit of your currency worth) +to convert the currency values in your preferred display currency automatically. ## (Optional) Create Measurement Units -By default Part-DB assumes that the parts in inventory can be counted by individual indivisible pieces, like LEDs in a box or books in a shelf. -However if you want to manage things, that are divisible and and the instock is described by a physical quantity, like length for cables, or volumina of a liquid, you have to define additional measurement units. +By default, Part-DB assumes that the parts in inventory can be counted by individual indivisible pieces, like LEDs in a +box or books on a shelf. +However, if you want to manage things, that are divisible and the stock is described by a physical quantity, like +length for cables, or volumes of a liquid, you have to define additional measurement units. This is possible under `Edit -> Measurement Units`: ![image]({% link assets/getting_started/units_admin.png %}) -You can give the measurement unit a name and an optional unit symbol (like `m` for meters) which is shown when quantities in this unit are displayed. The option `Use SI prefix` is useful for almost all physical quantities, as big and small numbers are automatically formatted with SI-prefixes (like 1.5kg instead 1500 grams). +You can give the measurement unit a name and an optional unit symbol (like `m` for meters) which is shown when +quantities in this unit are displayed. The option `Use SI prefix` is useful for almost all physical quantities, as big +and small numbers are automatically formatted with SI prefixes (like 1.5kg instead 1500 grams). -The measurement unit can be selected for each part individually, by setting the option in the advanced tab of a part`s edit menu. +The measurement unit can be selected for each part individually, by setting the option in the advanced tab of a part`s +edit menu. ## Create Categories -A category is used to group parts logically by their function (e.g. all NPN transistors would be put in a "NPN-Transistors" category). +A category is used to group parts logically by their function (e.g. all NPN transistors would be put in a " +NPN-Transistors" category). Categories are hierarchical structures meaning that you can create logical trees to group categories together. See [Concepts]({% link concepts.md %}) for an example tree structure. @@ -121,43 +155,51 @@ Every part has to be assigned to a category, so you should create at least one c ## (Optional) Create Footprints -Footprints are used to describe the physical shape of a part, like a resistor or a capacitor. -They can be used to group parts by their physical shape and to find parts with in the same package. +Footprints are used to describe the physical shape of a part, like a resistor or a capacitor. +They can be used to group parts by their physical shape and to find parts within the same package. You can create/edit footprints in the tools sidebar under "Edit -> Footprints". -It is useful to create footprints for the most common packages, like SMD resistors, capacitors, etc. to make it easier to find parts with the same footprint. -You should create these as a tree structure, so that you can group footprints by their type. +It is useful to create footprints for the most common packages, like SMD resistors, capacitors, etc. to make it easier +to find parts with the same footprint. +You should create these as a tree structure so that you can group footprints by their type. See [Concepts]({% link concepts.md %}) for an example tree structure. -You can define attachments here which are associated with the footprint. The attachment set as preview image, will be +You can define attachments here which are associated with the footprint. The attachment set as the preview image, will be used whenever a visual representation of the footprint is needed (e.g. in the part list). -For many common footprints, you can use the built-in footprints, which can be found in the "Builtin footprint image gallery", which you can find in the tools menu. -Type the name of the image you want to use in the URL field of the attachment and select the image from the dropdown menu. +For many common footprints, you can use the built-in footprints, which can be found in the "Builtin footprint image +gallery", which you can find in the "tools" menu. +Type the name of the image you want to use in the URL field of the attachment and select the image from the dropdown +menu. ## (Optional) Create Storage locations -A storelocation represents a place where parts can be stored. +A storage location represents a place where parts can be stored. You can create/edit storage locations in the tools sidebar under "Edit -> Storage locations". ## (Optional) Create Manufacturers and suppliers -You can create/edit [manufacturers]({% link concepts.md %}#manufacturers) and [suppliers]({% link concepts.md %}#suppliers) in the tools sidebar under "Edit -> Manufacturers" and "Edit -> Suppliers". +You can create/edit [manufacturers]({% link concepts.md %}#manufacturers) and [suppliers]({% link concepts.md +%}#suppliers) in the tools sidebar under "Edit -> Manufacturers" and "Edit -> Suppliers". ## Create parts -You are now ready to create your first part. You can do this by clicking either by clicking "Edit -> New Part" in the tools sidebar tree -or by clicking the "Create new Part" above the (empty) part list, after clicking on one of your newly created categories. +You are now ready to create your first part. You can do this by clicking either by clicking "Edit -> New Part" in the +tools sidebar tree +or by clicking the "Create new Part" above the (empty) part list, after clicking on one of your newly created +categories. You will be presented with a form where you can enter the basic information about your part: ![image]({% link assets/getting_started/new_part.png %}) You have to enter at least a name for the part and choose a category for it, the other fields are optional. -However, it is recommended to fill out as much information as possible, as this will make it easier to find the part later. +However, it is recommended to fill out as much information as possible, as this will make it easier to find the part +later. You can choose from your created datastructures to add manufacturer information, supplier information, etc. to the part. You can also create new datastructures on the fly, if you want to add additional information to the part, by typing the -name of the new datastructure in the field and select the "New ..." option in the dropdown menu. See [tips]({% link usage/tips_tricks.md %}) for more information. \ No newline at end of file +name of the new datastructure in the field and select the "New ..." option in the dropdown menu. See [tips]({% link +usage/tips_tricks.md %}) for more information. \ No newline at end of file diff --git a/docs/usage/import_export.md b/docs/usage/import_export.md new file mode 100644 index 00000000..e43936cc --- /dev/null +++ b/docs/usage/import_export.md @@ -0,0 +1,161 @@ +--- +layout: default +title: Import & Export data +nav_order: 4 +parent: Usage +--- + +# Import & Export data + +Part-DB offers the possibility to import existing data (parts, data structures, etc.) from existing data sources into +Part-DB. Data can also be exported from Part-DB into various formats. + +## Import + +{: .note } +> As data import is a very powerful feature and can easily fill up your database with lots of data, import is by default +> only available for +> administrators. If you want to allow other users to import data, or can not import data, check the permissions of the +> user. You can enable import for each data structure +> individually in the permissions settings. + +If you want to import data from PartKeepr you might want to look into the [PartKeepr migration guide]({% link +upgrade_legacy.md %}). + +### Import parts + +Part-DB supports the import of parts from CSV files and other formats. This can be used to import existing parts from +other databases or data sources into Part-DB. The import can be done via the "Tools -> Import parts" page, which you can +find in the "Tools" sidebar panel. + +{: .important } +> When importing data, the data is immediately written to database during the import process, when the data is formally +> valid. +> You will not be able to check the data before it is written to the database, so you should review the data before +> using the import tool. + +You can upload the file that should be imported here and choose various options on how the data should be treated: + +* **Format**: By default "auto" is selected here and Part-DB will try to detect the format of the file automatically + based on its file extension. If you want to force a specific format or Part-DB can not auto-detect the format, you can + select it here. +* **CSV delimiter**: If you upload a CSV file, you can select the delimiter character which is used to separate the + columns in the CSV file. Depending on the CSV file, this might be a comma (`,`) or semicolon (`;`). +* **Category override**: You can select (or create) a category here, to which all imported parts should be assigned, no + matter what was specified in the import file. This can be useful if you want to assign all imports to a certain + category or if no category is specified in the data. If you leave this field empty, the category will be determined by + the import file (or the export will error, if no category is specified). +* **Mark all imported parts as "Needs review"**: If this is selected, all imported parts will be marked as "Needs + review" after the import. This can be useful if you want to review all imported parts before using them. +* **Create unknown data structures**: If this is selected Part-DB will create new data structures (like categories, + manufacturers, etc.) if no data structure(s) with the same name and path already exists. If this is not selected, only + existing data structures will be used and if no matching data strucure is found, the imported parts field will be empty. +* **Path delimiter**: Part-DB allows you to create/select nested data structures (like categories, manufacturers, etc.) + by using a path (e.g. `Category 1->Category 1.1`, which will select/create the `Category 1.1` whose parent + is `Category 1`). This path is separated by the path delimiter. If you want to use a different path delimiter than the + default one (which is `>`), you can select it here. +* **Abort on validation error**: If this is selected, the import will be aborted if a validation error occurs (e.g. if a + required field is empty) for any of the imported parts and validation errors will be shown on top of the page. If this + is not selected, the import will continue for the other parts and only the invalid parts will be skipped. + +After you have selected the options, you can start the import by clicking the "Import" button. When the import is +finished, you will see the results of the import in the lower half of the page. You can find a table with the imported +parts (including links to them) there. + +#### Fields description + +For the importing of parts, you can use the following fields which will be imported into each part. Please note that the +field names are case-sensitive (so `name` is not the same as `Name`). All fields (besides name) are optional, so you can +leave them empty or do not include the column in your file. + +* **`name`** (required): The name of the part. This is the only required field, all other fields are optional. +* **`description`**: The description of the part, you can use markdown/HTML syntax here for rich text formatting. +* **`notes`** or **`comment`**: The notes of the part, you can use markdown/HTML syntax here for rich text formatting. +* **`category`**: The category of the part. This can be a path (e.g. `Category 1->Category 1.1`), which will + select/create the `Category 1.1` whose parent is `Category 1`. If you want to use a different path delimiter than the + default one (which is `->`), you can select it in the import options. If the category does not exist and the option " + Create unknown datastructures" is selected, it will be created. +* **`footprint`**: The footprint of the part. Can be a path similar to the category field. +* **`favorite`**: If this is set to `1`, the part will be marked as favorite. +* **`manufacturer`**: The manufacturer of the part. Can be a path similar to the category field. +* **`manufacturer_product_number`** or **`mpn`**: The manufacturer product number of the part. +* **`manufacturer_product_url`**: The URL to the product page of the manufacturer of the part. +* **`manufacturing_status`**: The manufacturing status of the part, must be one of the following + values: `announced`, `active`, `nrfnd`, `eol`, `discontinued` or left empty. +* **`needs_review`** or **`needs_review`**: If this is set to `1`, the part will be marked as "needs review". +* **`tags`**: A comma-separated list of tags for the part. +* **`mass`**: The mass of the part in grams. +* **`ipn`**: The IPN (Item Part Number) of the part. +* **`minamount`**: The minimum amount of the part which should be in stock. +* **`partUnit`**: The measurement unit of the part to use. Can be a path similar to the category field. + +With the following fields, you can specify storage locations and amount/quantity in stock of the part. A PartLot will +be created automatically from the data and assigned to the part. The following fields are helpers for an easy import of +parts at one storage location. If you need to create a Part with multiple PartLots you have to use JSON format (or CSV) +with nested objects: + +**`storage_location`** or **`storelocation`**: The storage location of the part. Can be a path similar to the category +field. +**`amount`**, **`quantity`** or **`instock`**: The amount of the part in stock. If this value is not set, the part lot +will be marked with "unknown amount" + +The following fields can be used to specify the supplier/distributor, supplier product number, and the price of the part. +This is only possible for a single supplier/distributor and price with these fields. If you need to specify multiple +suppliers/distributors or prices, you have to use JSON format (or CSV) with nested objects. +**Please note that the supplier fields is required, if you want to import prices or supplier product numbers**. If the +supplier is not specified, the price and supplier product number fields will be ignored: + +* **`supplier`**: The supplier of the part. Can be a path similar to the category field. +* **`supplier_product_number`** or **`supplier_part_number`** or * **`spn`**: The supplier product number of the part. +* **`price`**: The price of the part in the base currency of the database (by default euro). + +#### Example data + +Here you can find some example data for the import of parts, you can use it as a template for your own import ( +especially the CSV file). + +* [Part import CSV example]({% link assets/usage/import_export/part_import_example.csv %}) with all possible fields + +## Export + +By default, every user, who can read the datastructure, can also export the data of this datastructure, as this does not +give the user any additional information. + +### Exporting data structures (categories, manufacturers, etc.) + +You can export data structures (like categories, manufacturers, etc.) in the respective edit page (e.g. Tools Panel -> +Edit -> Category). +If you select a certain data structure from your list, you can export it (and optionally all sub data structures) in the " +Export" tab. +If you want to export all data structures of a certain type (e.g. all categories in your database), you can select the " +Export all" function in the "Import / Export" tab of the "new element" page. + +You can select between the following export formats: + +* **CSV** (Comma Separated Values): A semicolon-separated list of values, where every line represents an element. This + format can be imported into Excel or LibreOffice Calc and is easy to work with. However, it does not support nested + data structures or sub data (like parameters, attachments, etc.), very well (many columns are generated, as every + possible sub-data is exported as a separate column). +* **JSON** (JavaScript Object Notation): A text-based format, which is easy to work with programming languages. It + supports nested data structures and sub-data (like parameters, attachments, etc.) very well. However, it is not easy to + work with in Excel or LibreOffice Calc and you may need to write some code to work with the exported data + efficiently. +* **YAML** (Yet Another Markup Language): Very similar to JSON +* **XML** (Extensible Markup Language): Good support with nested data structures. Similar use cases as JSON and YAML. + +Also, you can select between the following export levels: + +* **Simple**: This will only export very basic information about the name (like the name, or description for parts) +* **Extended**: This will export all commonly used information about this data structure (like notes, options, etc.) +* **Full**: This will export all available information about this data structure (like all parameters, attachments) + +Please note that the level will also be applied to all sub-data or children elements. So if you select "Full" for a +part, all the associated categories, manufacturers, footprints, etc. will also be exported with all available +information, this can lead to very large export files. + +### Exporting parts + +You can export parts in all part tables. Select the parts you want via the checkbox in the table line and select the +export format and level in the appearing menu. + +See the section about exporting data structures for more information about the export formats and levels. \ No newline at end of file diff --git a/docs/usage/information_provider_system.md b/docs/usage/information_provider_system.md new file mode 100644 index 00000000..8de83a8e --- /dev/null +++ b/docs/usage/information_provider_system.md @@ -0,0 +1,273 @@ +--- +title: Information provider system +layout: default +parent: Usage +--- + +# Information provider system + +Part-DB can create parts based on information from external sources: For example, with the right setup you can just +search for a part number +and Part-DB will query selected distributors and manufacturers for the part and create a part with the information it +found. +This way your Part-DB parts automatically get datasheet links, prices, parameters, and more, with just a few clicks. + +## Usage + +Before you can use the information provider system, you have to configure at least one information provider, which act +as data source. +See below for a list of available information providers and available configuration options. +For many providers it is enough, to set up the API keys in the env configuration, some require an additional OAuth +connection. +You can list all enabled information providers in the browser +at `https://your-partdb-instance.tld/tools/info_providers/providers` (you need the right permission for it, see below). + +To use the information provider system, your user need to have the right permissions. Go to the permission management +page of +a user or a group and assign the permissions of the "Info providers" group in the "Miscellaneous" tab. + +If you have the required permission you will find in the sidebar in the "Tools" section the entry "Create part from info +provider". +Click this and you will land on a search page. Enter the part number you want to search for and select the information +providers you want to use. + +After you click Search, you will be presented with the results and can select the result that fits best. +With a click on the blue plus button, you will be redirected to the part creation page with the information already +filled in. + +![image]({% link assets/usage/information_provider_system/animation.gif %}) + +If you want to update an existing part, go to the parts info page and click on the "Update from info provider" button in +the tools tab. You will be redirected to a search page, where you can search the info providers to automatically update this +part. + +## Alternative names + +Part-DB tries to automatically find existing elements from your database for the information it got from the providers +for fields like manufacturer, footprint, etc. +For this, it searches for an element with the same name (case-insensitive) as the information it got from the provider. So +e.g. if the provider returns "EXAMPLE CORP" as the manufacturer, +Part-DB will automatically select the element with the name "Example Corp" from your database. + +As the names of these fields differ from provider to provider (and maybe not even normalized for the same provider), you +can define multiple alternative names for an element (on their editing page). +For example, if you define a manufacturer "Example Corp" with the alternative names "Example Corp.", "Example Corp", "Example +Corp. Inc." and "Example Corporation", +then the provider can return any of these names and Part-DB will still automatically select the right element. + +If Part-DB finds no matching element, it will automatically create a new one, when you do not change the value before +saving. + +## Attachment types + +The information provider system uses attachment types to differentiate between datasheets and image attachments. +For this it will create a "Datasheet" and "Image" attachment type on the first run. You can change the names of these +types in the attachment type settings (as long as you keep the "Datasheet"/"Image" in the alternative names field). + +If you already have attachment types for images and datasheets and want the information provider system to use them, you +can +add the alternative names "Datasheet" and "Image" to the alternative names field of the attachment types. + +## Data providers + +The system tries to be as flexible as possible, so many different information sources can be used. +Each information source is called am "info provider" and handles the communication with the external source. +The providers are just a driver that handles the communication with the different external sources and converts them +into a common format Part-DB understands. +That way it is pretty easy to create new providers as they just need to do very little work. + +Normally the providers utilize an API of a service, and you need to create an account at the provider and get an API key. +Also, there are limits on how many requests you can do per day or month, depending on the provider and your contract +with them. + +The following providers are currently available and shipped with Part-DB: + +(All trademarks are property of their respective owners. Part-DB is not affiliated with any of the companies.) + +### Octopart + +The Octopart provider uses the [Octopart / Nexar API](https://nexar.com/api) to search for parts and get information. +To use it you have to create an account at Nexar and create a new application on +the [Nexar Portal](https://portal.nexar.com/). +The name does not matter, but it is important that the application has access to the "Supply" scope. +In the Authorization tab, you will find the client ID and client secret, which you have to put in the Part-DB env +configuration (see below). + +Please note that the Nexar API in the free plan is limited to 1000 results per month. +That means if you search for a keyword and results in 10 parts, then 10 will be subtracted from your monthly limit. You +can see your current usage on the Nexar portal. +Part-DB caches the search results internally, so if you have searched for a part before, it will not count against your +monthly limit again, when you create it from the search results. + +The following env configuration options are available: + +* `PROVIDER_OCTOPART_CLIENT_ID`: The client ID you got from Nexar (mandatory) +* `PROVIDER_OCTOPART_SECRET`: The client secret you got from Nexar (mandatory) +* `PROVIDER_OCTOPART_CURRENCY`: The currency you want to get prices in if available (optional, 3 letter ISO-code, + default: `EUR`). If an offer is only available in a certain currency, + Part-DB will save the prices in their native currency, and you can use Part-DB currency conversion feature to convert + it to your preferred currency. +* `PROVIDER_OCTOPART_COUNTRY`: The country you want to get prices in if available (optional, 2 letter ISO-code, + default: `DE`). To get the correct prices, you have to set this and the currency setting to the correct value. +* `PROVIDER_OCTOPART_SEARCH_LIMIT`: The maximum number of results to return per search (optional, default: `10`). This + affects how quickly your monthly limit is used up. +* `PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS`: If set to `true`, only offers + from [authorized sellers](https://octopart.com/authorized) will be returned (optional, default: `false`). + +**Attention**: If you change the Octopart clientID after you have already used the provider, you have to remove the +OAuth token in the Part-DB database. Remove the entry in the table `oauth_tokens` with the name `ip_octopart_oauth`. + +### Digi-Key + +The Digi-Key provider uses the [Digi-Key API](https://developer.digikey.com/) to search for parts and get shopping +information from [Digi-Key](https://www.digikey.com/). +To use it you have to create an account at Digi-Key and get an API key on +the [Digi-Key API page](https://developer.digikey.com/). +You must create an organization there and create a "Production app". Most settings are not important, you just have to +grant access to the "Product Information" API. +You will get a Client ID and a Client Secret, which you have to put in the Part-DB env configuration (see below). + +The following env configuration options are available: + +* `PROVIDER_DIGIKEY_CLIENT_ID`: The client ID you got from Digi-Key (mandatory) +* `PROVIDER_DIGIKEY_SECRET`: The client secret you got from Digi-Key (mandatory) +* `PROVIDER_DIGIKEY_CURRENCY`: The currency you want to get prices in (optional, default: `EUR`) +* `PROVIDER_DIGIKEY_LANGUAGE`: The language you want to get the descriptions in (optional, default: `en`) +* `PROVIDER_DIGIKEY_COUNTRY`: The country you want to get the prices for (optional, default: `DE`) + +The Digi-Key provider needs an additional OAuth connection. To do this, go to the information provider +list (`https://your-partdb-instance.tld/tools/info_providers/providers`), +go to Digi-Key provider (in the disabled page), and click on the "Connect OAuth" button. You will be redirected to +Digi-Key, where you have to log in and grant access to the app. +To do this your user needs the "Manage OAuth tokens" permission from the "System" section in the "System" tab. +The OAuth connection should only be needed once, but if you have any problems with the provider, just click the button +again, to establish a new connection. + +### TME + +The TME provider uses the API of [TME](https://www.tme.eu/) to search for parts and getting shopping information from +them. +To use it you have to create an account at TME and get an API key on the [TME API page](https://developers.tme.eu/en/). +You have to generate a new anonymous key there and enter the key and secret in the Part-DB env configuration (see +below). + +The following env configuration options are available: + +* `PROVIDER_TME_KEY`: The API key you got from TME (mandatory) +* `PROVIDER_TME_SECRET`: The API secret you got from TME (mandatory) +* `PROVIDER_TME_CURRENCY`: The currency you want to get prices in (optional, default: `EUR`) +* `PROVIDER_TME_LANGUAGE`: The language you want to get the descriptions in (`en`, `de` and `pl`) (optional, + default: `en`) +* `PROVIDER_TME_COUNTRY`: The country you want to get the prices for (optional, default: `DE`) +* `PROVIDER_TME_GET_GROSS_PRICES`: If this is set to `1` the prices will be gross prices (including tax), otherwise net + prices (optional, default: `0`) + +### Farnell / Element14 / Newark + +The Farnell provider uses the [Farnell API](https://partner.element14.com/) to search for parts and getting shopping +information from [Farnell](https://www.farnell.com/). +You have to create an account at Farnell and get an API key on the [Farnell API page](https://partner.element14.com/). +Register a new application there (settings does not matter, as long as you select the "Product Search API") and you will +get an API key. + +The following env configuration options are available: + +* `PROVIDER_ELEMENT14_KEY`: The API key you got from Farnell (mandatory) +* `PROVIDER_ELEMENT14_STORE_ID`: The store ID you want to use. This decides the language of results, currency and + country of prices (optional, default: `de.farnell.com`, + see [here](https://partner.element14.com/docs/Product_Search_API_REST__Description) for available values) + +### Mouser + +The Mouser provider uses the [Mouser API](https://www.mouser.de/api-home/) to search for parts and getting shopping +information from [Mouser](https://www.mouser.com/). +You have to create an account at Mouser and register for an API key for the Search API on +the [Mouser API page](https://www.mouser.de/api-home/). +You will receive an API token, which you have to put in the Part-DB env configuration (see below): +At the registration you choose a country, language, and currency in which you want to get the results. + +*Attention*: Currently (January 2024) the mouser API seems to be somewhat broken, in the way that it does not return any +information about datasheets and part specifications. Therefore Part-DB can not retrieve them, even if they are shown +at the mouser page. See [issue #503](https://github.com/Part-DB/Part-DB-server/issues/503) for more info. + +Following env configuration options are available: + +* `PROVIDER_MOUSER_KEY`: The API key you got from Mouser (mandatory) +* `PROVIDER_MOUSER_SEARCH_LIMIT`: The maximum number of results to return per search (maximum 50) +* `PROVIDER_MOUSER_SEARCH_OPTION`: You can choose an option here to restrict the search results to RoHs compliant and + available parts. Possible values are `None`, `Rohs`, `InStock`, `RohsAndInStock`. +* `PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE`: A bit of an obscure option. The original description of Mouser is: Used + when searching for keywords in the language specified when you signed up for Search API. + +### LCSC + +[LCSC](https://www.lcsc.com/) is a Chinese distributor of electronic parts. It does not offer a public API, but the LCSC +webshop uses an internal JSON based API to render the page. Part-DB can use this inofficial API to get part information +from LCSC. + +**Please note, that the use of this internal API is not intended or endorsed by LCS and it could break at any time. So use it at your own risk.** + +An API key is not required, it is enough to enable the provider using the following env configuration options: + +* `PROVIDER_LCSC_ENABLED`: Set this to `1` to enable the LCSC provider +* `PROVIDER_LCSC_CURRENCY`: The currency you want to get prices in (see LCSC webshop for available currencies, default: `EUR`) + +### OEMsecrets + +The oemsecrets provider uses the [oemsecrets API](https://www.oemsecrets.com/) to search for parts and getting shopping +information from them. Similar to octopart it aggregates offers from different distributors. + +You can apply for a free API key on the [oemsecrets API page](https://www.oemsecrets.com/api/) and put the key you get +in the Part-DB env configuration (see below). + +The following env configuration options are available: + +* `PROVIDER_OEMSECRETS_KEY`: The API key you got from oemsecrets (mandatory) +* `PROVIDER_OEMSECRETS_COUNTRY_CODE`: The two-letter code of the country you want to get the prices for +* `PROVIDER_OEMSECRETS_CURRENCY`: The currency you want to get prices in (optional, default: `EUR`) +* `PROVIDER_OEMSECRETS_ZERO_PRICE`: If set to `1`, parts with a price of 0 will be included in the search results, otherwise + they will be excluded (optional, default: `0`) +* `PROVIDER_OEMSECRETS_SET_PARAM`: If set to `1`, the provider will try to extract parameters from the part description +* `PROVIDER_OEMSECRETS_SORT_CRITERIA`: The criteria to sort the search results by. 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 set to any other value, no sorting is performed. + +### Reichelt + +The reichelt provider uses webscraping from [reichelt.com](https://reichelt.com/) to get part information. +This is not an official API and could break at any time. So use it at your own risk. + +The following env configuration options are available: +* `PROVIDER_REICHELT_ENABLED`: Set this to `1` to enable the Reichelt provider +* `PROVIDER_REICHELT_CURRENCY`: The currency you want to get prices in. Only possible for countries which use Non-EUR (optional, default: `EUR`) +* `PROVIDER_REICHELT_COUNTRY`: The country you want to get the prices for (optional, default: `DE`) +* `PROVIDER_REICHELT_LANGUAGE`: The language you want to get the descriptions in (optional, default: `en`) +* `PROVIDER_REICHELT_INCLUDE_VAT`: If set to `1`, the prices will be gross prices (including tax), otherwise net prices (optional, default: `1`) + +### Pollin + +The pollin provider uses webscraping from [pollin.de](https://www.pollin.de/) to get part information. +This is not an official API and could break at any time. So use it at your own risk. + +The following env configuration options are available: +* `PROVIDER_POLLIN_ENABLED`: Set this to `1` to enable the Pollin provider + +### Custom provider + +To create a custom provider, you have to create a new class implementing the `InfoProviderInterface` interface. As long +as it is a valid Symfony service, it will be automatically loaded and can be used. +Besides some metadata functions, you have to implement the `searchByKeyword()` and `getDetails()` functions, which do +the actual API requests and return the information to Part-DB. +See the existing providers for examples. +If you created a new provider, feel free to create a pull request to add it to the Part-DB core. + +## Result caching + +To reduce the number of API calls against the providers, the results are cached: + +* The search results (exact search term) are cached for 7 days +* The product details are cached for 4 days + +If you need a fresh result, you can clear the cache by running `php .\bin\console cache:pool:clear info_provider.cache` +on the command line. +The default `php bin/console cache:clear` also clears the result cache, as it clears all caches. diff --git a/docs/usage/keybindings.md b/docs/usage/keybindings.md new file mode 100644 index 00000000..771d7684 --- /dev/null +++ b/docs/usage/keybindings.md @@ -0,0 +1,122 @@ +--- +title: Keybindings +layout: default +parent: Usage +--- + +# Keybindings + +This page lists all the keybindings of Part-DB. Currently, there are only the special character keybindings. + +## Special characters + +Using the keybindings below (Alt + key) you can insert special characters into the text fields of Part-DB. This works on +all text and search fields in Part-DB. + +### Greek letters + +| Key | Character | +|---------------------|-----------------------| +| **Alt + a** | α (Alpha) | +| **Alt + Shift + A** | Α (Alpha uppercase) | +| **Alt + b** | β (Beta) | +| **Alt + Shift + B** | Β (Beta uppercase) | +| **Alt + g** | γ (Gamma) | +| **Alt + Shift + G** | Γ (Gamma uppercase) | +| **Alt + d** | δ (Delta) | +| **Alt + Shift + D** | Δ (Delta uppercase) | +| **Alt + e** | ε (Epsilon) | +| **Alt + Shift + E** | Ε (Epsilon uppercase) | +| **Alt + z** | ζ (Zeta) | +| **Alt + Shift + Z** | Ζ (Zeta uppercase) | +| **Alt + h** | η (Eta) | +| **Alt + Shift + H** | Η (Eta uppercase) | +| **Alt + q** | θ (Theta) | +| **Alt + Shift + Q** | Θ (Theta uppercase) | +| **Alt + i** | ι (Iota) | +| **Alt + Shift + I** | Ι (Iota uppercase) | +| **Alt + k** | κ (Kappa) | +| **Alt + Shift + K** | Κ (Kappa uppercase) | +| **Alt + l** | λ (Lambda) | +| **Alt + Shift + L** | Λ (Lambda uppercase) | +| **Alt + m** | μ (Mu) | +| **Alt + Shift + M** | Μ (Mu uppercase) | +| **Alt + n** | ν (Nu) | +| **Alt + Shift + N** | Ν (Nu uppercase) | +| **Alt + x** | ξ (Xi) | +| **Alt + Shift + x** | Ξ (Xi uppercase) | +| **Alt + o** | ο (Omicron) | +| **Alt + Shift + O** | Ο (Omicron uppercase) | +| **Alt + p** | π (Pi) | +| **Alt + Shift + P** | Π (Pi uppercase) | +| **Alt + r** | ρ (Rho) | +| **Alt + Shift + R** | Ρ (Rho uppercase) | +| **Alt + s** | σ (Sigma) | +| **Alt + Shift + S** | Σ (Sigma uppercase) | +| **Alt + t** | τ (Tau) | +| **Alt + Shift + T** | Τ (Tau uppercase) | +| **Alt + u** | υ (Upsilon) | +| **Alt + Shift + U** | Υ (Upsilon uppercase) | +| **Alt + f** | φ (Phi) | +| **Alt + Shift + F** | Φ (Phi uppercase) | +| **Alt + y** | ψ (Psi) | +| **Alt + Shift + Y** | Ψ (Psi uppercase) | +| **Alt + c** | χ (Chi) | +| **Alt + Shift + C** | Χ (Chi uppercase) | +| **Alt + w** | ω (Omega) | +| **Alt + Shift + W** | Ω (Omega uppercase) | + +### Mathematical symbols + +| Key | Character | +|---------------------|-----------------------------| +| **Alt + 1** | ∑ (Sum symbol) | +| **Alt + Shift + 1** | ∏ (Product symbol) | +| **Alt + 2** | ∫ (Integral symbol) | +| **Alt + Shift + 2** | ∂ (Partial derivation) | +| **Alt + 3** | ≤ (Less or equal symbol) | +| **Alt + Shift + 3** | ≥ (Greater or equal symbol) | +| **Alt + 4** | ∞ (Infinity symbol) | +| **Alt + Shift + 4** | ∅ (Empty set symbol) | +| **Alt + 5** | ≈ (Approximately) | +| **Alt + Shift + 5** | ≠ (Not equal symbol) | +| **Alt + 6** | ∈ (Element of) | +| **Alt + Shift + 6** | ∉ (Not element of) | +| **Alt + 7** | ∨ (Logical or) | +| **Alt + Shift + 7** | ∧ (Logical and) | +| **Alt + 8** | ∠ (Angle symbol) | +| **Alt + Shift + 8** | ∝ (Proportional to) | +| **Alt + 9** | √ (Square root) | +| **Alt + Shift + 9** | ∛ (Cube root) | +| **Alt + 0** | ± (Plus minus) | +| **Alt + Shift + 0** | ∓ (Minus plus) | + +### Currency symbols + +Please note, the following keybindings are bound to a specific keycode. The key character is not the same on all +keyboards. +It is given here for a US keyboard layout. + +For a German keyboard layout, replace ; with ö, and ' with ä. + +| Key | Character | +|---------------------------------|----------------------------| +| **Alt + ;** (code 192) | € (Euro currency symbol) | +| **Alt + Shift + ;** (code 192) | £ (Pound currency symbol) | +| **Alt + '** (code 222) | ¥ (Yen currency symbol) | +| **Alt + Shift + '** (code 222) | $ (Dollar currency symbol) | + +### Others + +Please note the following keybindings are bound to a specific keycode. The key character is not the same on all +keyboards. +It is given here for a US keyboard layout. + +For a German keyboard layout, replace `[` with `0`, and `]` with `´`. + +| Key | Character | +|--------------------------------|--------------------| +| **Alt + [** (code 219) | © (Copyright char) | +| **Alt + Shift + [** (code 219) | ® (Registered char) | +| **Alt + ]** (code 221) | ™ (Trademark char) | +| **Alt + Shift + ]** (code 221) | ° (Degree char) | diff --git a/docs/usage/labels.md b/docs/usage/labels.md index 0d885f12..e84f4d7f 100644 --- a/docs/usage/labels.md +++ b/docs/usage/labels.md @@ -6,90 +6,284 @@ parent: Usage # Labels -Part-DB support the generation and printing of labels for parts, part lots and storelocation. -You can use the "Tools -> Labelgenerator" menu entry to create labels, or click the label generation link on the part. +Part-DB support the generation and printing of labels for parts, part lots and storage locations. +You can use the "Tools -> Label generator" menu entry to create labels or click the label generation link on the part. -You can define label templates by creating Label profiles. This way you can create many similar looking labels with for +You can define label templates by creating Label profiles. This way you can create many similar-looking labels with for many parts. -The content of the labels is defined by the templates content field. You can use the WYSIWYG editor to create and style the content (or write HTML code). +The content of the labels is defined by the template's content field. You can use the WYSIWYG editor to create and style +the content (or write HTML code). Using the "Label placeholder" menu in the editor, you can insert placeholders for the data of the parts. It will be replaced by the concrete data when the label is generated. - + ## Label placeholders + A placeholder has the format `[[PLACEHOLDER]]` and will be filled with the concrete data by Part-DB. -You can use the "Placeholders" dropdown in content editor, to automatically insert the placeholders. +You can use the "Placeholders" dropdown in the content editor, to automatically insert the placeholders. ### Common -| Placeholder | Description | Example | -|---|---|---| -| `[[USERNAME]]` | The user name of the currently logged in user | admin | -| `[[USERNAME_FULL]]` | The full name of the current user | John Doe (@admin) | -| `[[DATETIME]]` | The current date and time in the selected locale | 31.12.2017, 18:34:11 | -| `[[DATE]]` | The current date in the selected locale | 31.12.2017 | -| `[[TIME]]` | The current time in the selected locale | 18:34:11 | -| `[[INSTALL_NAME]]` | The name of the current installation (see $config['partdb_title']) | Part-DB | -| `[[INSTANCE_URL]]` | The URL of the current installation | https://demo.part-db.de | +| Placeholder | Description | Example | +|---------------------|--------------------------------------------------------------------|-------------------------| +| `[[USERNAME]]` | The user name of the currently logged in user | admin | +| `[[USERNAME_FULL]]` | The full name of the current user | John Doe (@admin) | +| `[[DATETIME]]` | The current date and time in the selected locale | 31.12.2017, 18:34:11 | +| `[[DATE]]` | The current date in the selected locale | 31.12.2017 | +| `[[TIME]]` | The current time in the selected locale | 18:34:11 | +| `[[INSTALL_NAME]]` | The name of the current installation (see $config['partdb_title']) | Part-DB | +| `[[INSTANCE_URL]]` | The URL of the current installation | https://demo.part-db.de | ### Parts -| Placeholder | Description | Example | -|---|---|---| -| `[[ID]]` | The internal ID of the part | 24 | -| `[[NAME]]` | The name of the part | ATMega328 | -| `[[CATEGORY]]` | The name of the category (without path) | AVRs | -| `[[CATEGORY_FULL]]` | The full path of the category | Aktiv->MCUs->AVRs | -| `[[MANUFACTURER]]` | The name of the manufacturer | Atmel | -| `[[MANUFACTURER_FULL]]` | The full path of the manufacturer | Halbleiterhersteller->Atmel | -| `[[FOOTPRINT]]` | The name of the footprint (without path) | DIP-32 | -| `[[FOOTPRINT_FULL]]` | The full path of the footprint | Bedrahtet->DIP->DIP-32 | -| `[[MASS]]` | The mass of the part | 123.4 g | -| `[[MPN]]` | The manufacturer product number | BC547ACT | -| `[[TAGS]]` | The tags of the part | SMD, Tag1 | -| `[[M_STATUS]]` | The manufacturing status of the part | Active | -| `[[DESCRIPTION]]` | The rich text description of the part | *NPN* | -| `[[DESCRIPTION_T]]` | The description as plain text | NPN | -| `[[COMMENT]]` | The rich text comment of the part | | -| `[[COMMENT_T]]` | The comment as plain text | | -| `[[LAST_MODIFIED]]` | The datetime when the element was last modified | 2/26/16, 5:38 PM | -| `[[CREATION_DATE]]` | The datetime when the element was created | 2/26/16, 5:38 PM | +| Placeholder | Description | Example | +|-------------------------|-------------------------------------------------|-----------------------------| +| `[[ID]]` | The internal ID of the part | 24 | +| `[[NAME]]` | The name of the part | ATMega328 | +| `[[CATEGORY]]` | The name of the category (without path) | AVRs | +| `[[CATEGORY_FULL]]` | The full path of the category | Aktiv->MCUs->AVRs | +| `[[MANUFACTURER]]` | The name of the manufacturer | Atmel | +| `[[MANUFACTURER_FULL]]` | The full path of the manufacturer | Halbleiterhersteller->Atmel | +| `[[FOOTPRINT]]` | The name of the footprint (without path) | DIP-32 | +| `[[FOOTPRINT_FULL]]` | The full path of the footprint | Bedrahtet->DIP->DIP-32 | +| `[[MASS]]` | The mass of the part | 123.4 g | +| `[[MPN]]` | The manufacturer product number | BC547ACT | +| `[[TAGS]]` | The tags of the part | SMD, Tag1 | +| `[[M_STATUS]]` | The manufacturing status of the part | Active | +| `[[DESCRIPTION]]` | The rich text description of the part | *NPN* | +| `[[DESCRIPTION_T]]` | The description as plain text | NPN | +| `[[COMMENT]]` | The rich text comment of the part | | +| `[[COMMENT_T]]` | The comment as plain text | | +| `[[LAST_MODIFIED]]` | The datetime when the element was last modified | 2/26/16, 5:38 PM | +| `[[CREATION_DATE]]` | The datetime when the element was created | 2/26/16, 5:38 PM | ### Part lot -| Placeholder | Description | Example | -|---|---|---| -| `[[LOT_ID]]` | Part lot ID | 123 | -| `[[LOT_NAME]]` | Part lot name | | -| `[[LOT_COMMENT]]` | Part lot comment | | -| `[[EXPIRATION_DATE]]` | Expiration date of the part lot | | -| `[[AMOUNT]]` | The amount of parts in this lot | 12 | -| `[[LOCATION]]` | The storage location of this part lot | Location A | -| `[[LOCATION_FULL]]` | The full path of the storage location | Location -> Location A | +| Placeholder | Description | Example | +|-----------------------|---------------------------------------|------------------------| +| `[[LOT_ID]]` | Part lot ID | 123 | +| `[[LOT_NAME]]` | Part lot name | | +| `[[LOT_COMMENT]]` | Part lot comment | | +| `[[EXPIRATION_DATE]]` | Expiration date of the part lot | | +| `[[AMOUNT]]` | The amount of parts in this lot | 12 | +| `[[LOCATION]]` | The storage location of this part lot | Location A | +| `[[LOCATION_FULL]]` | The full path of the storage location | Location -> Location A | ### Storelocation -| Placeholder | Description | Example | -|---|---|---| -| `[[ID]]` | ID of the storage location | | -| `[[NAME]]` | Name of the storage location | Location A | -| `[[FULL_PATH]]` | The full path of the storage location | Location -> Location A | -| `[[PARENT]]` | The name of the parent location | Location | -| `[[PARENT_FULL_PATH]]` | The full path of the storage location | | -| `[[COMMENT]]` | The comment of the storage location | | -| `[[COMMENT_T]]` | The plain text version of the comment | -| `[[LAST_MODIFIED]]` | The datetime when the element was last modified | 2/26/16, 5:38 PM | -| `[[CREATION_DATE]]` | The datetime when the element was created | 2/26/16, 5:38 PM | +| Placeholder | Description | Example | +|------------------------|-------------------------------------------------|------------------------| +| `[[ID]]` | ID of the storage location | | +| `[[NAME]]` | Name of the storage location | Location A | +| `[[FULL_PATH]]` | The full path of the storage location | Location -> Location A | +| `[[PARENT]]` | The name of the parent location | Location | +| `[[PARENT_FULL_PATH]]` | The full path of the storage location | | +| `[[COMMENT]]` | The comment of the storage location | | +| `[[COMMENT_T]]` | The plain text version of the comment | +| `[[LAST_MODIFIED]]` | The datetime when the element was last modified | 2/26/16, 5:38 PM | +| `[[CREATION_DATE]]` | The datetime when the element was created | 2/26/16, 5:38 PM | ## Twig mode -If you select "Twig" in parser mode under advanced settings, you can input a twig template in the lines field (activate source mode). You can use most of the twig tags and filters listed in [offical documentation](https://twig.symfony.com/doc/3.x/). +If you select "Twig" in parser mode under advanced settings, you can input a twig template in the lines field (activate +source mode). You can use most of the twig tags and filters listed +in [official documentation](https://twig.symfony.com/doc/3.x/). -The following variables are in injected into Twig and can be accessed using `{% raw %}{{ variable }}` (or `{% raw %}{{ variable.property }}{% endraw %}`): +Twig allows you for much more complex and dynamic label generation. You can use loops, conditions, and functions to create +the label content and you can access almost all data Part-DB offers. The label templates are evaluated in a special sandboxed environment, +where only certain operations are allowed. Only read access to entities is allowed. However as it circumvents Part-DB normal permission system, +the twig mode is only available to users with the "Twig mode" permission. -| Variable name | Description | -|--------------------------------------| ----------- | -| `{% raw %}{{ element }}{% endraw %}` | The target element, selected in label dialog | -| `{% raw %}{{ user }}{% endraw %}` | The current logged in user. Null if you are not logged in | -| `{% raw %}{{ install_title }}{% endraw %}` | The name of the current Part-DB instance (similar to [[INSTALL_NAME]] placeholder). | -| `{% raw %}{{ page }}{% endraw %}` | The page number (the nth-element for which the label is generated | \ No newline at end of file +The following variables are in injected into Twig and can be accessed using `{% raw %}{{ variable }}{% endraw %}` ( +or `{% raw %}{{ variable.property }}{% endraw %}`): + +| Variable name | Description | +|--------------------------------------------|--------------------------------------------------------------------------------------| +| `{% raw %}{{ element }}{% endraw %}` | The target element, selected in label dialog. | +| `{% raw %}{{ user }}{% endraw %}` | The current logged in user. Null if you are not logged in | +| `{% raw %}{{ install_title }}{% endraw %}` | The name of the current Part-DB instance (similar to [[INSTALL_NAME]] placeholder). | +| `{% raw %}{{ page }}{% endraw %}` | The page number (the nth-element for which the label is generated | +| `{% raw %}{{ last_page }}{% endraw %}` | The page number of the last element. Equals the number of all pages / element labels | +| `{% raw %}{{ paper_width }}{% endraw %}` | The width of the label paper in mm | +| `{% raw %}{{ paper_height }}{% endraw %}` | The height of the label paper in mm | + +### Use the placeholders in twig mode + +You can use the placeholders described above in the twig mode on `element` using the `{% raw %}{{ placeholder('PLACEHOLDER', element) }}{% endraw %}` +function or the ``{{ "[[PLACEHOLDER]]"|placeholders(element) }}`` filter: + +```twig +{% raw %} +{# The function can be used to get the a single placeholder value of an element, if the placeholder does not exist, null is returned #} +{{ placeholder('[[NAME]]', element) }} + +{# The filter can be used to replace all placeholders in a string with the values of the element #} +{{ "[[NAME]]: [[DESCRIPTION]]"|placeholders(element) }} + +{# Using the apply environment every placeholder in the apply block will be replaced automatically #} +{% apply placeholders(element) %} + [[NAME]]: [[DESCRIPTION]] +{% endapply %} + +{# If the block contains HTML use placeholders(element)|raw to prevent escaping of the HTML #} +{% apply placeholders(element)|raw %} + [[NAME]]: [[DESCRIPTION]] +{% endapply %} + +{% endraw %} +``` + +### Important entity fields in twig mode + +In twig mode you have access to many fields of the entity you are generating the label for and their associated entities. +Following are some important fields of the entities listed. See the [SandboxedTwigFactory service](https://github.com/Part-DB/Part-DB-server/blob/master/src/Services/LabelSystem/SandboxedTwigFactory.php) for the full list of allowed class methods. + +Please not that the field names might change in the future. + +#### Part + +| Field name | Description | +|---------------------|-----------------------------------------------------------------------------------------------| +| `id` | The internal ID of the part | +| `name` | The name of the part | +| `category` | The category of the part | +| `manufacturer` | The manufacturer of the part | +| `footprint` | The footprint of the part | +| `mass` | The mass of the part | +| `ManufacturerProductNumber` | The manufacturer product number of the part | +| `tags` | The tags of the part | +| `description` | The rich text (markdown) description of the part | +| `comment` | The rich text (markdown) comment of the part | +| `lastModified` | The datetime object when the part was last modified | +| `creationDate` | The datetime object when the part was created | +| `ipn` | The internal part number of the part | +| `partUnit` | The unit of the part | +| `amountSum` | The sum of the amount of all part lots of this part | +| `amountUnknwon` | Bool: True if there is at least one part lot with unknown amount | +| `partLots` | The part lots of the part | +| `parameters` | The parameters of the part | +| `orderdetails` | The order details of the part as array of Orderdetails | + +#### Part lot + +| Field name | Description | +|---------------------|-----------------------------------------------------------------------------------------------| +| `id` | The internal ID of the part lot | +| `name` | The name of the part lot | +| `comment` | The rich text (markdown) comment of the part lot | +| `expirationDate` | The expiration date of the part lot (as Datetime object) | +| `amount` | The amount of parts in this lot | +| `storageLocation` | The storage location of this part lot | +| `part` | The part of this part lot | +| `needsRefill` | Bool: True if the part lot needs a refill | +| `expired` | Bool: True if the part lot is expired | +| `vendorBarcode` | The vendor barcode field of the lot | + +#### Structural entities like categories, manufacturers, footprints, and storage locations + +| Field name | Description | +|---------------------|-----------------------------------------------------------------------------------------------| +| `id` | The internal ID of the entity | +| `name` | The name of the entity | +| `comment` | The rich text (markdown) comment of the entity | +| `parent` | The parent entity of the entity | +| `children` | The children entities of the entity | +| `lastModified` | The datetime object when the entity was last modified | +| `creationDate` | The datetime object when the entity was created | +| `level` | The level of the entity in the hierarchy | +| `fullPath` | The full path of the entity (you can pass the delimiter as parameter) | +| `pathArray` | The path of the entity as array of strings | + +#### Orderdetails + +| Field name | Description | +|---------------------|-----------------------------------------------------------------------------------------------| +| `id` | The internal ID of the order detail | +| `part` | The part of the order detail | +| `supplier` | The supplier/distributor of the order detail | +| `obsolete` | Bool: True if the order detail is obsolete | +| `pricedetails` | The price details of the order detail as array of Pricedetails | + +#### Pricedetails + +| Field name | Description | +|---------------------|-----------------------------------------------------------------------------------------------| +| `id` | The internal ID of the price detail | +| `price` | The price of the price detail | +| `currency` | The currency of the price detail | +| `currencyIsoCode` | The ISO code of the used currency | +| `pricePerUnit` | The price per unit of the price detail | +| `priceRelatedQuantity` | The related quantity of the price detail | +| `minDiscountQuantity` | The minimum discount quantity of the price detail | + +#### User + +| Field name | Description | +|---------------------|-----------------------------------------------------------------------------------------------| +| `id` | The internal ID of the user | +| `username` | The username of the user | +| `email` | The email of the user | +| `fullName` | The full name of the user | +| `lastName` | The last name of the user | +| `firstName` | The first name of the user | +| `department` | The department of the user | + + +### Part-DB specific twig functions and filters + +Part-DB offers some custom twig functions and filters, which can be used in the twig mode and ease the rendering of +certain data: + +#### Functions + +| Function name | Description | +|----------------------------------------------|-----------------------------------------------------------------------------------------------| +| `placeholder(placeholder, element)` | Get the value of a placeholder of an element | +| `entity_type(element)` | Get the type of an entity as string | +| `entity_url(element, type)` | Get the URL to a specific entity type page (e.g. `info`, `edit`, etc.) | +| `barcode_svg(content, type)` | Generate a barcode SVG from the content and type (e.g. `QRCODE`, `CODE128` etc.). A svg string is returned, which you need to data uri encode to inline it. | + +### Filters + +| Filter name | Description | +|----------------------------------------------|-----------------------------------------------------------------------------------------------| +| `format_bytes` | Format a byte count to a human readable string | +| `format_money(price, currency)` | Format a price to a human readable string with the currency | +| `format_amount(amount, unit)` | Format an amount to a human readable string with the unit object | +| `format_si(value, unit_str)` | Format a value using SI prefixes and the given unit string | +| `placeholders(element)` | Replace all placeholders in a string with the values of the element | + +## Use custom fonts for PDF labels + +You can use your own fonts for label generation. To do this, put the TTF files of the fonts you want to use into +the `assets/fonts/dompdf` folder. +The filename will be used as name for the font family, and you can use a `_bold` (or `_b`), `_italic` (or `_i`) +or `_bold_italic` (or `_bi`) suffix to define +different styles of the font. So for example, if you copy the file `myfont.ttf` and `myfont_bold.ttf` into +the `assets/fonts/dompdf` folder, you can use the font family `myfont` with regular and bold style. +Afterward regenerate cache with `php bin/console cache:clear`, so the new fonts will be available for label generation. + +The fonts will not be available from the UI directly, you have to use it in the HTML directly either by defining +a `style="font-family: 'myfont';"` attribute on the HTML element or by using a CSS class. +You can define the font globally for the label, by adding following statement to the "Additional styles (CSS)" option in +the label generator settings: + +```css +* { + font-family: 'myfont'; +} +``` + +## Non-latin characters in PDF labels + +The default used font (DejaVu) does not support all characters. Especially characters from non-latin languages like +Chinese, Japanese, Korean, Arabic, Hebrew, Cyrillic, etc. are not supported. +For this, we use [Unifont](http://unifoundry.com/unifont.html) as fallback font. This font supports all (or most) Unicode +characters but is not as beautiful as DejaVu. + +If you want to use a different (more beautiful) font, you can use the [custom fonts](#use-custom-fonts-for-pdf-labels) +feature. +There is the [Noto](https://www.google.com/get/noto/) font family from Google, which supports a lot of languages and is +available in different styles (regular, bold, italic, bold-italic). +For example, you can use [Noto CJK](https://github.com/notofonts/noto-cjk) for more beautiful Chinese, Japanese, +and Korean characters. \ No newline at end of file diff --git a/docs/usage/tips_tricks.md b/docs/usage/tips_tricks.md index a9ca994a..d033cbe8 100644 --- a/docs/usage/tips_tricks.md +++ b/docs/usage/tips_tricks.md @@ -8,42 +8,49 @@ parent: Usage Following you can find miscellaneous tips and tricks for using Part-DB. -## Create datastructures directly from part edit page +## Create data structures directly from part edit page -Instead of first creating a category, manufacturer, footprint, etc. and then creating the part, you can create the -datastructures directly from the part edit page: Just type the name of the datastructure you want to create into the -select field on the part edit page and press "Create new ...". The new datastructure will be created, when you save +Instead of first creating a category, manufacturer, footprint, etc., and then creating the part, you can create the +data structures directly from the part edit page: Just type the name of the data structure you want to create into the +select field on the part edit page and press "Create new ...". The new data structure will be created when you save the part changes. -You can create also create nested datastructures this way. For example, if you want to create a new category "AVRs", +You can create also create nested data structures this way. For example, if you want to create a new category "AVRs", as a subcategory of "MCUs", you can just type "MCUs->AVRs" into the category select field and press "Create new". The new category "AVRs" will be created as a subcategory of "MCUs". If the category "MCUs" does not exist, it will be created too. -## Builtin footprint images -Part-DB includes several builtin images for common footprints. You can use these images in your footprint datastructures, -by creating an attachment on the datastructure and selecting it as preview image. +## Built-in footprint images + +Part-DB includes several built-in images for common footprints. You can use these images in your footprint +data structures, +by creating an attachment on the data structure and selecting it as the preview image. Type the name of the footprint image you want to use into the URL field of the attachment and select it from the -dropdown menu. You can find a gallery of all builtin footprint images and their names in the "Builtin footprint image gallery", -which you can find in the "Tools" menu (you maybe need to give your user the permission to access this tool). +dropdown menu. You can find a gallery of all builtin footprint images and their names in the "Builtin footprint image +gallery", +which you can find in the "Tools" menu (you may need to give your user the permission to access this tool). ## Parametric search -In the "parameters" tab of the filter panel on parts list page, you can define constraints, which parameter values -have to fullfill. This allows you to search for parts with specific parameters (or parameter ranges), for example you + +In the "parameters" tab of the filter panel on parts list page, you can define constraints, and which parameter values +have to fulfill. This allows you to search for parts with specific parameters (or parameter ranges), for example, you can search for all parts with a voltage rating of greater than 5 V. -## View own users permissions +## View own user's permissions + If you want to see which permissions your user has, you can find a list of the permissions in the "Permissions" panel on the user info page. ## Use LaTeX equations -You can use LaTeX equations everywhere where markdown is supported (for example in the description or notes field of a part). + +You can use LaTeX equations everywhere where markdown is supported (for example in the description or notes field of a +part). [KaTeX](https://katex.org/) is used to render the equations. You can find a list of supported features in the [KaTeX documentation](https://katex.org/docs/supported.html). -To input a LaTeX equation, you have to wrap it in a pair of dollar signs (`$`). Single dollar signs mark inline equations, -double dollar signs mark displayed equations (which will be its own line and centered). For example, the following equation -will be rendered as an inline equation: +To input a LaTeX equation, you have to wrap it in a pair of dollar signs (`$`). Single dollar signs mark inline +equations, double dollar signs mark displayed equations (which will be their own line and centered). +For example, the following equation will be rendered as an inline equation: ``` $E=mc^2$ @@ -56,10 +63,36 @@ $$E=mc^2$$ ``` ## Update currency exchange rates automatically -Part-DB can update the currency exchange rates of all defined currencies programatically + +Part-DB can update the currency exchange rates of all defined currencies programmatically by calling the `php bin/console partdb:currencies:update-exchange-rates`. If you call this command regularly (e.g. with a cronjob), you can keep the exchange rates up-to-date. Please note that if you use a base currency, which is not the Euro, you have to configure an exchange rate API, as the -free API used by default only supports the Euro as base currency. \ No newline at end of file +free API used by default only supports the Euro as base currency. + +## Enforce log comments + +On almost any editing operation it is possible to add a comment describing, what or why you changed something. +This comment will be written to changelog and can be viewed later. +If you want to force your users to add comments to certain operations, you can do this by setting +the `ENFORCE_CHANGE_COMMENTS_FOR` option. +See the configuration reference for more information. + +## Personal stocks and stock locations + +For maker spaces and universities with a lot of users, where each user can have his own stock, which only he should be +able to access, you can assign +the user as "owner" of a part lot. This way, only he is allowed to add or remove parts from this lot. + +## Update notifications + +Part-DB can show you a notification that there is a newer version than currently installed available. The notification +will be shown on the homepage and the server info page. +It is only be shown to users which has the `Show available Part-DB updates` permission. + +For the notification to work, Part-DB queries the GitHub API every 2 days to check for new releases. No data is sent to +GitHub besides the metadata required for the connection (so the public IP address of your computer running Part-DB). +If you don't want Part-DB to query the GitHub API, or if your server can not reach the internet, you can disable the +update notifications by setting the `CHECK_FOR_UPDATES` option to `false`. \ No newline at end of file diff --git a/migrations/Version1.php b/migrations/Version1.php index f1389801..fc4c7b2e 100644 --- a/migrations/Version1.php +++ b/migrations/Version1.php @@ -160,7 +160,7 @@ EOD; 21840,21840,21840,21840,21840,21520,21520,21520,20480,21520,20480, 20480,20480,20480,20480,21504,20480), ( - 2,'admin', '${admin_pw}','','', + 2,'admin', '$admin_pw','','', '','',1,1,21845,21845,21845,21,85,21,349525,21845,21845,21845,21845 ,21845,21845,21845,21845,21845,21845,21845,21845,21845,21845,21845, 21845,21845,21845,21845,21845,21845); @@ -235,4 +235,14 @@ EOD; { $this->warnIf(true, "Migration not needed for SQLite. Skipping..."); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20190902140506.php b/migrations/Version20190902140506.php index 38f1e4c7..8984cb06 100644 --- a/migrations/Version20190902140506.php +++ b/migrations/Version20190902140506.php @@ -69,6 +69,12 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration //For attachments $this->addSql('DELETE FROM `attachements` WHERE attachements.class_name = "Part" AND (SELECT COUNT(parts.id) FROM parts WHERE parts.id = attachements.element_id) = 0;'); + //Add perms_labels column to groups table if not existing (it was not created in certain legacy versions) + //This prevents the migration failing (see https://github.com/Part-DB/Part-DB-server/issues/366 and https://github.com/Part-DB/Part-DB-server/issues/67) + if (!$this->doesColumnExist('groups', 'perms_labels')) { + $this->addSql('ALTER TABLE `groups` ADD `perms_labels` SMALLINT NOT NULL AFTER `perms_tools`'); + } + /************************************************************************************************************** * Doctrine generated SQL **************************************************************************************************************/ @@ -100,11 +106,17 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration $this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES `storelocations` (id)'); $this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES `parts` (id) ON DELETE CASCADE'); $this->addSql('ALTER TABLE parts DROP INDEX parts_order_orderdetails_id_k, ADD UNIQUE INDEX UNIQ_6940A7FE81081E9B (order_orderdetails_id)'); - $this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_storelocation_fk'); + if ($this->doesFKExists('parts', 'parts_id_storelocation_fk')) { + $this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_storelocation_fk'); + } $this->addSql('DROP INDEX favorite ON parts'); $this->addSql('DROP INDEX parts_id_storelocation_k ON parts'); - $this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_footprint_fk'); - $this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_manufacturer_fk'); + if ($this->doesFKExists('parts', 'parts_id_footprint_fk')) { + $this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_footprint_fk'); + } + if ($this->doesFKExists('parts', 'parts_id_manufacturer_fk')) { + $this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_manufacturer_fk'); + } $this->addSql('ALTER TABLE parts CHANGE mininstock minamount DOUBLE PRECISION NOT NULL, ADD id_part_unit INT DEFAULT NULL, ADD manufacturer_product_number VARCHAR(255) NOT NULL, ADD manufacturing_status VARCHAR(255) DEFAULT NULL, ADD needs_review TINYINT(1) NOT NULL, ADD tags LONGTEXT NOT NULL, ADD mass DOUBLE PRECISION DEFAULT NULL, DROP instock, CHANGE id_category id_category INT NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE order_quantity order_quantity INT NOT NULL, CHANGE manual_order manual_order TINYINT(1) NOT NULL, CHANGE manufacturer_product_url manufacturer_product_url VARCHAR(255) NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CHANGE favorite favorite TINYINT(1) NOT NULL, DROP id_storelocation'); $this->addSql('ALTER TABLE parts ADD CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES `categories` (id)'); $this->addSql('ALTER TABLE parts ADD CONSTRAINT FK_6940A7FEEBBCC786 FOREIGN KEY (id_master_picture_attachement) REFERENCES `attachments` (id)'); @@ -119,39 +131,54 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON parts (id_manufacturer)'); $this->addSql('ALTER TABLE parts ADD CONSTRAINT parts_id_footprint_fk FOREIGN KEY (id_footprint) REFERENCES footprints (id)'); $this->addSql('ALTER TABLE parts ADD CONSTRAINT parts_id_manufacturer_fk FOREIGN KEY (id_manufacturer) REFERENCES manufacturers (id)'); - $this->addSql('ALTER TABLE attachment_types DROP FOREIGN KEY attachement_types_parent_id_fk'); + //We have to use the old table name here, because the if is executed before any sql command added by addSQL is executed (and thus the table name is not changed yet) + if ($this->doesFKExists('attachement_types', 'attachement_types_parent_id_fk')) { + $this->addSql('ALTER TABLE attachment_types DROP FOREIGN KEY attachement_types_parent_id_fk'); + } $this->addSql('ALTER TABLE attachment_types ADD filetype_filter LONGTEXT NOT NULL, ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); $this->addSql('DROP INDEX attachement_types_parent_id_k ON attachment_types'); $this->addSql('CREATE INDEX IDX_EFAED719727ACA70 ON attachment_types (parent_id)'); $this->addSql('ALTER TABLE attachment_types ADD CONSTRAINT attachement_types_parent_id_fk FOREIGN KEY (parent_id) REFERENCES attachment_types (id)'); - $this->addSql('ALTER TABLE categories DROP FOREIGN KEY categories_parent_id_fk'); + if ($this->doesFKExists('categories', 'categories_parent_id_fk')) { + $this->addSql('ALTER TABLE categories DROP FOREIGN KEY categories_parent_id_fk'); + } $this->addSql('ALTER TABLE categories ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE disable_footprints disable_footprints TINYINT(1) NOT NULL, CHANGE disable_manufacturers disable_manufacturers TINYINT(1) NOT NULL, CHANGE disable_autodatasheets disable_autodatasheets TINYINT(1) NOT NULL, CHANGE disable_properties disable_properties TINYINT(1) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); $this->addSql('DROP INDEX categories_parent_id_k ON categories'); $this->addSql('CREATE INDEX IDX_3AF34668727ACA70 ON categories (parent_id)'); $this->addSql('ALTER TABLE categories ADD CONSTRAINT categories_parent_id_fk FOREIGN KEY (parent_id) REFERENCES categories (id)'); - $this->addSql('ALTER TABLE devices DROP FOREIGN KEY devices_parent_id_fk'); + if ($this->doesFKExists('devices', 'devices_parent_id_fk')) { + $this->addSql('ALTER TABLE devices DROP FOREIGN KEY devices_parent_id_fk'); + } $this->addSql('ALTER TABLE devices ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE order_quantity order_quantity INT NOT NULL, CHANGE order_only_missing_parts order_only_missing_parts TINYINT(1) NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CHANGE comment comment LONGTEXT NOT NULL'); $this->addSql('DROP INDEX devices_parent_id_k ON devices'); $this->addSql('CREATE INDEX IDX_11074E9A727ACA70 ON devices (parent_id)'); $this->addSql('ALTER TABLE devices ADD CONSTRAINT devices_parent_id_fk FOREIGN KEY (parent_id) REFERENCES devices (id)'); - $this->addSql('ALTER TABLE footprints DROP FOREIGN KEY footprints_parent_id_fk'); + if ($this->doesFKExists('footprints', 'footprints_parent_id_fk')) { + $this->addSql('ALTER TABLE footprints DROP FOREIGN KEY footprints_parent_id_fk'); + } $this->addSql('ALTER TABLE footprints ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); $this->addSql('DROP INDEX footprints_parent_id_k ON footprints'); $this->addSql('CREATE INDEX IDX_A34D68A2727ACA70 ON footprints (parent_id)'); $this->addSql('ALTER TABLE footprints ADD CONSTRAINT footprints_parent_id_fk FOREIGN KEY (parent_id) REFERENCES footprints (id)'); - $this->addSql('ALTER TABLE manufacturers DROP FOREIGN KEY manufacturers_parent_id_fk'); + if ($this->doesFKExists('manufacturers', 'manufacturers_parent_id_fk')) { + $this->addSql('ALTER TABLE manufacturers DROP FOREIGN KEY manufacturers_parent_id_fk'); + } $this->addSql('ALTER TABLE manufacturers ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE address address VARCHAR(255) NOT NULL, CHANGE phone_number phone_number VARCHAR(255) NOT NULL, CHANGE fax_number fax_number VARCHAR(255) NOT NULL, CHANGE email_address email_address VARCHAR(255) NOT NULL, CHANGE website website VARCHAR(255) NOT NULL, CHANGE auto_product_url auto_product_url VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); $this->addSql('DROP INDEX manufacturers_parent_id_k ON manufacturers'); $this->addSql('CREATE INDEX IDX_94565B12727ACA70 ON manufacturers (parent_id)'); $this->addSql('ALTER TABLE manufacturers ADD CONSTRAINT manufacturers_parent_id_fk FOREIGN KEY (parent_id) REFERENCES manufacturers (id)'); - $this->addSql('ALTER TABLE storelocations DROP FOREIGN KEY storelocations_parent_id_fk'); + if ($this->doesFKExists('storelocations', 'storelocations_parent_id_fk')) { + $this->addSql('ALTER TABLE storelocations DROP FOREIGN KEY storelocations_parent_id_fk'); + } $this->addSql('ALTER TABLE storelocations ADD storage_type_id INT DEFAULT NULL, ADD only_single_part TINYINT(1) NOT NULL, ADD limit_to_existing_parts TINYINT(1) NOT NULL, ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE is_full is_full TINYINT(1) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); $this->addSql('ALTER TABLE storelocations ADD CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES `measurement_units` (id)'); $this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON storelocations (storage_type_id)'); $this->addSql('DROP INDEX storelocations_parent_id_k ON storelocations'); $this->addSql('CREATE INDEX IDX_7517020727ACA70 ON storelocations (parent_id)'); $this->addSql('ALTER TABLE storelocations ADD CONSTRAINT storelocations_parent_id_fk FOREIGN KEY (parent_id) REFERENCES storelocations (id)'); - $this->addSql('ALTER TABLE suppliers DROP FOREIGN KEY suppliers_parent_id_fk'); + if ($this->doesFKExists('suppliers', 'suppliers_parent_id_fk')) { + $this->addSql('ALTER TABLE suppliers DROP FOREIGN KEY suppliers_parent_id_fk'); + } $this->addSql('ALTER TABLE suppliers ADD default_currency_id INT DEFAULT NULL, ADD shipping_costs NUMERIC(11, 5) DEFAULT NULL, ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE address address VARCHAR(255) NOT NULL, CHANGE phone_number phone_number VARCHAR(255) NOT NULL, CHANGE fax_number fax_number VARCHAR(255) NOT NULL, CHANGE email_address email_address VARCHAR(255) NOT NULL, CHANGE website website VARCHAR(255) NOT NULL, CHANGE auto_product_url auto_product_url VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); $this->addSql('ALTER TABLE suppliers ADD CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id)'); $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)'); @@ -159,7 +186,10 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)'); $this->addSql('ALTER TABLE suppliers ADD CONSTRAINT suppliers_parent_id_fk FOREIGN KEY (parent_id) REFERENCES suppliers (id)'); $this->addSql('DROP INDEX attachements_class_name_k ON attachments'); - $this->addSql('ALTER TABLE attachments DROP FOREIGN KEY attachements_type_id_fk'); + //We have to use the old table name here, because the if is executed before any sql command added by addSQL is executed (and thus the table name is not changed yet) + if ($this->doesFKExists('attachements', 'attachements_type_id_fk')) { + $this->addSql('ALTER TABLE attachments DROP FOREIGN KEY attachements_type_id_fk'); + } $this->addSql('ALTER TABLE attachments ADD datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CHANGE type_id type_id INT DEFAULT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE filename filename VARCHAR(255) NOT NULL, CHANGE show_in_table show_in_table TINYINT(1) NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); $this->addSql('ALTER TABLE attachments ADD CONSTRAINT FK_47C4FAD61F1F2A24 FOREIGN KEY (element_id) REFERENCES `parts` (id) ON DELETE CASCADE'); $this->addSql('DROP INDEX attachements_type_id_fk ON attachments'); @@ -194,22 +224,26 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)'); $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)'); $this->addSql('DROP INDEX pricedetails_orderdetails_id_k ON pricedetails'); - $this->addSql('DROP INDEX name ON groups'); - $this->addSql('ALTER TABLE groups ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE perms_labels perms_labels INT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); - $this->addSql('ALTER TABLE groups ADD CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES `groups` (id)'); - $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)'); + $this->addSql('DROP INDEX name ON `groups`'); + $this->addSql('ALTER TABLE `groups` ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE perms_labels perms_labels INT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL'); + $this->addSql('ALTER TABLE `groups` ADD CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES `groups` (id)'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON `groups` (parent_id)'); //Fill empty timestamps with current date $tables = ['attachments', 'attachment_types', 'categories', 'devices', 'footprints', 'manufacturers', 'orderdetails', 'pricedetails', 'storelocations', 'suppliers', ]; foreach ($tables as $table) { - $this->addSql("UPDATE ${table} SET datetime_added = NOW() WHERE datetime_added = '0000-00-00 00:00:00'"); - $this->addSql("UPDATE ${table} SET last_modified = datetime_added WHERE last_modified = '0000-00-00 00:00:00'"); + $this->addSql("UPDATE $table SET datetime_added = NOW() WHERE datetime_added = '0000-00-00 00:00:00'"); + $this->addSql("UPDATE $table SET last_modified = datetime_added WHERE last_modified = '0000-00-00 00:00:00'"); } //Set the dbVersion to a high value, to prevent the old Part-DB versions to upgrade DB! $this->addSql("UPDATE `internal` SET `keyValue` = '99' WHERE `internal`.`keyName` = 'dbVersion'"); + + //Migrate theme config to new format + $this->addSql('UPDATE users SET users.config_theme = REPLACE(users.config_theme ,".min.css", "") WHERE users.config_theme LIKE "%.min.css"'); + $this->addSql('UPDATE users SET users.config_theme = REPLACE(users.config_theme ,".css", "") WHERE users.config_theme LIKE "%.css"'); } public function mySQLDown(Schema $schema): void @@ -335,10 +369,6 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration $this->addSql('ALTER TABLE `users` CHANGE name name VARCHAR(32) NOT NULL COLLATE utf8_general_ci, CHANGE need_pw_change need_pw_change TINYINT(1) DEFAULT \'0\' NOT NULL, CHANGE first_name first_name TINYTEXT DEFAULT NULL COLLATE utf8_general_ci, CHANGE last_name last_name TINYTEXT DEFAULT NULL COLLATE utf8_general_ci, CHANGE department department TINYTEXT DEFAULT NULL COLLATE utf8_general_ci, CHANGE email email TINYTEXT DEFAULT NULL COLLATE utf8_general_ci, CHANGE config_language config_language TINYTEXT DEFAULT NULL COLLATE utf8_general_ci, CHANGE config_timezone config_timezone TINYTEXT DEFAULT NULL COLLATE utf8_general_ci, CHANGE config_theme config_theme TINYTEXT DEFAULT NULL COLLATE utf8_general_ci, CHANGE config_currency config_currency TINYTEXT DEFAULT NULL COLLATE utf8_general_ci, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CHANGE perms_labels perms_labels SMALLINT NOT NULL'); $this->addSql('DROP INDEX uniq_1483a5e95e237e06 ON `users`'); $this->addSql('CREATE UNIQUE INDEX name ON `users` (name)'); - - //Migrate theme config to new format - $this->addSql('UPDATE users SET users.config_theme = REPLACE(users.config_theme ,".min.css", "") WHERE users.config_theme LIKE "%.min.css"'); - $this->addSql('UPDATE users SET users.config_theme = REPLACE(users.config_theme ,".css", "") WHERE users.config_theme LIKE "%.css"'); } public function sqLiteUp(Schema $schema): void @@ -350,4 +380,14 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration { $this->warnIf(true, "Migration not needed for SQLite. Skipping..."); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20190913141126.php b/migrations/Version20190913141126.php index 28b8a0cc..31eed64a 100644 --- a/migrations/Version20190913141126.php +++ b/migrations/Version20190913141126.php @@ -66,8 +66,6 @@ final class Version20190913141126 extends AbstractMultiPlatformMigration '; $this->addSql($sql); - - $this->printPermissionUpdateMessage(); } public function mySQLDown(Schema $schema): void @@ -90,4 +88,14 @@ final class Version20190913141126 extends AbstractMultiPlatformMigration { $this->warnIf(true, "Migration not needed for SQLite. Skipping..."); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20190924113252.php b/migrations/Version20190924113252.php index fe7c9c8b..8221c686 100644 --- a/migrations/Version20190924113252.php +++ b/migrations/Version20190924113252.php @@ -179,4 +179,14 @@ final class Version20190924113252 extends AbstractMultiPlatformMigration { $this->warnIf(true, "Migration not needed for SQLite. Skipping..."); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20191214153125.php b/migrations/Version20191214153125.php index 9a61c10d..83803d13 100644 --- a/migrations/Version20191214153125.php +++ b/migrations/Version20191214153125.php @@ -65,4 +65,14 @@ final class Version20191214153125 extends AbstractMultiPlatformMigration { $this->warnIf(true, "Migration not needed for SQLite. Skipping..."); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20200126191823.php b/migrations/Version20200126191823.php index c093a4d7..cf362ea5 100644 --- a/migrations/Version20200126191823.php +++ b/migrations/Version20200126191823.php @@ -68,4 +68,14 @@ final class Version20200126191823 extends AbstractMultiPlatformMigration { $this->warnIf(true, "Migration not needed for SQLite. Skipping..."); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20200311204104.php b/migrations/Version20200311204104.php index a1b13baf..daa6fd28 100644 --- a/migrations/Version20200311204104.php +++ b/migrations/Version20200311204104.php @@ -34,8 +34,6 @@ final class Version20200311204104 extends AbstractMultiPlatformMigration $sql = 'UPDATE `groups`'. 'SET perms_parts_parameters = 681 WHERE (id = 2 AND name = "readonly");'; $this->addSql($sql); - - $this->printPermissionUpdateMessage(); } public function mySQLDown(Schema $schema): void @@ -58,4 +56,14 @@ final class Version20200311204104 extends AbstractMultiPlatformMigration { $this->warnIf(true, "Migration not needed for SQLite. Skipping..."); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20200409130946.php b/migrations/Version20200409130946.php index 72bdda9c..ef2dc7ab 100644 --- a/migrations/Version20200409130946.php +++ b/migrations/Version20200409130946.php @@ -42,4 +42,14 @@ final class Version20200409130946 extends AbstractMultiPlatformMigration { $this->warnIf(true, "Migration not needed for SQLite. Skipping..."); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20200502161750.php b/migrations/Version20200502161750.php index 1b5b4f52..55117541 100644 --- a/migrations/Version20200502161750.php +++ b/migrations/Version20200502161750.php @@ -134,8 +134,6 @@ EOD; (2, 1, 'admin', '{$admin_pw}', 1, '', '', '', '', NULL, NULL, NULL, '', '', '2020-06-13 23:39:26', '2020-06-13 23:39:26', 21845, 21845, 21845, 21, 85, 21, 349525, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 0, NULL, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, 0, NULL, '', NULL, 0, 0); EOD; $this->addSql($sql); - - $this->printPermissionUpdateMessage(); } public function sqLiteDown(Schema $schema): void @@ -165,4 +163,14 @@ EOD; $this->addSql('DROP TABLE u2f_keys'); $this->addSql('DROP TABLE "users"'); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20220925162725.php b/migrations/Version20220925162725.php index 52caa2a9..21ac8946 100644 --- a/migrations/Version20220925162725.php +++ b/migrations/Version20220925162725.php @@ -71,7 +71,10 @@ final class Version20220925162725 extends AbstractMultiPlatformMigration // this up() migration is auto-generated, please modify it to your needs $this->addSql('CREATE INDEX attachment_types_idx_name ON attachment_types (name)'); $this->addSql('CREATE INDEX attachment_types_idx_parent_name ON attachment_types (parent_id, name)'); + //We have to disable foreign key checks here, or we get a error on MySQL for legacy database migration. On MariaDB this works fine (with enabled foreign key checks), so I guess this is not so bad... + $this->addSql('SET foreign_key_checks = 0'); $this->addSql('ALTER TABLE attachments CHANGE type_id type_id INT NOT NULL'); + $this->addSql('SET foreign_key_checks = 1'); $this->addSql('CREATE INDEX attachments_idx_id_element_id_class_name ON attachments (id, element_id, class_name)'); $this->addSql('CREATE INDEX attachments_idx_class_name_id ON attachments (class_name, id)'); $this->addSql('CREATE INDEX attachment_name_idx ON attachments (name)'); @@ -532,4 +535,14 @@ final class Version20220925162725 extends AbstractMultiPlatformMigration $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); $this->addSql('CREATE INDEX IDX_1483A5E96DEDCEC2 ON "users" (id_preview_attachement)'); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20221003212851.php b/migrations/Version20221003212851.php index 3a7379ca..81e33c0e 100644 --- a/migrations/Version20221003212851.php +++ b/migrations/Version20221003212851.php @@ -47,4 +47,14 @@ final class Version20221003212851 extends AbstractMultiPlatformMigration { $this->addSql('DROP TABLE webauthn_keys'); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20221114193325.php b/migrations/Version20221114193325.php index 2f6aab6c..9766ccf3 100644 --- a/migrations/Version20221114193325.php +++ b/migrations/Version20221114193325.php @@ -4,24 +4,20 @@ declare(strict_types=1); namespace DoctrineMigrations; -use App\Entity\UserSystem\PermissionData; use App\Migration\AbstractMultiPlatformMigration; -use App\Security\Interfaces\HasPermissionsInterface; +use App\Migration\WithPermPresetsTrait; use App\Services\UserSystem\PermissionPresetsHelper; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Schema\Schema; -use Doctrine\Migrations\AbstractMigration; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20221114193325 extends AbstractMultiPlatformMigration implements ContainerAwareInterface { - private ?ContainerInterface $container = null; - private ?PermissionPresetsHelper $permission_presets_helper = null; + use WithPermPresetsTrait; public function __construct(Connection $connection, LoggerInterface $logger) { @@ -33,34 +29,6 @@ final class Version20221114193325 extends AbstractMultiPlatformMigration impleme return 'Update the permission system to the new system. Please note that all permissions will be reset!'; } - private function getJSONPermDataFromPreset(string $preset): string - { - if ($this->permission_presets_helper === null) { - throw new \RuntimeException('PermissionPresetsHelper not set! There seems to be some issue with the dependency injection!'); - } - - //Create a virtual user on which we can apply the preset - $user = new class implements HasPermissionsInterface { - - public PermissionData $perm_data; - - public function __construct() - { - $this->perm_data = new PermissionData(); - } - - public function getPermissions(): PermissionData - { - return $this->perm_data; - } - }; - - //Apply the preset to the virtual user - $this->permission_presets_helper->applyPreset($user, $preset); - - //And return the json data - return json_encode($user->getPermissions()); - } private function addDataMigrationAndWarning(): void { @@ -83,9 +51,12 @@ final class Version20221114193325 extends AbstractMultiPlatformMigration impleme //Reset the permissions of the admin user, to allow admin permissions (like the admins group) $this->addSql("UPDATE `users` SET permissions_data = '$admin' WHERE id = 2;"); - $this->warnIf(true, "\x1b[1;37;43m\n!!! All permissions were reset! Please change them to the desired state, immediately !!!\x1b[0;39;49m"); - $this->warnIf(true, "\x1b[1;37;43m\n!!! For security reasons all users (except the admin user) were disabled. Login with admin user and reenable other users after checking their permissions !!!\x1b[0;39;49m"); - $this->warnIf(true, "\x1b[1;37;43m\n!!! For more infos see: https://github.com/Part-DB/Part-DB-symfony/discussions/193 !!!\x1b[0;39;49m"); + //This warning should not be needed, anymore, as almost everybody should have updated to the new version by now, and this warning would just irritate new users of the software + /* + $this->logger->warning('!!! All permissions were reset! Please change them to the desired state, immediately !!!'); + $this->logger->warning('!!! For security reasons all users (except the admin user) were disabled. Login with admin user and reenable other users after checking their permissions !!!'); + $this->logger->warning('!!! For more infos see: https://github.com/Part-DB/Part-DB-symfony/discussions/193 !!!'); + */ } public function mySQLUp(Schema $schema): void @@ -161,11 +132,15 @@ final class Version20221114193325 extends AbstractMultiPlatformMigration impleme $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); } - public function setContainer(ContainerInterface $container = null) + + + public function postgreSQLUp(Schema $schema): void { - if ($container) { - $this->container = $container; - $this->permission_presets_helper = $container->get(PermissionPresetsHelper::class); - } + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); } } diff --git a/migrations/Version20221204004815.php b/migrations/Version20221204004815.php index 20e151db..c6c7b6ab 100644 --- a/migrations/Version20221204004815.php +++ b/migrations/Version20221204004815.php @@ -55,4 +55,14 @@ final class Version20221204004815 extends AbstractMultiPlatformMigration $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON "parts" (datetime_added, name, last_modified, id, needs_review)'); $this->addSql('CREATE INDEX parts_idx_name ON "parts" (name)'); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20221216224745.php b/migrations/Version20221216224745.php index 63bb535e..9ac3b8ba 100644 --- a/migrations/Version20221216224745.php +++ b/migrations/Version20221216224745.php @@ -67,6 +67,15 @@ final class Version20221216224745 extends AbstractMultiPlatformMigration $this->addSql('CREATE INDEX log_idx_type ON log (type)'); $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + } + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); } } diff --git a/migrations/Version20230108165410.php b/migrations/Version20230108165410.php index 4124a95a..90f6314e 100644 --- a/migrations/Version20230108165410.php +++ b/migrations/Version20230108165410.php @@ -320,4 +320,14 @@ final class Version20230108165410 extends AbstractMultiPlatformMigration $this->addSql('ALTER TABLE projects RENAME TO devices'); $this->addSql('ALTER TABLE project_bom_entries RENAME TO device_parts'); } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } } diff --git a/migrations/Version20230219225340.php b/migrations/Version20230219225340.php new file mode 100644 index 00000000..2740c85e --- /dev/null +++ b/migrations/Version20230219225340.php @@ -0,0 +1,534 @@ +addSql('ALTER TABLE attachment_types DROP FOREIGN KEY FK_EFAED7196DEDCEC2'); + $this->addSql('DROP INDEX IDX_EFAED7196DEDCEC2 ON attachment_types'); + $this->addSql('ALTER TABLE attachment_types CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE attachment_types ADD CONSTRAINT FK_EFAED719EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_EFAED719EA7100A1 ON attachment_types (id_preview_attachment)'); + $this->addSql('ALTER TABLE categories DROP FOREIGN KEY FK_3AF346686DEDCEC2'); + $this->addSql('DROP INDEX IDX_3AF346686DEDCEC2 ON categories'); + $this->addSql('ALTER TABLE categories CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE categories ADD CONSTRAINT FK_3AF34668EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_3AF34668EA7100A1 ON categories (id_preview_attachment)'); + $this->addSql('ALTER TABLE currencies DROP FOREIGN KEY FK_37C446936DEDCEC2'); + $this->addSql('DROP INDEX IDX_37C446936DEDCEC2 ON currencies'); + $this->addSql('ALTER TABLE currencies CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE currencies ADD CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('ALTER TABLE footprints DROP FOREIGN KEY FK_A34D68A26DEDCEC2'); + $this->addSql('DROP INDEX IDX_A34D68A26DEDCEC2 ON footprints'); + $this->addSql('ALTER TABLE footprints CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE footprints ADD CONSTRAINT FK_A34D68A2EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_A34D68A2EA7100A1 ON footprints (id_preview_attachment)'); + $this->addSql('ALTER TABLE `groups` DROP FOREIGN KEY FK_F06D39706DEDCEC2'); + $this->addSql('DROP INDEX IDX_F06D39706DEDCEC2 ON `groups`'); + $this->addSql('ALTER TABLE `groups` CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `groups` ADD CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON `groups` (id_preview_attachment)'); + $this->addSql('ALTER TABLE label_profiles DROP FOREIGN KEY FK_C93E9CF56DEDCEC2'); + $this->addSql('DROP INDEX IDX_C93E9CF56DEDCEC2 ON label_profiles'); + $this->addSql('ALTER TABLE label_profiles CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE label_profiles ADD CONSTRAINT FK_C93E9CF5EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_C93E9CF5EA7100A1 ON label_profiles (id_preview_attachment)'); + $this->addSql('ALTER TABLE manufacturers DROP FOREIGN KEY FK_94565B126DEDCEC2'); + $this->addSql('DROP INDEX IDX_94565B126DEDCEC2 ON manufacturers'); + $this->addSql('ALTER TABLE manufacturers CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE manufacturers ADD CONSTRAINT FK_94565B12EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_94565B12EA7100A1 ON manufacturers (id_preview_attachment)'); + $this->addSql('ALTER TABLE measurement_units DROP FOREIGN KEY FK_F5AF83CF6DEDCEC2'); + $this->addSql('DROP INDEX IDX_F5AF83CF6DEDCEC2 ON measurement_units'); + $this->addSql('ALTER TABLE measurement_units CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE measurement_units ADD CONSTRAINT FK_F5AF83CFEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_F5AF83CFEA7100A1 ON measurement_units (id_preview_attachment)'); + $this->addSql('ALTER TABLE parts DROP FOREIGN KEY FK_6940A7FE6DEDCEC2'); + $this->addSql('DROP INDEX IDX_6940A7FE6DEDCEC2 ON parts'); + $this->addSql('ALTER TABLE parts CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_6940A7FEEA7100A1 ON parts (id_preview_attachment)'); + $this->addSql('ALTER TABLE projects DROP FOREIGN KEY FK_11074E9A6DEDCEC2'); + $this->addSql('ALTER TABLE projects DROP FOREIGN KEY FK_5C93B3A4727ACA70'); + $this->addSql('DROP INDEX IDX_5C93B3A46DEDCEC2 ON projects'); + $this->addSql('ALTER TABLE projects CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE projects ADD CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + $this->addSql('ALTER TABLE storelocations DROP FOREIGN KEY FK_75170206DEDCEC2'); + $this->addSql('DROP INDEX IDX_75170206DEDCEC2 ON storelocations'); + $this->addSql('ALTER TABLE storelocations CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE storelocations ADD CONSTRAINT FK_7517020EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_7517020EA7100A1 ON storelocations (id_preview_attachment)'); + $this->addSql('ALTER TABLE suppliers DROP FOREIGN KEY FK_AC28B95C6DEDCEC2'); + $this->addSql('DROP INDEX IDX_AC28B95C6DEDCEC2 ON suppliers'); + $this->addSql('ALTER TABLE suppliers CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE suppliers ADD CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)'); + $this->addSql('ALTER TABLE `users` DROP FOREIGN KEY FK_1483A5E96DEDCEC2'); + $this->addSql('DROP INDEX IDX_1483A5E96DEDCEC2 ON `users`'); + $this->addSql('ALTER TABLE `users` CHANGE id_preview_attachement id_preview_attachment INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `users` ADD CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON `users` (id_preview_attachment)'); + } + + public function mySQLDown(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE `attachment_types` DROP FOREIGN KEY FK_EFAED719EA7100A1'); + $this->addSql('DROP INDEX IDX_EFAED719EA7100A1 ON `attachment_types`'); + $this->addSql('ALTER TABLE `attachment_types` CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `attachment_types` ADD CONSTRAINT FK_EFAED7196DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_EFAED7196DEDCEC2 ON `attachment_types` (id_preview_attachement)'); + $this->addSql('ALTER TABLE `categories` DROP FOREIGN KEY FK_3AF34668EA7100A1'); + $this->addSql('DROP INDEX IDX_3AF34668EA7100A1 ON `categories`'); + $this->addSql('ALTER TABLE `categories` CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `categories` ADD CONSTRAINT FK_3AF346686DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_3AF346686DEDCEC2 ON `categories` (id_preview_attachement)'); + $this->addSql('ALTER TABLE currencies DROP FOREIGN KEY FK_37C44693EA7100A1'); + $this->addSql('DROP INDEX IDX_37C44693EA7100A1 ON currencies'); + $this->addSql('ALTER TABLE currencies CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE currencies ADD CONSTRAINT FK_37C446936DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_37C446936DEDCEC2 ON currencies (id_preview_attachement)'); + $this->addSql('ALTER TABLE `footprints` DROP FOREIGN KEY FK_A34D68A2EA7100A1'); + $this->addSql('DROP INDEX IDX_A34D68A2EA7100A1 ON `footprints`'); + $this->addSql('ALTER TABLE `footprints` CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `footprints` ADD CONSTRAINT FK_A34D68A26DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_A34D68A26DEDCEC2 ON `footprints` (id_preview_attachement)'); + $this->addSql('ALTER TABLE `groups` DROP FOREIGN KEY FK_F06D3970EA7100A1'); + $this->addSql('DROP INDEX IDX_F06D3970EA7100A1 ON `groups`'); + $this->addSql('ALTER TABLE `groups` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `groups` ADD CONSTRAINT FK_F06D39706DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_F06D39706DEDCEC2 ON `groups` (id_preview_attachement)'); + $this->addSql('ALTER TABLE label_profiles DROP FOREIGN KEY FK_C93E9CF5EA7100A1'); + $this->addSql('DROP INDEX IDX_C93E9CF5EA7100A1 ON label_profiles'); + $this->addSql('ALTER TABLE label_profiles CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE label_profiles ADD CONSTRAINT FK_C93E9CF56DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_C93E9CF56DEDCEC2 ON label_profiles (id_preview_attachement)'); + $this->addSql('ALTER TABLE log CHANGE level level TINYINT(1) NOT NULL'); + $this->addSql('ALTER TABLE `manufacturers` DROP FOREIGN KEY FK_94565B12EA7100A1'); + $this->addSql('DROP INDEX IDX_94565B12EA7100A1 ON `manufacturers`'); + $this->addSql('ALTER TABLE `manufacturers` CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `manufacturers` ADD CONSTRAINT FK_94565B126DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_94565B126DEDCEC2 ON `manufacturers` (id_preview_attachement)'); + $this->addSql('ALTER TABLE `measurement_units` DROP FOREIGN KEY FK_F5AF83CFEA7100A1'); + $this->addSql('DROP INDEX IDX_F5AF83CFEA7100A1 ON `measurement_units`'); + $this->addSql('ALTER TABLE `measurement_units` CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `measurement_units` ADD CONSTRAINT FK_F5AF83CF6DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_F5AF83CF6DEDCEC2 ON `measurement_units` (id_preview_attachement)'); + $this->addSql('ALTER TABLE `parts` DROP FOREIGN KEY FK_6940A7FEEA7100A1'); + $this->addSql('DROP INDEX IDX_6940A7FEEA7100A1 ON `parts`'); + $this->addSql('ALTER TABLE `parts` CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `parts` ADD CONSTRAINT FK_6940A7FE6DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_6940A7FE6DEDCEC2 ON `parts` (id_preview_attachement)'); + $this->addSql('ALTER TABLE projects DROP FOREIGN KEY FK_5C93B3A4EA7100A1'); + $this->addSql('DROP INDEX IDX_5C93B3A4EA7100A1 ON projects'); + $this->addSql('ALTER TABLE projects CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE projects ADD CONSTRAINT FK_11074E9A6DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A46DEDCEC2 ON projects (id_preview_attachement)'); + $this->addSql('ALTER TABLE `storelocations` DROP FOREIGN KEY FK_7517020EA7100A1'); + $this->addSql('DROP INDEX IDX_7517020EA7100A1 ON `storelocations`'); + $this->addSql('ALTER TABLE `storelocations` CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `storelocations` ADD CONSTRAINT FK_75170206DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_75170206DEDCEC2 ON `storelocations` (id_preview_attachement)'); + $this->addSql('ALTER TABLE `suppliers` DROP FOREIGN KEY FK_AC28B95CEA7100A1'); + $this->addSql('DROP INDEX IDX_AC28B95CEA7100A1 ON `suppliers`'); + $this->addSql('ALTER TABLE `suppliers` CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `suppliers` ADD CONSTRAINT FK_AC28B95C6DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_AC28B95C6DEDCEC2 ON `suppliers` (id_preview_attachement)'); + $this->addSql('ALTER TABLE `users` DROP FOREIGN KEY FK_1483A5E9EA7100A1'); + $this->addSql('DROP INDEX IDX_1483A5E9EA7100A1 ON `users`'); + $this->addSql('ALTER TABLE `users` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', CHANGE id_preview_attachment id_preview_attachement INT DEFAULT NULL'); + $this->addSql('ALTER TABLE `users` ADD CONSTRAINT FK_1483A5E96DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id)'); + $this->addSql('CREATE INDEX IDX_1483A5E96DEDCEC2 ON `users` (id_preview_attachement)'); + } + + public function sqLiteUp(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__attachment_types AS SELECT id, parent_id, id_preview_attachement, filetype_filter, comment, not_selectable, name, last_modified, datetime_added FROM attachment_types'); + $this->addSql('DROP TABLE attachment_types'); + $this->addSql('CREATE TABLE attachment_types (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, filetype_filter CLOB NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_EFAED719727ACA70 FOREIGN KEY (parent_id) REFERENCES attachment_types (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EFAED719EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO attachment_types (id, parent_id, id_preview_attachment, filetype_filter, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachement, filetype_filter, comment, not_selectable, name, last_modified, datetime_added FROM __temp__attachment_types'); + $this->addSql('DROP TABLE __temp__attachment_types'); + $this->addSql('CREATE INDEX attachment_types_idx_parent_name ON attachment_types (parent_id, name)'); + $this->addSql('CREATE INDEX attachment_types_idx_name ON attachment_types (name)'); + $this->addSql('CREATE INDEX IDX_EFAED719727ACA70 ON attachment_types (parent_id)'); + $this->addSql('CREATE INDEX IDX_EFAED719EA7100A1 ON attachment_types (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__categories AS SELECT id, parent_id, id_preview_attachement, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added FROM categories'); + $this->addSql('DROP TABLE categories'); + $this->addSql('CREATE TABLE categories (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, partname_hint CLOB NOT NULL, partname_regex CLOB NOT NULL, disable_footprints BOOLEAN NOT NULL, disable_manufacturers BOOLEAN NOT NULL, disable_autodatasheets BOOLEAN NOT NULL, disable_properties BOOLEAN NOT NULL, default_description CLOB NOT NULL, default_comment CLOB NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_3AF34668727ACA70 FOREIGN KEY (parent_id) REFERENCES categories (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_3AF34668EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO categories (id, parent_id, id_preview_attachment, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachement, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added FROM __temp__categories'); + $this->addSql('DROP TABLE __temp__categories'); + $this->addSql('CREATE INDEX category_idx_parent_name ON categories (parent_id, name)'); + $this->addSql('CREATE INDEX category_idx_name ON categories (name)'); + $this->addSql('CREATE INDEX IDX_3AF34668727ACA70 ON categories (parent_id)'); + $this->addSql('CREATE INDEX IDX_3AF34668EA7100A1 ON categories (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachement, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachement, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__footprints AS SELECT id, parent_id, id_footprint_3d, id_preview_attachement, comment, not_selectable, name, last_modified, datetime_added FROM footprints'); + $this->addSql('DROP TABLE footprints'); + $this->addSql('CREATE TABLE footprints (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_footprint_3d INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_A34D68A2727ACA70 FOREIGN KEY (parent_id) REFERENCES footprints (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A34D68A232A38C34 FOREIGN KEY (id_footprint_3d) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A34D68A2EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO footprints (id, parent_id, id_footprint_3d, id_preview_attachment, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_footprint_3d, id_preview_attachement, comment, not_selectable, name, last_modified, datetime_added FROM __temp__footprints'); + $this->addSql('DROP TABLE __temp__footprints'); + $this->addSql('CREATE INDEX footprint_idx_parent_name ON footprints (parent_id, name)'); + $this->addSql('CREATE INDEX footprint_idx_name ON footprints (name)'); + $this->addSql('CREATE INDEX IDX_A34D68A2727ACA70 ON footprints (parent_id)'); + $this->addSql('CREATE INDEX IDX_A34D68A232A38C34 ON footprints (id_footprint_3d)'); + $this->addSql('CREATE INDEX IDX_A34D68A2EA7100A1 ON footprints (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM groups'); + $this->addSql('DROP TABLE groups'); + $this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)'); + $this->addSql('CREATE INDEX group_idx_name ON groups (name)'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__label_profiles AS SELECT id, id_preview_attachement, comment, show_in_dropdown, name, last_modified, datetime_added, options_width, options_height, options_barcode_type, options_picture_type, options_supported_element, options_additional_css, options_lines_mode, options_lines FROM label_profiles'); + $this->addSql('DROP TABLE label_profiles'); + $this->addSql('CREATE TABLE label_profiles (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachment INTEGER DEFAULT NULL, comment CLOB NOT NULL, show_in_dropdown BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, options_width DOUBLE PRECISION NOT NULL, options_height DOUBLE PRECISION NOT NULL, options_barcode_type VARCHAR(255) NOT NULL, options_picture_type VARCHAR(255) NOT NULL, options_supported_element VARCHAR(255) NOT NULL, options_additional_css CLOB NOT NULL, options_lines_mode VARCHAR(255) NOT NULL, options_lines CLOB NOT NULL, CONSTRAINT FK_C93E9CF5EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO label_profiles (id, id_preview_attachment, comment, show_in_dropdown, name, last_modified, datetime_added, options_width, options_height, options_barcode_type, options_picture_type, options_supported_element, options_additional_css, options_lines_mode, options_lines) SELECT id, id_preview_attachement, comment, show_in_dropdown, name, last_modified, datetime_added, options_width, options_height, options_barcode_type, options_picture_type, options_supported_element, options_additional_css, options_lines_mode, options_lines FROM __temp__label_profiles'); + $this->addSql('DROP TABLE __temp__label_profiles'); + $this->addSql('CREATE INDEX IDX_C93E9CF5EA7100A1 ON label_profiles (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level TINYINT(4) NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json) + , type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__manufacturers AS SELECT id, parent_id, id_preview_attachement, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM manufacturers'); + $this->addSql('DROP TABLE manufacturers'); + $this->addSql('CREATE TABLE manufacturers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_94565B12727ACA70 FOREIGN KEY (parent_id) REFERENCES manufacturers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_94565B12EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO manufacturers (id, parent_id, id_preview_attachment, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachement, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__manufacturers'); + $this->addSql('DROP TABLE __temp__manufacturers'); + $this->addSql('CREATE INDEX manufacturer_idx_parent_name ON manufacturers (parent_id, name)'); + $this->addSql('CREATE INDEX manufacturer_name ON manufacturers (name)'); + $this->addSql('CREATE INDEX IDX_94565B12727ACA70 ON manufacturers (parent_id)'); + $this->addSql('CREATE INDEX IDX_94565B12EA7100A1 ON manufacturers (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__measurement_units AS SELECT id, parent_id, id_preview_attachement, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added FROM measurement_units'); + $this->addSql('DROP TABLE measurement_units'); + $this->addSql('CREATE TABLE measurement_units (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, unit VARCHAR(255) DEFAULT NULL, is_integer BOOLEAN NOT NULL, use_si_prefix BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_F5AF83CF727ACA70 FOREIGN KEY (parent_id) REFERENCES measurement_units (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F5AF83CFEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO measurement_units (id, parent_id, id_preview_attachment, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachement, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added FROM __temp__measurement_units'); + $this->addSql('DROP TABLE __temp__measurement_units'); + $this->addSql('CREATE INDEX unit_idx_parent_name ON measurement_units (parent_id, name)'); + $this->addSql('CREATE INDEX unit_idx_name ON measurement_units (name)'); + $this->addSql('CREATE INDEX IDX_F5AF83CF727ACA70 ON measurement_units (parent_id)'); + $this->addSql('CREATE INDEX IDX_F5AF83CFEA7100A1 ON measurement_units (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__parts AS SELECT id, id_preview_attachement, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn FROM parts'); + $this->addSql('DROP TABLE parts'); + $this->addSql('CREATE TABLE parts (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_category INTEGER NOT NULL, id_footprint INTEGER DEFAULT NULL, id_part_unit INTEGER DEFAULT NULL, id_manufacturer INTEGER DEFAULT NULL, order_orderdetails_id INTEGER DEFAULT NULL, built_project_id INTEGER DEFAULT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, needs_review BOOLEAN NOT NULL, tags CLOB NOT NULL, mass DOUBLE PRECISION DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, visible BOOLEAN NOT NULL, favorite BOOLEAN NOT NULL, minamount DOUBLE PRECISION NOT NULL, manufacturer_product_url VARCHAR(255) NOT NULL, manufacturer_product_number VARCHAR(255) NOT NULL, manufacturing_status VARCHAR(255) DEFAULT NULL, order_quantity INTEGER NOT NULL, manual_order BOOLEAN NOT NULL, ipn VARCHAR(100) DEFAULT NULL, CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES categories (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES footprints (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES measurement_units (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES manufacturers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO parts (id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn) SELECT id, id_preview_attachement, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn FROM __temp__parts'); + $this->addSql('DROP TABLE __temp__parts'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON parts (built_project_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON parts (order_orderdetails_id)'); + $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON parts (id_manufacturer)'); + $this->addSql('CREATE INDEX IDX_6940A7FE2626CEF9 ON parts (id_part_unit)'); + $this->addSql('CREATE INDEX IDX_6940A7FE7E371A10 ON parts (id_footprint)'); + $this->addSql('CREATE INDEX IDX_6940A7FE5697F554 ON parts (id_category)'); + $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON parts (datetime_added, name, last_modified, id, needs_review)'); + $this->addSql('CREATE INDEX parts_idx_name ON parts (name)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON parts (ipn)'); + $this->addSql('CREATE INDEX parts_idx_ipn ON parts (ipn)'); + $this->addSql('CREATE INDEX IDX_6940A7FEEA7100A1 ON parts (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails'); + $this->addSql('DROP TABLE pricedetails'); + $this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachement, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM projects'); + $this->addSql('DROP TABLE projects'); + $this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description CLOB DEFAULT \'\', CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description) SELECT id, parent_id, id_preview_attachement, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM __temp__projects'); + $this->addSql('DROP TABLE __temp__projects'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__storelocations AS SELECT id, parent_id, storage_type_id, id_preview_attachement, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM storelocations'); + $this->addSql('DROP TABLE storelocations'); + $this->addSql('CREATE TABLE storelocations (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, storage_type_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, is_full BOOLEAN NOT NULL, only_single_part BOOLEAN NOT NULL, limit_to_existing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_7517020727ACA70 FOREIGN KEY (parent_id) REFERENCES storelocations (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES measurement_units (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO storelocations (id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, storage_type_id, id_preview_attachement, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM __temp__storelocations'); + $this->addSql('DROP TABLE __temp__storelocations'); + $this->addSql('CREATE INDEX location_idx_parent_name ON storelocations (parent_id, name)'); + $this->addSql('CREATE INDEX location_idx_name ON storelocations (name)'); + $this->addSql('CREATE INDEX IDX_7517020727ACA70 ON storelocations (parent_id)'); + $this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON storelocations (storage_type_id)'); + $this->addSql('CREATE INDEX IDX_7517020EA7100A1 ON storelocations (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachement, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM suppliers'); + $this->addSql('DROP TABLE suppliers'); + $this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachement, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)'); + $this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data FROM users'); + $this->addSql('DROP TABLE users'); + $this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json) + , CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE INDEX user_idx_username ON users (name)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path) + , aaguid CLOB NOT NULL --(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL --(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function sqLiteDown(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__attachment_types AS SELECT id, parent_id, id_preview_attachment, filetype_filter, comment, not_selectable, name, last_modified, datetime_added FROM "attachment_types"'); + $this->addSql('DROP TABLE "attachment_types"'); + $this->addSql('CREATE TABLE "attachment_types" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, filetype_filter CLOB NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_EFAED719727ACA70 FOREIGN KEY (parent_id) REFERENCES "attachment_types" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EFAED7196DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "attachment_types" (id, parent_id, id_preview_attachement, filetype_filter, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, filetype_filter, comment, not_selectable, name, last_modified, datetime_added FROM __temp__attachment_types'); + $this->addSql('DROP TABLE __temp__attachment_types'); + $this->addSql('CREATE INDEX IDX_EFAED719727ACA70 ON "attachment_types" (parent_id)'); + $this->addSql('CREATE INDEX attachment_types_idx_name ON "attachment_types" (name)'); + $this->addSql('CREATE INDEX attachment_types_idx_parent_name ON "attachment_types" (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_EFAED7196DEDCEC2 ON "attachment_types" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__categories AS SELECT id, parent_id, id_preview_attachment, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added FROM "categories"'); + $this->addSql('DROP TABLE "categories"'); + $this->addSql('CREATE TABLE "categories" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, partname_hint CLOB NOT NULL, partname_regex CLOB NOT NULL, disable_footprints BOOLEAN NOT NULL, disable_manufacturers BOOLEAN NOT NULL, disable_autodatasheets BOOLEAN NOT NULL, disable_properties BOOLEAN NOT NULL, default_description CLOB NOT NULL, default_comment CLOB NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_3AF34668727ACA70 FOREIGN KEY (parent_id) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_3AF346686DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "categories" (id, parent_id, id_preview_attachement, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added FROM __temp__categories'); + $this->addSql('DROP TABLE __temp__categories'); + $this->addSql('CREATE INDEX IDX_3AF34668727ACA70 ON "categories" (parent_id)'); + $this->addSql('CREATE INDEX category_idx_name ON "categories" (name)'); + $this->addSql('CREATE INDEX category_idx_parent_name ON "categories" (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_3AF346686DEDCEC2 ON "categories" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C446936DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachement, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_37C446936DEDCEC2 ON currencies (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__footprints AS SELECT id, parent_id, id_footprint_3d, id_preview_attachment, comment, not_selectable, name, last_modified, datetime_added FROM "footprints"'); + $this->addSql('DROP TABLE "footprints"'); + $this->addSql('CREATE TABLE "footprints" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_footprint_3d INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_A34D68A2727ACA70 FOREIGN KEY (parent_id) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A34D68A232A38C34 FOREIGN KEY (id_footprint_3d) REFERENCES "attachments" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A34D68A26DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "footprints" (id, parent_id, id_footprint_3d, id_preview_attachement, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_footprint_3d, id_preview_attachment, comment, not_selectable, name, last_modified, datetime_added FROM __temp__footprints'); + $this->addSql('DROP TABLE __temp__footprints'); + $this->addSql('CREATE INDEX IDX_A34D68A2727ACA70 ON "footprints" (parent_id)'); + $this->addSql('CREATE INDEX IDX_A34D68A232A38C34 ON "footprints" (id_footprint_3d)'); + $this->addSql('CREATE INDEX footprint_idx_name ON "footprints" (name)'); + $this->addSql('CREATE INDEX footprint_idx_parent_name ON "footprints" (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_A34D68A26DEDCEC2 ON "footprints" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM "groups"'); + $this->addSql('DROP TABLE "groups"'); + $this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D39706DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)'); + $this->addSql('CREATE INDEX group_idx_name ON "groups" (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_F06D39706DEDCEC2 ON "groups" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__label_profiles AS SELECT id, id_preview_attachment, comment, show_in_dropdown, name, last_modified, datetime_added, options_width, options_height, options_barcode_type, options_picture_type, options_supported_element, options_additional_css, options_lines_mode, options_lines FROM label_profiles'); + $this->addSql('DROP TABLE label_profiles'); + $this->addSql('CREATE TABLE label_profiles (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachement INTEGER DEFAULT NULL, comment CLOB NOT NULL, show_in_dropdown BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, options_width DOUBLE PRECISION NOT NULL, options_height DOUBLE PRECISION NOT NULL, options_barcode_type VARCHAR(255) NOT NULL, options_picture_type VARCHAR(255) NOT NULL, options_supported_element VARCHAR(255) NOT NULL, options_additional_css CLOB NOT NULL, options_lines_mode VARCHAR(255) NOT NULL, options_lines CLOB NOT NULL, CONSTRAINT FK_C93E9CF56DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO label_profiles (id, id_preview_attachement, comment, show_in_dropdown, name, last_modified, datetime_added, options_width, options_height, options_barcode_type, options_picture_type, options_supported_element, options_additional_css, options_lines_mode, options_lines) SELECT id, id_preview_attachment, comment, show_in_dropdown, name, last_modified, datetime_added, options_width, options_height, options_barcode_type, options_picture_type, options_supported_element, options_additional_css, options_lines_mode, options_lines FROM __temp__label_profiles'); + $this->addSql('DROP TABLE __temp__label_profiles'); + $this->addSql('CREATE INDEX IDX_C93E9CF56DEDCEC2 ON label_profiles (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level BOOLEAN NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL -- +(DC2Type:json) + , type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, username, datetime, level, target_id, target_type, extra, type) SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__manufacturers AS SELECT id, parent_id, id_preview_attachment, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "manufacturers"'); + $this->addSql('DROP TABLE "manufacturers"'); + $this->addSql('CREATE TABLE "manufacturers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_94565B12727ACA70 FOREIGN KEY (parent_id) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_94565B126DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "manufacturers" (id, parent_id, id_preview_attachement, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__manufacturers'); + $this->addSql('DROP TABLE __temp__manufacturers'); + $this->addSql('CREATE INDEX IDX_94565B12727ACA70 ON "manufacturers" (parent_id)'); + $this->addSql('CREATE INDEX manufacturer_name ON "manufacturers" (name)'); + $this->addSql('CREATE INDEX manufacturer_idx_parent_name ON "manufacturers" (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_94565B126DEDCEC2 ON "manufacturers" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__measurement_units AS SELECT id, parent_id, id_preview_attachment, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added FROM "measurement_units"'); + $this->addSql('DROP TABLE "measurement_units"'); + $this->addSql('CREATE TABLE "measurement_units" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, unit VARCHAR(255) DEFAULT NULL, is_integer BOOLEAN NOT NULL, use_si_prefix BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_F5AF83CF727ACA70 FOREIGN KEY (parent_id) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F5AF83CF6DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "measurement_units" (id, parent_id, id_preview_attachement, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added FROM __temp__measurement_units'); + $this->addSql('DROP TABLE __temp__measurement_units'); + $this->addSql('CREATE INDEX IDX_F5AF83CF727ACA70 ON "measurement_units" (parent_id)'); + $this->addSql('CREATE INDEX unit_idx_name ON "measurement_units" (name)'); + $this->addSql('CREATE INDEX unit_idx_parent_name ON "measurement_units" (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_F5AF83CF6DEDCEC2 ON "measurement_units" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__parts AS SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order FROM "parts"'); + $this->addSql('DROP TABLE "parts"'); + $this->addSql('CREATE TABLE "parts" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachement INTEGER DEFAULT NULL, id_category INTEGER NOT NULL, id_footprint INTEGER DEFAULT NULL, id_part_unit INTEGER DEFAULT NULL, id_manufacturer INTEGER DEFAULT NULL, order_orderdetails_id INTEGER DEFAULT NULL, built_project_id INTEGER DEFAULT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, needs_review BOOLEAN NOT NULL, tags CLOB NOT NULL, mass DOUBLE PRECISION DEFAULT NULL, ipn VARCHAR(100) DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, visible BOOLEAN NOT NULL, favorite BOOLEAN NOT NULL, minamount DOUBLE PRECISION NOT NULL, manufacturer_product_url VARCHAR(255) NOT NULL, manufacturer_product_number VARCHAR(255) NOT NULL, manufacturing_status VARCHAR(255) DEFAULT NULL, order_quantity INTEGER NOT NULL, manual_order BOOLEAN NOT NULL, CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES "orderdetails" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE6DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "parts" (id, id_preview_attachement, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order) SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order FROM __temp__parts'); + $this->addSql('DROP TABLE __temp__parts'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON "parts" (ipn)'); + $this->addSql('CREATE INDEX IDX_6940A7FE5697F554 ON "parts" (id_category)'); + $this->addSql('CREATE INDEX IDX_6940A7FE7E371A10 ON "parts" (id_footprint)'); + $this->addSql('CREATE INDEX IDX_6940A7FE2626CEF9 ON "parts" (id_part_unit)'); + $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON "parts" (id_manufacturer)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON "parts" (order_orderdetails_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON "parts" (built_project_id)'); + $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON "parts" (datetime_added, name, last_modified, id, needs_review)'); + $this->addSql('CREATE INDEX parts_idx_name ON "parts" (name)'); + $this->addSql('CREATE INDEX parts_idx_ipn ON "parts" (ipn)'); + $this->addSql('CREATE INDEX IDX_6940A7FE6DEDCEC2 ON "parts" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM "pricedetails"'); + $this->addSql('DROP TABLE "pricedetails"'); + $this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL -- +(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "pricedetails" (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM projects'); + $this->addSql('DROP TABLE projects'); + $this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts BOOLEAN NOT NULL, description CLOB DEFAULT \'""\' NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_11074E9A6DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachement, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM __temp__projects'); + $this->addSql('DROP TABLE __temp__projects'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A46DEDCEC2 ON projects (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__storelocations AS SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM "storelocations"'); + $this->addSql('DROP TABLE "storelocations"'); + $this->addSql('CREATE TABLE "storelocations" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, storage_type_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, is_full BOOLEAN NOT NULL, only_single_part BOOLEAN NOT NULL, limit_to_existing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_7517020727ACA70 FOREIGN KEY (parent_id) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_75170206DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "storelocations" (id, parent_id, storage_type_id, id_preview_attachement, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM __temp__storelocations'); + $this->addSql('DROP TABLE __temp__storelocations'); + $this->addSql('CREATE INDEX IDX_7517020727ACA70 ON "storelocations" (parent_id)'); + $this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON "storelocations" (storage_type_id)'); + $this->addSql('CREATE INDEX location_idx_name ON "storelocations" (name)'); + $this->addSql('CREATE INDEX location_idx_parent_name ON "storelocations" (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_75170206DEDCEC2 ON "storelocations" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "suppliers"'); + $this->addSql('DROP TABLE "suppliers"'); + $this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95C6DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "suppliers" (id, parent_id, default_currency_id, id_preview_attachement, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)'); + $this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_AC28B95C6DEDCEC2 ON "suppliers" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data FROM "users"'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL -- +(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL -- +(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E96DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E96DEDCEC2 ON "users" (id_preview_attachement)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL -- +(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL -- +(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL -- +(DC2Type:trust_path) + , aaguid CLOB NOT NULL -- +(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL -- +(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + +} diff --git a/migrations/Version20230220221024.php b/migrations/Version20230220221024.php new file mode 100644 index 00000000..b2209351 --- /dev/null +++ b/migrations/Version20230220221024.php @@ -0,0 +1,50 @@ +addSql('ALTER TABLE `users` ADD saml_user TINYINT(1) NOT NULL DEFAULT 0'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE `users` DROP saml_user'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('ALTER TABLE users ADD saml_user BOOLEAN NOT NULL DEFAULT 0'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('ALTER TABLE `users` DROP saml_user'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20230402170923.php b/migrations/Version20230402170923.php new file mode 100644 index 00000000..3325afb6 --- /dev/null +++ b/migrations/Version20230402170923.php @@ -0,0 +1,310 @@ +addSql('ALTER TABLE part_lots ADD id_owner INT DEFAULT NULL'); + $this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F94321E5A74C FOREIGN KEY (id_owner) REFERENCES `users` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_EBC8F94321E5A74C ON part_lots (id_owner)'); + $this->addSql('ALTER TABLE projects ADD CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id)'); + $this->addSql('ALTER TABLE storelocations ADD id_owner INT DEFAULT NULL, ADD part_owner_must_match TINYINT(1) DEFAULT 0 NOT NULL'); + $this->addSql('ALTER TABLE storelocations ADD CONSTRAINT FK_751702021E5A74C FOREIGN KEY (id_owner) REFERENCES `users` (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_751702021E5A74C ON storelocations (id_owner)'); + $this->addSql('ALTER TABLE `users` ADD about_me LONGTEXT NOT NULL'); + + $this->addSql('ALTER TABLE projects CHANGE description description LONGTEXT NOT NULL'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE log CHANGE level level TINYINT(1) NOT NULL'); + $this->addSql('ALTER TABLE part_lots DROP FOREIGN KEY FK_EBC8F94321E5A74C'); + $this->addSql('DROP INDEX IDX_EBC8F94321E5A74C ON part_lots'); + $this->addSql('ALTER TABLE part_lots DROP id_owner'); + $this->addSql('ALTER TABLE projects DROP FOREIGN KEY FK_5C93B3A4727ACA70'); + $this->addSql('ALTER TABLE `storelocations` DROP FOREIGN KEY FK_751702021E5A74C'); + $this->addSql('DROP INDEX IDX_751702021E5A74C ON `storelocations`'); + $this->addSql('ALTER TABLE `storelocations` DROP id_owner, DROP part_owner_must_match'); + $this->addSql('ALTER TABLE `users` DROP about_me'); + } + + public function sqLiteUp(Schema $schema): void + { + //In Version20230219225340 the type of project description was set to an empty string, which caused problems with doctrine migrations, fix it here + $this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM projects'); + $this->addSql('DROP TABLE projects'); + $this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description CLOB DEFAULT \'\', CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description) SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM __temp__projects'); + $this->addSql('DROP TABLE __temp__projects'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM groups'); + $this->addSql('DROP TABLE groups'); + $this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)'); + $this->addSql('CREATE INDEX group_idx_name ON groups (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level TINYINT(4) NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json) + , type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__part_lots AS SELECT id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM part_lots'); + $this->addSql('DROP TABLE part_lots'); + $this->addSql('CREATE TABLE part_lots (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_store_location INTEGER DEFAULT NULL, id_part INTEGER NOT NULL, id_owner INTEGER DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, expiration_date DATETIME DEFAULT NULL, instock_unknown BOOLEAN NOT NULL, amount DOUBLE PRECISION NOT NULL, needs_refill BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES storelocations (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F94321E5A74C FOREIGN KEY (id_owner) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO part_lots (id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added) SELECT id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM __temp__part_lots'); + $this->addSql('DROP TABLE __temp__part_lots'); + $this->addSql('CREATE INDEX part_lots_idx_needs_refill ON part_lots (needs_refill)'); + $this->addSql('CREATE INDEX part_lots_idx_instock_un_expiration_id_part ON part_lots (instock_unknown, expiration_date, id_part)'); + $this->addSql('CREATE INDEX IDX_EBC8F9435D8F4B37 ON part_lots (id_store_location)'); + $this->addSql('CREATE INDEX IDX_EBC8F943C22F6CC4 ON part_lots (id_part)'); + $this->addSql('CREATE INDEX IDX_EBC8F94321E5A74C ON part_lots (id_owner)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails'); + $this->addSql('DROP TABLE pricedetails'); + $this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM projects'); + $this->addSql('DROP TABLE projects'); + $this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description CLOB DEFAULT \'\' NOT NULL, CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description) SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM __temp__projects'); + $this->addSql('DROP TABLE __temp__projects'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__storelocations AS SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM storelocations'); + $this->addSql('DROP TABLE storelocations'); + $this->addSql('CREATE TABLE storelocations (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, storage_type_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_owner INTEGER DEFAULT NULL, is_full BOOLEAN NOT NULL, only_single_part BOOLEAN NOT NULL, limit_to_existing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, part_owner_must_match BOOLEAN DEFAULT 0 NOT NULL, CONSTRAINT FK_7517020727ACA70 FOREIGN KEY (parent_id) REFERENCES storelocations (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES measurement_units (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_751702021E5A74C FOREIGN KEY (id_owner) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO storelocations (id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM __temp__storelocations'); + $this->addSql('DROP TABLE __temp__storelocations'); + $this->addSql('CREATE INDEX IDX_7517020EA7100A1 ON storelocations (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON storelocations (storage_type_id)'); + $this->addSql('CREATE INDEX IDX_7517020727ACA70 ON storelocations (parent_id)'); + $this->addSql('CREATE INDEX location_idx_name ON storelocations (name)'); + $this->addSql('CREATE INDEX location_idx_parent_name ON storelocations (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_751702021E5A74C ON storelocations (id_owner)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM suppliers'); + $this->addSql('DROP TABLE suppliers'); + $this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)'); + $this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user FROM users'); + $this->addSql('DROP TABLE users'); + $this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json) + , saml_user BOOLEAN NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)'); + $this->addSql('CREATE INDEX user_idx_username ON users (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path) + , aaguid CLOB NOT NULL --(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL --(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM "groups"'); + $this->addSql('DROP TABLE "groups"'); + $this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)'); + $this->addSql('CREATE INDEX group_idx_name ON "groups" (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level BOOLEAN NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL -- +(DC2Type:json) + , type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, username, datetime, level, target_id, target_type, extra, type) SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__part_lots AS SELECT id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM part_lots'); + $this->addSql('DROP TABLE part_lots'); + $this->addSql('CREATE TABLE part_lots (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_store_location INTEGER DEFAULT NULL, id_part INTEGER NOT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, expiration_date DATETIME DEFAULT NULL, instock_unknown BOOLEAN NOT NULL, amount DOUBLE PRECISION NOT NULL, needs_refill BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO part_lots (id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added) SELECT id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM __temp__part_lots'); + $this->addSql('DROP TABLE __temp__part_lots'); + $this->addSql('CREATE INDEX IDX_EBC8F9435D8F4B37 ON part_lots (id_store_location)'); + $this->addSql('CREATE INDEX IDX_EBC8F943C22F6CC4 ON part_lots (id_part)'); + $this->addSql('CREATE INDEX part_lots_idx_instock_un_expiration_id_part ON part_lots (instock_unknown, expiration_date, id_part)'); + $this->addSql('CREATE INDEX part_lots_idx_needs_refill ON part_lots (needs_refill)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM "pricedetails"'); + $this->addSql('DROP TABLE "pricedetails"'); + $this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL -- +(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "pricedetails" (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM projects'); + $this->addSql('DROP TABLE projects'); + $this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts BOOLEAN NOT NULL, description CLOB DEFAULT \'\', comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM __temp__projects'); + $this->addSql('DROP TABLE __temp__projects'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__storelocations AS SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM "storelocations"'); + $this->addSql('DROP TABLE "storelocations"'); + $this->addSql('CREATE TABLE "storelocations" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, storage_type_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, is_full BOOLEAN NOT NULL, only_single_part BOOLEAN NOT NULL, limit_to_existing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_7517020727ACA70 FOREIGN KEY (parent_id) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "storelocations" (id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM __temp__storelocations'); + $this->addSql('DROP TABLE __temp__storelocations'); + $this->addSql('CREATE INDEX IDX_7517020727ACA70 ON "storelocations" (parent_id)'); + $this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON "storelocations" (storage_type_id)'); + $this->addSql('CREATE INDEX IDX_7517020EA7100A1 ON "storelocations" (id_preview_attachment)'); + $this->addSql('CREATE INDEX location_idx_name ON "storelocations" (name)'); + $this->addSql('CREATE INDEX location_idx_parent_name ON "storelocations" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "suppliers"'); + $this->addSql('DROP TABLE "suppliers"'); + $this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "suppliers" (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL -- +(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL -- +(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN DEFAULT 0 NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL -- +(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL -- +(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL -- +(DC2Type:trust_path) + , aaguid CLOB NOT NULL -- +(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL -- +(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20230408170059.php b/migrations/Version20230408170059.php new file mode 100644 index 00000000..e3662e16 --- /dev/null +++ b/migrations/Version20230408170059.php @@ -0,0 +1,62 @@ +addSql('ALTER TABLE `users` ADD show_email_on_profile TINYINT(1) DEFAULT 0 NOT NULL'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE `users` DROP show_email_on_profile'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('ALTER TABLE users ADD COLUMN show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json) + , CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20230408213957.php b/migrations/Version20230408213957.php new file mode 100644 index 00000000..47807126 --- /dev/null +++ b/migrations/Version20230408213957.php @@ -0,0 +1,447 @@ +addSql('ALTER TABLE `groups` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE `log` CHANGE level level TINYINT(4) NOT NULL'); + if ($this->doesFKExists('projects', 'FK_11074E9A727ACA70')){ + $this->addSql('ALTER TABLE projects DROP FOREIGN KEY FK_11074E9A727ACA70'); + } + $this->addSql('ALTER TABLE projects CHANGE description description LONGTEXT NOT NULL'); + $this->addSql('ALTER TABLE `users` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', CHANGE saml_user saml_user TINYINT(1) NOT NULL, CHANGE about_me about_me LONGTEXT NOT NULL'); + $this->addSql('ALTER TABLE `log` CHANGE level level TINYINT NOT NULL COMMENT \'(DC2Type:tinyint)\''); + } + + public function mySQLDown(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE `groups` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE log CHANGE level level TINYINT(1) NOT NULL'); + $this->addSql('ALTER TABLE projects CHANGE description description LONGTEXT NOT NULL'); + $this->addSql('ALTER TABLE `users` CHANGE about_me about_me LONGTEXT NOT NULL, CHANGE saml_user saml_user TINYINT(1) DEFAULT 0 NOT NULL, CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE log CHANGE level level TINYINT(1) NOT NULL'); + } + + public function sqLiteUp(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM groups'); + $this->addSql('DROP TABLE groups'); + $this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)'); + $this->addSql('CREATE INDEX group_idx_name ON groups (name)'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level TINYINT NOT NULL --(DC2Type:tinyint) + , target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json) + , type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails'); + $this->addSql('DROP TABLE pricedetails'); + $this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM suppliers'); + $this->addSql('DROP TABLE suppliers'); + $this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)'); + $this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM users'); + $this->addSql('DROP TABLE users'); + $this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json) + , saml_user BOOLEAN NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE INDEX user_idx_username ON users (name)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path) + , aaguid CLOB NOT NULL --(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL --(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM groups'); + $this->addSql('DROP TABLE groups'); + $this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL --(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)'); + $this->addSql('CREATE INDEX group_idx_name ON groups (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level TINYINT NOT NULL --(DC2Type:tinyint) + , target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json) + , type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails'); + $this->addSql('DROP TABLE pricedetails'); + $this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM projects'); + $this->addSql('DROP TABLE projects'); + $this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description CLOB NOT NULL, CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description) SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM __temp__projects'); + $this->addSql('DROP TABLE __temp__projects'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM suppliers'); + $this->addSql('DROP TABLE suppliers'); + $this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)'); + $this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM users'); + $this->addSql('DROP TABLE users'); + $this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL --(DC2Type:json) + , saml_user BOOLEAN NOT NULL, about_me CLOB NOT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)'); + $this->addSql('CREATE INDEX user_idx_username ON users (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path) + , aaguid CLOB NOT NULL --(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL --(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function sqLiteDown(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM "groups"'); + $this->addSql('DROP TABLE "groups"'); + $this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)'); + $this->addSql('CREATE INDEX group_idx_name ON "groups" (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level BOOLEAN NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL -- +(DC2Type:json) + , type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, username, datetime, level, target_id, target_type, extra, type) SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM "pricedetails"'); + $this->addSql('DROP TABLE "pricedetails"'); + $this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL -- +(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "pricedetails" (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "suppliers"'); + $this->addSql('DROP TABLE "suppliers"'); + $this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "suppliers" (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL -- +(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL -- +(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL -- +(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL -- +(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL -- +(DC2Type:trust_path) + , aaguid CLOB NOT NULL -- +(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL -- +(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM "groups"'); + $this->addSql('DROP TABLE "groups"'); + $this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)'); + $this->addSql('CREATE INDEX group_idx_name ON "groups" (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level TINYINT NOT NULL -- +(DC2Type:tinyint) + , target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL -- +(DC2Type:json) + , type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, username, datetime, level, target_id, target_type, extra, type) SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM "pricedetails"'); + $this->addSql('DROP TABLE "pricedetails"'); + $this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL -- +(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "pricedetails" (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM projects'); + $this->addSql('DROP TABLE projects'); + $this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts BOOLEAN NOT NULL, description CLOB DEFAULT \'\' NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM __temp__projects'); + $this->addSql('DROP TABLE __temp__projects'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "suppliers"'); + $this->addSql('DROP TABLE "suppliers"'); + $this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "suppliers" (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL -- +(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL -- +(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL -- +(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL -- +(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL -- +(DC2Type:trust_path) + , aaguid CLOB NOT NULL -- +(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL -- +(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20230417211732.php b/migrations/Version20230417211732.php new file mode 100644 index 00000000..5fa3b5f7 --- /dev/null +++ b/migrations/Version20230417211732.php @@ -0,0 +1,58 @@ +addSql('DELETE FROM attachments WHERE class_name = "PartDB\\\\Part" AND NOT EXISTS (SELECT id FROM parts WHERE id = attachments.element_id)'); + $this->addSql('DELETE FROM attachments WHERE class_name = "PartDB\\\\Device" AND NOT EXISTS (SELECT id FROM projects WHERE id = attachments.element_id)'); + + // Replace all attachments where class_name is the legacy "PartDB\Part" with the new version "Part" + //We have to use 4 backslashes here, as PHP reduces them to 2 backslashes, which MySQL interprets as an escaped backslash. + $this->addSql('UPDATE attachments SET class_name = "Part" WHERE class_name = "PartDB\\\\Part"'); + //Do the same with PartDB\Device and Device + $this->addSql('UPDATE attachments SET class_name = "Device" WHERE class_name = "PartDB\\\\Device"'); + + + } + + public function mySQLDown(Schema $schema): void + { + // We can not revert this migration, because we don't know the old class name. + } + + public function sqLiteUp(Schema $schema): void + { + //As legacy database can only be migrated to MySQL, we don't need to implement this method. + //Dont skip here, as this causes this migration always to be executed. Do nothing instead. + } + + public function sqLiteDown(Schema $schema): void + { + //As we done nothing, we don't need to implement this method. + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20230528000149.php b/migrations/Version20230528000149.php new file mode 100644 index 00000000..5e742383 --- /dev/null +++ b/migrations/Version20230528000149.php @@ -0,0 +1,75 @@ +addSql('ALTER TABLE webauthn_keys ADD other_ui LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\''); + } + + public function mySQLDown(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE webauthn_keys DROP other_ui'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path) + , aaguid CLOB NOT NULL --(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL --(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, other_ui CLOB DEFAULT NULL --(DC2Type:array) + , CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL -- +(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL -- +(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL -- +(DC2Type:trust_path) + , aaguid CLOB NOT NULL -- +(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL -- +(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20230716184033.php b/migrations/Version20230716184033.php new file mode 100644 index 00000000..7c0d8a12 --- /dev/null +++ b/migrations/Version20230716184033.php @@ -0,0 +1,361 @@ +addSql('CREATE TABLE oauth_tokens (id INT AUTO_INCREMENT NOT NULL, token VARCHAR(255) DEFAULT NULL, expires_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', refresh_token VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, UNIQUE INDEX oauth_tokens_unique_name (name), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE attachment_types ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE categories ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE currencies ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE footprints ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE `groups` ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE manufacturers ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE measurement_units ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD provider_reference_provider_key VARCHAR(255) DEFAULT NULL, ADD provider_reference_provider_id VARCHAR(255) DEFAULT NULL, ADD provider_reference_provider_url VARCHAR(255) DEFAULT NULL, ADD provider_reference_last_updated DATETIME DEFAULT NULL'); + $this->addSql('ALTER TABLE projects ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE storelocations ADD alternative_names LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE suppliers ADD alternative_names LONGTEXT DEFAULT NULL'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('DROP TABLE oauth_tokens'); + $this->addSql('ALTER TABLE `attachment_types` DROP alternative_names'); + $this->addSql('ALTER TABLE `categories` DROP alternative_names'); + $this->addSql('ALTER TABLE currencies DROP alternative_names'); + $this->addSql('ALTER TABLE `footprints` DROP alternative_names'); + $this->addSql('ALTER TABLE `groups` DROP alternative_names'); + $this->addSql('ALTER TABLE `manufacturers` DROP alternative_names'); + $this->addSql('ALTER TABLE `measurement_units` DROP alternative_names'); + $this->addSql('ALTER TABLE `parts` DROP provider_reference_provider_key, DROP provider_reference_provider_id, DROP provider_reference_provider_url, DROP provider_reference_last_updated'); + $this->addSql('ALTER TABLE projects DROP alternative_names'); + $this->addSql('ALTER TABLE `storelocations` DROP alternative_names'); + $this->addSql('ALTER TABLE `suppliers` DROP alternative_names'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('CREATE TABLE oauth_tokens (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, token VARCHAR(255) DEFAULT NULL, expires_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , refresh_token VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL)'); + $this->addSql('CREATE UNIQUE INDEX oauth_tokens_unique_name ON oauth_tokens (name)'); + $this->addSql('ALTER TABLE attachment_types ADD COLUMN alternative_names CLOB DEFAULT NULL'); + $this->addSql('ALTER TABLE categories ADD COLUMN alternative_names CLOB DEFAULT NULL'); + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, alternative_names CLOB DEFAULT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('ALTER TABLE footprints ADD COLUMN alternative_names CLOB DEFAULT NULL'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM groups'); + $this->addSql('DROP TABLE groups'); + $this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL --(DC2Type:json) + , alternative_names CLOB DEFAULT NULL, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)'); + $this->addSql('CREATE INDEX group_idx_name ON groups (name)'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level TINYINT NOT NULL --(DC2Type:tinyint) + , target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json) + , type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('ALTER TABLE manufacturers ADD COLUMN alternative_names CLOB DEFAULT NULL'); + $this->addSql('ALTER TABLE measurement_units ADD COLUMN alternative_names CLOB DEFAULT NULL'); + $this->addSql('CREATE TEMPORARY TABLE __temp__parts AS SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn FROM parts'); + $this->addSql('DROP TABLE parts'); + $this->addSql('CREATE TABLE parts (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_category INTEGER NOT NULL, id_footprint INTEGER DEFAULT NULL, id_part_unit INTEGER DEFAULT NULL, id_manufacturer INTEGER DEFAULT NULL, order_orderdetails_id INTEGER DEFAULT NULL, built_project_id INTEGER DEFAULT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, needs_review BOOLEAN NOT NULL, tags CLOB NOT NULL, mass DOUBLE PRECISION DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, visible BOOLEAN NOT NULL, favorite BOOLEAN NOT NULL, minamount DOUBLE PRECISION NOT NULL, manufacturer_product_url VARCHAR(255) NOT NULL, manufacturer_product_number VARCHAR(255) NOT NULL, manufacturing_status VARCHAR(255) DEFAULT NULL, order_quantity INTEGER NOT NULL, manual_order BOOLEAN NOT NULL, ipn VARCHAR(100) DEFAULT NULL, provider_reference_provider_key VARCHAR(255) DEFAULT NULL, provider_reference_provider_id VARCHAR(255) DEFAULT NULL, provider_reference_provider_url VARCHAR(255) DEFAULT NULL, provider_reference_last_updated DATETIME DEFAULT NULL, CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES categories (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES footprints (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES measurement_units (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES manufacturers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO parts (id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn) SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn FROM __temp__parts'); + $this->addSql('DROP TABLE __temp__parts'); + $this->addSql('CREATE INDEX IDX_6940A7FEEA7100A1 ON parts (id_preview_attachment)'); + $this->addSql('CREATE INDEX parts_idx_ipn ON parts (ipn)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON parts (ipn)'); + $this->addSql('CREATE INDEX parts_idx_name ON parts (name)'); + $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON parts (datetime_added, name, last_modified, id, needs_review)'); + $this->addSql('CREATE INDEX IDX_6940A7FE5697F554 ON parts (id_category)'); + $this->addSql('CREATE INDEX IDX_6940A7FE7E371A10 ON parts (id_footprint)'); + $this->addSql('CREATE INDEX IDX_6940A7FE2626CEF9 ON parts (id_part_unit)'); + $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON parts (id_manufacturer)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON parts (order_orderdetails_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON parts (built_project_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails'); + $this->addSql('DROP TABLE pricedetails'); + $this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('ALTER TABLE projects ADD COLUMN alternative_names CLOB DEFAULT NULL'); + $this->addSql('ALTER TABLE storelocations ADD COLUMN alternative_names CLOB DEFAULT NULL'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM suppliers'); + $this->addSql('DROP TABLE suppliers'); + $this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, alternative_names CLOB DEFAULT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)'); + $this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM users'); + $this->addSql('DROP TABLE users'); + $this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL --(DC2Type:json) + , saml_user BOOLEAN NOT NULL, about_me CLOB NOT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE INDEX user_idx_username ON users (name)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path) + , aaguid CLOB NOT NULL --(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL --(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, other_ui CLOB DEFAULT NULL --(DC2Type:array) + , CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('DROP TABLE oauth_tokens'); + $this->addSql('CREATE TEMPORARY TABLE __temp__attachment_types AS SELECT id, parent_id, id_preview_attachment, filetype_filter, comment, not_selectable, name, last_modified, datetime_added FROM "attachment_types"'); + $this->addSql('DROP TABLE "attachment_types"'); + $this->addSql('CREATE TABLE "attachment_types" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, filetype_filter CLOB NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_EFAED719727ACA70 FOREIGN KEY (parent_id) REFERENCES "attachment_types" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EFAED719EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "attachment_types" (id, parent_id, id_preview_attachment, filetype_filter, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, filetype_filter, comment, not_selectable, name, last_modified, datetime_added FROM __temp__attachment_types'); + $this->addSql('DROP TABLE __temp__attachment_types'); + $this->addSql('CREATE INDEX IDX_EFAED719727ACA70 ON "attachment_types" (parent_id)'); + $this->addSql('CREATE INDEX IDX_EFAED719EA7100A1 ON "attachment_types" (id_preview_attachment)'); + $this->addSql('CREATE INDEX attachment_types_idx_name ON "attachment_types" (name)'); + $this->addSql('CREATE INDEX attachment_types_idx_parent_name ON "attachment_types" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__categories AS SELECT id, parent_id, id_preview_attachment, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added FROM "categories"'); + $this->addSql('DROP TABLE "categories"'); + $this->addSql('CREATE TABLE "categories" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, partname_hint CLOB NOT NULL, partname_regex CLOB NOT NULL, disable_footprints BOOLEAN NOT NULL, disable_manufacturers BOOLEAN NOT NULL, disable_autodatasheets BOOLEAN NOT NULL, disable_properties BOOLEAN NOT NULL, default_description CLOB NOT NULL, default_comment CLOB NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_3AF34668727ACA70 FOREIGN KEY (parent_id) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_3AF34668EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "categories" (id, parent_id, id_preview_attachment, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment, comment, not_selectable, name, last_modified, datetime_added FROM __temp__categories'); + $this->addSql('DROP TABLE __temp__categories'); + $this->addSql('CREATE INDEX IDX_3AF34668727ACA70 ON "categories" (parent_id)'); + $this->addSql('CREATE INDEX IDX_3AF34668EA7100A1 ON "categories" (id_preview_attachment)'); + $this->addSql('CREATE INDEX category_idx_name ON "categories" (name)'); + $this->addSql('CREATE INDEX category_idx_parent_name ON "categories" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__footprints AS SELECT id, parent_id, id_footprint_3d, id_preview_attachment, comment, not_selectable, name, last_modified, datetime_added FROM "footprints"'); + $this->addSql('DROP TABLE "footprints"'); + $this->addSql('CREATE TABLE "footprints" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_footprint_3d INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_A34D68A2727ACA70 FOREIGN KEY (parent_id) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A34D68A232A38C34 FOREIGN KEY (id_footprint_3d) REFERENCES "attachments" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A34D68A2EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "footprints" (id, parent_id, id_footprint_3d, id_preview_attachment, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_footprint_3d, id_preview_attachment, comment, not_selectable, name, last_modified, datetime_added FROM __temp__footprints'); + $this->addSql('DROP TABLE __temp__footprints'); + $this->addSql('CREATE INDEX IDX_A34D68A2727ACA70 ON "footprints" (parent_id)'); + $this->addSql('CREATE INDEX IDX_A34D68A232A38C34 ON "footprints" (id_footprint_3d)'); + $this->addSql('CREATE INDEX IDX_A34D68A2EA7100A1 ON "footprints" (id_preview_attachment)'); + $this->addSql('CREATE INDEX footprint_idx_name ON "footprints" (name)'); + $this->addSql('CREATE INDEX footprint_idx_parent_name ON "footprints" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM "groups"'); + $this->addSql('DROP TABLE "groups"'); + $this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)'); + $this->addSql('CREATE INDEX group_idx_name ON "groups" (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level TINYINT NOT NULL -- +(DC2Type:tinyint) + , target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL -- +(DC2Type:json) + , type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, username, datetime, level, target_id, target_type, extra, type) SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__manufacturers AS SELECT id, parent_id, id_preview_attachment, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "manufacturers"'); + $this->addSql('DROP TABLE "manufacturers"'); + $this->addSql('CREATE TABLE "manufacturers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_94565B12727ACA70 FOREIGN KEY (parent_id) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_94565B12EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "manufacturers" (id, parent_id, id_preview_attachment, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__manufacturers'); + $this->addSql('DROP TABLE __temp__manufacturers'); + $this->addSql('CREATE INDEX IDX_94565B12727ACA70 ON "manufacturers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_94565B12EA7100A1 ON "manufacturers" (id_preview_attachment)'); + $this->addSql('CREATE INDEX manufacturer_name ON "manufacturers" (name)'); + $this->addSql('CREATE INDEX manufacturer_idx_parent_name ON "manufacturers" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__measurement_units AS SELECT id, parent_id, id_preview_attachment, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added FROM "measurement_units"'); + $this->addSql('DROP TABLE "measurement_units"'); + $this->addSql('CREATE TABLE "measurement_units" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, unit VARCHAR(255) DEFAULT NULL, is_integer BOOLEAN NOT NULL, use_si_prefix BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_F5AF83CF727ACA70 FOREIGN KEY (parent_id) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F5AF83CFEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "measurement_units" (id, parent_id, id_preview_attachment, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, unit, is_integer, use_si_prefix, comment, not_selectable, name, last_modified, datetime_added FROM __temp__measurement_units'); + $this->addSql('DROP TABLE __temp__measurement_units'); + $this->addSql('CREATE INDEX IDX_F5AF83CF727ACA70 ON "measurement_units" (parent_id)'); + $this->addSql('CREATE INDEX IDX_F5AF83CFEA7100A1 ON "measurement_units" (id_preview_attachment)'); + $this->addSql('CREATE INDEX unit_idx_name ON "measurement_units" (name)'); + $this->addSql('CREATE INDEX unit_idx_parent_name ON "measurement_units" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__parts AS SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order FROM "parts"'); + $this->addSql('DROP TABLE "parts"'); + $this->addSql('CREATE TABLE "parts" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_category INTEGER NOT NULL, id_footprint INTEGER DEFAULT NULL, id_part_unit INTEGER DEFAULT NULL, id_manufacturer INTEGER DEFAULT NULL, order_orderdetails_id INTEGER DEFAULT NULL, built_project_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, needs_review BOOLEAN NOT NULL, tags CLOB NOT NULL, mass DOUBLE PRECISION DEFAULT NULL, ipn VARCHAR(100) DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, visible BOOLEAN NOT NULL, favorite BOOLEAN NOT NULL, minamount DOUBLE PRECISION NOT NULL, manufacturer_product_url VARCHAR(255) NOT NULL, manufacturer_product_number VARCHAR(255) NOT NULL, manufacturing_status VARCHAR(255) DEFAULT NULL, order_quantity INTEGER NOT NULL, manual_order BOOLEAN NOT NULL, CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES "orderdetails" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "parts" (id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order) SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order FROM __temp__parts'); + $this->addSql('DROP TABLE __temp__parts'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON "parts" (ipn)'); + $this->addSql('CREATE INDEX IDX_6940A7FEEA7100A1 ON "parts" (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_6940A7FE5697F554 ON "parts" (id_category)'); + $this->addSql('CREATE INDEX IDX_6940A7FE7E371A10 ON "parts" (id_footprint)'); + $this->addSql('CREATE INDEX IDX_6940A7FE2626CEF9 ON "parts" (id_part_unit)'); + $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON "parts" (id_manufacturer)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON "parts" (order_orderdetails_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON "parts" (built_project_id)'); + $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON "parts" (datetime_added, name, last_modified, id, needs_review)'); + $this->addSql('CREATE INDEX parts_idx_name ON "parts" (name)'); + $this->addSql('CREATE INDEX parts_idx_ipn ON "parts" (ipn)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM "pricedetails"'); + $this->addSql('DROP TABLE "pricedetails"'); + $this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL -- +(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "pricedetails" (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM projects'); + $this->addSql('DROP TABLE projects'); + $this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts BOOLEAN NOT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM __temp__projects'); + $this->addSql('DROP TABLE __temp__projects'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__storelocations AS SELECT id, parent_id, storage_type_id, id_owner, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, part_owner_must_match, comment, not_selectable, name, last_modified, datetime_added FROM "storelocations"'); + $this->addSql('DROP TABLE "storelocations"'); + $this->addSql('CREATE TABLE "storelocations" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, storage_type_id INTEGER DEFAULT NULL, id_owner INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, is_full BOOLEAN NOT NULL, only_single_part BOOLEAN NOT NULL, limit_to_existing_parts BOOLEAN NOT NULL, part_owner_must_match BOOLEAN DEFAULT 0 NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_7517020727ACA70 FOREIGN KEY (parent_id) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_751702021E5A74C FOREIGN KEY (id_owner) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "storelocations" (id, parent_id, storage_type_id, id_owner, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, part_owner_must_match, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, storage_type_id, id_owner, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, part_owner_must_match, comment, not_selectable, name, last_modified, datetime_added FROM __temp__storelocations'); + $this->addSql('DROP TABLE __temp__storelocations'); + $this->addSql('CREATE INDEX IDX_7517020727ACA70 ON "storelocations" (parent_id)'); + $this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON "storelocations" (storage_type_id)'); + $this->addSql('CREATE INDEX IDX_751702021E5A74C ON "storelocations" (id_owner)'); + $this->addSql('CREATE INDEX IDX_7517020EA7100A1 ON "storelocations" (id_preview_attachment)'); + $this->addSql('CREATE INDEX location_idx_name ON "storelocations" (name)'); + $this->addSql('CREATE INDEX location_idx_parent_name ON "storelocations" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "suppliers"'); + $this->addSql('DROP TABLE "suppliers"'); + $this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL -- +(DC2Type:big_decimal) + , address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "suppliers" (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL -- +(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL -- +(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL -- +(DC2Type:json) + , CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL -- +(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL -- +(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL -- +(DC2Type:trust_path) + , aaguid CLOB NOT NULL -- +(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL -- +(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, other_ui CLOB DEFAULT NULL -- +(DC2Type:array) + , name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20230730131708.php b/migrations/Version20230730131708.php new file mode 100644 index 00000000..a12c3b8d --- /dev/null +++ b/migrations/Version20230730131708.php @@ -0,0 +1,112 @@ +addSql('ALTER TABLE oauth_tokens CHANGE token token LONGTEXT DEFAULT NULL, CHANGE refresh_token refresh_token LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE orderdetails CHANGE supplier_product_url supplier_product_url LONGTEXT NOT NULL'); + $this->addSql('ALTER TABLE parts CHANGE manufacturer_product_url manufacturer_product_url LONGTEXT NOT NULL'); + } + + public function mySQLDown(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE oauth_tokens CHANGE token token VARCHAR(255) DEFAULT NULL, CHANGE refresh_token refresh_token VARCHAR(255) NOT NULL'); + $this->addSql('ALTER TABLE `orderdetails` CHANGE supplier_product_url supplier_product_url VARCHAR(255) NOT NULL'); + $this->addSql('ALTER TABLE `parts` CHANGE manufacturer_product_url manufacturer_product_url VARCHAR(255) NOT NULL'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__oauth_tokens AS SELECT id, token, expires_at, refresh_token, name, last_modified, datetime_added FROM oauth_tokens'); + $this->addSql('DROP TABLE oauth_tokens'); + $this->addSql('CREATE TABLE oauth_tokens (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, token CLOB DEFAULT NULL, expires_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , refresh_token CLOB DEFAULT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL)'); + $this->addSql('INSERT INTO oauth_tokens (id, token, expires_at, refresh_token, name, last_modified, datetime_added) SELECT id, token, expires_at, refresh_token, name, last_modified, datetime_added FROM __temp__oauth_tokens'); + $this->addSql('DROP TABLE __temp__oauth_tokens'); + $this->addSql('CREATE UNIQUE INDEX oauth_tokens_unique_name ON oauth_tokens (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__orderdetails AS SELECT id, part_id, id_supplier, supplierpartnr, obsolete, supplier_product_url, last_modified, datetime_added FROM orderdetails'); + $this->addSql('DROP TABLE orderdetails'); + $this->addSql('CREATE TABLE orderdetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, part_id INTEGER NOT NULL, id_supplier INTEGER DEFAULT NULL, supplierpartnr VARCHAR(255) NOT NULL, obsolete BOOLEAN NOT NULL, supplier_product_url CLOB NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_489AFCDC4CE34BEC FOREIGN KEY (part_id) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_489AFCDCCBF180EB FOREIGN KEY (id_supplier) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO orderdetails (id, part_id, id_supplier, supplierpartnr, obsolete, supplier_product_url, last_modified, datetime_added) SELECT id, part_id, id_supplier, supplierpartnr, obsolete, supplier_product_url, last_modified, datetime_added FROM __temp__orderdetails'); + $this->addSql('DROP TABLE __temp__orderdetails'); + $this->addSql('CREATE INDEX orderdetails_supplier_part_nr ON orderdetails (supplierpartnr)'); + $this->addSql('CREATE INDEX IDX_489AFCDC4CE34BEC ON orderdetails (part_id)'); + $this->addSql('CREATE INDEX IDX_489AFCDCCBF180EB ON orderdetails (id_supplier)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__parts AS SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated FROM parts'); + $this->addSql('DROP TABLE parts'); + $this->addSql('CREATE TABLE parts (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_category INTEGER NOT NULL, id_footprint INTEGER DEFAULT NULL, id_part_unit INTEGER DEFAULT NULL, id_manufacturer INTEGER DEFAULT NULL, order_orderdetails_id INTEGER DEFAULT NULL, built_project_id INTEGER DEFAULT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, needs_review BOOLEAN NOT NULL, tags CLOB NOT NULL, mass DOUBLE PRECISION DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, visible BOOLEAN NOT NULL, favorite BOOLEAN NOT NULL, minamount DOUBLE PRECISION NOT NULL, manufacturer_product_url CLOB NOT NULL, manufacturer_product_number VARCHAR(255) NOT NULL, manufacturing_status VARCHAR(255) DEFAULT NULL, order_quantity INTEGER NOT NULL, manual_order BOOLEAN NOT NULL, ipn VARCHAR(100) DEFAULT NULL, provider_reference_provider_key VARCHAR(255) DEFAULT NULL, provider_reference_provider_id VARCHAR(255) DEFAULT NULL, provider_reference_provider_url VARCHAR(255) DEFAULT NULL, provider_reference_last_updated DATETIME DEFAULT NULL, CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES categories (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES footprints (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES measurement_units (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES manufacturers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO parts (id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated) SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, datetime_added, name, last_modified, needs_review, tags, mass, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, ipn, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated FROM __temp__parts'); + $this->addSql('DROP TABLE __temp__parts'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON parts (built_project_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON parts (order_orderdetails_id)'); + $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON parts (id_manufacturer)'); + $this->addSql('CREATE INDEX IDX_6940A7FE2626CEF9 ON parts (id_part_unit)'); + $this->addSql('CREATE INDEX IDX_6940A7FE7E371A10 ON parts (id_footprint)'); + $this->addSql('CREATE INDEX IDX_6940A7FE5697F554 ON parts (id_category)'); + $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON parts (datetime_added, name, last_modified, id, needs_review)'); + $this->addSql('CREATE INDEX parts_idx_name ON parts (name)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON parts (ipn)'); + $this->addSql('CREATE INDEX parts_idx_ipn ON parts (ipn)'); + $this->addSql('CREATE INDEX IDX_6940A7FEEA7100A1 ON parts (id_preview_attachment)'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__oauth_tokens AS SELECT id, token, expires_at, refresh_token, name, last_modified, datetime_added FROM oauth_tokens'); + $this->addSql('DROP TABLE oauth_tokens'); + $this->addSql('CREATE TABLE oauth_tokens (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, token VARCHAR(255) DEFAULT NULL, expires_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , refresh_token VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL)'); + $this->addSql('INSERT INTO oauth_tokens (id, token, expires_at, refresh_token, name, last_modified, datetime_added) SELECT id, token, expires_at, refresh_token, name, last_modified, datetime_added FROM __temp__oauth_tokens'); + $this->addSql('DROP TABLE __temp__oauth_tokens'); + $this->addSql('CREATE UNIQUE INDEX oauth_tokens_unique_name ON oauth_tokens (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__orderdetails AS SELECT id, part_id, id_supplier, supplierpartnr, obsolete, supplier_product_url, last_modified, datetime_added FROM "orderdetails"'); + $this->addSql('DROP TABLE "orderdetails"'); + $this->addSql('CREATE TABLE "orderdetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, part_id INTEGER NOT NULL, id_supplier INTEGER DEFAULT NULL, supplierpartnr VARCHAR(255) NOT NULL, obsolete BOOLEAN NOT NULL, supplier_product_url VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_489AFCDC4CE34BEC FOREIGN KEY (part_id) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_489AFCDCCBF180EB FOREIGN KEY (id_supplier) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "orderdetails" (id, part_id, id_supplier, supplierpartnr, obsolete, supplier_product_url, last_modified, datetime_added) SELECT id, part_id, id_supplier, supplierpartnr, obsolete, supplier_product_url, last_modified, datetime_added FROM __temp__orderdetails'); + $this->addSql('DROP TABLE __temp__orderdetails'); + $this->addSql('CREATE INDEX IDX_489AFCDC4CE34BEC ON "orderdetails" (part_id)'); + $this->addSql('CREATE INDEX IDX_489AFCDCCBF180EB ON "orderdetails" (id_supplier)'); + $this->addSql('CREATE INDEX orderdetails_supplier_part_nr ON "orderdetails" (supplierpartnr)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__parts AS SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated FROM "parts"'); + $this->addSql('DROP TABLE "parts"'); + $this->addSql('CREATE TABLE "parts" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_category INTEGER NOT NULL, id_footprint INTEGER DEFAULT NULL, id_part_unit INTEGER DEFAULT NULL, id_manufacturer INTEGER DEFAULT NULL, order_orderdetails_id INTEGER DEFAULT NULL, built_project_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, needs_review BOOLEAN NOT NULL, tags CLOB NOT NULL, mass DOUBLE PRECISION DEFAULT NULL, ipn VARCHAR(100) DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, visible BOOLEAN NOT NULL, favorite BOOLEAN NOT NULL, minamount DOUBLE PRECISION NOT NULL, manufacturer_product_url VARCHAR(255) NOT NULL, manufacturer_product_number VARCHAR(255) NOT NULL, manufacturing_status VARCHAR(255) DEFAULT NULL, order_quantity INTEGER NOT NULL, manual_order BOOLEAN NOT NULL, provider_reference_provider_key VARCHAR(255) DEFAULT NULL, provider_reference_provider_id VARCHAR(255) DEFAULT NULL, provider_reference_provider_url VARCHAR(255) DEFAULT NULL, provider_reference_last_updated DATETIME DEFAULT NULL, CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES "orderdetails" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "parts" (id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated) SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated FROM __temp__parts'); + $this->addSql('DROP TABLE __temp__parts'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON "parts" (ipn)'); + $this->addSql('CREATE INDEX IDX_6940A7FEEA7100A1 ON "parts" (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_6940A7FE5697F554 ON "parts" (id_category)'); + $this->addSql('CREATE INDEX IDX_6940A7FE7E371A10 ON "parts" (id_footprint)'); + $this->addSql('CREATE INDEX IDX_6940A7FE2626CEF9 ON "parts" (id_part_unit)'); + $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON "parts" (id_manufacturer)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON "parts" (order_orderdetails_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON "parts" (built_project_id)'); + $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON "parts" (datetime_added, name, last_modified, id, needs_review)'); + $this->addSql('CREATE INDEX parts_idx_name ON "parts" (name)'); + $this->addSql('CREATE INDEX parts_idx_ipn ON "parts" (ipn)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20230816213201.php b/migrations/Version20230816213201.php new file mode 100644 index 00000000..d776da26 --- /dev/null +++ b/migrations/Version20230816213201.php @@ -0,0 +1,54 @@ +addSql('CREATE TABLE api_tokens (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, valid_until DATETIME DEFAULT NULL, token VARCHAR(68) NOT NULL, level SMALLINT NOT NULL, last_time_used DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, UNIQUE INDEX UNIQ_2CAD560E5F37A13B (token), INDEX IDX_2CAD560EA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE api_tokens ADD CONSTRAINT FK_2CAD560EA76ED395 FOREIGN KEY (user_id) REFERENCES `users` (id)'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE api_tokens DROP FOREIGN KEY FK_2CAD560EA76ED395'); + $this->addSql('DROP TABLE api_tokens'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('CREATE TABLE api_tokens (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, valid_until DATETIME DEFAULT NULL, token VARCHAR(68) NOT NULL, level SMALLINT NOT NULL, last_time_used DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_2CAD560EA76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2CAD560E5F37A13B ON api_tokens (token)'); + $this->addSql('CREATE INDEX IDX_2CAD560EA76ED395 ON api_tokens (user_id)'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('DROP TABLE api_tokens'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20231114223101.php b/migrations/Version20231114223101.php new file mode 100644 index 00000000..c06cb279 --- /dev/null +++ b/migrations/Version20231114223101.php @@ -0,0 +1,81 @@ +addSql('CREATE TABLE part_association (id INT AUTO_INCREMENT NOT NULL, owner_id INT NOT NULL, other_id INT NOT NULL, type SMALLINT NOT NULL, other_type VARCHAR(255) DEFAULT NULL, comment LONGTEXT DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, INDEX IDX_61B952E07E3C61F9 (owner_id), INDEX IDX_61B952E0998D9879 (other_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE part_association ADD CONSTRAINT FK_61B952E07E3C61F9 FOREIGN KEY (owner_id) REFERENCES `parts` (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE part_association ADD CONSTRAINT FK_61B952E0998D9879 FOREIGN KEY (other_id) REFERENCES `parts` (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE part_lots ADD vendor_barcode VARCHAR(255) DEFAULT NULL'); + $this->addSql('CREATE INDEX part_lots_idx_barcode ON part_lots (vendor_barcode)'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE part_association DROP FOREIGN KEY FK_61B952E07E3C61F9'); + $this->addSql('ALTER TABLE part_association DROP FOREIGN KEY FK_61B952E0998D9879'); + $this->addSql('DROP TABLE part_association'); + $this->addSql('DROP INDEX part_lots_idx_barcode ON part_lots'); + $this->addSql('ALTER TABLE part_lots DROP vendor_barcode'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('CREATE TABLE part_association (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, owner_id INTEGER NOT NULL, other_id INTEGER NOT NULL, type SMALLINT NOT NULL, other_type VARCHAR(255) DEFAULT NULL, comment CLOB DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_61B952E07E3C61F9 FOREIGN KEY (owner_id) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_61B952E0998D9879 FOREIGN KEY (other_id) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('CREATE INDEX IDX_61B952E07E3C61F9 ON part_association (owner_id)'); + $this->addSql('CREATE INDEX IDX_61B952E0998D9879 ON part_association (other_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__part_lots AS SELECT id, id_store_location, id_part, id_owner, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM part_lots'); + $this->addSql('DROP TABLE part_lots'); + $this->addSql('CREATE TABLE part_lots (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_store_location INTEGER DEFAULT NULL, id_part INTEGER NOT NULL, id_owner INTEGER DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, expiration_date DATETIME DEFAULT NULL, instock_unknown BOOLEAN NOT NULL, amount DOUBLE PRECISION NOT NULL, needs_refill BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, vendor_barcode VARCHAR(255) DEFAULT NULL, CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES storelocations (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F94321E5A74C FOREIGN KEY (id_owner) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO part_lots (id, id_store_location, id_part, id_owner, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added) SELECT id, id_store_location, id_part, id_owner, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM __temp__part_lots'); + $this->addSql('DROP TABLE __temp__part_lots'); + $this->addSql('CREATE INDEX IDX_EBC8F94321E5A74C ON part_lots (id_owner)'); + $this->addSql('CREATE INDEX IDX_EBC8F943C22F6CC4 ON part_lots (id_part)'); + $this->addSql('CREATE INDEX IDX_EBC8F9435D8F4B37 ON part_lots (id_store_location)'); + $this->addSql('CREATE INDEX part_lots_idx_instock_un_expiration_id_part ON part_lots (instock_unknown, expiration_date, id_part)'); + $this->addSql('CREATE INDEX part_lots_idx_needs_refill ON part_lots (needs_refill)'); + $this->addSql('CREATE INDEX part_lots_idx_barcode ON part_lots (vendor_barcode)'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('DROP TABLE part_association'); + $this->addSql('CREATE TEMPORARY TABLE __temp__part_lots AS SELECT id, id_store_location, id_part, id_owner, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM part_lots'); + $this->addSql('DROP TABLE part_lots'); + $this->addSql('CREATE TABLE part_lots (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_store_location INTEGER DEFAULT NULL, id_part INTEGER NOT NULL, id_owner INTEGER DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, expiration_date DATETIME DEFAULT NULL, instock_unknown BOOLEAN NOT NULL, amount DOUBLE PRECISION NOT NULL, needs_refill BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F94321E5A74C FOREIGN KEY (id_owner) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO part_lots (id, id_store_location, id_part, id_owner, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added) SELECT id, id_store_location, id_part, id_owner, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM __temp__part_lots'); + $this->addSql('DROP TABLE __temp__part_lots'); + $this->addSql('CREATE INDEX IDX_EBC8F9435D8F4B37 ON part_lots (id_store_location)'); + $this->addSql('CREATE INDEX IDX_EBC8F943C22F6CC4 ON part_lots (id_part)'); + $this->addSql('CREATE INDEX IDX_EBC8F94321E5A74C ON part_lots (id_owner)'); + $this->addSql('CREATE INDEX part_lots_idx_instock_un_expiration_id_part ON part_lots (instock_unknown, expiration_date, id_part)'); + $this->addSql('CREATE INDEX part_lots_idx_needs_refill ON part_lots (needs_refill)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20231130180903.php b/migrations/Version20231130180903.php new file mode 100644 index 00000000..0eccb5aa --- /dev/null +++ b/migrations/Version20231130180903.php @@ -0,0 +1,98 @@ +addSql('ALTER TABLE categories ADD eda_info_reference_prefix VARCHAR(255) DEFAULT NULL, ADD eda_info_invisible TINYINT(1) DEFAULT NULL, ADD eda_info_exclude_from_bom TINYINT(1) DEFAULT NULL, ADD eda_info_exclude_from_board TINYINT(1) DEFAULT NULL, ADD eda_info_exclude_from_sim TINYINT(1) DEFAULT NULL, ADD eda_info_kicad_symbol VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE footprints ADD eda_info_kicad_footprint VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD eda_info_reference_prefix VARCHAR(255) DEFAULT NULL, ADD eda_info_value VARCHAR(255) DEFAULT NULL, ADD eda_info_invisible TINYINT(1) DEFAULT NULL, ADD eda_info_exclude_from_bom TINYINT(1) DEFAULT NULL, ADD eda_info_exclude_from_board TINYINT(1) DEFAULT NULL, ADD eda_info_exclude_from_sim TINYINT(1) DEFAULT NULL, ADD eda_info_kicad_symbol VARCHAR(255) DEFAULT NULL, ADD eda_info_kicad_footprint VARCHAR(255) DEFAULT NULL'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE `categories` DROP eda_info_reference_prefix, DROP eda_info_invisible, DROP eda_info_exclude_from_bom, DROP eda_info_exclude_from_board, DROP eda_info_exclude_from_sim, DROP eda_info_kicad_symbol'); + $this->addSql('ALTER TABLE `footprints` DROP eda_info_kicad_footprint'); + $this->addSql('ALTER TABLE `parts` DROP eda_info_reference_prefix, DROP eda_info_value, DROP eda_info_invisible, DROP eda_info_exclude_from_bom, DROP eda_info_exclude_from_board, DROP eda_info_exclude_from_sim, DROP eda_info_kicad_symbol, DROP eda_info_kicad_footprint'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('ALTER TABLE categories ADD COLUMN eda_info_reference_prefix VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE categories ADD COLUMN eda_info_invisible BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE categories ADD COLUMN eda_info_exclude_from_bom BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE categories ADD COLUMN eda_info_exclude_from_board BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE categories ADD COLUMN eda_info_exclude_from_sim BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE categories ADD COLUMN eda_info_kicad_symbol VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE footprints ADD COLUMN eda_info_kicad_footprint VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD COLUMN eda_info_reference_prefix VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD COLUMN eda_info_value VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD COLUMN eda_info_invisible BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD COLUMN eda_info_exclude_from_bom BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD COLUMN eda_info_exclude_from_board BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD COLUMN eda_info_exclude_from_sim BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD COLUMN eda_info_kicad_symbol VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE parts ADD COLUMN eda_info_kicad_footprint VARCHAR(255) DEFAULT NULL'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__categories AS SELECT id, parent_id, id_preview_attachment, name, last_modified, datetime_added, comment, not_selectable, alternative_names, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment FROM "categories"'); + $this->addSql('DROP TABLE "categories"'); + $this->addSql('CREATE TABLE "categories" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names CLOB DEFAULT NULL, partname_hint CLOB NOT NULL, partname_regex CLOB NOT NULL, disable_footprints BOOLEAN NOT NULL, disable_manufacturers BOOLEAN NOT NULL, disable_autodatasheets BOOLEAN NOT NULL, disable_properties BOOLEAN NOT NULL, default_description CLOB NOT NULL, default_comment CLOB NOT NULL, CONSTRAINT FK_3AF34668727ACA70 FOREIGN KEY (parent_id) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_3AF34668EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "categories" (id, parent_id, id_preview_attachment, name, last_modified, datetime_added, comment, not_selectable, alternative_names, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment) SELECT id, parent_id, id_preview_attachment, name, last_modified, datetime_added, comment, not_selectable, alternative_names, partname_hint, partname_regex, disable_footprints, disable_manufacturers, disable_autodatasheets, disable_properties, default_description, default_comment FROM __temp__categories'); + $this->addSql('DROP TABLE __temp__categories'); + $this->addSql('CREATE INDEX IDX_3AF34668727ACA70 ON "categories" (parent_id)'); + $this->addSql('CREATE INDEX IDX_3AF34668EA7100A1 ON "categories" (id_preview_attachment)'); + $this->addSql('CREATE INDEX category_idx_name ON "categories" (name)'); + $this->addSql('CREATE INDEX category_idx_parent_name ON "categories" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__footprints AS SELECT id, parent_id, id_preview_attachment, id_footprint_3d, name, last_modified, datetime_added, comment, not_selectable, alternative_names FROM "footprints"'); + $this->addSql('DROP TABLE "footprints"'); + $this->addSql('CREATE TABLE "footprints" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_footprint_3d INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names CLOB DEFAULT NULL, CONSTRAINT FK_A34D68A2727ACA70 FOREIGN KEY (parent_id) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A34D68A2EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A34D68A232A38C34 FOREIGN KEY (id_footprint_3d) REFERENCES "attachments" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "footprints" (id, parent_id, id_preview_attachment, id_footprint_3d, name, last_modified, datetime_added, comment, not_selectable, alternative_names) SELECT id, parent_id, id_preview_attachment, id_footprint_3d, name, last_modified, datetime_added, comment, not_selectable, alternative_names FROM __temp__footprints'); + $this->addSql('DROP TABLE __temp__footprints'); + $this->addSql('CREATE INDEX IDX_A34D68A2727ACA70 ON "footprints" (parent_id)'); + $this->addSql('CREATE INDEX IDX_A34D68A2EA7100A1 ON "footprints" (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_A34D68A232A38C34 ON "footprints" (id_footprint_3d)'); + $this->addSql('CREATE INDEX footprint_idx_name ON "footprints" (name)'); + $this->addSql('CREATE INDEX footprint_idx_parent_name ON "footprints" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__parts AS SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated FROM "parts"'); + $this->addSql('DROP TABLE "parts"'); + $this->addSql('CREATE TABLE "parts" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_category INTEGER NOT NULL, id_footprint INTEGER DEFAULT NULL, id_part_unit INTEGER DEFAULT NULL, id_manufacturer INTEGER DEFAULT NULL, order_orderdetails_id INTEGER DEFAULT NULL, built_project_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, needs_review BOOLEAN NOT NULL, tags CLOB NOT NULL, mass DOUBLE PRECISION DEFAULT NULL, ipn VARCHAR(100) DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, visible BOOLEAN NOT NULL, favorite BOOLEAN NOT NULL, minamount DOUBLE PRECISION NOT NULL, manufacturer_product_url CLOB NOT NULL, manufacturer_product_number VARCHAR(255) NOT NULL, manufacturing_status VARCHAR(255) DEFAULT NULL, order_quantity INTEGER NOT NULL, manual_order BOOLEAN NOT NULL, provider_reference_provider_key VARCHAR(255) DEFAULT NULL, provider_reference_provider_id VARCHAR(255) DEFAULT NULL, provider_reference_provider_url VARCHAR(255) DEFAULT NULL, provider_reference_last_updated DATETIME DEFAULT NULL, CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES "orderdetails" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "parts" (id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated) SELECT id, id_preview_attachment, id_category, id_footprint, id_part_unit, id_manufacturer, order_orderdetails_id, built_project_id, name, last_modified, datetime_added, needs_review, tags, mass, ipn, description, comment, visible, favorite, minamount, manufacturer_product_url, manufacturer_product_number, manufacturing_status, order_quantity, manual_order, provider_reference_provider_key, provider_reference_provider_id, provider_reference_provider_url, provider_reference_last_updated FROM __temp__parts'); + $this->addSql('DROP TABLE __temp__parts'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON "parts" (ipn)'); + $this->addSql('CREATE INDEX IDX_6940A7FEEA7100A1 ON "parts" (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_6940A7FE5697F554 ON "parts" (id_category)'); + $this->addSql('CREATE INDEX IDX_6940A7FE7E371A10 ON "parts" (id_footprint)'); + $this->addSql('CREATE INDEX IDX_6940A7FE2626CEF9 ON "parts" (id_part_unit)'); + $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON "parts" (id_manufacturer)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON "parts" (order_orderdetails_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON "parts" (built_project_id)'); + $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON "parts" (datetime_added, name, last_modified, id, needs_review)'); + $this->addSql('CREATE INDEX parts_idx_name ON "parts" (name)'); + $this->addSql('CREATE INDEX parts_idx_ipn ON "parts" (ipn)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20240427222442.php b/migrations/Version20240427222442.php new file mode 100644 index 00000000..92b2733d --- /dev/null +++ b/migrations/Version20240427222442.php @@ -0,0 +1,75 @@ +addSql('ALTER TABLE webauthn_keys ADD backup_eligible TINYINT(1) DEFAULT NULL, ADD backup_status TINYINT(1) DEFAULT NULL, ADD uv_initialized TINYINT(1) DEFAULT NULL, ADD last_time_used DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\''); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE webauthn_keys DROP backup_eligible, DROP backup_status, DROP uv_initialized, DROP last_time_used'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path) + , aaguid CLOB NOT NULL --(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL --(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, other_ui CLOB DEFAULT NULL --(DC2Type:array) + , backup_eligible BOOLEAN DEFAULT NULL, backup_status BOOLEAN DEFAULT NULL, uv_initialized BOOLEAN DEFAULT NULL, last_time_used DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, name, last_modified, datetime_added FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL -- +(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL -- +(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL -- +(DC2Type:trust_path) + , aaguid CLOB NOT NULL -- +(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL -- +(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, other_ui CLOB DEFAULT NULL -- +(DC2Type:array) + , name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, name, last_modified, datetime_added FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->warnIf(true, "Migration not needed for Postgres. Skipping..."); + } +} diff --git a/migrations/Version20240606203053.php b/migrations/Version20240606203053.php new file mode 100644 index 00000000..83370ad6 --- /dev/null +++ b/migrations/Version20240606203053.php @@ -0,0 +1,654 @@ +addSql("CREATE COLLATION numeric (provider = icu, locale = 'en-u-kn-true');"); + + $this->addSql('CREATE TABLE api_tokens (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, valid_until TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, token VARCHAR(68) NOT NULL, level SMALLINT NOT NULL, last_time_used TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2CAD560E5F37A13B ON api_tokens (token)'); + $this->addSql('CREATE INDEX IDX_2CAD560EA76ED395 ON api_tokens (user_id)'); + $this->addSql('CREATE TABLE "attachment_types" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, filetype_filter TEXT NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_EFAED719727ACA70 ON "attachment_types" (parent_id)'); + $this->addSql('CREATE INDEX IDX_EFAED719EA7100A1 ON "attachment_types" (id_preview_attachment)'); + $this->addSql('CREATE INDEX attachment_types_idx_name ON "attachment_types" (name)'); + $this->addSql('CREATE INDEX attachment_types_idx_parent_name ON "attachment_types" (parent_id, name)'); + $this->addSql('CREATE TABLE "attachments" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, original_filename VARCHAR(255) DEFAULT NULL, path VARCHAR(255) NOT NULL, show_in_table BOOLEAN NOT NULL, type_id INT NOT NULL, class_name VARCHAR(255) NOT NULL, element_id INT NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_47C4FAD6C54C8C93 ON "attachments" (type_id)'); + $this->addSql('CREATE INDEX IDX_47C4FAD61F1F2A24 ON "attachments" (element_id)'); + $this->addSql('CREATE INDEX attachments_idx_id_element_id_class_name ON "attachments" (id, element_id, class_name)'); + $this->addSql('CREATE INDEX attachments_idx_class_name_id ON "attachments" (class_name, id)'); + $this->addSql('CREATE INDEX attachment_name_idx ON "attachments" (name)'); + $this->addSql('CREATE INDEX attachment_element_idx ON "attachments" (class_name, element_id)'); + $this->addSql('CREATE TABLE "categories" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, partname_hint TEXT NOT NULL, partname_regex TEXT NOT NULL, disable_footprints BOOLEAN NOT NULL, disable_manufacturers BOOLEAN NOT NULL, disable_autodatasheets BOOLEAN NOT NULL, disable_properties BOOLEAN NOT NULL, default_description TEXT NOT NULL, default_comment TEXT NOT NULL, eda_info_reference_prefix VARCHAR(255) DEFAULT NULL, eda_info_invisible BOOLEAN DEFAULT NULL, eda_info_exclude_from_bom BOOLEAN DEFAULT NULL, eda_info_exclude_from_board BOOLEAN DEFAULT NULL, eda_info_exclude_from_sim BOOLEAN DEFAULT NULL, eda_info_kicad_symbol VARCHAR(255) DEFAULT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_3AF34668727ACA70 ON "categories" (parent_id)'); + $this->addSql('CREATE INDEX IDX_3AF34668EA7100A1 ON "categories" (id_preview_attachment)'); + $this->addSql('CREATE INDEX category_idx_name ON "categories" (name)'); + $this->addSql('CREATE INDEX category_idx_parent_name ON "categories" (parent_id, name)'); + $this->addSql('CREATE TABLE currencies (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL, iso_code VARCHAR(255) NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE TABLE "footprints" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, eda_info_kicad_footprint VARCHAR(255) DEFAULT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, id_footprint_3d INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_A34D68A2727ACA70 ON "footprints" (parent_id)'); + $this->addSql('CREATE INDEX IDX_A34D68A2EA7100A1 ON "footprints" (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_A34D68A232A38C34 ON "footprints" (id_footprint_3d)'); + $this->addSql('CREATE INDEX footprint_idx_name ON "footprints" (name)'); + $this->addSql('CREATE INDEX footprint_idx_parent_name ON "footprints" (parent_id, name)'); + $this->addSql('CREATE TABLE "groups" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, permissions_data JSON NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)'); + $this->addSql('CREATE INDEX group_idx_name ON "groups" (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)'); + $this->addSql('CREATE TABLE label_profiles (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, show_in_dropdown BOOLEAN NOT NULL, options_width DOUBLE PRECISION NOT NULL, options_height DOUBLE PRECISION NOT NULL, options_barcode_type VARCHAR(255) NOT NULL, options_picture_type VARCHAR(255) NOT NULL, options_supported_element VARCHAR(255) NOT NULL, options_additional_css TEXT NOT NULL, options_lines_mode VARCHAR(255) NOT NULL, options_lines TEXT NOT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_C93E9CF5EA7100A1 ON label_profiles (id_preview_attachment)'); + $this->addSql('CREATE TABLE log (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, username VARCHAR(255) NOT NULL, datetime TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, level SMALLINT NOT NULL, target_id INT NOT NULL, target_type SMALLINT NOT NULL, extra JSON NOT NULL, id_user INT DEFAULT NULL, type SMALLINT NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TABLE "manufacturers" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_94565B12727ACA70 ON "manufacturers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_94565B12EA7100A1 ON "manufacturers" (id_preview_attachment)'); + $this->addSql('CREATE INDEX manufacturer_name ON "manufacturers" (name)'); + $this->addSql('CREATE INDEX manufacturer_idx_parent_name ON "manufacturers" (parent_id, name)'); + $this->addSql('CREATE TABLE "measurement_units" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, unit VARCHAR(255) DEFAULT NULL, is_integer BOOLEAN NOT NULL, use_si_prefix BOOLEAN NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_F5AF83CF727ACA70 ON "measurement_units" (parent_id)'); + $this->addSql('CREATE INDEX IDX_F5AF83CFEA7100A1 ON "measurement_units" (id_preview_attachment)'); + $this->addSql('CREATE INDEX unit_idx_name ON "measurement_units" (name)'); + $this->addSql('CREATE INDEX unit_idx_parent_name ON "measurement_units" (parent_id, name)'); + $this->addSql('CREATE TABLE oauth_tokens (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, token TEXT DEFAULT NULL, expires_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, refresh_token TEXT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX oauth_tokens_unique_name ON oauth_tokens (name)'); + $this->addSql('CREATE TABLE "orderdetails" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, supplierpartnr VARCHAR(255) NOT NULL, obsolete BOOLEAN NOT NULL, supplier_product_url TEXT NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, part_id INT NOT NULL, id_supplier INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_489AFCDC4CE34BEC ON "orderdetails" (part_id)'); + $this->addSql('CREATE INDEX IDX_489AFCDCCBF180EB ON "orderdetails" (id_supplier)'); + $this->addSql('CREATE INDEX orderdetails_supplier_part_nr ON "orderdetails" (supplierpartnr)'); + $this->addSql('CREATE TABLE parameters (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, symbol VARCHAR(255) NOT NULL, value_min DOUBLE PRECISION DEFAULT NULL, value_typical DOUBLE PRECISION DEFAULT NULL, value_max DOUBLE PRECISION DEFAULT NULL, unit VARCHAR(255) NOT NULL, value_text VARCHAR(255) NOT NULL, param_group VARCHAR(255) NOT NULL, type SMALLINT NOT NULL, element_id INT NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_69348FE1F1F2A24 ON parameters (element_id)'); + $this->addSql('CREATE INDEX parameter_name_idx ON parameters (name)'); + $this->addSql('CREATE INDEX parameter_group_idx ON parameters (param_group)'); + $this->addSql('CREATE INDEX parameter_type_element_idx ON parameters (type, element_id)'); + $this->addSql('CREATE TABLE part_association (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, type SMALLINT NOT NULL, other_type VARCHAR(255) DEFAULT NULL, comment TEXT DEFAULT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, owner_id INT NOT NULL, other_id INT NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_61B952E07E3C61F9 ON part_association (owner_id)'); + $this->addSql('CREATE INDEX IDX_61B952E0998D9879 ON part_association (other_id)'); + $this->addSql('CREATE TABLE part_lots (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, description TEXT NOT NULL, comment TEXT NOT NULL, expiration_date TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, instock_unknown BOOLEAN NOT NULL, amount DOUBLE PRECISION NOT NULL, needs_refill BOOLEAN NOT NULL, vendor_barcode VARCHAR(255) DEFAULT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, id_store_location INT DEFAULT NULL, id_part INT NOT NULL, id_owner INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_EBC8F9435D8F4B37 ON part_lots (id_store_location)'); + $this->addSql('CREATE INDEX IDX_EBC8F943C22F6CC4 ON part_lots (id_part)'); + $this->addSql('CREATE INDEX IDX_EBC8F94321E5A74C ON part_lots (id_owner)'); + $this->addSql('CREATE INDEX part_lots_idx_instock_un_expiration_id_part ON part_lots (instock_unknown, expiration_date, id_part)'); + $this->addSql('CREATE INDEX part_lots_idx_needs_refill ON part_lots (needs_refill)'); + $this->addSql('CREATE INDEX part_lots_idx_barcode ON part_lots (vendor_barcode)'); + $this->addSql('CREATE TABLE "parts" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, needs_review BOOLEAN NOT NULL, tags TEXT NOT NULL, mass DOUBLE PRECISION DEFAULT NULL, ipn VARCHAR(100) DEFAULT NULL, description TEXT NOT NULL, comment TEXT NOT NULL, visible BOOLEAN NOT NULL, favorite BOOLEAN NOT NULL, minamount DOUBLE PRECISION NOT NULL, manufacturer_product_url TEXT NOT NULL, manufacturer_product_number VARCHAR(255) NOT NULL, manufacturing_status VARCHAR(255) DEFAULT NULL, order_quantity INT NOT NULL, manual_order BOOLEAN NOT NULL, provider_reference_provider_key VARCHAR(255) DEFAULT NULL, provider_reference_provider_id VARCHAR(255) DEFAULT NULL, provider_reference_provider_url VARCHAR(255) DEFAULT NULL, provider_reference_last_updated TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, eda_info_reference_prefix VARCHAR(255) DEFAULT NULL, eda_info_value VARCHAR(255) DEFAULT NULL, eda_info_invisible BOOLEAN DEFAULT NULL, eda_info_exclude_from_bom BOOLEAN DEFAULT NULL, eda_info_exclude_from_board BOOLEAN DEFAULT NULL, eda_info_exclude_from_sim BOOLEAN DEFAULT NULL, eda_info_kicad_symbol VARCHAR(255) DEFAULT NULL, eda_info_kicad_footprint VARCHAR(255) DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, id_category INT NOT NULL, id_footprint INT DEFAULT NULL, id_part_unit INT DEFAULT NULL, id_manufacturer INT DEFAULT NULL, order_orderdetails_id INT DEFAULT NULL, built_project_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON "parts" (ipn)'); + $this->addSql('CREATE INDEX IDX_6940A7FEEA7100A1 ON "parts" (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_6940A7FE5697F554 ON "parts" (id_category)'); + $this->addSql('CREATE INDEX IDX_6940A7FE7E371A10 ON "parts" (id_footprint)'); + $this->addSql('CREATE INDEX IDX_6940A7FE2626CEF9 ON "parts" (id_part_unit)'); + $this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON "parts" (id_manufacturer)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON "parts" (order_orderdetails_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON "parts" (built_project_id)'); + $this->addSql('CREATE INDEX parts_idx_datet_name_last_id_needs ON "parts" (datetime_added, name, last_modified, id, needs_review)'); + $this->addSql('CREATE INDEX parts_idx_name ON "parts" (name)'); + $this->addSql('CREATE INDEX parts_idx_ipn ON "parts" (ipn)'); + $this->addSql('CREATE TABLE "pricedetails" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, price NUMERIC(11, 5) NOT NULL, price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, id_currency INT DEFAULT NULL, orderdetails_id INT NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TABLE project_bom_entries (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames TEXT NOT NULL, name VARCHAR(255) DEFAULT NULL, comment TEXT NOT NULL, price NUMERIC(11, 5) DEFAULT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, id_device INT DEFAULT NULL, id_part INT DEFAULT NULL, price_currency_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TABLE projects (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, order_quantity INT NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts BOOLEAN NOT NULL, description TEXT NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)'); + $this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)'); + $this->addSql('CREATE TABLE "storelocations" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, is_full BOOLEAN NOT NULL, only_single_part BOOLEAN NOT NULL, limit_to_existing_parts BOOLEAN NOT NULL, part_owner_must_match BOOLEAN DEFAULT false NOT NULL, parent_id INT DEFAULT NULL, storage_type_id INT DEFAULT NULL, id_owner INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_7517020727ACA70 ON "storelocations" (parent_id)'); + $this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON "storelocations" (storage_type_id)'); + $this->addSql('CREATE INDEX IDX_751702021E5A74C ON "storelocations" (id_owner)'); + $this->addSql('CREATE INDEX IDX_7517020EA7100A1 ON "storelocations" (id_preview_attachment)'); + $this->addSql('CREATE INDEX location_idx_name ON "storelocations" (name)'); + $this->addSql('CREATE INDEX location_idx_parent_name ON "storelocations" (parent_id, name)'); + $this->addSql('CREATE TABLE "suppliers" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, comment TEXT NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names TEXT DEFAULT NULL, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL, parent_id INT DEFAULT NULL, default_currency_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)'); + $this->addSql('CREATE TABLE u2f_keys (key_handle VARCHAR(128) NOT NULL, public_key VARCHAR(255) NOT NULL, certificate TEXT NOT NULL, counter VARCHAR(255) NOT NULL, id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_4F4ADB4BA76ED395 ON u2f_keys (user_id)'); + $this->addSql('CREATE UNIQUE INDEX user_unique ON u2f_keys (user_id, key_handle)'); + $this->addSql('CREATE TABLE "users" (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a TEXT NOT NULL, config_instock_comment_w TEXT NOT NULL, about_me TEXT NOT NULL, trusted_device_cookie_version INT NOT NULL, backup_codes JSON NOT NULL, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, show_email_on_profile BOOLEAN DEFAULT false NOT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, settings JSON NOT NULL, backup_codes_generation_date TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, pw_reset_expires TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, saml_user BOOLEAN NOT NULL, name VARCHAR(180) NOT NULL, permissions_data JSON NOT NULL, group_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, currency_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + $this->addSql('CREATE TABLE webauthn_keys (public_key_credential_id TEXT NOT NULL, type VARCHAR(255) NOT NULL, transports TEXT NOT NULL, attestation_type VARCHAR(255) NOT NULL, trust_path JSON NOT NULL, aaguid TEXT NOT NULL, credential_public_key TEXT NOT NULL, user_handle VARCHAR(255) NOT NULL, counter INT NOT NULL, other_ui TEXT DEFAULT NULL, backup_eligible BOOLEAN DEFAULT NULL, backup_status BOOLEAN DEFAULT NULL, uv_initialized BOOLEAN DEFAULT NULL, id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, last_time_used TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + $this->addSql('ALTER TABLE api_tokens ADD CONSTRAINT FK_2CAD560EA76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "attachment_types" ADD CONSTRAINT FK_EFAED719727ACA70 FOREIGN KEY (parent_id) REFERENCES "attachment_types" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "attachment_types" ADD CONSTRAINT FK_EFAED719EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "attachments" ADD CONSTRAINT FK_47C4FAD6C54C8C93 FOREIGN KEY (type_id) REFERENCES "attachment_types" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "categories" ADD CONSTRAINT FK_3AF34668727ACA70 FOREIGN KEY (parent_id) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "categories" ADD CONSTRAINT FK_3AF34668EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE currencies ADD CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE currencies ADD CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "footprints" ADD CONSTRAINT FK_A34D68A2727ACA70 FOREIGN KEY (parent_id) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "footprints" ADD CONSTRAINT FK_A34D68A2EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "footprints" ADD CONSTRAINT FK_A34D68A232A38C34 FOREIGN KEY (id_footprint_3d) REFERENCES "attachments" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "groups" ADD CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "groups" ADD CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE label_profiles ADD CONSTRAINT FK_C93E9CF5EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE log ADD CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "manufacturers" ADD CONSTRAINT FK_94565B12727ACA70 FOREIGN KEY (parent_id) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "manufacturers" ADD CONSTRAINT FK_94565B12EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "measurement_units" ADD CONSTRAINT FK_F5AF83CF727ACA70 FOREIGN KEY (parent_id) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "measurement_units" ADD CONSTRAINT FK_F5AF83CFEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "orderdetails" ADD CONSTRAINT FK_489AFCDC4CE34BEC FOREIGN KEY (part_id) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "orderdetails" ADD CONSTRAINT FK_489AFCDCCBF180EB FOREIGN KEY (id_supplier) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE part_association ADD CONSTRAINT FK_61B952E07E3C61F9 FOREIGN KEY (owner_id) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE part_association ADD CONSTRAINT FK_61B952E0998D9879 FOREIGN KEY (other_id) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F94321E5A74C FOREIGN KEY (id_owner) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "parts" ADD CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "parts" ADD CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "parts" ADD CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "parts" ADD CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "parts" ADD CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "parts" ADD CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES "orderdetails" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "parts" ADD CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "pricedetails" ADD CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "pricedetails" ADD CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE project_bom_entries ADD CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE project_bom_entries ADD CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE project_bom_entries ADD CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE projects ADD CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE projects ADD CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "storelocations" ADD CONSTRAINT FK_7517020727ACA70 FOREIGN KEY (parent_id) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "storelocations" ADD CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "storelocations" ADD CONSTRAINT FK_751702021E5A74C FOREIGN KEY (id_owner) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "storelocations" ADD CONSTRAINT FK_7517020EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "suppliers" ADD CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "suppliers" ADD CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "suppliers" ADD CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE u2f_keys ADD CONSTRAINT FK_4F4ADB4BA76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "users" ADD CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "users" ADD CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "users" ADD CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE webauthn_keys ADD CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + + //Create the initial groups and users + //Retrieve the json representations of the presets + $admin = $this->getJSONPermDataFromPreset(PermissionPresetsHelper::PRESET_ADMIN); + $editor = $this->getJSONPermDataFromPreset(PermissionPresetsHelper::PRESET_EDITOR); + $read_only = $this->getJSONPermDataFromPreset(PermissionPresetsHelper::PRESET_READ_ONLY); + + + $sql = <<addSql($sql); + + //Increase the sequence for the groups, to avoid conflicts later + $this->addSql('SELECT setval(\'groups_id_seq\', 4)'); + + + $admin_pw = $this->getInitalAdminPW(); + + $sql = <<addSql($sql); + + //Increase the sequence for the users, to avoid conflicts later + $this->addSql('SELECT setval(\'users_id_seq\', 3)'); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE api_tokens DROP CONSTRAINT FK_2CAD560EA76ED395'); + $this->addSql('ALTER TABLE "attachment_types" DROP CONSTRAINT FK_EFAED719727ACA70'); + $this->addSql('ALTER TABLE "attachment_types" DROP CONSTRAINT FK_EFAED719EA7100A1'); + $this->addSql('ALTER TABLE "attachments" DROP CONSTRAINT FK_47C4FAD6C54C8C93'); + $this->addSql('ALTER TABLE "categories" DROP CONSTRAINT FK_3AF34668727ACA70'); + $this->addSql('ALTER TABLE "categories" DROP CONSTRAINT FK_3AF34668EA7100A1'); + $this->addSql('ALTER TABLE currencies DROP CONSTRAINT FK_37C44693727ACA70'); + $this->addSql('ALTER TABLE currencies DROP CONSTRAINT FK_37C44693EA7100A1'); + $this->addSql('ALTER TABLE "footprints" DROP CONSTRAINT FK_A34D68A2727ACA70'); + $this->addSql('ALTER TABLE "footprints" DROP CONSTRAINT FK_A34D68A2EA7100A1'); + $this->addSql('ALTER TABLE "footprints" DROP CONSTRAINT FK_A34D68A232A38C34'); + $this->addSql('ALTER TABLE "groups" DROP CONSTRAINT FK_F06D3970727ACA70'); + $this->addSql('ALTER TABLE "groups" DROP CONSTRAINT FK_F06D3970EA7100A1'); + $this->addSql('ALTER TABLE label_profiles DROP CONSTRAINT FK_C93E9CF5EA7100A1'); + $this->addSql('ALTER TABLE log DROP CONSTRAINT FK_8F3F68C56B3CA4B'); + $this->addSql('ALTER TABLE "manufacturers" DROP CONSTRAINT FK_94565B12727ACA70'); + $this->addSql('ALTER TABLE "manufacturers" DROP CONSTRAINT FK_94565B12EA7100A1'); + $this->addSql('ALTER TABLE "measurement_units" DROP CONSTRAINT FK_F5AF83CF727ACA70'); + $this->addSql('ALTER TABLE "measurement_units" DROP CONSTRAINT FK_F5AF83CFEA7100A1'); + $this->addSql('ALTER TABLE "orderdetails" DROP CONSTRAINT FK_489AFCDC4CE34BEC'); + $this->addSql('ALTER TABLE "orderdetails" DROP CONSTRAINT FK_489AFCDCCBF180EB'); + $this->addSql('ALTER TABLE part_association DROP CONSTRAINT FK_61B952E07E3C61F9'); + $this->addSql('ALTER TABLE part_association DROP CONSTRAINT FK_61B952E0998D9879'); + $this->addSql('ALTER TABLE part_lots DROP CONSTRAINT FK_EBC8F9435D8F4B37'); + $this->addSql('ALTER TABLE part_lots DROP CONSTRAINT FK_EBC8F943C22F6CC4'); + $this->addSql('ALTER TABLE part_lots DROP CONSTRAINT FK_EBC8F94321E5A74C'); + $this->addSql('ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FEEA7100A1'); + $this->addSql('ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FE5697F554'); + $this->addSql('ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FE7E371A10'); + $this->addSql('ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FE2626CEF9'); + $this->addSql('ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FE1ECB93AE'); + $this->addSql('ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FE81081E9B'); + $this->addSql('ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FEE8AE70D9'); + $this->addSql('ALTER TABLE "pricedetails" DROP CONSTRAINT FK_C68C4459398D64AA'); + $this->addSql('ALTER TABLE "pricedetails" DROP CONSTRAINT FK_C68C44594A01DDC7'); + $this->addSql('ALTER TABLE project_bom_entries DROP CONSTRAINT FK_1AA2DD312F180363'); + $this->addSql('ALTER TABLE project_bom_entries DROP CONSTRAINT FK_1AA2DD31C22F6CC4'); + $this->addSql('ALTER TABLE project_bom_entries DROP CONSTRAINT FK_1AA2DD313FFDCD60'); + $this->addSql('ALTER TABLE projects DROP CONSTRAINT FK_5C93B3A4727ACA70'); + $this->addSql('ALTER TABLE projects DROP CONSTRAINT FK_5C93B3A4EA7100A1'); + $this->addSql('ALTER TABLE "storelocations" DROP CONSTRAINT FK_7517020727ACA70'); + $this->addSql('ALTER TABLE "storelocations" DROP CONSTRAINT FK_7517020B270BFF1'); + $this->addSql('ALTER TABLE "storelocations" DROP CONSTRAINT FK_751702021E5A74C'); + $this->addSql('ALTER TABLE "storelocations" DROP CONSTRAINT FK_7517020EA7100A1'); + $this->addSql('ALTER TABLE "suppliers" DROP CONSTRAINT FK_AC28B95C727ACA70'); + $this->addSql('ALTER TABLE "suppliers" DROP CONSTRAINT FK_AC28B95CECD792C0'); + $this->addSql('ALTER TABLE "suppliers" DROP CONSTRAINT FK_AC28B95CEA7100A1'); + $this->addSql('ALTER TABLE u2f_keys DROP CONSTRAINT FK_4F4ADB4BA76ED395'); + $this->addSql('ALTER TABLE "users" DROP CONSTRAINT FK_1483A5E9FE54D947'); + $this->addSql('ALTER TABLE "users" DROP CONSTRAINT FK_1483A5E9EA7100A1'); + $this->addSql('ALTER TABLE "users" DROP CONSTRAINT FK_1483A5E938248176'); + $this->addSql('ALTER TABLE webauthn_keys DROP CONSTRAINT FK_799FD143A76ED395'); + $this->addSql('DROP TABLE api_tokens'); + $this->addSql('DROP TABLE "attachment_types"'); + $this->addSql('DROP TABLE "attachments"'); + $this->addSql('DROP TABLE "categories"'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('DROP TABLE "footprints"'); + $this->addSql('DROP TABLE "groups"'); + $this->addSql('DROP TABLE label_profiles'); + $this->addSql('DROP TABLE log'); + $this->addSql('DROP TABLE "manufacturers"'); + $this->addSql('DROP TABLE "measurement_units"'); + $this->addSql('DROP TABLE oauth_tokens'); + $this->addSql('DROP TABLE "orderdetails"'); + $this->addSql('DROP TABLE parameters'); + $this->addSql('DROP TABLE part_association'); + $this->addSql('DROP TABLE part_lots'); + $this->addSql('DROP TABLE "parts"'); + $this->addSql('DROP TABLE "pricedetails"'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('DROP TABLE projects'); + $this->addSql('DROP TABLE "storelocations"'); + $this->addSql('DROP TABLE "suppliers"'); + $this->addSql('DROP TABLE u2f_keys'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('DROP TABLE webauthn_keys'); + } + + public function mySQLUp(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE currencies CHANGE exchange_rate exchange_rate NUMERIC(11, 5) DEFAULT NULL'); + //Set empty JSON fields to "{}" to avoid issues with failing JSON validation + $this->addSql('UPDATE `groups` SET permissions_data = "{}" WHERE permissions_data = ""'); + $this->addSql('ALTER TABLE `groups` CHANGE permissions_data permissions_data JSON NOT NULL'); + //Set the empty JSON fields to "{}" to avoid issues with failing JSON validation + $this->addSql('UPDATE `log` SET extra = "{}" WHERE extra = ""'); + $this->addSql('ALTER TABLE `log` CHANGE level level TINYINT NOT NULL, CHANGE extra extra JSON NOT NULL'); + $this->addSql('ALTER TABLE oauth_tokens CHANGE expires_at expires_at DATETIME DEFAULT NULL'); + $this->addSql('ALTER TABLE pricedetails CHANGE price price NUMERIC(11, 5) NOT NULL'); + $this->addSql('ALTER TABLE project_bom_entries CHANGE price price NUMERIC(11, 5) DEFAULT NULL'); + $this->addSql('ALTER TABLE suppliers CHANGE shipping_costs shipping_costs NUMERIC(11, 5) DEFAULT NULL'); + //Set the empty JSON fields to "{}" to avoid issues with failing JSON validation + $this->addSql('UPDATE `users` SET settings = "{}" WHERE settings = ""'); + $this->addSql('UPDATE `users` SET backup_codes = "{}" WHERE backup_codes = ""'); + $this->addSql('UPDATE `users` SET permissions_data = "{}" WHERE permissions_data = ""'); + $this->addSql('ALTER TABLE `users` CHANGE settings settings JSON NOT NULL, CHANGE backup_codes backup_codes JSON NOT NULL, CHANGE permissions_data permissions_data JSON NOT NULL'); + $this->addSql('ALTER TABLE webauthn_keys CHANGE public_key_credential_id public_key_credential_id LONGTEXT NOT NULL, CHANGE transports transports LONGTEXT NOT NULL, CHANGE trust_path trust_path JSON NOT NULL, CHANGE aaguid aaguid TINYTEXT NOT NULL, CHANGE credential_public_key credential_public_key LONGTEXT NOT NULL, CHANGE other_ui other_ui LONGTEXT DEFAULT NULL, CHANGE last_time_used last_time_used DATETIME DEFAULT NULL'); + + // Add the natural sort emulation function to the database (based on this stackoverflow: https://stackoverflow.com/questions/153633/natural-sort-in-mysql/58154535#58154535) + //This version here is wrong, and will be replaced by the correct version in the next migration (we need to use nowdoc instead of heredoc, otherwise the slashes will be wrongly escaped!!) + $this->addSql(<<0, only the first n numbers in the input string will be converted for nat-sort (so strings that differ only after the first n numbers will not nat-sort amongst themselves). + Total sort-ordering is preserved, i.e. if s1!=s2, then NatSortKey(s1,n)!=NatSortKey(s2,n), for any given n. + Numbers may contain ',' as a thousands separator, and '.' as a decimal point. To reverse these (as appropriate for some European locales), the code would require modification. + Numbers preceded by '+' sort with numbers not preceded with either a '+' or '-' sign. + Negative numbers (preceded with '-') sort before positive numbers, but are sorted in order of ascending absolute value (so -7 sorts BEFORE -1001). + Numbers with leading zeros sort after the same number with no (or fewer) leading zeros. + Decimal-part-only numbers (like .75) are recognised, provided the decimal point is not immediately preceded by either another '.', or by a letter-type character. + Numbers with thousand separators sort after the same number without them. + Thousand separators are only recognised in numbers with no leading zeros that don't immediately follow a ',', and when they format the number correctly. + (When not recognised as a thousand separator, a ',' will instead be treated as separating two distinct numbers). + Version-number-like sequences consisting of 3 or more numbers separated by '.' are treated as distinct entities, and each component number will be nat-sorted. + The entire entity will sort after any number beginning with the first component (so e.g. 10.2.1 sorts after both 10 and 10.995, but before 11) + Note that The first number component in an entity like this is also permitted to contain thousand separators. + + To achieve this, numbers within the input string are prefixed and suffixed according to the following format: + - The number is prefixed by a 2-digit base-36 number representing its length, excluding leading zeros. If there is a decimal point, this length only includes the integer part of the number. + - A 3-character suffix is appended after the number (after the decimals if present). + - The first character is a space, or a '+' sign if the number was preceded by '+'. Any preceding '+' sign is also removed from the front of the number. + - This is followed by a 2-digit base-36 number that encodes the number of leading zeros and whether the number was expressed in comma-separated form (e.g. 1,000,000.25 vs 1000000.25) + - The value of this 2-digit number is: (number of leading zeros)*2 + (1 if comma-separated, 0 otherwise) + - For version number sequences, each component number has the prefix in front of it, and the separating dots are removed. + Then there is a single suffix that consists of a ' ' or '+' character, followed by a pair base-36 digits for each number component in the sequence. + + e.g. here is how some simple sample strings get converted: + 'Foo055' --> 'Foo0255 02' + 'Absolute zero is around -273 centigrade' --> 'Absolute zero is around -03273 00 centigrade' + 'The $1,000,000 prize' --> 'The $071000000 01 prize' + '+99.74 degrees' --> '0299.74+00 degrees' + 'I have 0 apples' --> 'I have 00 02 apples' + '.5 is the same value as 0000.5000' --> '00.5 00 is the same value as 00.5000 08' + 'MariaDB v10.3.0018' --> 'MariaDB v02100130218 000004' + + The restriction to numbers of up to 359 digits comes from the fact that the first character of the base-36 prefix MUST be a decimal digit, and so the highest permitted prefix value is '9Z' or 359 decimal. + The code could be modified to handle longer numbers by increasing the size of (both) the prefix and suffix. + A higher base could also be used (by replacing CONV() with a custom function), provided that the collation you are using sorts the "digits" of the base in the correct order, starting with 0123456789. + However, while the maximum number length may be increased this way, note that the technique this function uses is NOT applicable where strings may contain numbers of unlimited length. + + The function definition does not specify the charset or collation to be used for string-type parameters or variables: The default database charset & collation at the time the function is defined will be used. + This is to make the function code more portable. However, there are some important restrictions: + + - Collation is important here only when comparing (or storing) the output value from this function, but it MUST order the characters " +0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" in that order for the natural sort to work. + This is true for most collations, but not all of them, e.g. in Lithuanian 'Y' comes before 'J' (according to Wikipedia). + To adapt the function to work with such collations, replace CONV() in the function code with a custom function that emits "digits" above 9 that are characters ordered according to the collation in use. + + - For efficiency, the function code uses LENGTH() rather than CHAR_LENGTH() to measure the length of strings that consist only of digits 0-9, '.', and ',' characters. + This works for any single-byte charset, as well as any charset that maps standard ASCII characters to single bytes (such as utf8 or utf8mb4). + If using a charset that maps these characters to multiple bytes (such as, e.g. utf16 or utf32), you MUST replace all instances of LENGTH() in the function definition with CHAR_LENGTH() + + Length of the output: + + Each number converted adds 5 characters (2 prefix + 3 suffix) to the length of the string. n is the maximum count of numbers to convert; + This parameter is provided as a means to limit the maximum output length (to input length + 5*n). + If you do not require the total-ordering property, you could edit the code to use suffixes of 1 character (space or plus) only; this would reduce the maximum output length for any given n. + Since a string of length L has at most ((L+1) DIV 2) individual numbers in it (every 2nd character a digit), for n<=0 the maximum output length is (inputlength + 5*((inputlength+1) DIV 2)) + So for the current input length of 100, the maximum output length is 350. + If changing the input length, the output length must be modified according to the above formula. The DECLARE statements for x,y,r, and suf must also be modified, as the code comments indicate. + ****/ + + DECLARE x,y varchar(1000); # need to be same length as input s + DECLARE r varchar(3500) DEFAULT ''; # return value: needs to be same length as return type + DECLARE suf varchar(1001); # suffix for a number or version string. Must be (((inputlength+1) DIV 2)*2 + 1) chars to support version strings (e.g. '1.2.33.5'), though it's usually just 3 chars. (Max version string e.g. 1.2. ... .5 has ((length of input + 1) DIV 2) numeric components) + DECLARE i,j,k int UNSIGNED; + IF n<=0 THEN SET n := -1; END IF; # n<=0 means "process all numbers" + LOOP + SET i := REGEXP_INSTR(s,'\\d'); # find position of next digit + IF i=0 OR n=0 THEN RETURN CONCAT(r,s); END IF; # no more numbers to process -> we're done + SET n := n-1, suf := ' '; + IF i>1 THEN + IF SUBSTRING(s,i-1,1)='.' AND (i=2 OR SUBSTRING(s,i-2,1) RLIKE '[^.\\p{L}\\p{N}\\p{M}\\x{608}\\x{200C}\\x{200D}\\x{2100}-\\x{214F}\\x{24B6}-\\x{24E9}\\x{1F130}-\\x{1F149}\\x{1F150}-\\x{1F169}\\x{1F170}-\\x{1F189}]') AND (SUBSTRING(s,i) NOT RLIKE '^\\d++\\.\\d') THEN SET i:=i-1; END IF; # Allow decimal number (but not version string) to begin with a '.', provided preceding char is neither another '.', nor a member of the unicode character classes: "Alphabetic", "Letter", "Block=Letterlike Symbols" "Number", "Mark", "Join_Control" + IF i>1 AND SUBSTRING(s,i-1,1)='+' THEN SET suf := '+', j := i-1; ELSE SET j := i; END IF; # move any preceding '+' into the suffix, so equal numbers with and without preceding "+" signs sort together + SET r := CONCAT(r,SUBSTRING(s,1,j-1)); SET s = SUBSTRING(s,i); # add everything before the number to r and strip it from the start of s; preceding '+' is dropped (not included in either r or s) + END IF; + SET x := REGEXP_SUBSTR(s,IF(SUBSTRING(s,1,1) IN ('0','.') OR (SUBSTRING(r,-1)=',' AND suf=' '),'^\\d*+(?:\\.\\d++)*','^(?:[1-9]\\d{0,2}(?:,\\d{3}(?!\\d))++|\\d++)(?:\\.\\d++)*+')); # capture the number + following decimals (including multiple consecutive '.' sequences) + SET s := SUBSTRING(s,CHAR_LENGTH(x)+1); # NOTE: CHAR_LENGTH() can be safely used instead of CHAR_LENGTH() here & below PROVIDED we're using a charset that represents digits, ',' and '.' characters using single bytes (e.g. latin1, utf8) + SET i := INSTR(x,'.'); + IF i=0 THEN SET y := ''; ELSE SET y := SUBSTRING(x,i); SET x := SUBSTRING(x,1,i-1); END IF; # move any following decimals into y + SET i := CHAR_LENGTH(x); + SET x := REPLACE(x,',',''); + SET j := CHAR_LENGTH(x); + SET x := TRIM(LEADING '0' FROM x); # strip leading zeros + SET k := CHAR_LENGTH(x); + SET suf := CONCAT(suf,LPAD(CONV(LEAST((j-k)*2,1294) + IF(i=j,0,1),10,36),2,'0')); # (j-k)*2 + IF(i=j,0,1) = (count of leading zeros)*2 + (1 if there are thousands-separators, 0 otherwise) Note the first term is bounded to <= base-36 'ZY' as it must fit within 2 characters + SET i := LOCATE('.',y,2); + IF i=0 THEN + SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x,y,suf); # k = count of digits in number, bounded to be <= '9Z' base-36 + ELSE # encode a version number (like 3.12.707, etc) + SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x); # k = count of digits in number, bounded to be <= '9Z' base-36 + WHILE CHAR_LENGTH(y)>0 AND n!=0 DO + IF i=0 THEN SET x := SUBSTRING(y,2); SET y := ''; ELSE SET x := SUBSTRING(y,2,i-2); SET y := SUBSTRING(y,i); SET i := LOCATE('.',y,2); END IF; + SET j := CHAR_LENGTH(x); + SET x := TRIM(LEADING '0' FROM x); # strip leading zeros + SET k := CHAR_LENGTH(x); + SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x); # k = count of digits in number, bounded to be <= '9Z' base-36 + SET suf := CONCAT(suf,LPAD(CONV(LEAST((j-k)*2,1294),10,36),2,'0')); # (j-k)*2 = (count of leading zeros)*2, bounded to fit within 2 base-36 digits + SET n := n-1; + END WHILE; + SET r := CONCAT(r,y,suf); + END IF; + END LOOP; + END + EOD + ); + + } + + public function mySQLDown(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE currencies CHANGE exchange_rate exchange_rate NUMERIC(11, 5) DEFAULT NULL COMMENT \'(DC2Type:big_decimal)\''); + $this->addSql('ALTER TABLE `groups` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE log CHANGE level level TINYINT(1) NOT NULL COMMENT \'(DC2Type:tinyint)\', CHANGE extra extra LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE oauth_tokens CHANGE expires_at expires_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE `pricedetails` CHANGE price price NUMERIC(11, 5) NOT NULL COMMENT \'(DC2Type:big_decimal)\''); + $this->addSql('ALTER TABLE project_bom_entries CHANGE price price NUMERIC(11, 5) DEFAULT NULL COMMENT \'(DC2Type:big_decimal)\''); + $this->addSql('ALTER TABLE `suppliers` CHANGE shipping_costs shipping_costs NUMERIC(11, 5) DEFAULT NULL COMMENT \'(DC2Type:big_decimal)\''); + $this->addSql('ALTER TABLE `users` CHANGE backup_codes backup_codes LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', CHANGE settings settings LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE webauthn_keys CHANGE public_key_credential_id public_key_credential_id LONGTEXT NOT NULL COMMENT \'(DC2Type:base64)\', CHANGE transports transports LONGTEXT NOT NULL COMMENT \'(DC2Type:array)\', CHANGE trust_path trust_path LONGTEXT NOT NULL COMMENT \'(DC2Type:trust_path)\', CHANGE aaguid aaguid TINYTEXT NOT NULL COMMENT \'(DC2Type:aaguid)\', CHANGE credential_public_key credential_public_key LONGTEXT NOT NULL COMMENT \'(DC2Type:base64)\', CHANGE other_ui other_ui LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\', CHANGE last_time_used last_time_used DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\''); + + //Drop custom function + $this->addSql('DROP FUNCTION IF EXISTS NatSortKey'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added, alternative_names FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL, iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, alternative_names CLOB DEFAULT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added, alternative_names) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added, alternative_names FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data, alternative_names FROM groups'); + $this->addSql('DROP TABLE groups'); + $this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL, alternative_names CLOB DEFAULT NULL, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data, alternative_names) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data, alternative_names FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)'); + $this->addSql('CREATE INDEX group_idx_name ON groups (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level SMALLINT NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL, type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__oauth_tokens AS SELECT id, token, expires_at, refresh_token, name, last_modified, datetime_added FROM oauth_tokens'); + $this->addSql('DROP TABLE oauth_tokens'); + $this->addSql('CREATE TABLE oauth_tokens (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, token CLOB DEFAULT NULL, expires_at DATETIME DEFAULT NULL, refresh_token CLOB DEFAULT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL)'); + $this->addSql('INSERT INTO oauth_tokens (id, token, expires_at, refresh_token, name, last_modified, datetime_added) SELECT id, token, expires_at, refresh_token, name, last_modified, datetime_added FROM __temp__oauth_tokens'); + $this->addSql('DROP TABLE __temp__oauth_tokens'); + $this->addSql('CREATE UNIQUE INDEX oauth_tokens_unique_name ON oauth_tokens (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails'); + $this->addSql('DROP TABLE pricedetails'); + $this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL, price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added, alternative_names FROM suppliers'); + $this->addSql('DROP TABLE suppliers'); + $this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, alternative_names CLOB DEFAULT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added, alternative_names) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added, alternative_names FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)'); + $this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM users'); + $this->addSql('DROP TABLE users'); + $this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL, saml_user BOOLEAN NOT NULL, about_me CLOB NOT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)'); + $this->addSql('CREATE INDEX user_idx_username ON users (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui, backup_eligible, backup_status, uv_initialized, last_time_used FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL, type VARCHAR(255) NOT NULL, transports CLOB NOT NULL, attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL, aaguid CLOB NOT NULL, credential_public_key CLOB NOT NULL, user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, other_ui CLOB DEFAULT NULL, backup_eligible BOOLEAN DEFAULT NULL, backup_status BOOLEAN DEFAULT NULL, uv_initialized BOOLEAN DEFAULT NULL, last_time_used DATETIME DEFAULT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui, backup_eligible, backup_status, uv_initialized, last_time_used) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added, other_ui, backup_eligible, backup_status, uv_initialized, last_time_used FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, exchange_rate, iso_code, parent_id, id_preview_attachment FROM currencies'); + $this->addSql('DROP TABLE currencies'); + $this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names CLOB DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , iso_code VARCHAR(255) NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO currencies (id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, exchange_rate, iso_code, parent_id, id_preview_attachment) SELECT id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, exchange_rate, iso_code, parent_id, id_preview_attachment FROM __temp__currencies'); + $this->addSql('DROP TABLE __temp__currencies'); + $this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)'); + $this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)'); + $this->addSql('CREATE INDEX currency_idx_name ON currencies (name)'); + $this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, enforce_2fa, permissions_data, parent_id, id_preview_attachment FROM "groups"'); + $this->addSql('DROP TABLE "groups"'); + $this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names CLOB DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, permissions_data CLOB NOT NULL --(DC2Type:json) + , parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "groups" (id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, enforce_2fa, permissions_data, parent_id, id_preview_attachment) SELECT id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, enforce_2fa, permissions_data, parent_id, id_preview_attachment FROM __temp__groups'); + $this->addSql('DROP TABLE __temp__groups'); + $this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)'); + $this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)'); + $this->addSql('CREATE INDEX group_idx_name ON "groups" (name)'); + $this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, username, datetime, level, target_id, target_type, extra, id_user, type FROM log'); + $this->addSql('DROP TABLE log'); + $this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level BOOLEAN NOT NULL --(DC2Type:tinyint) + , target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json) + , id_user INTEGER DEFAULT NULL, type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO log (id, username, datetime, level, target_id, target_type, extra, id_user, type) SELECT id, username, datetime, level, target_id, target_type, extra, id_user, type FROM __temp__log'); + $this->addSql('DROP TABLE __temp__log'); + $this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)'); + $this->addSql('CREATE INDEX log_idx_type ON log (type)'); + $this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)'); + $this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__oauth_tokens AS SELECT id, name, last_modified, datetime_added, token, expires_at, refresh_token FROM oauth_tokens'); + $this->addSql('DROP TABLE oauth_tokens'); + $this->addSql('CREATE TABLE oauth_tokens (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, token CLOB DEFAULT NULL, expires_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , refresh_token CLOB DEFAULT NULL)'); + $this->addSql('INSERT INTO oauth_tokens (id, name, last_modified, datetime_added, token, expires_at, refresh_token) SELECT id, name, last_modified, datetime_added, token, expires_at, refresh_token FROM __temp__oauth_tokens'); + $this->addSql('DROP TABLE __temp__oauth_tokens'); + $this->addSql('CREATE UNIQUE INDEX oauth_tokens_unique_name ON oauth_tokens (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added, id_currency, orderdetails_id FROM "pricedetails"'); + $this->addSql('DROP TABLE "pricedetails"'); + $this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal) + , price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "pricedetails" (id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added, id_currency, orderdetails_id) SELECT id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added, id_currency, orderdetails_id FROM __temp__pricedetails'); + $this->addSql('DROP TABLE __temp__pricedetails'); + $this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)'); + $this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)'); + $this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, quantity, mountnames, name, comment, price, last_modified, datetime_added, id_device, id_part, price_currency_id FROM project_bom_entries'); + $this->addSql('DROP TABLE project_bom_entries'); + $this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO project_bom_entries (id, quantity, mountnames, name, comment, price, last_modified, datetime_added, id_device, id_part, price_currency_id) SELECT id, quantity, mountnames, name, comment, price, last_modified, datetime_added, id_device, id_part, price_currency_id FROM __temp__project_bom_entries'); + $this->addSql('DROP TABLE __temp__project_bom_entries'); + $this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)'); + $this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)'); + $this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, address, phone_number, fax_number, email_address, website, auto_product_url, shipping_costs, parent_id, default_currency_id, id_preview_attachment FROM "suppliers"'); + $this->addSql('DROP TABLE "suppliers"'); + $this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, alternative_names CLOB DEFAULT NULL, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal) + , parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "suppliers" (id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, address, phone_number, fax_number, email_address, website, auto_product_url, shipping_costs, parent_id, default_currency_id, id_preview_attachment) SELECT id, name, last_modified, datetime_added, comment, not_selectable, alternative_names, address, phone_number, fax_number, email_address, website, auto_product_url, shipping_costs, parent_id, default_currency_id, id_preview_attachment FROM __temp__suppliers'); + $this->addSql('DROP TABLE __temp__suppliers'); + $this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)'); + $this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)'); + $this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)'); + $this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, last_modified, datetime_added, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, settings, backup_codes_generation_date, pw_reset_expires, saml_user, name, permissions_data, group_id, id_preview_attachment, currency_id FROM "users"'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, settings CLOB NOT NULL --(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, name VARCHAR(180) NOT NULL, permissions_data CLOB NOT NULL --(DC2Type:json) + , group_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "users" (id, last_modified, datetime_added, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, settings, backup_codes_generation_date, pw_reset_expires, saml_user, name, permissions_data, group_id, id_preview_attachment, currency_id) SELECT id, last_modified, datetime_added, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, settings, backup_codes_generation_date, pw_reset_expires, saml_user, name, permissions_data, group_id, id_preview_attachment, currency_id FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, backup_eligible, backup_status, uv_initialized, id, name, last_time_used, last_modified, datetime_added, user_id FROM webauthn_keys'); + $this->addSql('DROP TABLE webauthn_keys'); + $this->addSql('CREATE TABLE webauthn_keys (public_key_credential_id CLOB NOT NULL --(DC2Type:base64) + , type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array) + , attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path) + , aaguid CLOB NOT NULL --(DC2Type:aaguid) + , credential_public_key CLOB NOT NULL --(DC2Type:base64) + , user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, other_ui CLOB DEFAULT NULL --(DC2Type:array) + , backup_eligible BOOLEAN DEFAULT NULL, backup_status BOOLEAN DEFAULT NULL, uv_initialized BOOLEAN DEFAULT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, last_time_used DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, user_id INTEGER DEFAULT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO webauthn_keys (public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, backup_eligible, backup_status, uv_initialized, id, name, last_time_used, last_modified, datetime_added, user_id) SELECT public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, other_ui, backup_eligible, backup_status, uv_initialized, id, name, last_time_used, last_modified, datetime_added, user_id FROM __temp__webauthn_keys'); + $this->addSql('DROP TABLE __temp__webauthn_keys'); + $this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)'); + } +} diff --git a/migrations/Version20240728145604.php b/migrations/Version20240728145604.php new file mode 100644 index 00000000..42309d70 --- /dev/null +++ b/migrations/Version20240728145604.php @@ -0,0 +1,165 @@ +addSql('DROP FUNCTION IF EXISTS NatSortKey'); + + //The difference to the original function is the correct length of the suf variable and correct escaping + //We now use heredoc syntax to avoid escaping issues with the \ (which resulted in "range out of order in character class"). + $this->addSql(<<<'EOD' + CREATE DEFINER=CURRENT_USER FUNCTION `NatSortKey`(`s` VARCHAR(1000) CHARSET utf8mb4, `n` INT) RETURNS varchar(3500) CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci + DETERMINISTIC + SQL SECURITY INVOKER + BEGIN + /**** + Converts numbers in the input string s into a format such that sorting results in a nat-sort. + Numbers of up to 359 digits (before the decimal point, if one is present) are supported. Sort results are undefined if the input string contains numbers longer than this. + For n>0, only the first n numbers in the input string will be converted for nat-sort (so strings that differ only after the first n numbers will not nat-sort amongst themselves). + Total sort-ordering is preserved, i.e. if s1!=s2, then NatSortKey(s1,n)!=NatSortKey(s2,n), for any given n. + Numbers may contain ',' as a thousands separator, and '.' as a decimal point. To reverse these (as appropriate for some European locales), the code would require modification. + Numbers preceded by '+' sort with numbers not preceded with either a '+' or '-' sign. + Negative numbers (preceded with '-') sort before positive numbers, but are sorted in order of ascending absolute value (so -7 sorts BEFORE -1001). + Numbers with leading zeros sort after the same number with no (or fewer) leading zeros. + Decimal-part-only numbers (like .75) are recognised, provided the decimal point is not immediately preceded by either another '.', or by a letter-type character. + Numbers with thousand separators sort after the same number without them. + Thousand separators are only recognised in numbers with no leading zeros that don't immediately follow a ',', and when they format the number correctly. + (When not recognised as a thousand separator, a ',' will instead be treated as separating two distinct numbers). + Version-number-like sequences consisting of 3 or more numbers separated by '.' are treated as distinct entities, and each component number will be nat-sorted. + The entire entity will sort after any number beginning with the first component (so e.g. 10.2.1 sorts after both 10 and 10.995, but before 11) + Note that The first number component in an entity like this is also permitted to contain thousand separators. + + To achieve this, numbers within the input string are prefixed and suffixed according to the following format: + - The number is prefixed by a 2-digit base-36 number representing its length, excluding leading zeros. If there is a decimal point, this length only includes the integer part of the number. + - A 3-character suffix is appended after the number (after the decimals if present). + - The first character is a space, or a '+' sign if the number was preceded by '+'. Any preceding '+' sign is also removed from the front of the number. + - This is followed by a 2-digit base-36 number that encodes the number of leading zeros and whether the number was expressed in comma-separated form (e.g. 1,000,000.25 vs 1000000.25) + - The value of this 2-digit number is: (number of leading zeros)*2 + (1 if comma-separated, 0 otherwise) + - For version number sequences, each component number has the prefix in front of it, and the separating dots are removed. + Then there is a single suffix that consists of a ' ' or '+' character, followed by a pair base-36 digits for each number component in the sequence. + + e.g. here is how some simple sample strings get converted: + 'Foo055' --> 'Foo0255 02' + 'Absolute zero is around -273 centigrade' --> 'Absolute zero is around -03273 00 centigrade' + 'The $1,000,000 prize' --> 'The $071000000 01 prize' + '+99.74 degrees' --> '0299.74+00 degrees' + 'I have 0 apples' --> 'I have 00 02 apples' + '.5 is the same value as 0000.5000' --> '00.5 00 is the same value as 00.5000 08' + 'MariaDB v10.3.0018' --> 'MariaDB v02100130218 000004' + + The restriction to numbers of up to 359 digits comes from the fact that the first character of the base-36 prefix MUST be a decimal digit, and so the highest permitted prefix value is '9Z' or 359 decimal. + The code could be modified to handle longer numbers by increasing the size of (both) the prefix and suffix. + A higher base could also be used (by replacing CONV() with a custom function), provided that the collation you are using sorts the "digits" of the base in the correct order, starting with 0123456789. + However, while the maximum number length may be increased this way, note that the technique this function uses is NOT applicable where strings may contain numbers of unlimited length. + + The function definition does not specify the charset or collation to be used for string-type parameters or variables: The default database charset & collation at the time the function is defined will be used. + This is to make the function code more portable. However, there are some important restrictions: + + - Collation is important here only when comparing (or storing) the output value from this function, but it MUST order the characters " +0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" in that order for the natural sort to work. + This is true for most collations, but not all of them, e.g. in Lithuanian 'Y' comes before 'J' (according to Wikipedia). + To adapt the function to work with such collations, replace CONV() in the function code with a custom function that emits "digits" above 9 that are characters ordered according to the collation in use. + + - For efficiency, the function code uses LENGTH() rather than CHAR_LENGTH() to measure the length of strings that consist only of digits 0-9, '.', and ',' characters. + This works for any single-byte charset, as well as any charset that maps standard ASCII characters to single bytes (such as utf8 or utf8mb4). + If using a charset that maps these characters to multiple bytes (such as, e.g. utf16 or utf32), you MUST replace all instances of LENGTH() in the function definition with CHAR_LENGTH() + + Length of the output: + + Each number converted adds 5 characters (2 prefix + 3 suffix) to the length of the string. n is the maximum count of numbers to convert; + This parameter is provided as a means to limit the maximum output length (to input length + 5*n). + If you do not require the total-ordering property, you could edit the code to use suffixes of 1 character (space or plus) only; this would reduce the maximum output length for any given n. + Since a string of length L has at most ((L+1) DIV 2) individual numbers in it (every 2nd character a digit), for n<=0 the maximum output length is (inputlength + 5*((inputlength+1) DIV 2)) + So for the current input length of 100, the maximum output length is 350. + If changing the input length, the output length must be modified according to the above formula. The DECLARE statements for x,y,r, and suf must also be modified, as the code comments indicate. + ****/ + + DECLARE x,y varchar(1000); # need to be same length as input s + DECLARE r varchar(3500) DEFAULT ''; # return value: needs to be same length as return type + DECLARE suf varchar(1001); # suffix for a number or version string. Must be (((inputlength+1) DIV 2)*2 + 1) chars to support version strings (e.g. '1.2.33.5'), though it's usually just 3 chars. (Max version string e.g. 1.2. ... .5 has ((length of input + 1) DIV 2) numeric components) + DECLARE i,j,k int UNSIGNED; + IF n<=0 THEN SET n := -1; END IF; # n<=0 means "process all numbers" + LOOP + SET i := REGEXP_INSTR(s,'\\d'); # find position of next digit + IF i=0 OR n=0 THEN RETURN CONCAT(r,s); END IF; # no more numbers to process -> we're done + SET n := n-1, suf := ' '; + IF i>1 THEN + IF SUBSTRING(s,i-1,1)='.' AND (i=2 OR SUBSTRING(s,i-2,1) RLIKE '[^.\\p{L}\\p{N}\\p{M}\\x{608}\\x{200C}\\x{200D}\\x{2100}-\\x{214F}\\x{24B6}-\\x{24E9}\\x{1F130}-\\x{1F149}\\x{1F150}-\\x{1F169}\\x{1F170}-\\x{1F189}]') AND (SUBSTRING(s,i) NOT RLIKE '^\\d++\\.\\d') THEN SET i:=i-1; END IF; # Allow decimal number (but not version string) to begin with a '.', provided preceding char is neither another '.', nor a member of the unicode character classes: "Alphabetic", "Letter", "Block=Letterlike Symbols" "Number", "Mark", "Join_Control" + IF i>1 AND SUBSTRING(s,i-1,1)='+' THEN SET suf := '+', j := i-1; ELSE SET j := i; END IF; # move any preceding '+' into the suffix, so equal numbers with and without preceding "+" signs sort together + SET r := CONCAT(r,SUBSTRING(s,1,j-1)); SET s = SUBSTRING(s,i); # add everything before the number to r and strip it from the start of s; preceding '+' is dropped (not included in either r or s) + END IF; + SET x := REGEXP_SUBSTR(s,IF(SUBSTRING(s,1,1) IN ('0','.') OR (SUBSTRING(r,-1)=',' AND suf=' '),'^\\d*+(?:\\.\\d++)*','^(?:[1-9]\\d{0,2}(?:,\\d{3}(?!\\d))++|\\d++)(?:\\.\\d++)*+')); # capture the number + following decimals (including multiple consecutive '.' sequences) + SET s := SUBSTRING(s,CHAR_LENGTH(x)+1); # NOTE: CHAR_LENGTH() can be safely used instead of CHAR_LENGTH() here & below PROVIDED we're using a charset that represents digits, ',' and '.' characters using single bytes (e.g. latin1, utf8) + SET i := INSTR(x,'.'); + IF i=0 THEN SET y := ''; ELSE SET y := SUBSTRING(x,i); SET x := SUBSTRING(x,1,i-1); END IF; # move any following decimals into y + SET i := CHAR_LENGTH(x); + SET x := REPLACE(x,',',''); + SET j := CHAR_LENGTH(x); + SET x := TRIM(LEADING '0' FROM x); # strip leading zeros + SET k := CHAR_LENGTH(x); + SET suf := CONCAT(suf,LPAD(CONV(LEAST((j-k)*2,1294) + IF(i=j,0,1),10,36),2,'0')); # (j-k)*2 + IF(i=j,0,1) = (count of leading zeros)*2 + (1 if there are thousands-separators, 0 otherwise) Note the first term is bounded to <= base-36 'ZY' as it must fit within 2 characters + SET i := LOCATE('.',y,2); + IF i=0 THEN + SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x,y,suf); # k = count of digits in number, bounded to be <= '9Z' base-36 + ELSE # encode a version number (like 3.12.707, etc) + SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x); # k = count of digits in number, bounded to be <= '9Z' base-36 + WHILE CHAR_LENGTH(y)>0 AND n!=0 DO + IF i=0 THEN SET x := SUBSTRING(y,2); SET y := ''; ELSE SET x := SUBSTRING(y,2,i-2); SET y := SUBSTRING(y,i); SET i := LOCATE('.',y,2); END IF; + SET j := CHAR_LENGTH(x); + SET x := TRIM(LEADING '0' FROM x); # strip leading zeros + SET k := CHAR_LENGTH(x); + SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x); # k = count of digits in number, bounded to be <= '9Z' base-36 + SET suf := CONCAT(suf,LPAD(CONV(LEAST((j-k)*2,1294),10,36),2,'0')); # (j-k)*2 = (count of leading zeros)*2, bounded to fit within 2 base-36 digits + SET n := n-1; + END WHILE; + SET r := CONCAT(r,y,suf); + END IF; + END LOOP; + END + EOD + ); + } + + public function mySQLDown(Schema $schema): void + { + //Not needed + } + + public function sqLiteUp(Schema $schema): void + { + //Not needed + } + + public function sqLiteDown(Schema $schema): void + { + //Not needed + } + + public function postgreSQLUp(Schema $schema): void + { + //Not needed + } + + public function postgreSQLDown(Schema $schema): void + { + //Not needed + } +} diff --git a/migrations/Version20250220215048.php b/migrations/Version20250220215048.php new file mode 100644 index 00000000..90a73eb1 --- /dev/null +++ b/migrations/Version20250220215048.php @@ -0,0 +1,42 @@ +addSql('ALTER TABLE attachments ADD internal_path VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE attachments ADD external_path VARCHAR(255) DEFAULT NULL'); + + //Copy the data from path to external_path and remove the path column + $this->addSql('UPDATE attachments SET external_path=path'); + $this->addSql('ALTER TABLE attachments DROP COLUMN path'); + + + $this->addSql('UPDATE attachments SET internal_path=external_path WHERE external_path LIKE \'#%MEDIA#%%\' ESCAPE \'#\''); + $this->addSql('UPDATE attachments SET internal_path=external_path WHERE external_path LIKE \'#%BASE#%%\' ESCAPE \'#\''); + $this->addSql('UPDATE attachments SET internal_path=external_path WHERE external_path LIKE \'#%SECURE#%%\' ESCAPE \'#\''); + $this->addSql('UPDATE attachments SET internal_path=external_path WHERE external_path LIKE \'#%FOOTPRINTS#%%\' ESCAPE \'#\''); + $this->addSql('UPDATE attachments SET internal_path=external_path WHERE external_path LIKE \'#%FOOTPRINTS3D#%%\' ESCAPE \'#\''); + $this->addSql('UPDATE attachments SET external_path=NULL WHERE internal_path IS NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('UPDATE attachments SET external_path=internal_path WHERE internal_path IS NOT NULL'); + $this->addSql('ALTER TABLE attachments DROP COLUMN internal_path'); + $this->addSql('ALTER TABLE attachments RENAME COLUMN external_path TO path'); + } +} diff --git a/migrations/Version20250222165240.php b/migrations/Version20250222165240.php new file mode 100644 index 00000000..57cd3970 --- /dev/null +++ b/migrations/Version20250222165240.php @@ -0,0 +1,31 @@ +addSql("UPDATE attachments SET class_name = 'Part' WHERE class_name = 'PartDB\Part'"); + $this->addSql("UPDATE attachments SET class_name = 'Device' WHERE class_name = 'PartDB\Device'"); + } + + public function down(Schema $schema): void + { + //No down required, as the new format can also be read by older Part-DB version + } +} diff --git a/package.json b/package.json index ca99967e..38656c72 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,21 @@ "@babel/preset-env": "^7.19.4", "@fortawesome/fontawesome-free": "^6.1.1", "@hotwired/stimulus": "^3.0.0", - "@hotwired/turbo": "^7.0.1", + "@hotwired/turbo": "^8.0.1", "@popperjs/core": "^2.10.2", "@symfony/stimulus-bridge": "^3.2.0", + "@symfony/ux-translator": "file:vendor/symfony/ux-translator/assets", "@symfony/ux-turbo": "file:vendor/symfony/ux-turbo/assets", - "@symfony/webpack-encore": "^4.1.0", + "@symfony/webpack-encore": "^5.0.0", "bootstrap": "^5.1.3", "core-js": "^3.23.0", + "intl-messageformat": "^10.2.5", "jquery": "^3.5.1", "popper.js": "^1.14.7", "regenerator-runtime": "^0.13.9", "webpack": "^5.74.0", "webpack-bundle-analyzer": "^4.3.0", - "webpack-cli": "^4.10.0", + "webpack-cli": "^5.1.0", "webpack-notifier": "^1.15.0" }, "license": "AGPL-3.0-or-later", @@ -28,62 +30,75 @@ "build": "encore production --progress" }, "dependencies": { - "@ckeditor/ckeditor5-alignment": "^36.0.0", - "@ckeditor/ckeditor5-autoformat": "^36.0.0", - "@ckeditor/ckeditor5-basic-styles": "^36.0.0", - "@ckeditor/ckeditor5-block-quote": "^36.0.0", - "@ckeditor/ckeditor5-code-block": "^36.0.0", - "@ckeditor/ckeditor5-dev-utils": "^33.0.0", - "@ckeditor/ckeditor5-dev-webpack-plugin": "^31.1.13", - "@ckeditor/ckeditor5-editor-classic": "^36.0.0", - "@ckeditor/ckeditor5-essentials": "^36.0.0", - "@ckeditor/ckeditor5-find-and-replace": "^36.0.0", - "@ckeditor/ckeditor5-font": "^36.0.0", - "@ckeditor/ckeditor5-heading": "^36.0.0", - "@ckeditor/ckeditor5-highlight": "^36.0.0", - "@ckeditor/ckeditor5-horizontal-line": "^36.0.0", - "@ckeditor/ckeditor5-html-embed": "^36.0.0", - "@ckeditor/ckeditor5-html-support": "^36.0.0", - "@ckeditor/ckeditor5-image": "^36.0.0", - "@ckeditor/ckeditor5-indent": "^36.0.0", - "@ckeditor/ckeditor5-link": "^36.0.0", - "@ckeditor/ckeditor5-list": "^36.0.0", - "@ckeditor/ckeditor5-markdown-gfm": "^36.0.0", - "@ckeditor/ckeditor5-media-embed": "^36.0.0", - "@ckeditor/ckeditor5-paragraph": "^36.0.0", - "@ckeditor/ckeditor5-paste-from-office": "^36.0.0", - "@ckeditor/ckeditor5-remove-format": "^36.0.0", - "@ckeditor/ckeditor5-source-editing": "^36.0.0", - "@ckeditor/ckeditor5-special-characters": "^36.0.0", - "@ckeditor/ckeditor5-table": "^36.0.0", - "@ckeditor/ckeditor5-theme-lark": "^36.0.0", - "@ckeditor/ckeditor5-upload": "^36.0.0", - "@ckeditor/ckeditor5-watchdog": "^36.0.0", - "@ckeditor/ckeditor5-word-count": "^36.0.0", + "@algolia/autocomplete-js": "^1.17.0", + "@algolia/autocomplete-plugin-recent-searches": "^1.17.0", + "@algolia/autocomplete-theme-classic": "^1.17.0", + "@ckeditor/ckeditor5-alignment": "^44.0.0", + "@ckeditor/ckeditor5-autoformat": "^44.0.0", + "@ckeditor/ckeditor5-basic-styles": "^44.0.0", + "@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-editor-classic": "^44.0.0", + "@ckeditor/ckeditor5-essentials": "^44.0.0", + "@ckeditor/ckeditor5-find-and-replace": "^44.0.0", + "@ckeditor/ckeditor5-font": "^44.0.0", + "@ckeditor/ckeditor5-heading": "^44.0.0", + "@ckeditor/ckeditor5-highlight": "^44.0.0", + "@ckeditor/ckeditor5-horizontal-line": "^44.0.0", + "@ckeditor/ckeditor5-html-embed": "^44.0.0", + "@ckeditor/ckeditor5-html-support": "^44.0.0", + "@ckeditor/ckeditor5-image": "^44.0.0", + "@ckeditor/ckeditor5-indent": "^44.0.0", + "@ckeditor/ckeditor5-link": "^44.0.0", + "@ckeditor/ckeditor5-list": "^44.0.0", + "@ckeditor/ckeditor5-markdown-gfm": "^44.0.0", + "@ckeditor/ckeditor5-media-embed": "^44.0.0", + "@ckeditor/ckeditor5-paragraph": "^44.0.0", + "@ckeditor/ckeditor5-paste-from-office": "^44.0.0", + "@ckeditor/ckeditor5-remove-format": "^44.0.0", + "@ckeditor/ckeditor5-source-editing": "^44.0.0", + "@ckeditor/ckeditor5-special-characters": "^44.0.0", + "@ckeditor/ckeditor5-table": "^44.0.0", + "@ckeditor/ckeditor5-theme-lark": "^44.0.0", + "@ckeditor/ckeditor5-upload": "^44.0.0", + "@ckeditor/ckeditor5-watchdog": "^44.0.0", + "@ckeditor/ckeditor5-word-count": "^44.0.0", "@jbtronics/bs-treeview": "^1.0.1", + "@part-db/html5-qrcode": "^3.1.0", + "@zxcvbn-ts/core": "^3.0.2", + "@zxcvbn-ts/language-common": "^3.0.3", + "@zxcvbn-ts/language-de": "^3.0.1", + "@zxcvbn-ts/language-en": "^3.0.1", + "@zxcvbn-ts/language-fr": "^3.0.1", + "@zxcvbn-ts/language-ja": "^3.0.1", + "barcode-detector": "^2.3.1", "bootbox": "^6.0.0", "bootswatch": "^5.1.3", "bs-custom-file-input": "^1.3.4", "clipboard": "^2.0.4", - "compression-webpack-plugin": "^10.0.0", - "darkmode-js": "^1.5.0", - "datatables.net-bs5": "^1.10.20", - "datatables.net-buttons-bs5": "^2.2.2", - "datatables.net-colreorder-bs5": "^1.5.1", - "datatables.net-fixedheader-bs5": "^3.1.5", - "datatables.net-responsive-bs5": "^2.2.3", - "datatables.net-select-bs5": "^1.2.7", - "dompurify": "^2.0.6", - "emoji.json": "^14.0.0", - "exports-loader": "^3.0.0", - "html5-qrcode": "^2.2.1", + "compression-webpack-plugin": "^11.1.0", + "datatables.net": "^2.0.0", + "datatables.net-bs5": "^2.0.0", + "datatables.net-buttons-bs5": "^3.0.0", + "datatables.net-colreorder-bs5": "^2.0.0", + "datatables.net-fixedheader-bs5": "^4.0.0", + "datatables.net-responsive-bs5": "^3.0.0", + "datatables.net-select-bs5": "^2.0.0", + "dompurify": "^3.0.3", + "emoji.json": "^15.0.0", + "exports-loader": "^5.0.0", + "json-formatter-js": "^2.3.4", "jszip": "^3.2.0", "katex": "^0.16.0", - "marked": "^4.0.3", + "marked": "^15.0.4", + "marked-gfm-heading-id": "^4.1.1", + "marked-mangle": "^1.0.1", "pdfmake": "^0.2.2", - "stimulus-use": "^0.51.1", + "stimulus-use": "^0.52.0", "tom-select": "^2.1.0", "ts-loader": "^9.2.6", - "typescript": "^4.0.2" + "typescript": "^5.7.2" } } diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 00000000..fc7b3524 --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,63 @@ +parameters: + + level: 5 + + paths: + - src + # - tests + + excludePaths: + - src/DataTables/Adapter/* + - src/Configuration/* + - src/Doctrine/Purger/* + - src/DataTables/Adapters/TwoStepORMAdapter.php + - src/Form/Fixes/* + - src/Translation/Fixes/* + + + + inferPrivatePropertyTypeFromConstructor: true + treatPhpDocTypesAsCertain: false + + symfony: + containerXmlPath: '%rootDir%/../../../var/cache/dev/App_KernelDevDebugContainer.xml' + + doctrine: + objectManagerLoader: tests/object-manager.php + allowNullablePropertyForRequiredField: true + + checkUninitializedProperties: true + + checkFunctionNameCase: false + + reportMaybesInPropertyPhpDocTypes: false + reportMaybesInMethodSignatures: false + + strictRules: + disallowedLooseComparison: false + booleansInConditions: false + uselessCast: false + requireParentConstructorCall: true + overwriteVariablesWithLoop: false + closureUsesThis: false + matchingInheritedMethodNames: true + numericOperandsInArithmeticOperators: true + switchConditionsMatchingType: false + noVariableVariables: false + disallowedEmpty: false + disallowedShortTernary: false + + ignoreErrors: + # Ignore errors caused by complex mapping with AbstractStructuralDBElement + - '#AbstractStructuralDBElement does not have a field named \$parent#' + #- '#AbstractStructuralDBElement does not have a field named \$name#' + + # Ignore errors related to the use of the ParametersTrait in Part entity + - '#expects .*PartParameter, .*AbstractParameter given.#' + - '#Part::getParameters\(\) should return .*AbstractParameter#' + + # Ignore doctrine type mapping mismatch + - '#Property .* type mapping mismatch: property can contain .* but database expects .*#' + + # Ignore error of unused WithPermPresetsTrait, as it is used in the migrations which are not analyzed by Phpstan + - '#Trait App\\Migration\\WithPermPresetsTrait is used zero times and is not analysed#' diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index e0e0e662..00000000 --- a/phpstan.neon +++ /dev/null @@ -1,11 +0,0 @@ -parameters: - inferPrivatePropertyTypeFromConstructor: true - treatPhpDocTypesAsCertain: false - - symfony: - container_xml_path: '%rootDir%/../../../var/cache/dev/App_KernelDevDebugContainer.xml' - - excludes_analyse: - - src/DataTables/Adapter/* - - src/Configuration/* - - src/Doctrine/Purger/* \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 94534e9d..7ee7596f 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,20 +1,27 @@ - - - - src - - + - - + + + + + src + + + tests diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 872169ac..00000000 --- a/psalm.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/.htaccess b/public/.htaccess index ee3b5450..bfaab5de 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -118,3 +118,10 @@ DirectoryIndex index.php # RedirectTemp cannot be used instead + +# Set Content-Security-Policy for svg files (and compressed variants), to block embedded javascript in there + + + Header set Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';" + + \ No newline at end of file diff --git a/public/img/default_avatar.png b/public/img/default_avatar.png deleted file mode 100644 index 17d6a106..00000000 Binary files a/public/img/default_avatar.png and /dev/null differ diff --git a/public/img/default_avatar.svg b/public/img/default_avatar.svg new file mode 100644 index 00000000..4586017b --- /dev/null +++ b/public/img/default_avatar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/kicad/footprints.txt b/public/kicad/footprints.txt new file mode 100644 index 00000000..8f0f944c --- /dev/null +++ b/public/kicad/footprints.txt @@ -0,0 +1,14213 @@ +# This file contains all the KiCad footprints available in the official library +# Generated by footprints.sh +# on Sun Feb 16 21:19:56 CET 2025 +Audio_Module:Reverb_BTDR-1H +Audio_Module:Reverb_BTDR-1V +Battery:BatteryClip_Keystone_54_D16-19mm +Battery:BatteryHolder_Bulgin_BX0036_1xC +Battery:BatteryHolder_ComfortableElectronic_CH273-2450_1x2450 +Battery:BatteryHolder_Eagle_12BH611-GR +Battery:BatteryHolder_Keystone_103_1x20mm +Battery:BatteryHolder_Keystone_1042_1x18650 +Battery:BatteryHolder_Keystone_104_1x23mm +Battery:BatteryHolder_Keystone_1057_1x2032 +Battery:BatteryHolder_Keystone_1058_1x2032 +Battery:BatteryHolder_Keystone_105_1x2430 +Battery:BatteryHolder_Keystone_1060_1x2032 +Battery:BatteryHolder_Keystone_106_1x20mm +Battery:BatteryHolder_Keystone_107_1x23mm +Battery:BatteryHolder_Keystone_2460_1xAA +Battery:BatteryHolder_Keystone_2462_2xAA +Battery:BatteryHolder_Keystone_2466_1xAAA +Battery:BatteryHolder_Keystone_2468_2xAAA +Battery:BatteryHolder_Keystone_2479_3xAAA +Battery:BatteryHolder_Keystone_2993 +Battery:BatteryHolder_Keystone_2998_1x6.8mm +Battery:BatteryHolder_Keystone_3000_1x12mm +Battery:BatteryHolder_Keystone_3001_1x12mm +Battery:BatteryHolder_Keystone_3002_1x2032 +Battery:BatteryHolder_Keystone_3008_1x2450 +Battery:BatteryHolder_Keystone_3009_1x2450 +Battery:BatteryHolder_Keystone_3034_1x20mm +Battery:BatteryHolder_Keystone_500 +Battery:BatteryHolder_Keystone_590 +Battery:BatteryHolder_LINX_BAT-HLD-012-SMT +Battery:BatteryHolder_MPD_BA9VPC_1xPP3 +Battery:BatteryHolder_MPD_BC12AAPC_2xAA +Battery:BatteryHolder_MPD_BC2003_1x2032 +Battery:BatteryHolder_MPD_BC2AAPC_2xAA +Battery:BatteryHolder_MPD_BH-18650-PC2 +Battery:BatteryHolder_Multicomp_BC-2001_1x2032 +Battery:BatteryHolder_MYOUNG_BS-07-A1BJ001_CR2032 +Battery:BatteryHolder_Renata_SMTU2032-LF_1x2032 +Battery:BatteryHolder_Seiko_MS621F +Battery:BatteryHolder_TruPower_BH-331P_3xAA +Battery:Battery_CR1225 +Battery:Battery_Panasonic_CR1025-VSK_Vertical_CircularHoles +Battery:Battery_Panasonic_CR1220-VCN_Vertical_CircularHoles +Battery:Battery_Panasonic_CR1632-V1AN_Vertical_CircularHoles +Battery:Battery_Panasonic_CR2025-V1AK_Vertical_CircularHoles +Battery:Battery_Panasonic_CR2032-HFN_Horizontal_CircularHoles +Battery:Battery_Panasonic_CR2032-VS1N_Vertical_CircularHoles +Battery:Battery_Panasonic_CR2354-VCN_Vertical_CircularHoles +Battery:Battery_Panasonic_CR2450-VAN_Vertical_CircularHoles +Battery:Battery_Panasonic_CR2477-VCN_Vertical_CircularHoles +Battery:Battery_Panasonic_CR3032-VCN_Vertical_CircularHoles +Button_Switch_Keyboard:SW_Cherry_MX_1.00u_PCB +Button_Switch_Keyboard:SW_Cherry_MX_1.00u_Plate +Button_Switch_Keyboard:SW_Cherry_MX_1.25u_PCB +Button_Switch_Keyboard:SW_Cherry_MX_1.25u_Plate +Button_Switch_Keyboard:SW_Cherry_MX_1.50u_PCB +Button_Switch_Keyboard:SW_Cherry_MX_1.50u_Plate +Button_Switch_Keyboard:SW_Cherry_MX_1.75u_PCB +Button_Switch_Keyboard:SW_Cherry_MX_1.75u_Plate +Button_Switch_Keyboard:SW_Cherry_MX_2.00u_PCB +Button_Switch_Keyboard:SW_Cherry_MX_2.00u_Plate +Button_Switch_Keyboard:SW_Cherry_MX_2.00u_Vertical_PCB +Button_Switch_Keyboard:SW_Cherry_MX_2.00u_Vertical_Plate +Button_Switch_Keyboard:SW_Cherry_MX_2.25u_PCB +Button_Switch_Keyboard:SW_Cherry_MX_2.25u_Plate +Button_Switch_Keyboard:SW_Cherry_MX_2.75u_PCB +Button_Switch_Keyboard:SW_Cherry_MX_2.75u_Plate +Button_Switch_Keyboard:SW_Cherry_MX_6.25u_PCB +Button_Switch_Keyboard:SW_Cherry_MX_6.25u_Plate +Button_Switch_Keyboard:SW_Cherry_MX_ISOEnter_PCB +Button_Switch_Keyboard:SW_Cherry_MX_ISOEnter_Plate +Button_Switch_Keyboard:SW_Matias_1.00u +Button_Switch_Keyboard:SW_Matias_1.25u +Button_Switch_Keyboard:SW_Matias_1.50u +Button_Switch_Keyboard:SW_Matias_1.75u +Button_Switch_Keyboard:SW_Matias_2.00u +Button_Switch_Keyboard:SW_Matias_2.25u +Button_Switch_Keyboard:SW_Matias_2.75u +Button_Switch_Keyboard:SW_Matias_6.25u +Button_Switch_Keyboard:SW_Matias_ISOEnter +Button_Switch_SMD:Nidec_Copal_CAS-120A +Button_Switch_SMD:Nidec_Copal_SH-7010A +Button_Switch_SMD:Nidec_Copal_SH-7010B +Button_Switch_SMD:Nidec_Copal_SH-7040B +Button_Switch_SMD:Panasonic_EVQPUJ_EVQPUA +Button_Switch_SMD:Panasonic_EVQPUK_EVQPUB +Button_Switch_SMD:Panasonic_EVQPUL_EVQPUC +Button_Switch_SMD:Panasonic_EVQPUM_EVQPUD +Button_Switch_SMD:SW_DIP_SPSTx01_Slide_6.7x4.1mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx01_Slide_6.7x4.1mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx01_Slide_9.78x4.72mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx01_Slide_Copal_CHS-01A_W5.08mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx01_Slide_Copal_CHS-01B_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx01_Slide_Copal_CVS-01xB_W5.9mm_P1mm +Button_Switch_SMD:SW_DIP_SPSTx01_Slide_Omron_A6S-110x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_6.7x6.64mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_6.7x6.64mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_9.78x7.26mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_Copal_CHS-02A_W5.08mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_Copal_CHS-02B_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_Copal_CVS-02xB_W5.9mm_P1mm +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_KingTek_DSHP02TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_KingTek_DSHP02TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_Omron_A6H-2101_W6.15mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx02_Slide_Omron_A6S-210x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx03_Slide_6.7x9.18mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx03_Slide_6.7x9.18mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx03_Slide_9.78x9.8mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx03_Slide_Copal_CVS-03xB_W5.9mm_P1mm +Button_Switch_SMD:SW_DIP_SPSTx03_Slide_KingTek_DSHP03TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx03_Slide_KingTek_DSHP03TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx03_Slide_Omron_A6S-310x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_6.7x11.72mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_6.7x11.72mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_9.78x12.34mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_Copal_CHS-04A_W5.08mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_Copal_CHS-04B_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_Copal_CVS-04xB_W5.9mm_P1mm +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_KingTek_DSHP04TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_KingTek_DSHP04TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_Omron_A6H-4101_W6.15mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx04_Slide_Omron_A6S-410x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx05_Slide_6.7x14.26mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx05_Slide_6.7x14.26mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx05_Slide_9.78x14.88mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx05_Slide_KingTek_DSHP05TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx05_Slide_KingTek_DSHP05TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx05_Slide_Omron_A6S-510x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_6.7x16.8mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_6.7x16.8mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_9.78x17.42mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_Copal_CHS-06A_W5.08mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_Copal_CHS-06B_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_KingTek_DSHP06TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_KingTek_DSHP06TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_Omron_A6H-6101_W6.15mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx06_Slide_Omron_A6S-610x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx07_Slide_6.7x19.34mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx07_Slide_6.7x19.34mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx07_Slide_9.78x19.96mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx07_Slide_KingTek_DSHP07TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx07_Slide_KingTek_DSHP07TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx07_Slide_Omron_A6S-710x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_6.7x21.88mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_6.7x21.88mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_9.78x22.5mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_Copal_CHS-08A_W5.08mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_Copal_CHS-08B_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_Copal_CVS-08xB_W5.9mm_P1mm +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_KingTek_DSHP08TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_KingTek_DSHP08TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_Omron_A6H-8101_W6.15mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx08_Slide_Omron_A6S-810x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx09_Slide_6.7x24.42mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx09_Slide_6.7x24.42mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx09_Slide_9.78x25.04mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx09_Slide_KingTek_DSHP09TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx09_Slide_KingTek_DSHP09TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx09_Slide_Omron_A6S-910x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_6.7x26.96mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_6.7x26.96mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_9.78x27.58mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_Copal_CHS-10A_W5.08mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_Copal_CHS-10B_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_KingTek_DSHP10TJ_W5.25mm_P1.27mm_JPin +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_KingTek_DSHP10TS_W7.62mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_Omron_A6H-10101_W6.15mm_P1.27mm +Button_Switch_SMD:SW_DIP_SPSTx10_Slide_Omron_A6S-1010x_W8.9mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx11_Slide_6.7x29.5mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx11_Slide_6.7x29.5mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx11_Slide_9.78x30.12mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DIP_SPSTx12_Slide_6.7x32.04mm_W6.73mm_P2.54mm_LowProfile_JPin +Button_Switch_SMD:SW_DIP_SPSTx12_Slide_6.7x32.04mm_W8.61mm_P2.54mm_LowProfile +Button_Switch_SMD:SW_DIP_SPSTx12_Slide_9.78x32.66mm_W8.61mm_P2.54mm +Button_Switch_SMD:SW_DPDT_CK_JS202011JCQN +Button_Switch_SMD:SW_MEC_5GSH9 +Button_Switch_SMD:SW_Push_1P1T-MP_NO_Horizontal_Alps_SKRTLAE010 +Button_Switch_SMD:SW_Push_1P1T-SH_NO_CK_KMR2xxG +Button_Switch_SMD:SW_Push_1P1T_NO_CK_KMR2 +Button_Switch_SMD:SW_Push_1P1T_NO_CK_KSC6xxJ +Button_Switch_SMD:SW_Push_1P1T_NO_CK_KSC7xxJ +Button_Switch_SMD:SW_Push_1P1T_NO_CK_PTS125Sx43PSMTR +Button_Switch_SMD:SW_Push_1P1T_NO_Vertical_Wuerth_434133025816 +Button_Switch_SMD:SW_Push_1P1T_XKB_TS-1187A +Button_Switch_SMD:SW_Push_1TS009xxxx-xxxx-xxxx_6x6x5mm +Button_Switch_SMD:SW_Push_SPST_NO_Alps_SKRK +Button_Switch_SMD:SW_SP3T_PCM13 +Button_Switch_SMD:SW_SPDT_CK_JS102011SAQN +Button_Switch_SMD:SW_SPDT_PCM12 +Button_Switch_SMD:SW_SPDT_REED_MSDM-DT +Button_Switch_SMD:SW_SPST_B3S-1000 +Button_Switch_SMD:SW_SPST_B3S-1100 +Button_Switch_SMD:SW_SPST_B3SL-1002P +Button_Switch_SMD:SW_SPST_B3SL-1022P +Button_Switch_SMD:SW_SPST_B3U-1000P-B +Button_Switch_SMD:SW_SPST_B3U-1000P +Button_Switch_SMD:SW_SPST_B3U-1100P-B +Button_Switch_SMD:SW_SPST_B3U-1100P +Button_Switch_SMD:SW_SPST_B3U-3000P-B +Button_Switch_SMD:SW_SPST_B3U-3000P +Button_Switch_SMD:SW_SPST_B3U-3100P-B +Button_Switch_SMD:SW_SPST_B3U-3100P +Button_Switch_SMD:SW_SPST_CK_KMS2xxG +Button_Switch_SMD:SW_SPST_CK_KMS2xxGP +Button_Switch_SMD:SW_SPST_CK_KXT3 +Button_Switch_SMD:SW_SPST_CK_RS282G05A3 +Button_Switch_SMD:SW_SPST_EVPBF +Button_Switch_SMD:SW_SPST_EVQP0 +Button_Switch_SMD:SW_SPST_EVQP2 +Button_Switch_SMD:SW_SPST_EVQP7A +Button_Switch_SMD:SW_SPST_EVQP7C +Button_Switch_SMD:SW_SPST_EVQPE1 +Button_Switch_SMD:SW_SPST_EVQQ2 +Button_Switch_SMD:SW_SPST_FSMSM +Button_Switch_SMD:SW_SPST_Omron_B3FS-100xP +Button_Switch_SMD:SW_SPST_Omron_B3FS-101xP +Button_Switch_SMD:SW_SPST_Omron_B3FS-105xP +Button_Switch_SMD:SW_SPST_Panasonic_EVQPL_3PL_5PL_PT_A08 +Button_Switch_SMD:SW_SPST_Panasonic_EVQPL_3PL_5PL_PT_A15 +Button_Switch_SMD:SW_SPST_PTS645 +Button_Switch_SMD:SW_SPST_PTS647_Sx38 +Button_Switch_SMD:SW_SPST_PTS647_Sx50 +Button_Switch_SMD:SW_SPST_PTS647_Sx70 +Button_Switch_SMD:SW_SPST_PTS810 +Button_Switch_SMD:SW_SPST_REED_CT05-XXXX-G1 +Button_Switch_SMD:SW_SPST_REED_CT05-XXXX-J1 +Button_Switch_SMD:SW_SPST_REED_CT10-XXXX-G1 +Button_Switch_SMD:SW_SPST_REED_CT10-XXXX-G2 +Button_Switch_SMD:SW_SPST_REED_CT10-XXXX-G4 +Button_Switch_SMD:SW_SPST_SKQG_WithoutStem +Button_Switch_SMD:SW_SPST_SKQG_WithStem +Button_Switch_SMD:SW_SPST_TL3305A +Button_Switch_SMD:SW_SPST_TL3305B +Button_Switch_SMD:SW_SPST_TL3305C +Button_Switch_SMD:SW_SPST_TL3342 +Button_Switch_SMD:SW_Tactile_SPST_NO_Straight_CK_PTS636Sx25SMTRLFS +Button_Switch_THT:KSA_Tactile_SPST +Button_Switch_THT:Nidec_Copal_SH-7010C +Button_Switch_THT:Push_E-Switch_KS01Q01 +Button_Switch_THT:SW_CK_JS202011AQN_DPDT_Angled +Button_Switch_THT:SW_CK_JS202011CQN_DPDT_Straight +Button_Switch_THT:SW_CW_GPTS203211B +Button_Switch_THT:SW_DIP_SPSTx01_Piano_10.8x4.1mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx01_Slide_6.7x4.1mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx01_Slide_9.78x4.72mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx02_Piano_10.8x6.64mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx02_Piano_CTS_Series194-2MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx02_Slide_6.7x6.64mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx02_Slide_9.78x7.26mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx03_Piano_10.8x9.18mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx03_Piano_CTS_Series194-3MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx03_Slide_6.7x9.18mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx03_Slide_9.78x9.8mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx04_Piano_10.8x11.72mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx04_Piano_CTS_Series194-4MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx04_Slide_6.7x11.72mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx04_Slide_9.78x12.34mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx05_Piano_10.8x14.26mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx05_Piano_CTS_Series194-5MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx05_Slide_6.7x14.26mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx05_Slide_9.78x14.88mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx06_Piano_10.8x16.8mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx06_Piano_CTS_Series194-6MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx06_Slide_6.7x16.8mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx06_Slide_9.78x17.42mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx07_Piano_10.8x19.34mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx07_Piano_CTS_Series194-7MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx07_Slide_6.7x19.34mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx07_Slide_9.78x19.96mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx08_Piano_10.8x21.88mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx08_Piano_CTS_Series194-8MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx08_Slide_6.7x21.88mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx08_Slide_9.78x22.5mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx09_Piano_10.8x24.42mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx09_Piano_CTS_Series194-9MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx09_Slide_6.7x24.42mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx09_Slide_9.78x25.04mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx10_Piano_10.8x26.96mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx10_Piano_CTS_Series194-10MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx10_Slide_6.7x26.96mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx10_Slide_9.78x27.58mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx11_Piano_10.8x29.5mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx11_Piano_CTS_Series194-11MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx11_Slide_6.7x29.5mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx11_Slide_9.78x30.12mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx12_Piano_10.8x32.04mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx12_Piano_CTS_Series194-12MSTN_W7.62mm_P2.54mm +Button_Switch_THT:SW_DIP_SPSTx12_Slide_6.7x32.04mm_W7.62mm_P2.54mm_LowProfile +Button_Switch_THT:SW_DIP_SPSTx12_Slide_9.78x32.66mm_W7.62mm_P2.54mm +Button_Switch_THT:SW_E-Switch_EG1224_SPDT_Angled +Button_Switch_THT:SW_E-Switch_EG1271_SPDT +Button_Switch_THT:SW_E-Switch_EG2219_DPDT_Angled +Button_Switch_THT:SW_Lever_1P2T_NKK_GW12LxH +Button_Switch_THT:SW_MEC_5GTH9 +Button_Switch_THT:SW_NKK_BB15AH +Button_Switch_THT:SW_NKK_G1xJP +Button_Switch_THT:SW_NKK_GW12LJP +Button_Switch_THT:SW_NKK_NR01 +Button_Switch_THT:SW_PUSH-12mm +Button_Switch_THT:SW_PUSH-12mm_Wuerth-430476085716 +Button_Switch_THT:SW_PUSH_1P1T_6x3.5mm_H4.3_APEM_MJTP1243 +Button_Switch_THT:SW_PUSH_1P1T_6x3.5mm_H5.0_APEM_MJTP1250 +Button_Switch_THT:SW_Push_1P1T_NO_LED_E-Switch_TL1250 +Button_Switch_THT:SW_Push_1P2T_Vertical_E-Switch_800UDP8P1A1M6 +Button_Switch_THT:SW_Push_2P1T_Toggle_CK_PVA1xxH1xxxxxxV2 +Button_Switch_THT:SW_Push_2P1T_Toggle_CK_PVA1xxH2xxxxxxV2 +Button_Switch_THT:SW_Push_2P1T_Toggle_CK_PVA1xxH3xxxxxxV2 +Button_Switch_THT:SW_Push_2P1T_Toggle_CK_PVA1xxH4xxxxxxV2 +Button_Switch_THT:SW_Push_2P2T_Toggle_CK_PVA2OAH5xxxxxxV2 +Button_Switch_THT:SW_Push_2P2T_Toggle_CK_PVA2xxH1xxxxxxV2 +Button_Switch_THT:SW_Push_2P2T_Toggle_CK_PVA2xxH2xxxxxxV2 +Button_Switch_THT:SW_Push_2P2T_Toggle_CK_PVA2xxH3xxxxxxV2 +Button_Switch_THT:SW_Push_2P2T_Toggle_CK_PVA2xxH4xxxxxxV2 +Button_Switch_THT:SW_Push_2P2T_Vertical_E-Switch_800UDP8P1A1M6 +Button_Switch_THT:SW_PUSH_6mm +Button_Switch_THT:SW_PUSH_6mm_H13mm +Button_Switch_THT:SW_PUSH_6mm_H4.3mm +Button_Switch_THT:SW_PUSH_6mm_H5mm +Button_Switch_THT:SW_PUSH_6mm_H7.3mm +Button_Switch_THT:SW_PUSH_6mm_H8.5mm +Button_Switch_THT:SW_PUSH_6mm_H8mm +Button_Switch_THT:SW_PUSH_6mm_H9.5mm +Button_Switch_THT:SW_PUSH_E-Switch_FS5700DP_DPDT +Button_Switch_THT:SW_PUSH_LCD_E3_SAxxxx +Button_Switch_THT:SW_PUSH_LCD_E3_SAxxxx_SocketPins +Button_Switch_THT:SW_Slide-03_Wuerth-WS-SLTV_10x2.5x6.4_P2.54mm +Button_Switch_THT:SW_Slide_SPDT_Angled_CK_OS102011MA1Q +Button_Switch_THT:SW_Slide_SPDT_Straight_CK_OS102011MS2Q +Button_Switch_THT:SW_SPST_Omron_B3F-315x_Angled +Button_Switch_THT:SW_SPST_Omron_B3F-40xx +Button_Switch_THT:SW_SPST_Omron_B3F-50xx +Button_Switch_THT:SW_Tactile_SKHH_Angled +Button_Switch_THT:SW_Tactile_SPST_Angled_PTS645Vx31-2LFS +Button_Switch_THT:SW_Tactile_SPST_Angled_PTS645Vx39-2LFS +Button_Switch_THT:SW_Tactile_SPST_Angled_PTS645Vx58-2LFS +Button_Switch_THT:SW_Tactile_SPST_Angled_PTS645Vx83-2LFS +Button_Switch_THT:SW_Tactile_Straight_KSA0Axx1LFTR +Button_Switch_THT:SW_Tactile_Straight_KSL0Axx1LFTR +Button_Switch_THT:SW_TH_Tactile_Omron_B3F-10xx +Button_Switch_THT:SW_XKB_DM1-16UC-1 +Button_Switch_THT:SW_XKB_DM1-16UD-1 +Button_Switch_THT:SW_XKB_DM1-16UP-1 +Buzzer_Beeper:Buzzer_12x9.5RM7.6 +Buzzer_Beeper:Buzzer_15x7.5RM7.6 +Buzzer_Beeper:Buzzer_CUI_CPT-9019S-SMT +Buzzer_Beeper:Buzzer_D14mm_H7mm_P10mm +Buzzer_Beeper:Buzzer_Mallory_AST1109MLTRQ +Buzzer_Beeper:Buzzer_Mallory_AST1240MLQ +Buzzer_Beeper:Buzzer_Murata_PKLCS1212E +Buzzer_Beeper:Buzzer_Murata_PKMCS0909E +Buzzer_Beeper:Buzzer_TDK_PS1240P02BT_D12.2mm_H6.5mm +Buzzer_Beeper:Indicator_PUI_AI-1440-TWT-24V-2-R +Buzzer_Beeper:MagneticBuzzer_CUI_CMT-8504-100-SMT +Buzzer_Beeper:MagneticBuzzer_CUI_CST-931RP-A +Buzzer_Beeper:MagneticBuzzer_Kingstate_KCG0601 +Buzzer_Beeper:MagneticBuzzer_Kobitone_254-EMB73-RO +Buzzer_Beeper:MagneticBuzzer_Kobitone_254-EMB84Q-RO +Buzzer_Beeper:MagneticBuzzer_ProjectsUnlimited_AI-4228-TWT-R +Buzzer_Beeper:MagneticBuzzer_ProSignal_ABI-009-RC +Buzzer_Beeper:MagneticBuzzer_ProSignal_ABI-010-RC +Buzzer_Beeper:MagneticBuzzer_ProSignal_ABT-410-RC +Buzzer_Beeper:MagneticBuzzer_PUI_AT-0927-TT-6-R +Buzzer_Beeper:MagneticBuzzer_PUI_SMT-1028-T-2-R +Buzzer_Beeper:MagneticBuzzer_StarMicronics_HMB-06_HMB-12 +Buzzer_Beeper:PUIAudio_SMT_0825_S_4_R +Buzzer_Beeper:Speaker_CUI_CMR-1206S-67 +Calibration_Scale:Gauge_100mm_Grid_Type1_CopperTop +Calibration_Scale:Gauge_100mm_Type1_CopperTop +Calibration_Scale:Gauge_100mm_Type1_SilkScreenTop +Calibration_Scale:Gauge_100mm_Type2_CopperTop +Calibration_Scale:Gauge_100mm_Type2_SilkScreenTop +Calibration_Scale:Gauge_10mm_Type1_CopperTop +Calibration_Scale:Gauge_10mm_Type1_SilkScreenTop +Calibration_Scale:Gauge_10mm_Type2_CopperTop +Calibration_Scale:Gauge_10mm_Type2_SilkScreenTop +Calibration_Scale:Gauge_10mm_Type3_CopperTop +Calibration_Scale:Gauge_10mm_Type3_SilkScreenTop +Calibration_Scale:Gauge_10mm_Type4_CopperTop +Calibration_Scale:Gauge_10mm_Type4_SilkScreenTop +Calibration_Scale:Gauge_10mm_Type5_CopperTop +Calibration_Scale:Gauge_10mm_Type5_SilkScreenTop +Calibration_Scale:Gauge_50mm_Type1_CopperTop +Calibration_Scale:Gauge_50mm_Type1_SilkScreenTop +Calibration_Scale:Gauge_50mm_Type2_CopperTop +Calibration_Scale:Gauge_50mm_Type2_SilkScreenTop +Capacitor_SMD:CP_Elec_10x10.5 +Capacitor_SMD:CP_Elec_10x10 +Capacitor_SMD:CP_Elec_10x12.5 +Capacitor_SMD:CP_Elec_10x12.6 +Capacitor_SMD:CP_Elec_10x14.3 +Capacitor_SMD:CP_Elec_10x7.7 +Capacitor_SMD:CP_Elec_10x7.9 +Capacitor_SMD:CP_Elec_16x17.5 +Capacitor_SMD:CP_Elec_16x22 +Capacitor_SMD:CP_Elec_18x17.5 +Capacitor_SMD:CP_Elec_18x22 +Capacitor_SMD:CP_Elec_3x5.3 +Capacitor_SMD:CP_Elec_3x5.4 +Capacitor_SMD:CP_Elec_4x3.9 +Capacitor_SMD:CP_Elec_4x3 +Capacitor_SMD:CP_Elec_4x4.5 +Capacitor_SMD:CP_Elec_4x5.3 +Capacitor_SMD:CP_Elec_4x5.4 +Capacitor_SMD:CP_Elec_4x5.7 +Capacitor_SMD:CP_Elec_4x5.8 +Capacitor_SMD:CP_Elec_5x3.9 +Capacitor_SMD:CP_Elec_5x3 +Capacitor_SMD:CP_Elec_5x4.4 +Capacitor_SMD:CP_Elec_5x4.5 +Capacitor_SMD:CP_Elec_5x5.3 +Capacitor_SMD:CP_Elec_5x5.4 +Capacitor_SMD:CP_Elec_5x5.7 +Capacitor_SMD:CP_Elec_5x5.8 +Capacitor_SMD:CP_Elec_5x5.9 +Capacitor_SMD:CP_Elec_6.3x3.9 +Capacitor_SMD:CP_Elec_6.3x3 +Capacitor_SMD:CP_Elec_6.3x4.5 +Capacitor_SMD:CP_Elec_6.3x4.9 +Capacitor_SMD:CP_Elec_6.3x5.2 +Capacitor_SMD:CP_Elec_6.3x5.3 +Capacitor_SMD:CP_Elec_6.3x5.4 +Capacitor_SMD:CP_Elec_6.3x5.4_Nichicon +Capacitor_SMD:CP_Elec_6.3x5.7 +Capacitor_SMD:CP_Elec_6.3x5.8 +Capacitor_SMD:CP_Elec_6.3x5.9 +Capacitor_SMD:CP_Elec_6.3x7.7 +Capacitor_SMD:CP_Elec_6.3x9.9 +Capacitor_SMD:CP_Elec_8x10.5 +Capacitor_SMD:CP_Elec_8x10 +Capacitor_SMD:CP_Elec_8x11.9 +Capacitor_SMD:CP_Elec_8x5.4 +Capacitor_SMD:CP_Elec_8x6.2 +Capacitor_SMD:CP_Elec_8x6.5 +Capacitor_SMD:CP_Elec_8x6.7 +Capacitor_SMD:CP_Elec_8x6.9 +Capacitor_SMD:CP_Elec_CAP-XX_DMF3Zxxxxxxxx3D +Capacitor_SMD:C_01005_0402Metric +Capacitor_SMD:C_01005_0402Metric_Pad0.57x0.30mm_HandSolder +Capacitor_SMD:C_0201_0603Metric +Capacitor_SMD:C_0201_0603Metric_Pad0.64x0.40mm_HandSolder +Capacitor_SMD:C_0402_1005Metric +Capacitor_SMD:C_0402_1005Metric_Pad0.74x0.62mm_HandSolder +Capacitor_SMD:C_0504_1310Metric +Capacitor_SMD:C_0504_1310Metric_Pad0.83x1.28mm_HandSolder +Capacitor_SMD:C_0603_1608Metric +Capacitor_SMD:C_0603_1608Metric_Pad1.08x0.95mm_HandSolder +Capacitor_SMD:C_0805_2012Metric +Capacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolder +Capacitor_SMD:C_1206_3216Metric +Capacitor_SMD:C_1206_3216Metric_Pad1.33x1.80mm_HandSolder +Capacitor_SMD:C_1210_3225Metric +Capacitor_SMD:C_1210_3225Metric_Pad1.33x2.70mm_HandSolder +Capacitor_SMD:C_1808_4520Metric +Capacitor_SMD:C_1808_4520Metric_Pad1.72x2.30mm_HandSolder +Capacitor_SMD:C_1812_4532Metric +Capacitor_SMD:C_1812_4532Metric_Pad1.57x3.40mm_HandSolder +Capacitor_SMD:C_1825_4564Metric +Capacitor_SMD:C_1825_4564Metric_Pad1.57x6.80mm_HandSolder +Capacitor_SMD:C_2220_5750Metric +Capacitor_SMD:C_2220_5750Metric_Pad1.97x5.40mm_HandSolder +Capacitor_SMD:C_2225_5664Metric +Capacitor_SMD:C_2225_5664Metric_Pad1.80x6.60mm_HandSolder +Capacitor_SMD:C_3640_9110Metric +Capacitor_SMD:C_3640_9110Metric_Pad2.10x10.45mm_HandSolder +Capacitor_SMD:C_Elec_10x10.2 +Capacitor_SMD:C_Elec_3x5.4 +Capacitor_SMD:C_Elec_4x5.4 +Capacitor_SMD:C_Elec_4x5.8 +Capacitor_SMD:C_Elec_5x5.4 +Capacitor_SMD:C_Elec_5x5.8 +Capacitor_SMD:C_Elec_6.3x5.4 +Capacitor_SMD:C_Elec_6.3x5.8 +Capacitor_SMD:C_Elec_6.3x7.7 +Capacitor_SMD:C_Elec_8x10.2 +Capacitor_SMD:C_Elec_8x5.4 +Capacitor_SMD:C_Elec_8x6.2 +Capacitor_SMD:C_Trimmer_Murata_TZB4-A +Capacitor_SMD:C_Trimmer_Murata_TZB4-B +Capacitor_SMD:C_Trimmer_Murata_TZC3 +Capacitor_SMD:C_Trimmer_Murata_TZR1 +Capacitor_SMD:C_Trimmer_Murata_TZW4 +Capacitor_SMD:C_Trimmer_Murata_TZY2 +Capacitor_SMD:C_Trimmer_Sprague-Goodman_SGC3 +Capacitor_SMD:C_Trimmer_Voltronics_JN +Capacitor_SMD:C_Trimmer_Voltronics_JQ +Capacitor_SMD:C_Trimmer_Voltronics_JR +Capacitor_SMD:C_Trimmer_Voltronics_JV +Capacitor_SMD:C_Trimmer_Voltronics_JZ +Capacitor_Tantalum_SMD:CP_EIA-1608-08_AVX-J +Capacitor_Tantalum_SMD:CP_EIA-1608-08_AVX-J_Pad1.25x1.05mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-1608-10_AVX-L +Capacitor_Tantalum_SMD:CP_EIA-1608-10_AVX-L_Pad1.25x1.05mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-2012-12_Kemet-R +Capacitor_Tantalum_SMD:CP_EIA-2012-12_Kemet-R_Pad1.30x1.05mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-2012-15_AVX-P +Capacitor_Tantalum_SMD:CP_EIA-2012-15_AVX-P_Pad1.30x1.05mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-3216-10_Kemet-I +Capacitor_Tantalum_SMD:CP_EIA-3216-10_Kemet-I_Pad1.58x1.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-3216-12_Kemet-S +Capacitor_Tantalum_SMD:CP_EIA-3216-12_Kemet-S_Pad1.58x1.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-3216-18_Kemet-A +Capacitor_Tantalum_SMD:CP_EIA-3216-18_Kemet-A_Pad1.58x1.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-3528-12_Kemet-T +Capacitor_Tantalum_SMD:CP_EIA-3528-12_Kemet-T_Pad1.50x2.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-3528-15_AVX-H +Capacitor_Tantalum_SMD:CP_EIA-3528-15_AVX-H_Pad1.50x2.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-3528-21_Kemet-B +Capacitor_Tantalum_SMD:CP_EIA-3528-21_Kemet-B_Pad1.50x2.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-6032-15_Kemet-U +Capacitor_Tantalum_SMD:CP_EIA-6032-15_Kemet-U_Pad2.25x2.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-6032-20_AVX-F +Capacitor_Tantalum_SMD:CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-6032-28_Kemet-C +Capacitor_Tantalum_SMD:CP_EIA-6032-28_Kemet-C_Pad2.25x2.35mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7132-20_AVX-U +Capacitor_Tantalum_SMD:CP_EIA-7132-20_AVX-U_Pad2.72x3.50mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7132-28_AVX-C +Capacitor_Tantalum_SMD:CP_EIA-7132-28_AVX-C_Pad2.72x3.50mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7260-15_AVX-R +Capacitor_Tantalum_SMD:CP_EIA-7260-15_AVX-R_Pad2.68x6.30mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7260-20_AVX-M +Capacitor_Tantalum_SMD:CP_EIA-7260-20_AVX-M_Pad2.68x6.30mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7260-28_AVX-M +Capacitor_Tantalum_SMD:CP_EIA-7260-28_AVX-M_Pad2.68x6.30mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7260-38_AVX-R +Capacitor_Tantalum_SMD:CP_EIA-7260-38_AVX-R_Pad2.68x6.30mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7343-15_Kemet-W +Capacitor_Tantalum_SMD:CP_EIA-7343-15_Kemet-W_Pad2.25x2.55mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7343-20_Kemet-V +Capacitor_Tantalum_SMD:CP_EIA-7343-20_Kemet-V_Pad2.25x2.55mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7343-30_AVX-N +Capacitor_Tantalum_SMD:CP_EIA-7343-30_AVX-N_Pad2.25x2.55mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7343-31_Kemet-D +Capacitor_Tantalum_SMD:CP_EIA-7343-31_Kemet-D_Pad2.25x2.55mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7343-40_Kemet-Y +Capacitor_Tantalum_SMD:CP_EIA-7343-40_Kemet-Y_Pad2.25x2.55mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7343-43_Kemet-X +Capacitor_Tantalum_SMD:CP_EIA-7343-43_Kemet-X_Pad2.25x2.55mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7360-38_Kemet-E +Capacitor_Tantalum_SMD:CP_EIA-7360-38_Kemet-E_Pad2.25x4.25mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7361-38_AVX-V +Capacitor_Tantalum_SMD:CP_EIA-7361-38_AVX-V_Pad2.18x3.30mm_HandSolder +Capacitor_Tantalum_SMD:CP_EIA-7361-438_AVX-U +Capacitor_Tantalum_SMD:CP_EIA-7361-438_AVX-U_Pad2.18x3.30mm_HandSolder +Capacitor_THT:CP_Axial_L10.0mm_D4.5mm_P15.00mm_Horizontal +Capacitor_THT:CP_Axial_L10.0mm_D6.0mm_P15.00mm_Horizontal +Capacitor_THT:CP_Axial_L11.0mm_D5.0mm_P18.00mm_Horizontal +Capacitor_THT:CP_Axial_L11.0mm_D6.0mm_P18.00mm_Horizontal +Capacitor_THT:CP_Axial_L11.0mm_D8.0mm_P15.00mm_Horizontal +Capacitor_THT:CP_Axial_L18.0mm_D10.0mm_P25.00mm_Horizontal +Capacitor_THT:CP_Axial_L18.0mm_D6.5mm_P25.00mm_Horizontal +Capacitor_THT:CP_Axial_L18.0mm_D8.0mm_P25.00mm_Horizontal +Capacitor_THT:CP_Axial_L20.0mm_D10.0mm_P26.00mm_Horizontal +Capacitor_THT:CP_Axial_L20.0mm_D13.0mm_P26.00mm_Horizontal +Capacitor_THT:CP_Axial_L21.0mm_D8.0mm_P28.00mm_Horizontal +Capacitor_THT:CP_Axial_L25.0mm_D10.0mm_P30.00mm_Horizontal +Capacitor_THT:CP_Axial_L26.5mm_D20.0mm_P33.00mm_Horizontal +Capacitor_THT:CP_Axial_L29.0mm_D10.0mm_P35.00mm_Horizontal +Capacitor_THT:CP_Axial_L29.0mm_D13.0mm_P35.00mm_Horizontal +Capacitor_THT:CP_Axial_L29.0mm_D16.0mm_P35.00mm_Horizontal +Capacitor_THT:CP_Axial_L29.0mm_D20.0mm_P35.00mm_Horizontal +Capacitor_THT:CP_Axial_L30.0mm_D10.0mm_P35.00mm_Horizontal +Capacitor_THT:CP_Axial_L30.0mm_D12.5mm_P35.00mm_Horizontal +Capacitor_THT:CP_Axial_L30.0mm_D15.0mm_P35.00mm_Horizontal +Capacitor_THT:CP_Axial_L30.0mm_D18.0mm_P35.00mm_Horizontal +Capacitor_THT:CP_Axial_L34.5mm_D20.0mm_P41.00mm_Horizontal +Capacitor_THT:CP_Axial_L37.0mm_D13.0mm_P43.00mm_Horizontal +Capacitor_THT:CP_Axial_L37.0mm_D16.0mm_P43.00mm_Horizontal +Capacitor_THT:CP_Axial_L37.0mm_D20.0mm_P43.00mm_Horizontal +Capacitor_THT:CP_Axial_L38.0mm_D18.0mm_P44.00mm_Horizontal +Capacitor_THT:CP_Axial_L38.0mm_D21.0mm_P44.00mm_Horizontal +Capacitor_THT:CP_Axial_L40.0mm_D16.0mm_P48.00mm_Horizontal +Capacitor_THT:CP_Axial_L42.0mm_D23.0mm_P45.00mm_Horizontal +Capacitor_THT:CP_Axial_L42.0mm_D26.0mm_P45.00mm_Horizontal +Capacitor_THT:CP_Axial_L42.0mm_D29.0mm_P45.00mm_Horizontal +Capacitor_THT:CP_Axial_L42.0mm_D32.0mm_P45.00mm_Horizontal +Capacitor_THT:CP_Axial_L42.0mm_D35.0mm_P45.00mm_Horizontal +Capacitor_THT:CP_Axial_L42.5mm_D20.0mm_P49.00mm_Horizontal +Capacitor_THT:CP_Axial_L46.0mm_D20.0mm_P52.00mm_Horizontal +Capacitor_THT:CP_Axial_L55.0mm_D23.0mm_P60.00mm_Horizontal +Capacitor_THT:CP_Axial_L55.0mm_D26.0mm_P60.00mm_Horizontal +Capacitor_THT:CP_Axial_L55.0mm_D29.0mm_P60.00mm_Horizontal +Capacitor_THT:CP_Axial_L55.0mm_D32.0mm_P60.00mm_Horizontal +Capacitor_THT:CP_Axial_L55.0mm_D35.0mm_P60.00mm_Horizontal +Capacitor_THT:CP_Axial_L67.0mm_D23.0mm_P75.00mm_Horizontal +Capacitor_THT:CP_Axial_L67.0mm_D26.0mm_P75.00mm_Horizontal +Capacitor_THT:CP_Axial_L67.0mm_D29.0mm_P75.00mm_Horizontal +Capacitor_THT:CP_Axial_L67.0mm_D32.0mm_P75.00mm_Horizontal +Capacitor_THT:CP_Axial_L67.0mm_D35.0mm_P75.00mm_Horizontal +Capacitor_THT:CP_Axial_L80.0mm_D23.0mm_P85.00mm_Horizontal +Capacitor_THT:CP_Axial_L80.0mm_D26.0mm_P85.00mm_Horizontal +Capacitor_THT:CP_Axial_L80.0mm_D29.0mm_P85.00mm_Horizontal +Capacitor_THT:CP_Axial_L80.0mm_D32.0mm_P85.00mm_Horizontal +Capacitor_THT:CP_Axial_L80.0mm_D35.0mm_P85.00mm_Horizontal +Capacitor_THT:CP_Axial_L93.0mm_D23.0mm_P100.00mm_Horizontal +Capacitor_THT:CP_Axial_L93.0mm_D26.0mm_P100.00mm_Horizontal +Capacitor_THT:CP_Axial_L93.0mm_D29.0mm_P100.00mm_Horizontal +Capacitor_THT:CP_Axial_L93.0mm_D32.0mm_P100.00mm_Horizontal +Capacitor_THT:CP_Axial_L93.0mm_D35.0mm_P100.00mm_Horizontal +Capacitor_THT:CP_Radial_D10.0mm_P2.50mm +Capacitor_THT:CP_Radial_D10.0mm_P2.50mm_P5.00mm +Capacitor_THT:CP_Radial_D10.0mm_P3.50mm +Capacitor_THT:CP_Radial_D10.0mm_P3.80mm +Capacitor_THT:CP_Radial_D10.0mm_P5.00mm +Capacitor_THT:CP_Radial_D10.0mm_P5.00mm_P7.50mm +Capacitor_THT:CP_Radial_D10.0mm_P7.50mm +Capacitor_THT:CP_Radial_D12.5mm_P2.50mm +Capacitor_THT:CP_Radial_D12.5mm_P5.00mm +Capacitor_THT:CP_Radial_D12.5mm_P7.50mm +Capacitor_THT:CP_Radial_D13.0mm_P2.50mm +Capacitor_THT:CP_Radial_D13.0mm_P5.00mm +Capacitor_THT:CP_Radial_D13.0mm_P7.50mm +Capacitor_THT:CP_Radial_D14.0mm_P5.00mm +Capacitor_THT:CP_Radial_D14.0mm_P7.50mm +Capacitor_THT:CP_Radial_D16.0mm_P7.50mm +Capacitor_THT:CP_Radial_D17.0mm_P7.50mm +Capacitor_THT:CP_Radial_D18.0mm_P7.50mm +Capacitor_THT:CP_Radial_D22.0mm_P10.00mm_3pin_SnapIn +Capacitor_THT:CP_Radial_D22.0mm_P10.00mm_SnapIn +Capacitor_THT:CP_Radial_D24.0mm_P10.00mm_3pin_SnapIn +Capacitor_THT:CP_Radial_D24.0mm_P10.00mm_SnapIn +Capacitor_THT:CP_Radial_D25.0mm_P10.00mm_3pin_SnapIn +Capacitor_THT:CP_Radial_D25.0mm_P10.00mm_SnapIn +Capacitor_THT:CP_Radial_D26.0mm_P10.00mm_3pin_SnapIn +Capacitor_THT:CP_Radial_D26.0mm_P10.00mm_SnapIn +Capacitor_THT:CP_Radial_D30.0mm_P10.00mm_3pin_SnapIn +Capacitor_THT:CP_Radial_D30.0mm_P10.00mm_SnapIn +Capacitor_THT:CP_Radial_D35.0mm_P10.00mm_3pin_SnapIn +Capacitor_THT:CP_Radial_D35.0mm_P10.00mm_SnapIn +Capacitor_THT:CP_Radial_D4.0mm_P1.50mm +Capacitor_THT:CP_Radial_D4.0mm_P2.00mm +Capacitor_THT:CP_Radial_D40.0mm_P10.00mm_3pin_SnapIn +Capacitor_THT:CP_Radial_D40.0mm_P10.00mm_SnapIn +Capacitor_THT:CP_Radial_D5.0mm_P2.00mm +Capacitor_THT:CP_Radial_D5.0mm_P2.50mm +Capacitor_THT:CP_Radial_D6.3mm_P2.50mm +Capacitor_THT:CP_Radial_D7.5mm_P2.50mm +Capacitor_THT:CP_Radial_D8.0mm_P2.50mm +Capacitor_THT:CP_Radial_D8.0mm_P3.50mm +Capacitor_THT:CP_Radial_D8.0mm_P3.80mm +Capacitor_THT:CP_Radial_D8.0mm_P5.00mm +Capacitor_THT:CP_Radial_Tantal_D10.5mm_P2.50mm +Capacitor_THT:CP_Radial_Tantal_D10.5mm_P5.00mm +Capacitor_THT:CP_Radial_Tantal_D4.5mm_P2.50mm +Capacitor_THT:CP_Radial_Tantal_D4.5mm_P5.00mm +Capacitor_THT:CP_Radial_Tantal_D5.0mm_P2.50mm +Capacitor_THT:CP_Radial_Tantal_D5.0mm_P5.00mm +Capacitor_THT:CP_Radial_Tantal_D5.5mm_P2.50mm +Capacitor_THT:CP_Radial_Tantal_D5.5mm_P5.00mm +Capacitor_THT:CP_Radial_Tantal_D6.0mm_P2.50mm +Capacitor_THT:CP_Radial_Tantal_D6.0mm_P5.00mm +Capacitor_THT:CP_Radial_Tantal_D7.0mm_P2.50mm +Capacitor_THT:CP_Radial_Tantal_D7.0mm_P5.00mm +Capacitor_THT:CP_Radial_Tantal_D8.0mm_P2.50mm +Capacitor_THT:CP_Radial_Tantal_D8.0mm_P5.00mm +Capacitor_THT:CP_Radial_Tantal_D9.0mm_P2.50mm +Capacitor_THT:CP_Radial_Tantal_D9.0mm_P5.00mm +Capacitor_THT:C_Axial_L12.0mm_D10.5mm_P15.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D10.5mm_P20.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D6.5mm_P15.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D6.5mm_P20.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D7.5mm_P15.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D7.5mm_P20.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D8.5mm_P15.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D8.5mm_P20.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D9.5mm_P15.00mm_Horizontal +Capacitor_THT:C_Axial_L12.0mm_D9.5mm_P20.00mm_Horizontal +Capacitor_THT:C_Axial_L17.0mm_D6.5mm_P20.00mm_Horizontal +Capacitor_THT:C_Axial_L17.0mm_D6.5mm_P25.00mm_Horizontal +Capacitor_THT:C_Axial_L17.0mm_D7.0mm_P20.00mm_Horizontal +Capacitor_THT:C_Axial_L17.0mm_D7.0mm_P25.00mm_Horizontal +Capacitor_THT:C_Axial_L19.0mm_D7.5mm_P25.00mm_Horizontal +Capacitor_THT:C_Axial_L19.0mm_D8.0mm_P25.00mm_Horizontal +Capacitor_THT:C_Axial_L19.0mm_D9.0mm_P25.00mm_Horizontal +Capacitor_THT:C_Axial_L19.0mm_D9.5mm_P25.00mm_Horizontal +Capacitor_THT:C_Axial_L22.0mm_D10.5mm_P27.50mm_Horizontal +Capacitor_THT:C_Axial_L22.0mm_D9.5mm_P27.50mm_Horizontal +Capacitor_THT:C_Axial_L3.8mm_D2.6mm_P10.00mm_Horizontal +Capacitor_THT:C_Axial_L3.8mm_D2.6mm_P12.50mm_Horizontal +Capacitor_THT:C_Axial_L3.8mm_D2.6mm_P15.00mm_Horizontal +Capacitor_THT:C_Axial_L3.8mm_D2.6mm_P7.50mm_Horizontal +Capacitor_THT:C_Axial_L5.1mm_D3.1mm_P10.00mm_Horizontal +Capacitor_THT:C_Axial_L5.1mm_D3.1mm_P12.50mm_Horizontal +Capacitor_THT:C_Axial_L5.1mm_D3.1mm_P15.00mm_Horizontal +Capacitor_THT:C_Axial_L5.1mm_D3.1mm_P7.50mm_Horizontal +Capacitor_THT:C_Disc_D10.0mm_W2.5mm_P5.00mm +Capacitor_THT:C_Disc_D10.5mm_W5.0mm_P10.00mm +Capacitor_THT:C_Disc_D10.5mm_W5.0mm_P5.00mm +Capacitor_THT:C_Disc_D10.5mm_W5.0mm_P7.50mm +Capacitor_THT:C_Disc_D11.0mm_W5.0mm_P10.00mm +Capacitor_THT:C_Disc_D11.0mm_W5.0mm_P5.00mm +Capacitor_THT:C_Disc_D11.0mm_W5.0mm_P7.50mm +Capacitor_THT:C_Disc_D12.0mm_W4.4mm_P7.75mm +Capacitor_THT:C_Disc_D12.5mm_W5.0mm_P10.00mm +Capacitor_THT:C_Disc_D12.5mm_W5.0mm_P7.50mm +Capacitor_THT:C_Disc_D14.5mm_W5.0mm_P10.00mm +Capacitor_THT:C_Disc_D14.5mm_W5.0mm_P7.50mm +Capacitor_THT:C_Disc_D16.0mm_W5.0mm_P10.00mm +Capacitor_THT:C_Disc_D16.0mm_W5.0mm_P7.50mm +Capacitor_THT:C_Disc_D3.0mm_W1.6mm_P2.50mm +Capacitor_THT:C_Disc_D3.0mm_W2.0mm_P2.50mm +Capacitor_THT:C_Disc_D3.4mm_W2.1mm_P2.50mm +Capacitor_THT:C_Disc_D3.8mm_W2.6mm_P2.50mm +Capacitor_THT:C_Disc_D4.3mm_W1.9mm_P5.00mm +Capacitor_THT:C_Disc_D4.7mm_W2.5mm_P5.00mm +Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P2.50mm +Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm +Capacitor_THT:C_Disc_D5.1mm_W3.2mm_P5.00mm +Capacitor_THT:C_Disc_D6.0mm_W2.5mm_P5.00mm +Capacitor_THT:C_Disc_D6.0mm_W4.4mm_P5.00mm +Capacitor_THT:C_Disc_D7.0mm_W2.5mm_P5.00mm +Capacitor_THT:C_Disc_D7.5mm_W2.5mm_P5.00mm +Capacitor_THT:C_Disc_D7.5mm_W4.4mm_P5.00mm +Capacitor_THT:C_Disc_D7.5mm_W5.0mm_P10.00mm +Capacitor_THT:C_Disc_D7.5mm_W5.0mm_P5.00mm +Capacitor_THT:C_Disc_D7.5mm_W5.0mm_P7.50mm +Capacitor_THT:C_Disc_D8.0mm_W2.5mm_P5.00mm +Capacitor_THT:C_Disc_D8.0mm_W5.0mm_P10.00mm +Capacitor_THT:C_Disc_D8.0mm_W5.0mm_P5.00mm +Capacitor_THT:C_Disc_D8.0mm_W5.0mm_P7.50mm +Capacitor_THT:C_Disc_D9.0mm_W2.5mm_P5.00mm +Capacitor_THT:C_Disc_D9.0mm_W5.0mm_P10.00mm +Capacitor_THT:C_Disc_D9.0mm_W5.0mm_P5.00mm +Capacitor_THT:C_Disc_D9.0mm_W5.0mm_P7.50mm +Capacitor_THT:C_Radial_D10.0mm_H12.5mm_P5.00mm +Capacitor_THT:C_Radial_D10.0mm_H16.0mm_P5.00mm +Capacitor_THT:C_Radial_D10.0mm_H20.0mm_P5.00mm +Capacitor_THT:C_Radial_D12.5mm_H20.0mm_P5.00mm +Capacitor_THT:C_Radial_D12.5mm_H25.0mm_P5.00mm +Capacitor_THT:C_Radial_D16.0mm_H25.0mm_P7.50mm +Capacitor_THT:C_Radial_D16.0mm_H31.5mm_P7.50mm +Capacitor_THT:C_Radial_D18.0mm_H35.5mm_P7.50mm +Capacitor_THT:C_Radial_D4.0mm_H5.0mm_P1.50mm +Capacitor_THT:C_Radial_D4.0mm_H7.0mm_P1.50mm +Capacitor_THT:C_Radial_D5.0mm_H11.0mm_P2.00mm +Capacitor_THT:C_Radial_D5.0mm_H5.0mm_P2.00mm +Capacitor_THT:C_Radial_D5.0mm_H7.0mm_P2.00mm +Capacitor_THT:C_Radial_D6.3mm_H11.0mm_P2.50mm +Capacitor_THT:C_Radial_D6.3mm_H5.0mm_P2.50mm +Capacitor_THT:C_Radial_D6.3mm_H7.0mm_P2.50mm +Capacitor_THT:C_Radial_D8.0mm_H11.5mm_P3.50mm +Capacitor_THT:C_Radial_D8.0mm_H7.0mm_P3.50mm +Capacitor_THT:C_Rect_L10.0mm_W2.5mm_P7.50mm_MKS4 +Capacitor_THT:C_Rect_L10.0mm_W3.0mm_P7.50mm_FKS3_FKP3 +Capacitor_THT:C_Rect_L10.0mm_W3.0mm_P7.50mm_MKS4 +Capacitor_THT:C_Rect_L10.0mm_W4.0mm_P7.50mm_FKS3_FKP3 +Capacitor_THT:C_Rect_L10.0mm_W4.0mm_P7.50mm_MKS4 +Capacitor_THT:C_Rect_L10.0mm_W5.0mm_P5.00mm_P7.50mm +Capacitor_THT:C_Rect_L10.3mm_W4.5mm_P7.50mm_MKS4 +Capacitor_THT:C_Rect_L10.3mm_W5.0mm_P7.50mm_MKS4 +Capacitor_THT:C_Rect_L10.3mm_W5.7mm_P7.50mm_MKS4 +Capacitor_THT:C_Rect_L10.3mm_W7.2mm_P7.50mm_MKS4 +Capacitor_THT:C_Rect_L11.0mm_W2.8mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W3.4mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W3.5mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W4.2mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W4.3mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W5.1mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W5.3mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W6.3mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W6.4mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W7.3mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.0mm_W8.8mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W2.0mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W2.6mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W2.8mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W3.2mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W3.5mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W3.6mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W4.0mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W4.3mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W4.5mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W5.0mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W5.1mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W5.2mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W5.6mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W6.4mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W6.6mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W6.9mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W7.3mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W7.5mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W7.8mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W8.0mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W8.8mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W9.5mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L11.5mm_W9.8mm_P10.00mm_MKT +Capacitor_THT:C_Rect_L13.0mm_W3.0mm_P10.00mm_FKS3_FKP3_MKS4 +Capacitor_THT:C_Rect_L13.0mm_W4.0mm_P10.00mm_FKS3_FKP3_MKS4 +Capacitor_THT:C_Rect_L13.0mm_W5.0mm_P10.00mm_FKS3_FKP3_MKS4 +Capacitor_THT:C_Rect_L13.0mm_W6.0mm_P10.00mm_FKS3_FKP3_MKS4 +Capacitor_THT:C_Rect_L13.0mm_W6.5mm_P7.50mm_P10.00mm +Capacitor_THT:C_Rect_L13.0mm_W8.0mm_P10.00mm_FKS3_FKP3_MKS4 +Capacitor_THT:C_Rect_L13.5mm_W4.0mm_P10.00mm_FKS3_FKP3_MKS4 +Capacitor_THT:C_Rect_L13.5mm_W5.0mm_P10.00mm_FKS3_FKP3_MKS4 +Capacitor_THT:C_Rect_L16.5mm_W10.7mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W10.9mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W11.2mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W11.8mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W13.5mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W13.7mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W13.9mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W4.7mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W4.9mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W5.0mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W6.0mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W7.0mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W7.3mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W8.7mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W8.9mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W9.0mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L16.5mm_W9.2mm_P15.00mm_MKT +Capacitor_THT:C_Rect_L18.0mm_W11.0mm_P15.00mm_FKS3_FKP3 +Capacitor_THT:C_Rect_L18.0mm_W5.0mm_P15.00mm_FKS3_FKP3 +Capacitor_THT:C_Rect_L18.0mm_W6.0mm_P15.00mm_FKS3_FKP3 +Capacitor_THT:C_Rect_L18.0mm_W7.0mm_P15.00mm_FKS3_FKP3 +Capacitor_THT:C_Rect_L18.0mm_W8.0mm_P15.00mm_FKS3_FKP3 +Capacitor_THT:C_Rect_L18.0mm_W9.0mm_P15.00mm_FKS3_FKP3 +Capacitor_THT:C_Rect_L19.0mm_W11.0mm_P15.00mm_MKS4 +Capacitor_THT:C_Rect_L19.0mm_W5.0mm_P15.00mm_MKS4 +Capacitor_THT:C_Rect_L19.0mm_W6.0mm_P15.00mm_MKS4 +Capacitor_THT:C_Rect_L19.0mm_W7.0mm_P15.00mm_MKS4 +Capacitor_THT:C_Rect_L19.0mm_W8.0mm_P15.00mm_MKS4 +Capacitor_THT:C_Rect_L19.0mm_W9.0mm_P15.00mm_MKS4 +Capacitor_THT:C_Rect_L24.0mm_W10.1mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L24.0mm_W10.3mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L24.0mm_W10.9mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L24.0mm_W12.2mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L24.0mm_W12.6mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L24.0mm_W12.8mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L24.0mm_W7.0mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L24.0mm_W8.3mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L24.0mm_W8.6mm_P22.50mm_MKT +Capacitor_THT:C_Rect_L26.5mm_W10.5mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L26.5mm_W11.5mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L26.5mm_W5.0mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L26.5mm_W6.0mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L26.5mm_W7.0mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L26.5mm_W8.5mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L27.0mm_W11.0mm_P22.00mm +Capacitor_THT:C_Rect_L27.0mm_W9.0mm_P22.00mm +Capacitor_THT:C_Rect_L27.0mm_W9.0mm_P23.00mm +Capacitor_THT:C_Rect_L28.0mm_W10.0mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L28.0mm_W12.0mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L28.0mm_W8.0mm_P22.50mm_MKS4 +Capacitor_THT:C_Rect_L29.0mm_W11.0mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W11.9mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W12.2mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W13.0mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W13.8mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W14.2mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W16.0mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W7.6mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W7.8mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W7.9mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W9.1mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L29.0mm_W9.6mm_P27.50mm_MKT +Capacitor_THT:C_Rect_L31.5mm_W11.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L31.5mm_W13.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L31.5mm_W15.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L31.5mm_W17.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L31.5mm_W20.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L31.5mm_W9.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L32.0mm_W15.0mm_P27.00mm +Capacitor_THT:C_Rect_L33.0mm_W13.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L33.0mm_W15.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L33.0mm_W20.0mm_P27.50mm_MKS4 +Capacitor_THT:C_Rect_L4.0mm_W2.5mm_P2.50mm +Capacitor_THT:C_Rect_L4.6mm_W2.0mm_P2.50mm_MKS02_FKP02 +Capacitor_THT:C_Rect_L4.6mm_W3.0mm_P2.50mm_MKS02_FKP02 +Capacitor_THT:C_Rect_L4.6mm_W3.8mm_P2.50mm_MKS02_FKP02 +Capacitor_THT:C_Rect_L4.6mm_W4.6mm_P2.50mm_MKS02_FKP02 +Capacitor_THT:C_Rect_L4.6mm_W5.5mm_P2.50mm_MKS02_FKP02 +Capacitor_THT:C_Rect_L41.5mm_W11.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W13.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W15.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W17.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W19.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W20.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W24.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W31.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W35.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W40.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L41.5mm_W9.0mm_P37.50mm_MKS4 +Capacitor_THT:C_Rect_L7.0mm_W2.0mm_P5.00mm +Capacitor_THT:C_Rect_L7.0mm_W2.5mm_P5.00mm +Capacitor_THT:C_Rect_L7.0mm_W3.5mm_P2.50mm_P5.00mm +Capacitor_THT:C_Rect_L7.0mm_W3.5mm_P5.00mm +Capacitor_THT:C_Rect_L7.0mm_W4.5mm_P5.00mm +Capacitor_THT:C_Rect_L7.0mm_W6.0mm_P5.00mm +Capacitor_THT:C_Rect_L7.0mm_W6.5mm_P5.00mm +Capacitor_THT:C_Rect_L7.2mm_W11.0mm_P5.00mm_FKS2_FKP2_MKS2_MKP2 +Capacitor_THT:C_Rect_L7.2mm_W2.5mm_P5.00mm_FKS2_FKP2_MKS2_MKP2 +Capacitor_THT:C_Rect_L7.2mm_W3.0mm_P5.00mm_FKS2_FKP2_MKS2_MKP2 +Capacitor_THT:C_Rect_L7.2mm_W3.5mm_P5.00mm_FKS2_FKP2_MKS2_MKP2 +Capacitor_THT:C_Rect_L7.2mm_W4.5mm_P5.00mm_FKS2_FKP2_MKS2_MKP2 +Capacitor_THT:C_Rect_L7.2mm_W5.5mm_P5.00mm_FKS2_FKP2_MKS2_MKP2 +Capacitor_THT:C_Rect_L7.2mm_W7.2mm_P5.00mm_FKS2_FKP2_MKS2_MKP2 +Capacitor_THT:C_Rect_L7.2mm_W8.5mm_P5.00mm_FKP2_FKP2_MKS2_MKP2 +Capacitor_THT:C_Rect_L7.5mm_W6.5mm_P5.00mm +Capacitor_THT:C_Rect_L9.0mm_W2.5mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W2.6mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W2.7mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W3.2mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W3.3mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W3.4mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W3.6mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W3.8mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W3.9mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W4.0mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W4.2mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W4.9mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W5.1mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W5.7mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W6.4mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W6.7mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W7.7mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W8.5mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W9.5mm_P7.50mm_MKT +Capacitor_THT:C_Rect_L9.0mm_W9.8mm_P7.50mm_MKT +Capacitor_THT:DX_5R5HxxxxU_D11.5mm_P10.00mm +Capacitor_THT:DX_5R5VxxxxU_D11.5mm_P5.00mm +Capacitor_THT:DX_5R5VxxxxU_D19.0mm_P5.00mm +Connector:Banana_Cliff_FCR7350B_S16N-PC_Horizontal +Connector:Banana_Cliff_FCR7350G_S16N-PC_Horizontal +Connector:Banana_Cliff_FCR7350L_S16N-PC_Horizontal +Connector:Banana_Cliff_FCR7350N_S16N-PC_Horizontal +Connector:Banana_Cliff_FCR7350R_S16N-PC_Horizontal +Connector:Banana_Cliff_FCR7350Y_S16N-PC_Horizontal +Connector:Banana_Jack_1Pin +Connector:Banana_Jack_2Pin +Connector:Banana_Jack_3Pin +Connector:CalTest_CT3151 +Connector:Connector_SFP_and_Cage +Connector:CUI_PD-30 +Connector:CUI_PD-30S +Connector:CUI_PD-30S_CircularHoles +Connector:DTF13-12Px +Connector:FanPinHeader_1x03_P2.54mm_Vertical +Connector:FanPinHeader_1x04_P2.54mm_Vertical +Connector:GB042-34S-H10 +Connector:IHI_B6A-PCB-45_Vertical +Connector:Joint-Tech_C5080WR-04P_1x04_P5.08mm_Vertical +Connector:JWT_A3963_1x02_P3.96mm_Vertical +Connector:NS-Tech_Grove_1x04_P2mm_Vertical +Connector:OCN_OK-01GM030-04_2x15_P0.4mm_Vertical +Connector:SpringContact_Harwin_S1941-46R +Connector:Tag-Connect_TC2030-IDC-FP_2x03_P1.27mm_Vertical +Connector:Tag-Connect_TC2030-IDC-NL_2x03_P1.27mm_Vertical +Connector:Tag-Connect_TC2050-IDC-FP_2x05_P1.27mm_Vertical +Connector:Tag-Connect_TC2050-IDC-NL_2x05_P1.27mm_Vertical +Connector:Tag-Connect_TC2050-IDC-NL_2x05_P1.27mm_Vertical_with_bottom_clip +Connector:Tag-Connect_TC2070-IDC-FP_2x07_P1.27mm_Vertical +Connector_AMASS:AMASS_MR30PW-FB_1x03_P3.50mm_Horizontal +Connector_AMASS:AMASS_MR30PW-M_1x03_P3.50mm_Horizontal +Connector_AMASS:AMASS_XT30PW-F_1x02_P2.50mm_Horizontal +Connector_AMASS:AMASS_XT30PW-M_1x02_P2.50mm_Horizontal +Connector_AMASS:AMASS_XT30U-F_1x02_P5.0mm_Vertical +Connector_AMASS:AMASS_XT30U-M_1x02_P5.0mm_Vertical +Connector_AMASS:AMASS_XT30UPB-F_1x02_P5.0mm_Vertical +Connector_AMASS:AMASS_XT30UPB-M_1x02_P5.0mm_Vertical +Connector_AMASS:AMASS_XT60-F_1x02_P7.20mm_Vertical +Connector_AMASS:AMASS_XT60-M_1x02_P7.20mm_Vertical +Connector_AMASS:AMASS_XT60IPW-M_1x03_P7.20mm_Horizontal +Connector_AMASS:AMASS_XT60PW-F_1x02_P7.20mm_Horizontal +Connector_AMASS:AMASS_XT60PW-M_1x02_P7.20mm_Horizontal +Connector_AMASS:AMASS_XT90PW-M_1x02_P10.90mm_Horizontal +Connector_Amphenol:Amphenol_M8S-03PMMR-SF8001 +Connector_Audio:Jack_3.5mm_CUI_SJ-3523-SMT_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ-3524-SMT_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3513N_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3514N_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3515N_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3523N_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3524N_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3525N_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3533NG_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3533NG_Horizontal_CircularHoles +Connector_Audio:Jack_3.5mm_CUI_SJ1-3535NG_Horizontal +Connector_Audio:Jack_3.5mm_CUI_SJ1-3535NG_Horizontal_CircularHoles +Connector_Audio:Jack_3.5mm_CUI_SJ2-3593D-SMT_Horizontal +Connector_Audio:Jack_3.5mm_KoreanHropartsElec_PJ-320D-4A_Horizontal +Connector_Audio:Jack_3.5mm_Ledino_KB3SPRS_Horizontal +Connector_Audio:Jack_3.5mm_Lumberg_1503_02_Horizontal +Connector_Audio:Jack_3.5mm_Lumberg_1503_03_Horizontal +Connector_Audio:Jack_3.5mm_Lumberg_1503_07_Horizontal +Connector_Audio:Jack_3.5mm_PJ31060-I_Horizontal +Connector_Audio:Jack_3.5mm_PJ311_Horizontal +Connector_Audio:Jack_3.5mm_PJ320D_Horizontal +Connector_Audio:Jack_3.5mm_PJ320E_Horizontal +Connector_Audio:Jack_3.5mm_QingPu_WQP-PJ398SM_Vertical_CircularHoles +Connector_Audio:Jack_3.5mm_Switronic_ST-005-G_horizontal +Connector_Audio:Jack_3.5mm_Technik_TWP-3002_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NJ2FD-V_Vertical +Connector_Audio:Jack_6.35mm_Neutrik_NJ3FD-V_Vertical +Connector_Audio:Jack_6.35mm_Neutrik_NJ5FD-V_Vertical +Connector_Audio:Jack_6.35mm_Neutrik_NJ6FD-V_Vertical +Connector_Audio:Jack_6.35mm_Neutrik_NJ6TB-V_Vertical +Connector_Audio:Jack_6.35mm_Neutrik_NMJ4HCD2_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ4HFD2_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ4HFD3_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ4HHD2_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ6HCD2_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ6HCD3_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ6HFD2-AU_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ6HFD2_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ6HFD3_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ6HFD4_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NMJ6HHD2_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ3HF-1_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ4HF-1_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ4HF_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ4HH-1_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ4HH_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HF-1-AU_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HF-1_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HF-AU_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HF_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HH-1_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HH-AU_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HH_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HM-1-AU_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HM-1-PRE_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NRJ6HM-1_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NSJ12HC_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NSJ12HF-1_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NSJ12HH-1_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NSJ12HL_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NSJ8HC_Horizontal +Connector_Audio:Jack_6.35mm_Neutrik_NSJ8HL_Horizontal +Connector_Audio:Jack_speakON-6.35mm_Neutrik_NLJ2MDXX-H_Horizontal +Connector_Audio:Jack_speakON-6.35mm_Neutrik_NLJ2MDXX-V_Vertical +Connector_Audio:Jack_speakON_Neutrik_NL2MDXX-H-3_Horizontal +Connector_Audio:Jack_speakON_Neutrik_NL2MDXX-V_Vertical +Connector_Audio:Jack_speakON_Neutrik_NL4MDXX-H-2_Horizontal +Connector_Audio:Jack_speakON_Neutrik_NL4MDXX-H-3_Horizontal +Connector_Audio:Jack_speakON_Neutrik_NL4MDXX-V-2_Vertical +Connector_Audio:Jack_speakON_Neutrik_NL4MDXX-V-3_Vertical +Connector_Audio:Jack_speakON_Neutrik_NL4MDXX-V_Vertical +Connector_Audio:Jack_speakON_Neutrik_NL8MDXX-V-3_Vertical +Connector_Audio:Jack_speakON_Neutrik_NL8MDXX-V_Vertical +Connector_Audio:Jack_speakON_Neutrik_NLT4MD-V_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ10FI-H-0_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ10FI-H_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ10FI-V-0_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ10FI-V_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ5FI-H-0_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ5FI-H_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ5FI-V-0_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ5FI-V_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FA-H-0_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FA-H-DA_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FA-H_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FA-V-0_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FA-V-DA_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FA-V_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FI-H-0_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FI-H_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FI-V-0_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ6FI-V_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ9FI-H-0_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ9FI-H_Horizontal +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ9FI-V-0_Vertical +Connector_Audio:Jack_XLR-6.35mm_Neutrik_NCJ9FI-V_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAAH-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAAH1-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAAH1-DA_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAAH1_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAAH2-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAAH2_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAAH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAAV-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAAV1-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAAV1-DA_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAAV1_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAAV2-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAAV2_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAAV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAH-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAH1-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAH1-DA_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAH1_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAH2-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAH2-DA_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAH2_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAHL-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAHL1-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAHL1_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAHR-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAHR1-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAHR1_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAHR2-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAHR2_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FAV-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAV1-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAV1-DA_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAV1_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAV2-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAV2-DA_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAV2_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FAV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FBH1-B_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBH1-DA_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBH1-E_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBH1_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBH2-B_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBH2-DA_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBH2-E_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBH2_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBHL1_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3FBV1-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FBV1-B_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FBV1-DA_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FBV1_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FBV2-B_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FBV2-DA_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FBV2-SW_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3FBV2_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MAAH-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAAH-1_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAAH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAAV-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MAAV-1_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MAAV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MAFH-PH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAH-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAHL_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAHR_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAMH-PH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MAV-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MAV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MBH-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBH-1_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBH-B_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBH-E_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBHL-B_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBHL_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBHR-B_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBHR_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC3MBV-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MBV-1_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MBV-B_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MBV-E_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MBV-SW_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC3MBV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC4FAH-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC4FAH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC4FAV-0_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC4FAV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC4FBH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC4FBV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC4MAH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC4MAV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC4MBH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC4MBV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5FAH-0_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC5FAH-DA_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC5FAH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC5FAV-DA_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5FAV-SW_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5FAV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5FBH-B_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC5FBH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC5FBV-B_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5FBV-SW_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5FBV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5MAH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC5MAV-SW_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5MAV_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5MBH-B_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC5MBH_Horizontal +Connector_Audio:Jack_XLR_Neutrik_NC5MBV-B_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5MBV-SW_Vertical +Connector_Audio:Jack_XLR_Neutrik_NC5MBV_Vertical +Connector_Audio:MiniXLR-5_Switchcraft_TRAPC_Horizontal +Connector_Audio:Plug_3.5mm_CUI_SP-3541 +Connector_BarrelJack:BarrelJack_CLIFF_FC681465S_SMT_Horizontal +Connector_BarrelJack:BarrelJack_CUI_PJ-036AH-SMT_Horizontal +Connector_BarrelJack:BarrelJack_CUI_PJ-063AH_Horizontal +Connector_BarrelJack:BarrelJack_CUI_PJ-063AH_Horizontal_CircularHoles +Connector_BarrelJack:BarrelJack_CUI_PJ-079BH_Horizontal +Connector_BarrelJack:BarrelJack_CUI_PJ-102AH_Horizontal +Connector_BarrelJack:BarrelJack_GCT_DCJ200-10-A_Horizontal +Connector_BarrelJack:BarrelJack_Horizontal +Connector_BarrelJack:BarrelJack_Kycon_KLDX-0202-xC_Horizontal +Connector_BarrelJack:BarrelJack_SwitchcraftConxall_RAPC10U_Horizontal +Connector_BarrelJack:BarrelJack_Wuerth_694102107102_1.0x3.9mm +Connector_BarrelJack:BarrelJack_Wuerth_694103107102_1.35x3.9mm +Connector_BarrelJack:BarrelJack_Wuerth_694106106102_2.0x5.5mm +Connector_BarrelJack:BarrelJack_Wuerth_694108106102_2.5x5.5mm +Connector_BarrelJack:BarrelJack_Wuerth_6941xx301002 +Connector_Card:CF-Card_3M_N7E50-A516xx-30 +Connector_Card:CF-Card_3M_N7E50-E516xx-30 +Connector_Card:microSD_HC_Hirose_DM3AT-SF-PEJM5 +Connector_Card:microSD_HC_Hirose_DM3BT-DSF-PEJS +Connector_Card:microSD_HC_Hirose_DM3D-SF +Connector_Card:microSD_HC_Molex_104031-0811 +Connector_Card:microSD_HC_Molex_47219-2001 +Connector_Card:microSD_HC_Wuerth_693072010801 +Connector_Card:microSIM_JAE_SF53S006VCBR2000 +Connector_Card:nanoSIM_GCT_SIM8060-6-0-14-00 +Connector_Card:nanoSIM_GCT_SIM8060-6-1-14-00 +Connector_Card:nanoSIM_Hinged_CUI_NSIM-2-C +Connector_Card:SD-SIM_microSD-microSIM_Molex_104168-1620 +Connector_Card:SD_Card_Device_16mm_SlotDepth +Connector_Card:SD_Hirose_DM1AA_SF_PEJ82 +Connector_Card:SD_Kyocera_145638009211859+ +Connector_Card:SD_Kyocera_145638009511859+ +Connector_Card:SD_Kyocera_145638109211859+ +Connector_Card:SD_Kyocera_145638109511859+ +Connector_Card:SD_TE_2041021 +Connector_Coaxial:BNC_Amphenol_031-5539_Vertical +Connector_Coaxial:BNC_Amphenol_031-6575_Horizontal +Connector_Coaxial:BNC_Amphenol_B6252HB-NPP3G-50_Horizontal +Connector_Coaxial:BNC_PanelMountable_Vertical +Connector_Coaxial:BNC_TEConnectivity_1478035_Horizontal +Connector_Coaxial:BNC_TEConnectivity_1478204_Vertical +Connector_Coaxial:BNC_Win_364A2x95_Horizontal +Connector_Coaxial:CoaxialSwitch_Hirose_MS-156C3_Horizontal +Connector_Coaxial:LEMO-EPG.00.302.NLN +Connector_Coaxial:LEMO-EPL.00.250.NTN +Connector_Coaxial:MMCX_Molex_73415-0961_Horizontal_0.8mm-PCB +Connector_Coaxial:MMCX_Molex_73415-0961_Horizontal_1.0mm-PCB +Connector_Coaxial:MMCX_Molex_73415-0961_Horizontal_1.6mm-PCB +Connector_Coaxial:MMCX_Molex_73415-1471_Vertical +Connector_Coaxial:SMA_Amphenol_132134-10_Vertical +Connector_Coaxial:SMA_Amphenol_132134-11_Vertical +Connector_Coaxial:SMA_Amphenol_132134-14_Vertical +Connector_Coaxial:SMA_Amphenol_132134-16_Vertical +Connector_Coaxial:SMA_Amphenol_132134_Vertical +Connector_Coaxial:SMA_Amphenol_132203-12_Horizontal +Connector_Coaxial:SMA_Amphenol_132289_EdgeMount +Connector_Coaxial:SMA_Amphenol_132291-12_Vertical +Connector_Coaxial:SMA_Amphenol_132291_Vertical +Connector_Coaxial:SMA_Amphenol_901-143_Horizontal +Connector_Coaxial:SMA_Amphenol_901-144_Vertical +Connector_Coaxial:SMA_BAT_Wireless_BWSMA-KWE-Z001 +Connector_Coaxial:SMA_Molex_73251-1153_EdgeMount_Horizontal +Connector_Coaxial:SMA_Molex_73251-2120_EdgeMount_Horizontal +Connector_Coaxial:SMA_Molex_73251-2200_Horizontal +Connector_Coaxial:SMA_Samtec_SMA-J-P-H-ST-EM1_EdgeMount +Connector_Coaxial:SMA_Wurth_60312002114503_Vertical +Connector_Coaxial:SMA_Wurth_60312102114405_Vertical +Connector_Coaxial:SMB_Jack_Vertical +Connector_Coaxial:U.FL_Hirose_U.FL-R-SMT-1_Vertical +Connector_Coaxial:U.FL_Molex_MCRF_73412-0110_Vertical +Connector_Coaxial:WR-MMCX_Wuerth_66011102111302_Horizontal +Connector_Coaxial:WR-MMCX_Wuerth_66012102111404_Vertical +Connector_DIN:DIN41612_B2_2x16_Female_Vertical_THT +Connector_DIN:DIN41612_B2_2x16_Male_Horizontal_THT +Connector_DIN:DIN41612_B2_2x8_Female_Vertical_THT +Connector_DIN:DIN41612_B2_2x8_Male_Horizontal_THT +Connector_DIN:DIN41612_B3_2x10_Female_Vertical_THT +Connector_DIN:DIN41612_B3_2x10_Male_Horizontal_THT +Connector_DIN:DIN41612_B3_2x5_Female_Vertical_THT +Connector_DIN:DIN41612_B3_2x5_Male_Horizontal_THT +Connector_DIN:DIN41612_B_1x32_Female_Vertical_THT +Connector_DIN:DIN41612_B_1x32_Male_Horizontal_THT +Connector_DIN:DIN41612_B_2x16_Female_Vertical_THT +Connector_DIN:DIN41612_B_2x16_Male_Horizontal_THT +Connector_DIN:DIN41612_B_2x32_Female_Vertical_THT +Connector_DIN:DIN41612_B_2x32_Male_Horizontal_THT +Connector_DIN:DIN41612_C2_2x16_Female_Vertical_THT +Connector_DIN:DIN41612_C2_2x16_Male_Horizontal_THT +Connector_DIN:DIN41612_C2_3x16_Female_Vertical_THT +Connector_DIN:DIN41612_C2_3x16_Male_Horizontal_THT +Connector_DIN:DIN41612_C3_2x10_Female_Vertical_THT +Connector_DIN:DIN41612_C3_2x10_Male_Horizontal_THT +Connector_DIN:DIN41612_C3_3x10_Female_Vertical_THT +Connector_DIN:DIN41612_C3_3x10_Male_Horizontal_THT +Connector_DIN:DIN41612_C_1x32_Female_Vertical_THT +Connector_DIN:DIN41612_C_1x32_Male_Horizontal_THT +Connector_DIN:DIN41612_C_2x16_Female_Vertical_THT +Connector_DIN:DIN41612_C_2x16_Male_Horizontal_THT +Connector_DIN:DIN41612_C_2x32_Female_Vertical_THT +Connector_DIN:DIN41612_C_2x32_Male_Horizontal_THT +Connector_DIN:DIN41612_C_3x16_Female_Vertical_THT +Connector_DIN:DIN41612_C_3x16_Male_Horizontal_THT +Connector_DIN:DIN41612_C_3x32_Female_Vertical_THT +Connector_DIN:DIN41612_C_3x32_Male_Horizontal_THT +Connector_DIN:DIN41612_D_2x16_Female_Vertical_THT +Connector_DIN:DIN41612_D_2x16_Male_Horizontal_THT +Connector_DIN:DIN41612_D_2x8_Female_Vertical_THT +Connector_DIN:DIN41612_D_2x8_Male_Horizontal_THT +Connector_DIN:DIN41612_E_2x16_Female_Vertical_THT +Connector_DIN:DIN41612_E_2x16_Male_Horizontal_THT +Connector_DIN:DIN41612_E_2x16_RowsAC_Female_Vertical_THT +Connector_DIN:DIN41612_E_2x16_RowsAC_Male_Horizontal_THT +Connector_DIN:DIN41612_E_3x16_Female_Vertical_THT +Connector_DIN:DIN41612_E_3x16_Male_Horizontal_THT +Connector_DIN:DIN41612_F_2x16_Female_Vertical_THT +Connector_DIN:DIN41612_F_2x16_Male_Horizontal_THT +Connector_DIN:DIN41612_F_2x16_RowsZD_Female_Vertical_THT +Connector_DIN:DIN41612_F_2x16_RowsZD_Male_Horizontal_THT +Connector_DIN:DIN41612_F_3x16_Female_Vertical_THT +Connector_DIN:DIN41612_F_3x16_Male_Horizontal_THT +Connector_DIN:DIN41612_Q2_2x16_Female_Horizontal_THT +Connector_DIN:DIN41612_Q2_2x16_Male_Vertical_THT +Connector_DIN:DIN41612_Q3_2x10_Female_Horizontal_THT +Connector_DIN:DIN41612_Q3_2x10_Male_Vertical_THT +Connector_DIN:DIN41612_Q_2x32_Female_Horizontal_THT +Connector_DIN:DIN41612_Q_2x32_Male_Vertical_THT +Connector_DIN:DIN41612_R2_2x16_Female_Horizontal_THT +Connector_DIN:DIN41612_R2_2x16_Male_Vertical_THT +Connector_DIN:DIN41612_R2_3x16_Female_Horizontal_THT +Connector_DIN:DIN41612_R2_3x16_Male_Vertical_THT +Connector_DIN:DIN41612_R3_2x10_Female_Horizontal_THT +Connector_DIN:DIN41612_R3_2x10_Male_Vertical_THT +Connector_DIN:DIN41612_R3_3x10_Female_Horizontal_THT +Connector_DIN:DIN41612_R3_3x10_Male_Vertical_THT +Connector_DIN:DIN41612_R_1x32_Female_Horizontal_THT +Connector_DIN:DIN41612_R_1x32_Male_Vertical_THT +Connector_DIN:DIN41612_R_2x16_Female_Horizontal_THT +Connector_DIN:DIN41612_R_2x16_Male_Vertical_THT +Connector_DIN:DIN41612_R_2x32_Female_Horizontal_THT +Connector_DIN:DIN41612_R_2x32_Male_Vertical_THT +Connector_DIN:DIN41612_R_3x16_Female_Horizontal_THT +Connector_DIN:DIN41612_R_3x16_Male_Vertical_THT +Connector_DIN:DIN41612_R_3x32_Female_Horizontal_THT +Connector_DIN:DIN41612_R_3x32_Male_Vertical_THT +Connector_Dsub:DSUB-15-HD_Pins_Horizontal_P2.29x1.90mm_EdgePinOffset3.03mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-15-HD_Pins_Horizontal_P2.29x2.54mm_EdgePinOffset8.35mm_Housed_MountingHolesOffset10.89mm +Connector_Dsub:DSUB-15-HD_Pins_Vertical_P2.29x1.98mm_MountingHoles +Connector_Dsub:DSUB-15-HD_Socket_Horizontal_P2.29x1.90mm_EdgePinOffset3.03mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-15-HD_Socket_Horizontal_P2.29x2.54mm_EdgePinOffset8.35mm_Housed_MountingHolesOffset10.89mm +Connector_Dsub:DSUB-15-HD_Socket_Vertical_P2.29x1.98mm_MountingHoles +Connector_Dsub:DSUB-15_Pins_EdgeMount_P2.77mm +Connector_Dsub:DSUB-15_Pins_Horizontal_P2.77x2.54mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-15_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset15.98mm +Connector_Dsub:DSUB-15_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset8.20mm +Connector_Dsub:DSUB-15_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset4.94mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-15_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset7.70mm_Housed_MountingHolesOffset9.12mm +Connector_Dsub:DSUB-15_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-15_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset9.90mm_Housed_MountingHolesOffset11.32mm +Connector_Dsub:DSUB-15_Pins_Vertical_P2.77x2.84mm +Connector_Dsub:DSUB-15_Pins_Vertical_P2.77x2.84mm_MountingHoles +Connector_Dsub:DSUB-15_Socket_EdgeMount_P2.77mm +Connector_Dsub:DSUB-15_Socket_Horizontal_P2.77x2.54mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-15_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset15.98mm +Connector_Dsub:DSUB-15_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset8.20mm +Connector_Dsub:DSUB-15_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset4.94mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-15_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset7.70mm_Housed_MountingHolesOffset9.12mm +Connector_Dsub:DSUB-15_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-15_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset9.90mm_Housed_MountingHolesOffset11.32mm +Connector_Dsub:DSUB-15_Socket_Vertical_P2.77x2.84mm +Connector_Dsub:DSUB-15_Socket_Vertical_P2.77x2.84mm_MountingHoles +Connector_Dsub:DSUB-25_Pins_EdgeMount_P2.77mm +Connector_Dsub:DSUB-25_Pins_Horizontal_P2.77x2.54mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-25_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset15.98mm +Connector_Dsub:DSUB-25_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset8.20mm +Connector_Dsub:DSUB-25_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset4.94mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-25_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset7.70mm_Housed_MountingHolesOffset9.12mm +Connector_Dsub:DSUB-25_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-25_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset9.90mm_Housed_MountingHolesOffset11.32mm +Connector_Dsub:DSUB-25_Pins_Vertical_P2.77x2.84mm +Connector_Dsub:DSUB-25_Pins_Vertical_P2.77x2.84mm_MountingHoles +Connector_Dsub:DSUB-25_Socket_EdgeMount_P2.77mm +Connector_Dsub:DSUB-25_Socket_Horizontal_P2.77x2.54mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-25_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset15.98mm +Connector_Dsub:DSUB-25_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset8.20mm +Connector_Dsub:DSUB-25_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset4.94mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-25_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset7.70mm_Housed_MountingHolesOffset9.12mm +Connector_Dsub:DSUB-25_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-25_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset9.90mm_Housed_MountingHolesOffset11.32mm +Connector_Dsub:DSUB-25_Socket_Vertical_P2.77x2.84mm +Connector_Dsub:DSUB-25_Socket_Vertical_P2.77x2.84mm_MountingHoles +Connector_Dsub:DSUB-26-HD_Pins_Horizontal_P2.29x1.90mm_EdgePinOffset3.03mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-26-HD_Pins_Horizontal_P2.29x2.54mm_EdgePinOffset8.35mm_Housed_MountingHolesOffset10.89mm +Connector_Dsub:DSUB-26-HD_Pins_Vertical_P2.29x1.98mm_MountingHoles +Connector_Dsub:DSUB-26-HD_Socket_Horizontal_P2.29x1.90mm_EdgePinOffset3.03mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-26-HD_Socket_Horizontal_P2.29x2.54mm_EdgePinOffset8.35mm_Housed_MountingHolesOffset10.89mm +Connector_Dsub:DSUB-26-HD_Socket_Vertical_P2.29x1.98mm_MountingHoles +Connector_Dsub:DSUB-37_Pins_EdgeMount_P2.77mm +Connector_Dsub:DSUB-37_Pins_Horizontal_P2.77x2.54mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-37_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset15.98mm +Connector_Dsub:DSUB-37_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset8.20mm +Connector_Dsub:DSUB-37_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset4.94mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-37_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset7.70mm_Housed_MountingHolesOffset9.12mm +Connector_Dsub:DSUB-37_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-37_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset9.90mm_Housed_MountingHolesOffset11.32mm +Connector_Dsub:DSUB-37_Pins_Vertical_P2.77x2.84mm +Connector_Dsub:DSUB-37_Pins_Vertical_P2.77x2.84mm_MountingHoles +Connector_Dsub:DSUB-37_Socket_EdgeMount_P2.77mm +Connector_Dsub:DSUB-37_Socket_Horizontal_P2.77x2.54mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-37_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset15.98mm +Connector_Dsub:DSUB-37_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset8.20mm +Connector_Dsub:DSUB-37_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset4.94mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-37_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset7.70mm_Housed_MountingHolesOffset9.12mm +Connector_Dsub:DSUB-37_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-37_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset9.90mm_Housed_MountingHolesOffset11.32mm +Connector_Dsub:DSUB-37_Socket_Vertical_P2.77x2.84mm +Connector_Dsub:DSUB-37_Socket_Vertical_P2.77x2.84mm_MountingHoles +Connector_Dsub:DSUB-44-HD_Pins_Horizontal_P2.29x1.90mm_EdgePinOffset3.03mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-44-HD_Pins_Horizontal_P2.29x2.54mm_EdgePinOffset8.35mm_Housed_MountingHolesOffset10.89mm +Connector_Dsub:DSUB-44-HD_Pins_Vertical_P2.29x1.98mm_MountingHoles +Connector_Dsub:DSUB-44-HD_Socket_Horizontal_P2.29x1.90mm_EdgePinOffset3.03mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-44-HD_Socket_Horizontal_P2.29x2.54mm_EdgePinOffset8.35mm_Housed_MountingHolesOffset10.89mm +Connector_Dsub:DSUB-44-HD_Socket_Vertical_P2.29x1.98mm_MountingHoles +Connector_Dsub:DSUB-62-HD_Pins_Horizontal_P2.41x1.90mm_EdgePinOffset3.03mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-62-HD_Pins_Horizontal_P2.41x2.54mm_EdgePinOffset8.35mm_Housed_MountingHolesOffset10.89mm +Connector_Dsub:DSUB-62-HD_Pins_Vertical_P2.41x1.98mm_MountingHoles +Connector_Dsub:DSUB-62-HD_Socket_Horizontal_P2.41x1.90mm_EdgePinOffset3.03mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-62-HD_Socket_Horizontal_P2.41x2.54mm_EdgePinOffset8.35mm_Housed_MountingHolesOffset10.89mm +Connector_Dsub:DSUB-62-HD_Socket_Vertical_P2.41x1.98mm_MountingHoles +Connector_Dsub:DSUB-9_Pins_EdgeMount_P2.77mm +Connector_Dsub:DSUB-9_Pins_Horizontal_P2.77x2.54mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-9_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset15.98mm +Connector_Dsub:DSUB-9_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset8.20mm +Connector_Dsub:DSUB-9_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset4.94mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-9_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset7.70mm_Housed_MountingHolesOffset9.12mm +Connector_Dsub:DSUB-9_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-9_Pins_Horizontal_P2.77x2.84mm_EdgePinOffset9.90mm_Housed_MountingHolesOffset11.32mm +Connector_Dsub:DSUB-9_Pins_Vertical_P2.77x2.84mm +Connector_Dsub:DSUB-9_Pins_Vertical_P2.77x2.84mm_MountingHoles +Connector_Dsub:DSUB-9_Socket_EdgeMount_P2.77mm +Connector_Dsub:DSUB-9_Socket_Horizontal_P2.77x2.54mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-9_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset15.98mm +Connector_Dsub:DSUB-9_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset14.56mm_Housed_MountingHolesOffset8.20mm +Connector_Dsub:DSUB-9_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset4.94mm_Housed_MountingHolesOffset4.94mm +Connector_Dsub:DSUB-9_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset7.70mm_Housed_MountingHolesOffset9.12mm +Connector_Dsub:DSUB-9_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset9.40mm +Connector_Dsub:DSUB-9_Socket_Horizontal_P2.77x2.84mm_EdgePinOffset9.90mm_Housed_MountingHolesOffset11.32mm +Connector_Dsub:DSUB-9_Socket_Vertical_P2.77x2.84mm +Connector_Dsub:DSUB-9_Socket_Vertical_P2.77x2.84mm_MountingHoles +Connector_FFC-FPC:Hirose_FH12-10S-0.5SH_1x10-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-11S-0.5SH_1x11-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-12S-0.5SH_1x12-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-13S-0.5SH_1x13-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-14S-0.5SH_1x14-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-15S-0.5SH_1x15-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-16S-0.5SH_1x16-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-17S-0.5SH_1x17-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-18S-0.5SH_1x18-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-19S-0.5SH_1x19-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-20S-0.5SH_1x20-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-22S-0.5SH_1x22-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-24S-0.5SH_1x24-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-25S-0.5SH_1x25-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-26S-0.5SH_1x26-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-28S-0.5SH_1x28-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-30S-0.5SH_1x30-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-32S-0.5SH_1x32-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-33S-0.5SH_1x33-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-34S-0.5SH_1x34-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-35S-0.5SH_1x35-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-36S-0.5SH_1x36-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-40S-0.5SH_1x40-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-45S-0.5SH_1x45-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-50S-0.5SH_1x50-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-53S-0.5SH_1x53-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-6S-0.5SH_1x06-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH12-8S-0.5SH_1x08-1MP_P0.50mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-13S-0.3SHW_2Rows-13Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-15S-0.3SHW_2Rows-15Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-17S-0.3SHW_2Rows-17Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-19S-0.3SHW_2Rows-19Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-21S-0.3SHW_2Rows-21Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-23S-0.3SHW_2Rows-23Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-25S-0.3SHW_2Rows-25Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-27S-0.3SHW_2Rows-27Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-29S-0.3SHW_2Rows-29Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-31S-0.3SHW_2Rows-31Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-33S-0.3SHW_2Rows-33Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-35S-0.3SHW_2Rows-35Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-37S-0.3SHW_2Rows-37Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-39S-0.3SHW_2Rows-39Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-41S-0.3SHW_2Rows-41Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-45S-0.3SHW_2Rows-45Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-51S-0.3SHW_2Rows-51Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-55S-0.3SHW_2Rows-55Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-57S-0.3SHW_2Rows-57Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-61S-0.3SHW_2Rows-61Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH26-71S-0.3SHW_2Rows-71Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Hirose_FH41-30S-0.5SH_1x30_1MP_1SH_P0.5mm_Horizontal +Connector_FFC-FPC:JAE_FF0825SA1_2Rows-25Pins_P0.40mm_Horizontal +Connector_FFC-FPC:JAE_FF0829SA1_2Rows-29Pins_P0.40mm_Horizontal +Connector_FFC-FPC:JAE_FF0841SA1_2Rows-41Pins_P0.40mm_Horizontal +Connector_FFC-FPC:JAE_FF0851SA1_2Rows-51Pins_P0.40mm_Horizontal +Connector_FFC-FPC:JAE_FF0871SA1_2Rows-71Pins_P0.40mm_Horizontal +Connector_FFC-FPC:JAE_FF0881SA1_2Rows-81Pins_P0.40mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S04FCA-00_1x4-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S05FCA-00_1x5-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S06FCA-00_1x6-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S07FCA-00_1x7-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S08FCA-00_1x8-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S09FCA-00_1x9-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S10FCA-00_1x10-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S11FCA-00_1x11-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S12FCA-00_1x12-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S13FCA-00_1x13-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S14FCA-00_1x14-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S15FCA-00_1x15-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S16FCA-00_1x16-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S17FCA-00_1x17-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S18FCA-00_1x18-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S19FCA-00_1x19-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S20FCA-00_1x20-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S21FCA-00_1x21-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S22FCA-00_1x22-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S23FCA-00_1x23-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S24FCA-00_1x24-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S25FCA-00_1x25-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S26FCA-00_1x26-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S27FCA-00_1x27-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S28FCA-00_1x28-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:JUSHUO_AFA07-S29FCA-00_1x29-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:Jushuo_AFC07-S06FCA-00_1x6-1MP_P0.50_Horizontal +Connector_FFC-FPC:Jushuo_AFC07-S24FCA-00_1x24-1MP_P0.50_Horizontal +Connector_FFC-FPC:Molex_200528-0040_1x04-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0050_1x05-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0060_1x06-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0070_1x07-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0080_1x08-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0090_1x09-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0100_1x10-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0110_1x11-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0120_1x12-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0130_1x13-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0140_1x14-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0150_1x15-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0160_1x16-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0170_1x17-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0180_1x18-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0190_1x19-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0200_1x20-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0210_1x21-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0220_1x22-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0230_1x23-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0240_1x24-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0250_1x25-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0260_1x26-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0270_1x27-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0280_1x28-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0290_1x29-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_200528-0300_1x30-1MP_P1.00mm_Horizontal +Connector_FFC-FPC:Molex_502231-1500_1x15-1SH_P0.5mm_Vertical +Connector_FFC-FPC:Molex_502231-2400_1x24-1SH_P0.5mm_Vertical +Connector_FFC-FPC:Molex_502231-3300_1x33-1SH_P0.5mm_Vertical +Connector_FFC-FPC:Molex_502244-1530_1x15-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:Molex_502244-2430_1x24-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:Molex_502244-3330_1x33-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:Molex_502250-1791_2Rows-17Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_502250-2191_2Rows-21Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_502250-2391_2Rows-23Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_502250-2791_2Rows-27Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_502250-3391_2Rows-33Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_502250-3591_2Rows-35Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_502250-3991_2Rows-39Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_502250-4191_2Rows-41Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_502250-5191_2Rows-51Pins-1MP_P0.60mm_Horizontal +Connector_FFC-FPC:Molex_52559-3652_2x18-1MP_P0.5mm_Vertical +Connector_FFC-FPC:Molex_54132-5033_1x50-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:Molex_54548-1071_1x10-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:Omron_XF2M-4015-1A_1x40-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_0-1734839-5_1x05-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_0-1734839-6_1x06-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_0-1734839-7_1x07-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_0-1734839-8_1x08-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_0-1734839-9_1x09-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-0_1x10-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-1_1x11-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-2_1x12-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-3_1x13-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-4_1x14-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-5_1x15-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-6_1x16-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-7_1x17-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-8_1x18-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-1734839-9_1x19-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_1-84952-0_1x10-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-1_1x11-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-2_1x12-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-3_1x13-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-4_1x14-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-5_1x15-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-6_1x16-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-7_1x17-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-8_1x18-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84952-9_1x19-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-0_1x10-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-1_1x11-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-2_1x12-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-3_1x13-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-4_1x14-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-5_1x15-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-6_1x16-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-7_1x17-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-8_1x18-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_1-84953-9_1x19-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-0_1x20-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-1_1x21-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-2_1x22-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-3_1x23-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-4_1x24-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-5_1x25-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-6_1x26-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-7_1x27-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-8_1x28-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-1734839-9_1x29-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_2-84952-0_1x20-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-1_1x21-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-2_1x22-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-3_1x23-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-4_1x24-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-5_1x25-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-6_1x26-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-7_1x27-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-8_1x28-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84952-9_1x29-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-0_1x20-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-1_1x21-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-2_1x22-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-3_1x23-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-4_1x24-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-5_1x25-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-6_1x26-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-7_1x27-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-8_1x28-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_2-84953-9_1x29-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-0_1x30-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-1_1x31-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-2_1x32-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-3_1x33-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-4_1x34-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-5_1x35-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-6_1x36-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-7_1x37-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-8_1x38-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-1734839-9_1x39-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_3-84952-0_1x30-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_3-84953-0_1x30-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-0_1x40-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-1_1x41-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-2_1x42-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-3_1x43-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-4_1x44-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-5_1x45-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-6_1x46-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-7_1x47-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-8_1x48-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_4-1734839-9_1x49-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_5-1734839-0_1x50-1MP_P0.5mm_Horizontal +Connector_FFC-FPC:TE_84952-4_1x04-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84952-5_1x05-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84952-6_1x06-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84952-7_1x07-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84952-8_1x08-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84952-9_1x09-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84953-4_1x04-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84953-5_1x05-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84953-6_1x06-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84953-7_1x07-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84953-8_1x08-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:TE_84953-9_1x09-1MP_P1.0mm_Horizontal +Connector_FFC-FPC:Wuerth_68611214422_1x12-1MP_P1.0mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110213001xxx_1x02-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14110213002xxx_1x02-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110213010xxx_1x02-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110313001xxx_1x03-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14110313002xxx_1x03-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110313010xxx_1x03-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110413001xxx_1x04-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14110413002xxx_1x04-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110413010xxx_1x04-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110513001xxx_1x05-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14110513002xxx_1x05-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110513010xxx_1x05-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110613001xxx_1x06-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14110613002xxx_1x06-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110613010xxx_1x06-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110713001xxx_1x07-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14110713002xxx_1x07-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110713010xxx_1x07-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110813001xxx_1x08-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14110813002xxx_1x08-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110813010xxx_1x08-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110913001xxx_1x09-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14110913002xxx_1x09-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14110913010xxx_1x09-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14111013001xxx_1x10-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14111013002xxx_1x10-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14111013010xxx_1x10-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14111113001xxx_1x11-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14111113002xxx_1x11-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14111113010xxx_1x11-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14111213001xxx_1x12-MP_P2.54mm_Vertical +Connector_Harting:Harting_har-flexicon_14111213002xxx_1x12-MP_P2.54mm_Horizontal +Connector_Harting:Harting_har-flexicon_14111213010xxx_1x12-MP_P2.54mm_Horizontal +Connector_Harwin:Harwin_Gecko-G125-FVX0605L0X_2x03_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-FVX1005L0X_2x05_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-FVX1205L0X_2x06_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-FVX1605L0X_2x08_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-FVX2005L0X_2x10_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-FVX2605L0X_2x13_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-FVX3405L0X_2x17_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-FVX5005L0X_2x25_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX0605L0X_2x03_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX0605L1X_2x03_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX1005L0X_2x05_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX1005L1X_2x05_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX1205L0X_2x06_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX1205L1X_2x06_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX1605L0X_2x08_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX1605L1X_2x08_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX2005L0X_2x10_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX2005L1X_2x10_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX2605L0X_2x13_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX2605L1X_2x13_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX3405L0X_2x17_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX3405L1X_2x17_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX5005L0X_2x25_P1.25mm_Vertical +Connector_Harwin:Harwin_Gecko-G125-MVX5005L1X_2x25_P1.25mm_Vertical +Connector_Harwin:Harwin_LTek-Male_02_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_02_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_03_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_03_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_04_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_04_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_05_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_05_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_06_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_06_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_07_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_07_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_17_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_17_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_22_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_22_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x02_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x02_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x03_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x03_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x04_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x04_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x05_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x05_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x06_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x06_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x07_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x07_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x08_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x08_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x09_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x09_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x10_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x10_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x13_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x13_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x17_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x17_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_LTek-Male_2x22_P2.00mm_Vertical +Connector_Harwin:Harwin_LTek-Male_2x22_P2.00mm_Vertical_StrainRelief +Connector_Harwin:Harwin_M20-7810245_2x02_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7810345_2x03_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7810445_2x04_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7810545_2x05_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7810645_2x06_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7810745_2x07_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7810845_2x08_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7810945_2x09_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7811045_2x10_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7811245_2x12_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7811545_2x15_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-7812045_2x20_P2.54mm_Vertical +Connector_Harwin:Harwin_M20-89003xx_1x03_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89004xx_1x04_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89005xx_1x05_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89006xx_1x06_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89007xx_1x07_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89008xx_1x08_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89009xx_1x09_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89010xx_1x10_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89011xx_1x11_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89012xx_1x12_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89013xx_1x13_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89014xx_1x14_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89015xx_1x15_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89016xx_1x16_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89017xx_1x17_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89018xx_1x18_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89019xx_1x19_P2.54mm_Horizontal +Connector_Harwin:Harwin_M20-89020xx_1x20_P2.54mm_Horizontal +Connector_Hirose:Hirose_BM23FR0.6-16DP-0.35V_2x08_1MP_Vertical +Connector_Hirose:Hirose_BM23FR0.6-16DS-0.35V_2x08_P0.35_1MP_Vertical +Connector_Hirose:Hirose_BM24_BM24-40DP-2-0.35V_2x20_P0.35mm_PowerPin2_Vertical +Connector_Hirose:Hirose_BM24_BM24-40DS-2-0.35V_2x20_P0.35mm_PowerPin2_Vertical +Connector_Hirose:Hirose_DF11-10DP-2DSA_2x05_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-12DP-2DSA_2x06_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-14DP-2DSA_2x07_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-16DP-2DSA_2x08_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-18DP-2DSA_2x09_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-20DP-2DSA_2x10_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-22DP-2DSA_2x11_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-24DP-2DSA_2x12_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-26DP-2DSA_2x13_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-28DP-2DSA_2x14_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-30DP-2DSA_2x15_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-32DP-2DSA_2x16_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-4DP-2DSA_2x02_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-6DP-2DSA_2x03_P2.00mm_Vertical +Connector_Hirose:Hirose_DF11-8DP-2DSA_2x04_P2.00mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-10DS-0.5V_2x05_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-14DS-0.5V_2x07_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-20DS-0.5V_2x10_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-30DS-0.5V_2x15_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-32DS-0.5V_2x16_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-36DS-0.5V_2x18_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-40DS-0.5V_2x20_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-50DS-0.5V_2x25_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12C3.0-60DS-0.5V_2x30_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-10DP-0.5V_2x05_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-14DP-0.5V_2x07_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-20DP-0.5V_2x10_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-30DP-0.5V_2x15_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-32DP-0.5V_2x16_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-36DP-0.5V_2x18_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-40DP-0.5V_2x20_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-50DP-0.5V_2x25_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-60DP-0.5V_2x30_P0.50mm_Vertical +Connector_Hirose:Hirose_DF12_DF12E3.0-80DP-0.5V_2x40_P0.50mm_Vertical +Connector_Hirose:Hirose_DF13-02P-1.25DSA_1x02_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-02P-1.25DS_1x02_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-03P-1.25DSA_1x03_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-03P-1.25DS_1x03_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-04P-1.25DSA_1x04_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-04P-1.25DS_1x04_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-05P-1.25DSA_1x05_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-05P-1.25DS_1x05_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-06P-1.25DSA_1x06_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-06P-1.25DS_1x06_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-07P-1.25DSA_1x07_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-07P-1.25DS_1x07_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-08P-1.25DSA_1x08_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-08P-1.25DS_1x08_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-09P-1.25DSA_1x09_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-09P-1.25DS_1x09_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-10P-1.25DSA_1x10_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-10P-1.25DS_1x10_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-11P-1.25DSA_1x11_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-11P-1.25DS_1x11_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-12P-1.25DSA_1x12_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-12P-1.25DS_1x12_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-13P-1.25DSA_1x13_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-14P-1.25DSA_1x14_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-14P-1.25DS_1x14_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13-15P-1.25DSA_1x15_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13-15P-1.25DS_1x15_P1.25mm_Horizontal +Connector_Hirose:Hirose_DF13C_CL535-0402-2-51_1x02-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0403-5-51_1x03-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0404-8-51_1x04-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0405-0-51_1x05-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0406-3-51_1x06-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0407-6-51_1x07-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0408-9-51_1x08-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0409-1-51_1x09-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0410-4-51_1x10-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0411-3-51_1x11-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0412-6-51_1x12-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0414-1-51_1x14-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF13C_CL535-0415-4-51_1x15-1MP_P1.25mm_Vertical +Connector_Hirose:Hirose_DF3EA-02P-2H_1x02-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-03P-2H_1x03-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-04P-2H_1x04-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-05P-2H_1x05-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-06P-2H_1x06-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-07P-2H_1x07-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-08P-2H_1x08-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-09P-2H_1x09-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-10P-2H_1x10-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-11P-2H_1x11-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-12P-2H_1x12-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-13P-2H_1x13-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-14P-2H_1x14-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF3EA-15P-2H_1x15-1MP_P2.00mm_Horizontal +Connector_Hirose:Hirose_DF52-10S-0.8H_1x10-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-11S-0.8H_1x11-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-12S-0.8H_1x12-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-14S-0.8H_1x14-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-15S-0.8H_1x15-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-2S-0.8H_1x02-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-3S-0.8H_1x03-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-4S-0.8H_1x04-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-5S-0.8H_1x05-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-6S-0.8H_1x06-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-7S-0.8H_1x07-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-8S-0.8H_1x08-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF52-9S-0.8H_1x09-1MP_P0.80mm_Horizontal +Connector_Hirose:Hirose_DF63-5P-3.96DSA_1x05_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63-6P-3.96DSA_1x06_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63M-1P-3.96DSA_1x01_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63M-2P-3.96DSA_1x02_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63M-3P-3.96DSA_1x03_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63M-4P-3.96DSA_1x04_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63R-1P-3.96DSA_1x01_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63R-2P-3.96DSA_1x02_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63R-3P-3.96DSA_1x03_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63R-4P-3.96DSA_1x04_P3.96mm_Vertical +Connector_Hirose:Hirose_DF63R-5P-3.96DSA_1x05_P3.96mm_Vertical +Connector_Hirose_FX8:Hirose_FX8-100P-SV_2x50_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-100S-SV_2x50_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-120P-SV_2x60_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-120S-SV_2x60_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-140P-SV_2x70_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-140S-SV_2x70_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-60P-SV_2x30_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-60S-SV_2x30_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-80P-SV_2x40_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-80S-SV_2x40_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-90P-SV_2x45_P0.6mm +Connector_Hirose_FX8:Hirose_FX8-90S-SV_2x45_P0.6mm +Connector_IDC:IDC-Header_2x03_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x03_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x03_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x04_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x04_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x04_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x05-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x05-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x05-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x05-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x05-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x05_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x05_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x05_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x05_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x05_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x05_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x05_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x05_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x06-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x06-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x06-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x06-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x06-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x06_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x06_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x06_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x06_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x06_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x06_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x06_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x06_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x07-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x07-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x07-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x07-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x07-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x07_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x07_P2.54mm_Horizontal_Lock +Connector_IDC:IDC-Header_2x07_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x07_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x07_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x07_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x07_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x07_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x07_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x08-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x08-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x08-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x08-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x08-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x08_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x08_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x08_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x08_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x08_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x08_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x08_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x08_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x09_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x09_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x09_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x10-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x10-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x10-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x10-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x10-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x10_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x10_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x10_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x10_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x10_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x10_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x10_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x10_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x11_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x11_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x11_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x12-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x12-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x12-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x12-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x12-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x12_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x12_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x12_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x12_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x12_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x12_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x12_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x12_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x13-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x13-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x13-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x13-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x13-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x13_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x13_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x13_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x13_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x13_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x13_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x13_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x13_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x15-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x15-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x15-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x15-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x15-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x15_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x15_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x15_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x15_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x15_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x15_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x15_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x17-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x17-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x17-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x17-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x17-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x17_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x17_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x17_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x17_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x17_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x17_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x17_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x20-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x20-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x20-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x20-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x20-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x20_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x20_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x20_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x20_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x20_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x20_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x20_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x20_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x22_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x22_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x22_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x25-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x25-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x25-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x25-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x25-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x25_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x25_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x25_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x25_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x25_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x25_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x25_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x25_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x30-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x30-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x30-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x30-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x30-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x30_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x30_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x30_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x30_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x30_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x30_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x30_P2.54mm_Vertical +Connector_IDC:IDC-Header_2x30_P2.54mm_Vertical_SMD +Connector_IDC:IDC-Header_2x32-1MP_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x32-1MP_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x32-1MP_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x32-1MP_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x32-1MP_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x32_P2.54mm_Horizontal +Connector_IDC:IDC-Header_2x32_P2.54mm_Latch12.0mm_Vertical +Connector_IDC:IDC-Header_2x32_P2.54mm_Latch6.5mm_Vertical +Connector_IDC:IDC-Header_2x32_P2.54mm_Latch9.5mm_Vertical +Connector_IDC:IDC-Header_2x32_P2.54mm_Latch_Horizontal +Connector_IDC:IDC-Header_2x32_P2.54mm_Latch_Vertical +Connector_IDC:IDC-Header_2x32_P2.54mm_Vertical +Connector_JAE:JAE_LY20-10P-DLT1_2x05_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-10P-DT1_2x05_P2.00mm_Vertical +Connector_JAE:JAE_LY20-12P-DLT1_2x06_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-12P-DT1_2x06_P2.00mm_Vertical +Connector_JAE:JAE_LY20-14P-DLT1_2x07_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-14P-DT1_2x07_P2.00mm_Vertical +Connector_JAE:JAE_LY20-16P-DLT1_2x08_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-16P-DT1_2x08_P2.00mm_Vertical +Connector_JAE:JAE_LY20-18P-DLT1_2x09_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-18P-DT1_2x09_P2.00mm_Vertical +Connector_JAE:JAE_LY20-20P-DLT1_2x10_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-20P-DT1_2x10_P2.00mm_Vertical +Connector_JAE:JAE_LY20-22P-DLT1_2x11_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-22P-DT1_2x11_P2.00mm_Vertical +Connector_JAE:JAE_LY20-24P-DLT1_2x12_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-24P-DT1_2x12_P2.00mm_Vertical +Connector_JAE:JAE_LY20-26P-DLT1_2x13_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-26P-DT1_2x13_P2.00mm_Vertical +Connector_JAE:JAE_LY20-28P-DLT1_2x14_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-28P-DT1_2x14_P2.00mm_Vertical +Connector_JAE:JAE_LY20-30P-DLT1_2x15_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-30P-DT1_2x15_P2.00mm_Vertical +Connector_JAE:JAE_LY20-32P-DLT1_2x16_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-32P-DT1_2x16_P2.00mm_Vertical +Connector_JAE:JAE_LY20-34P-DLT1_2x17_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-34P-DT1_2x17_P2.00mm_Vertical +Connector_JAE:JAE_LY20-36P-DLT1_2x18_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-36P-DT1_2x18_P2.00mm_Vertical +Connector_JAE:JAE_LY20-38P-DLT1_2x19_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-38P-DT1_2x19_P2.00mm_Vertical +Connector_JAE:JAE_LY20-40P-DLT1_2x20_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-40P-DT1_2x20_P2.00mm_Vertical +Connector_JAE:JAE_LY20-42P-DLT1_2x21_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-42P-DT1_2x21_P2.00mm_Vertical +Connector_JAE:JAE_LY20-44P-DLT1_2x22_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-44P-DT1_2x22_P2.00mm_Vertical +Connector_JAE:JAE_LY20-4P-DLT1_2x02_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-4P-DT1_2x02_P2.00mm_Vertical +Connector_JAE:JAE_LY20-6P-DLT1_2x03_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-6P-DT1_2x03_P2.00mm_Vertical +Connector_JAE:JAE_LY20-8P-DLT1_2x04_P2.00mm_Horizontal +Connector_JAE:JAE_LY20-8P-DT1_2x04_P2.00mm_Vertical +Connector_JAE:JAE_MM70-314-310B1 +Connector_JAE:JAE_SIM_Card_SF72S006 +Connector_JAE_WP7B:JAE_WP7B-P034VA1-R8000_2x17-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P034VA1-R8000_Longpads_2x17-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P040VA1-R8000_2x20-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P040VA1-R8000_Longpads_2x20-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P050VA1-R8000_2x25-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P050VA1-R8000_Longpads_2x25-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P060VA1-R8000_2x30-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P060VA1-R8000_Longpads_2x30-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P070VA1-R8000_2x35-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-P070VA1-R8000_Longpads_2x35-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S034VA1-R8000_2x17-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S034VA1-R8000_Longpads_2x17-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S040VA1-R8000_2x20-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S040VA1-R8000_Longpads_2x20-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S050VA1-R8000_2x25-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S050VA1-R8000_Longpads_2x25-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S060VA1-R8000_2x30-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S060VA1-R8000_Longpads_2x30-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S070VA1-R8000_2x35-1MP_P0.4mm +Connector_JAE_WP7B:JAE_WP7B-S070VA1-R8000_Longpads_2x35-1MP_P0.4mm +Connector_JST:JST_ACH_BM01B-ACHSS-A-GAN-ETF_1x01-1MP_P1.20mm_Vertical +Connector_JST:JST_ACH_BM02B-ACHSS-GAN-ETF_1x02-1MP_P1.20mm_Vertical +Connector_JST:JST_ACH_BM03B-ACHSS-GAN-ETF_1x03-1MP_P1.20mm_Vertical +Connector_JST:JST_ACH_BM04B-ACHSS-A-GAN-ETF_1x04-1MP_P1.20mm_Vertical +Connector_JST:JST_ACH_BM05B-ACHSS-A-GAN-ETF_1x05-1MP_P1.20mm_Vertical +Connector_JST:JST_AUH_BM03B-AUHKS-GA-TB_1x03-1MP_P1.50mm_Vertical +Connector_JST:JST_AUH_BM05B-AUHKS-GA-TB_1x05-1MP_P1.50mm_Vertical +Connector_JST:JST_EH_B10B-EH-A_1x10_P2.50mm_Vertical +Connector_JST:JST_EH_B11B-EH-A_1x11_P2.50mm_Vertical +Connector_JST:JST_EH_B12B-EH-A_1x12_P2.50mm_Vertical +Connector_JST:JST_EH_B13B-EH-A_1x13_P2.50mm_Vertical +Connector_JST:JST_EH_B14B-EH-A_1x14_P2.50mm_Vertical +Connector_JST:JST_EH_B15B-EH-A_1x15_P2.50mm_Vertical +Connector_JST:JST_EH_B2B-EH-A_1x02_P2.50mm_Vertical +Connector_JST:JST_EH_B3B-EH-A_1x03_P2.50mm_Vertical +Connector_JST:JST_EH_B4B-EH-A_1x04_P2.50mm_Vertical +Connector_JST:JST_EH_B5B-EH-A_1x05_P2.50mm_Vertical +Connector_JST:JST_EH_B6B-EH-A_1x06_P2.50mm_Vertical +Connector_JST:JST_EH_B7B-EH-A_1x07_P2.50mm_Vertical +Connector_JST:JST_EH_B8B-EH-A_1x08_P2.50mm_Vertical +Connector_JST:JST_EH_B9B-EH-A_1x09_P2.50mm_Vertical +Connector_JST:JST_EH_S10B-EH_1x10_P2.50mm_Horizontal +Connector_JST:JST_EH_S11B-EH_1x11_P2.50mm_Horizontal +Connector_JST:JST_EH_S12B-EH_1x12_P2.50mm_Horizontal +Connector_JST:JST_EH_S13B-EH_1x13_P2.50mm_Horizontal +Connector_JST:JST_EH_S14B-EH_1x14_P2.50mm_Horizontal +Connector_JST:JST_EH_S15B-EH_1x15_P2.50mm_Horizontal +Connector_JST:JST_EH_S2B-EH_1x02_P2.50mm_Horizontal +Connector_JST:JST_EH_S3B-EH_1x03_P2.50mm_Horizontal +Connector_JST:JST_EH_S4B-EH_1x04_P2.50mm_Horizontal +Connector_JST:JST_EH_S5B-EH_1x05_P2.50mm_Horizontal +Connector_JST:JST_EH_S6B-EH_1x06_P2.50mm_Horizontal +Connector_JST:JST_EH_S7B-EH_1x07_P2.50mm_Horizontal +Connector_JST:JST_EH_S8B-EH_1x08_P2.50mm_Horizontal +Connector_JST:JST_EH_S9B-EH_1x09_P2.50mm_Horizontal +Connector_JST:JST_GH_BM02B-GHS-TBT_1x02-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM03B-GHS-TBT_1x03-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM04B-GHS-TBT_1x04-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM05B-GHS-TBT_1x05-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM06B-GHS-TBT_1x06-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM07B-GHS-TBT_1x07-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM08B-GHS-TBT_1x08-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM09B-GHS-TBT_1x09-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM10B-GHS-TBT_1x10-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM11B-GHS-TBT_1x11-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM12B-GHS-TBT_1x12-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM13B-GHS-TBT_1x13-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM14B-GHS-TBT_1x14-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_BM15B-GHS-TBT_1x15-1MP_P1.25mm_Vertical +Connector_JST:JST_GH_SM02B-GHS-TB_1x02-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM03B-GHS-TB_1x03-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM04B-GHS-TB_1x04-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM05B-GHS-TB_1x05-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM06B-GHS-TB_1x06-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM07B-GHS-TB_1x07-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM08B-GHS-TB_1x08-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM09B-GHS-TB_1x09-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM10B-GHS-TB_1x10-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM11B-GHS-TB_1x11-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM12B-GHS-TB_1x12-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM13B-GHS-TB_1x13-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM14B-GHS-TB_1x14-1MP_P1.25mm_Horizontal +Connector_JST:JST_GH_SM15B-GHS-TB_1x15-1MP_P1.25mm_Horizontal +Connector_JST:JST_J2100_B06B-J21DK-GGXR_2x03_P2.50x4.00mm_Vertical +Connector_JST:JST_J2100_B08B-J21DK-GGXR_2x04_P2.50x4.00mm_Vertical +Connector_JST:JST_J2100_B10B-J21DK-GGXR_2x05_P2.50x4.00mm_Vertical +Connector_JST:JST_J2100_B12B-J21DK-GGXR_2x06_P2.50x4.00mm_Vertical +Connector_JST:JST_J2100_B16B-J21DK-GGXR_2x08_P2.50x4.00mm_Vertical +Connector_JST:JST_J2100_B20B-J21DK-GGXR_2x10_P2.50x4.00mm_Vertical +Connector_JST:JST_J2100_S06B-J21DK-GGXR_2x03_P2.50mm_Horizontal +Connector_JST:JST_J2100_S08B-J21DK-GGXR_2x04_P2.50mm_Horizontal +Connector_JST:JST_J2100_S10B-J21DK-GGXR_2x05_P2.50mm_Horizontal +Connector_JST:JST_J2100_S12B-J21DK-GGXR_2x06_P2.50mm_Horizontal +Connector_JST:JST_J2100_S16B-J21DK-GGXR_2x08_P2.50mm_Horizontal +Connector_JST:JST_J2100_S20B-J21DK-GGXR_2x10_P2.50mm_Horizontal +Connector_JST:JST_JWPF_B02B-JWPF-SK-R_1x02_P2.00mm_Vertical +Connector_JST:JST_JWPF_B03B-JWPF-SK-R_1x03_P2.00mm_Vertical +Connector_JST:JST_JWPF_B04B-JWPF-SK-R_1x04_P2.00mm_Vertical +Connector_JST:JST_JWPF_B06B-JWPF-SK-R_2x03_P2.00mm_Vertical +Connector_JST:JST_JWPF_B08B-JWPF-SK-R_2x04_P2.00mm_Vertical +Connector_JST:JST_LEA_SM02B-LEASS-TF_1x02-1MP_P4.20mm_Horizontal +Connector_JST:JST_NV_B02P-NV_1x02_P5.00mm_Vertical +Connector_JST:JST_NV_B03P-NV_1x03_P5.00mm_Vertical +Connector_JST:JST_NV_B04P-NV_1x04_P5.00mm_Vertical +Connector_JST:JST_PHD_B10B-PHDSS_2x05_P2.00mm_Vertical +Connector_JST:JST_PHD_B12B-PHDSS_2x06_P2.00mm_Vertical +Connector_JST:JST_PHD_B14B-PHDSS_2x07_P2.00mm_Vertical +Connector_JST:JST_PHD_B16B-PHDSS_2x08_P2.00mm_Vertical +Connector_JST:JST_PHD_B18B-PHDSS_2x09_P2.00mm_Vertical +Connector_JST:JST_PHD_B20B-PHDSS_2x10_P2.00mm_Vertical +Connector_JST:JST_PHD_B22B-PHDSS_2x11_P2.00mm_Vertical +Connector_JST:JST_PHD_B24B-PHDSS_2x12_P2.00mm_Vertical +Connector_JST:JST_PHD_B26B-PHDSS_2x13_P2.00mm_Vertical +Connector_JST:JST_PHD_B28B-PHDSS_2x14_P2.00mm_Vertical +Connector_JST:JST_PHD_B30B-PHDSS_2x15_P2.00mm_Vertical +Connector_JST:JST_PHD_B32B-PHDSS_2x16_P2.00mm_Vertical +Connector_JST:JST_PHD_B34B-PHDSS_2x17_P2.00mm_Vertical +Connector_JST:JST_PHD_B8B-PHDSS_2x04_P2.00mm_Vertical +Connector_JST:JST_PHD_S10B-PHDSS_2x05_P2.00mm_Horizontal +Connector_JST:JST_PHD_S12B-PHDSS_2x06_P2.00mm_Horizontal +Connector_JST:JST_PHD_S14B-PHDSS_2x07_P2.00mm_Horizontal +Connector_JST:JST_PHD_S16B-PHDSS_2x08_P2.00mm_Horizontal +Connector_JST:JST_PHD_S18B-PHDSS_2x09_P2.00mm_Horizontal +Connector_JST:JST_PHD_S20B-PHDSS_2x10_P2.00mm_Horizontal +Connector_JST:JST_PHD_S22B-PHDSS_2x11_P2.00mm_Horizontal +Connector_JST:JST_PHD_S24B-PHDSS_2x12_P2.00mm_Horizontal +Connector_JST:JST_PHD_S26B-PHDSS_2x13_P2.00mm_Horizontal +Connector_JST:JST_PHD_S28B-PHDSS_2x14_P2.00mm_Horizontal +Connector_JST:JST_PHD_S30B-PHDSS_2x15_P2.00mm_Horizontal +Connector_JST:JST_PHD_S32B-PHDSS_2x16_P2.00mm_Horizontal +Connector_JST:JST_PHD_S34B-PHDSS_2x17_P2.00mm_Horizontal +Connector_JST:JST_PHD_S8B-PHDSS_2x04_P2.00mm_Horizontal +Connector_JST:JST_PH_B10B-PH-K_1x10_P2.00mm_Vertical +Connector_JST:JST_PH_B10B-PH-SM4-TB_1x10-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B11B-PH-K_1x11_P2.00mm_Vertical +Connector_JST:JST_PH_B11B-PH-SM4-TB_1x11-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B12B-PH-K_1x12_P2.00mm_Vertical +Connector_JST:JST_PH_B12B-PH-SM4-TB_1x12-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B13B-PH-K_1x13_P2.00mm_Vertical +Connector_JST:JST_PH_B13B-PH-SM4-TB_1x13-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B14B-PH-K_1x14_P2.00mm_Vertical +Connector_JST:JST_PH_B14B-PH-SM4-TB_1x14-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B15B-PH-K_1x15_P2.00mm_Vertical +Connector_JST:JST_PH_B15B-PH-SM4-TB_1x15-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B16B-PH-K_1x16_P2.00mm_Vertical +Connector_JST:JST_PH_B16B-PH-SM4-TB_1x16-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical +Connector_JST:JST_PH_B2B-PH-SM4-TB_1x02-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B3B-PH-K_1x03_P2.00mm_Vertical +Connector_JST:JST_PH_B3B-PH-SM4-TB_1x03-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B4B-PH-K_1x04_P2.00mm_Vertical +Connector_JST:JST_PH_B4B-PH-SM4-TB_1x04-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B5B-PH-K_1x05_P2.00mm_Vertical +Connector_JST:JST_PH_B5B-PH-SM4-TB_1x05-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B6B-PH-K_1x06_P2.00mm_Vertical +Connector_JST:JST_PH_B6B-PH-SM4-TB_1x06-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B7B-PH-K_1x07_P2.00mm_Vertical +Connector_JST:JST_PH_B7B-PH-SM4-TB_1x07-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B8B-PH-K_1x08_P2.00mm_Vertical +Connector_JST:JST_PH_B8B-PH-SM4-TB_1x08-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_B9B-PH-K_1x09_P2.00mm_Vertical +Connector_JST:JST_PH_B9B-PH-SM4-TB_1x09-1MP_P2.00mm_Vertical +Connector_JST:JST_PH_S10B-PH-K_1x10_P2.00mm_Horizontal +Connector_JST:JST_PH_S10B-PH-SM4-TB_1x10-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S11B-PH-K_1x11_P2.00mm_Horizontal +Connector_JST:JST_PH_S11B-PH-SM4-TB_1x11-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S12B-PH-K_1x12_P2.00mm_Horizontal +Connector_JST:JST_PH_S12B-PH-SM4-TB_1x12-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S13B-PH-K_1x13_P2.00mm_Horizontal +Connector_JST:JST_PH_S13B-PH-SM4-TB_1x13-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S14B-PH-K_1x14_P2.00mm_Horizontal +Connector_JST:JST_PH_S14B-PH-SM4-TB_1x14-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S15B-PH-K_1x15_P2.00mm_Horizontal +Connector_JST:JST_PH_S15B-PH-SM4-TB_1x15-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S16B-PH-K_1x16_P2.00mm_Horizontal +Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal +Connector_JST:JST_PH_S2B-PH-SM4-TB_1x02-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S3B-PH-K_1x03_P2.00mm_Horizontal +Connector_JST:JST_PH_S3B-PH-SM4-TB_1x03-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S4B-PH-K_1x04_P2.00mm_Horizontal +Connector_JST:JST_PH_S4B-PH-SM4-TB_1x04-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S5B-PH-K_1x05_P2.00mm_Horizontal +Connector_JST:JST_PH_S5B-PH-SM4-TB_1x05-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S6B-PH-K_1x06_P2.00mm_Horizontal +Connector_JST:JST_PH_S6B-PH-SM4-TB_1x06-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S7B-PH-K_1x07_P2.00mm_Horizontal +Connector_JST:JST_PH_S7B-PH-SM4-TB_1x07-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S8B-PH-K_1x08_P2.00mm_Horizontal +Connector_JST:JST_PH_S8B-PH-SM4-TB_1x08-1MP_P2.00mm_Horizontal +Connector_JST:JST_PH_S9B-PH-K_1x09_P2.00mm_Horizontal +Connector_JST:JST_PH_S9B-PH-SM4-TB_1x09-1MP_P2.00mm_Horizontal +Connector_JST:JST_PUD_B08B-PUDSS_2x04_P2.00mm_Vertical +Connector_JST:JST_PUD_B10B-PUDSS_2x05_P2.00mm_Vertical +Connector_JST:JST_PUD_B12B-PUDSS_2x06_P2.00mm_Vertical +Connector_JST:JST_PUD_B14B-PUDSS_2x07_P2.00mm_Vertical +Connector_JST:JST_PUD_B16B-PUDSS_2x08_P2.00mm_Vertical +Connector_JST:JST_PUD_B18B-PUDSS_2x09_P2.00mm_Vertical +Connector_JST:JST_PUD_B20B-PUDSS_2x10_P2.00mm_Vertical +Connector_JST:JST_PUD_B22B-PUDSS_2x11_P2.00mm_Vertical +Connector_JST:JST_PUD_B24B-PUDSS_2x12_P2.00mm_Vertical +Connector_JST:JST_PUD_B26B-PUDSS_2x13_P2.00mm_Vertical +Connector_JST:JST_PUD_B28B-PUDSS_2x14_P2.00mm_Vertical +Connector_JST:JST_PUD_B30B-PUDSS_2x15_P2.00mm_Vertical +Connector_JST:JST_PUD_B32B-PUDSS_2x16_P2.00mm_Vertical +Connector_JST:JST_PUD_B34B-PUDSS_2x17_P2.00mm_Vertical +Connector_JST:JST_PUD_B36B-PUDSS_2x18_P2.00mm_Vertical +Connector_JST:JST_PUD_B38B-PUDSS_2x19_P2.00mm_Vertical +Connector_JST:JST_PUD_B40B-PUDSS_2x20_P2.00mm_Vertical +Connector_JST:JST_PUD_S08B-PUDSS-1_2x04_P2.00mm_Horizontal +Connector_JST:JST_PUD_S10B-PUDSS-1_2x05_P2.00mm_Horizontal +Connector_JST:JST_PUD_S12B-PUDSS-1_2x06_P2.00mm_Horizontal +Connector_JST:JST_PUD_S14B-PUDSS-1_2x07_P2.00mm_Horizontal +Connector_JST:JST_PUD_S16B-PUDSS-1_2x08_P2.00mm_Horizontal +Connector_JST:JST_PUD_S18B-PUDSS-1_2x09_P2.00mm_Horizontal +Connector_JST:JST_PUD_S20B-PUDSS-1_2x10_P2.00mm_Horizontal +Connector_JST:JST_PUD_S22B-PUDSS-1_2x11_P2.00mm_Horizontal +Connector_JST:JST_PUD_S24B-PUDSS-1_2x12_P2.00mm_Horizontal +Connector_JST:JST_PUD_S26B-PUDSS-1_2x13_P2.00mm_Horizontal +Connector_JST:JST_PUD_S28B-PUDSS-1_2x14_P2.00mm_Horizontal +Connector_JST:JST_PUD_S30B-PUDSS-1_2x15_P2.00mm_Horizontal +Connector_JST:JST_PUD_S32B-PUDSS-1_2x16_P2.00mm_Horizontal +Connector_JST:JST_PUD_S34B-PUDSS-1_2x17_P2.00mm_Horizontal +Connector_JST:JST_PUD_S36B-PUDSS-1_2x18_P2.00mm_Horizontal +Connector_JST:JST_PUD_S38B-PUDSS-1_2x19_P2.00mm_Horizontal +Connector_JST:JST_PUD_S40B-PUDSS-1_2x20_P2.00mm_Horizontal +Connector_JST:JST_SFH_SM02B-SFHRS-TF_1x02-1MP_P4.20mm_Horizontal +Connector_JST:JST_SHL_SM02B-SHLS-TF_1x02-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM05B-SHLS-TF_1x05-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM06B-SHLS-TF_1x06-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM07B-SHLS-TF_1x07-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM08B-SHLS-TF_1x08-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM10B-SHLS-TF_1x10-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM11B-SHLS-TF_1x11-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM12B-SHLS-TF_1x12-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM14B-SHLS-TF_1x14-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM16B-SHLS-TF_1x16-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM20B-SHLS-TF_1x20-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM22B-SHLS-TF_1x22-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM26B-SHLS-TF_1x26-1MP_P1.00mm_Horizontal +Connector_JST:JST_SHL_SM30B-SHLS-TF_1x30-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_BM02B-SRSS-TB_1x02-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM03B-SRSS-TB_1x03-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM04B-SRSS-TB_1x04-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM05B-SRSS-TB_1x05-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM06B-SRSS-TB_1x06-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM07B-SRSS-TB_1x07-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM08B-SRSS-TB_1x08-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM09B-SRSS-TB_1x09-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM10B-SRSS-TB_1x10-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM11B-SRSS-TB_1x11-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM12B-SRSS-TB_1x12-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM13B-SRSS-TB_1x13-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM14B-SRSS-TB_1x14-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_BM15B-SRSS-TB_1x15-1MP_P1.00mm_Vertical +Connector_JST:JST_SH_SM02B-SRSS-TB_1x02-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM03B-SRSS-TB_1x03-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM05B-SRSS-TB_1x05-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM06B-SRSS-TB_1x06-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM07B-SRSS-TB_1x07-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM08B-SRSS-TB_1x08-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM09B-SRSS-TB_1x09-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM10B-SRSS-TB_1x10-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM11B-SRSS-TB_1x11-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM12B-SRSS-TB_1x12-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM13B-SRSS-TB_1x13-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM14B-SRSS-TB_1x14-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM15B-SRSS-TB_1x15-1MP_P1.00mm_Horizontal +Connector_JST:JST_SH_SM20B-SRSS-TB_1x20-1MP_P1.00mm_Horizontal +Connector_JST:JST_SUR_BM02B-SURS-TF_1x02-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM03B-SURS-TF_1x03-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM04B-SURS-TF_1x04-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM05B-SURS-TF_1x05-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM06B-SURS-TF_1x06-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM08B-SURS-TF_1x08-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM10B-SURS-TF_1x10-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM12B-SURS-TF_1x12-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM14B-SURS-TF_1x14-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM15B-SURS-TF_1x15-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM16B-SURS-TF_1x16-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM17B-SURS-TF_1x17-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_BM20B-SURS-TF_1x20-1MP_P0.80mm_Vertical +Connector_JST:JST_SUR_SM02B-SURS-TF_1x02-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM03B-SURS-TF_1x03-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM04B-SURS-TF_1x04-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM05B-SURS-TF_1x05-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM06B-SURS-TF_1x06-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM08B-SURS-TF_1x08-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM10B-SURS-TF_1x10-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM12B-SURS-TF_1x12-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM14B-SURS-TF_1x14-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM15B-SURS-TF_1x15-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM16B-SURS-TF_1x16-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM17B-SURS-TF_1x17-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM20B-SURS-TF_1x20-1MP_P0.80mm_Horizontal +Connector_JST:JST_SUR_SM22B-SURS-TF_1x22-1MP_P0.80mm_Horizontal +Connector_JST:JST_VH_B10P-VH-B_1x10_P3.96mm_Vertical +Connector_JST:JST_VH_B10P-VH-FB-B_1x10_P3.96mm_Vertical +Connector_JST:JST_VH_B10P-VH_1x10_P3.96mm_Vertical +Connector_JST:JST_VH_B10PS-VH_1x10_P3.96mm_Horizontal +Connector_JST:JST_VH_B11P-VH-B_1x11_P3.96mm_Vertical +Connector_JST:JST_VH_B2P-VH-B_1x02_P3.96mm_Vertical +Connector_JST:JST_VH_B2P-VH-FB-B_1x02_P3.96mm_Vertical +Connector_JST:JST_VH_B2P-VH_1x02_P3.96mm_Vertical +Connector_JST:JST_VH_B2P3-VH_1x02_P7.92mm_Vertical +Connector_JST:JST_VH_B2PS-VH_1x02_P3.96mm_Horizontal +Connector_JST:JST_VH_B3P-VH-B_1x03_P3.96mm_Vertical +Connector_JST:JST_VH_B3P-VH-FB-B_1x03_P3.96mm_Vertical +Connector_JST:JST_VH_B3P-VH_1x03_P3.96mm_Vertical +Connector_JST:JST_VH_B3PS-VH_1x03_P3.96mm_Horizontal +Connector_JST:JST_VH_B4P-VH-B_1x04_P3.96mm_Vertical +Connector_JST:JST_VH_B4P-VH-FB-B_1x04_P3.96mm_Vertical +Connector_JST:JST_VH_B4P-VH_1x04_P3.96mm_Vertical +Connector_JST:JST_VH_B4PS-VH_1x04_P3.96mm_Horizontal +Connector_JST:JST_VH_B5P-VH-B_1x05_P3.96mm_Vertical +Connector_JST:JST_VH_B5P-VH-FB-B_1x05_P3.96mm_Vertical +Connector_JST:JST_VH_B5P-VH_1x05_P3.96mm_Vertical +Connector_JST:JST_VH_B5PS-VH_1x05_P3.96mm_Horizontal +Connector_JST:JST_VH_B6P-VH-B_1x06_P3.96mm_Vertical +Connector_JST:JST_VH_B6P-VH-FB-B_1x06_P3.96mm_Vertical +Connector_JST:JST_VH_B6P-VH_1x06_P3.96mm_Vertical +Connector_JST:JST_VH_B6PS-VH_1x06_P3.96mm_Horizontal +Connector_JST:JST_VH_B7P-VH-B_1x07_P3.96mm_Vertical +Connector_JST:JST_VH_B7P-VH-FB-B_1x07_P3.96mm_Vertical +Connector_JST:JST_VH_B7P-VH_1x07_P3.96mm_Vertical +Connector_JST:JST_VH_B7PS-VH_1x07_P3.96mm_Horizontal +Connector_JST:JST_VH_B8P-VH-B_1x08_P3.96mm_Vertical +Connector_JST:JST_VH_B8P-VH-FB-B_1x08_P3.96mm_Vertical +Connector_JST:JST_VH_B8P-VH_1x08_P3.96mm_Vertical +Connector_JST:JST_VH_B8PS-VH_1x08_P3.96mm_Horizontal +Connector_JST:JST_VH_B9P-VH-B_1x09_P3.96mm_Vertical +Connector_JST:JST_VH_B9P-VH-FB-B_1x09_P3.96mm_Vertical +Connector_JST:JST_VH_B9P-VH_1x09_P3.96mm_Vertical +Connector_JST:JST_VH_B9PS-VH_1x09_P3.96mm_Horizontal +Connector_JST:JST_VH_S2P-VH_1x02_P3.96mm_Horizontal +Connector_JST:JST_VH_S3P-VH_1x03_P3.96mm_Horizontal +Connector_JST:JST_VH_S4P-VH_1x04_P3.96mm_Horizontal +Connector_JST:JST_VH_S5P-VH_1x05_P3.96mm_Horizontal +Connector_JST:JST_VH_S6P-VH_1x06_P3.96mm_Horizontal +Connector_JST:JST_VH_S7P-VH_1x07_P3.96mm_Horizontal +Connector_JST:JST_XAG_SM05B-XAGKS-BN-TB_1x05-1MP_P2.50mm_Horizontal +Connector_JST:JST_XA_B02B-XASK-1-A_1x02_P2.50mm_Vertical +Connector_JST:JST_XA_B02B-XASK-1_1x02_P2.50mm_Vertical +Connector_JST:JST_XA_B03B-XASK-1-A_1x03_P2.50mm_Vertical +Connector_JST:JST_XA_B03B-XASK-1_1x03_P2.50mm_Vertical +Connector_JST:JST_XA_B04B-XASK-1-A_1x04_P2.50mm_Vertical +Connector_JST:JST_XA_B04B-XASK-1_1x04_P2.50mm_Vertical +Connector_JST:JST_XA_B05B-XASK-1-A_1x05_P2.50mm_Vertical +Connector_JST:JST_XA_B05B-XASK-1_1x05_P2.50mm_Vertical +Connector_JST:JST_XA_B06B-XASK-1-A_1x06_P2.50mm_Vertical +Connector_JST:JST_XA_B06B-XASK-1_1x06_P2.50mm_Vertical +Connector_JST:JST_XA_B07B-XASK-1-A_1x07_P2.50mm_Vertical +Connector_JST:JST_XA_B07B-XASK-1_1x07_P2.50mm_Vertical +Connector_JST:JST_XA_B08B-XASK-1-A_1x08_P2.50mm_Vertical +Connector_JST:JST_XA_B08B-XASK-1_1x08_P2.50mm_Vertical +Connector_JST:JST_XA_B09B-XASK-1-A_1x09_P2.50mm_Vertical +Connector_JST:JST_XA_B09B-XASK-1_1x09_P2.50mm_Vertical +Connector_JST:JST_XA_B10B-XASK-1-A_1x10_P2.50mm_Vertical +Connector_JST:JST_XA_B10B-XASK-1_1x10_P2.50mm_Vertical +Connector_JST:JST_XA_B11B-XASK-1-A_1x11_P2.50mm_Vertical +Connector_JST:JST_XA_B11B-XASK-1_1x11_P2.50mm_Vertical +Connector_JST:JST_XA_B12B-XASK-1-A_1x12_P2.50mm_Vertical +Connector_JST:JST_XA_B12B-XASK-1_1x12_P2.50mm_Vertical +Connector_JST:JST_XA_B13B-XASK-1-A_1x13_P2.50mm_Vertical +Connector_JST:JST_XA_B13B-XASK-1_1x13_P2.50mm_Vertical +Connector_JST:JST_XA_B14B-XASK-1-A_1x14_P2.50mm_Vertical +Connector_JST:JST_XA_B14B-XASK-1_1x14_P2.50mm_Vertical +Connector_JST:JST_XA_B15B-XASK-1-A_1x15_P2.50mm_Vertical +Connector_JST:JST_XA_B15B-XASK-1_1x15_P2.50mm_Vertical +Connector_JST:JST_XA_B18B-XASK-1_1x18_P2.50mm_Vertical +Connector_JST:JST_XA_B20B-XASK-1-A_1x20_P2.50mm_Vertical +Connector_JST:JST_XA_B20B-XASK-1_1x20_P2.50mm_Vertical +Connector_JST:JST_XA_S02B-XASK-1N-BN_1x02_P2.50mm_Horizontal +Connector_JST:JST_XA_S02B-XASK-1_1x02_P2.50mm_Horizontal +Connector_JST:JST_XA_S03B-XASK-1N-BN_1x03_P2.50mm_Horizontal +Connector_JST:JST_XA_S03B-XASK-1_1x03_P2.50mm_Horizontal +Connector_JST:JST_XA_S04B-XASK-1N-BN_1x04_P2.50mm_Horizontal +Connector_JST:JST_XA_S04B-XASK-1_1x04_P2.50mm_Horizontal +Connector_JST:JST_XA_S05B-XASK-1N-BN_1x05_P2.50mm_Horizontal +Connector_JST:JST_XA_S05B-XASK-1_1x05_P2.50mm_Horizontal +Connector_JST:JST_XA_S06B-XASK-1N-BN_1x06_P2.50mm_Horizontal +Connector_JST:JST_XA_S06B-XASK-1_1x06_P2.50mm_Horizontal +Connector_JST:JST_XA_S07B-XASK-1N-BN_1x07_P2.50mm_Horizontal +Connector_JST:JST_XA_S07B-XASK-1_1x07_P2.50mm_Horizontal +Connector_JST:JST_XA_S08B-XASK-1N-BN_1x08_P2.50mm_Horizontal +Connector_JST:JST_XA_S08B-XASK-1_1x08_P2.50mm_Horizontal +Connector_JST:JST_XA_S09B-XASK-1N-BN_1x09_P2.50mm_Horizontal +Connector_JST:JST_XA_S09B-XASK-1_1x09_P2.50mm_Horizontal +Connector_JST:JST_XA_S10B-XASK-1N-BN_1x10_P2.50mm_Horizontal +Connector_JST:JST_XA_S10B-XASK-1_1x10_P2.50mm_Horizontal +Connector_JST:JST_XA_S11B-XASK-1N-BN_1x11_P2.50mm_Horizontal +Connector_JST:JST_XA_S11B-XASK-1_1x11_P2.50mm_Horizontal +Connector_JST:JST_XA_S12B-XASK-1N-BN_1x12_P2.50mm_Horizontal +Connector_JST:JST_XA_S12B-XASK-1_1x12_P2.50mm_Horizontal +Connector_JST:JST_XA_S13B-XASK-1N-BN_1x13_P2.50mm_Horizontal +Connector_JST:JST_XA_S13B-XASK-1_1x13_P2.50mm_Horizontal +Connector_JST:JST_XA_S14B-XASK-1N-BN_1x14_P2.50mm_Horizontal +Connector_JST:JST_XA_S14B-XASK-1_1x14_P2.50mm_Horizontal +Connector_JST:JST_XH_B10B-XH-AM_1x10_P2.50mm_Vertical +Connector_JST:JST_XH_B10B-XH-A_1x10_P2.50mm_Vertical +Connector_JST:JST_XH_B11B-XH-A_1x11_P2.50mm_Vertical +Connector_JST:JST_XH_B12B-XH-AM_1x12_P2.50mm_Vertical +Connector_JST:JST_XH_B12B-XH-A_1x12_P2.50mm_Vertical +Connector_JST:JST_XH_B13B-XH-A_1x13_P2.50mm_Vertical +Connector_JST:JST_XH_B14B-XH-A_1x14_P2.50mm_Vertical +Connector_JST:JST_XH_B15B-XH-A_1x15_P2.50mm_Vertical +Connector_JST:JST_XH_B16B-XH-A_1x16_P2.50mm_Vertical +Connector_JST:JST_XH_B1B-XH-AM_1x01_P2.50mm_Vertical +Connector_JST:JST_XH_B20B-XH-A_1x20_P2.50mm_Vertical +Connector_JST:JST_XH_B2B-XH-AM_1x02_P2.50mm_Vertical +Connector_JST:JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical +Connector_JST:JST_XH_B3B-XH-AM_1x03_P2.50mm_Vertical +Connector_JST:JST_XH_B3B-XH-A_1x03_P2.50mm_Vertical +Connector_JST:JST_XH_B4B-XH-AM_1x04_P2.50mm_Vertical +Connector_JST:JST_XH_B4B-XH-A_1x04_P2.50mm_Vertical +Connector_JST:JST_XH_B5B-XH-AM_1x05_P2.50mm_Vertical +Connector_JST:JST_XH_B5B-XH-A_1x05_P2.50mm_Vertical +Connector_JST:JST_XH_B6B-XH-AM_1x06_P2.50mm_Vertical +Connector_JST:JST_XH_B6B-XH-A_1x06_P2.50mm_Vertical +Connector_JST:JST_XH_B7B-XH-AM_1x07_P2.50mm_Vertical +Connector_JST:JST_XH_B7B-XH-A_1x07_P2.50mm_Vertical +Connector_JST:JST_XH_B8B-XH-AM_1x08_P2.50mm_Vertical +Connector_JST:JST_XH_B8B-XH-A_1x08_P2.50mm_Vertical +Connector_JST:JST_XH_B9B-XH-AM_1x09_P2.50mm_Vertical +Connector_JST:JST_XH_B9B-XH-A_1x09_P2.50mm_Vertical +Connector_JST:JST_XH_S10B-XH-A-1_1x10_P2.50mm_Horizontal +Connector_JST:JST_XH_S10B-XH-A_1x10_P2.50mm_Horizontal +Connector_JST:JST_XH_S11B-XH-A-1_1x11_P2.50mm_Horizontal +Connector_JST:JST_XH_S11B-XH-A_1x11_P2.50mm_Horizontal +Connector_JST:JST_XH_S12B-XH-A-1_1x12_P2.50mm_Horizontal +Connector_JST:JST_XH_S12B-XH-A_1x12_P2.50mm_Horizontal +Connector_JST:JST_XH_S13B-XH-A-1_1x13_P2.50mm_Horizontal +Connector_JST:JST_XH_S13B-XH-A_1x13_P2.50mm_Horizontal +Connector_JST:JST_XH_S14B-XH-A-1_1x14_P2.50mm_Horizontal +Connector_JST:JST_XH_S14B-XH-A_1x14_P2.50mm_Horizontal +Connector_JST:JST_XH_S15B-XH-A-1_1x15_P2.50mm_Horizontal +Connector_JST:JST_XH_S15B-XH-A_1x15_P2.50mm_Horizontal +Connector_JST:JST_XH_S16B-XH-A_1x16_P2.50mm_Horizontal +Connector_JST:JST_XH_S2B-XH-A-1_1x02_P2.50mm_Horizontal +Connector_JST:JST_XH_S2B-XH-A_1x02_P2.50mm_Horizontal +Connector_JST:JST_XH_S3B-XH-A-1_1x03_P2.50mm_Horizontal +Connector_JST:JST_XH_S3B-XH-A_1x03_P2.50mm_Horizontal +Connector_JST:JST_XH_S3B-XH-SM4-TB_1x03-1MP_P2.50mm_Horizontal +Connector_JST:JST_XH_S4B-XH-A-1_1x04_P2.50mm_Horizontal +Connector_JST:JST_XH_S4B-XH-A_1x04_P2.50mm_Horizontal +Connector_JST:JST_XH_S4B-XH-SM4-TB_1x04-1MP_P2.50mm_Horizontal +Connector_JST:JST_XH_S5B-XH-A-1_1x05_P2.50mm_Horizontal +Connector_JST:JST_XH_S5B-XH-A_1x05_P2.50mm_Horizontal +Connector_JST:JST_XH_S6B-XH-A-1_1x06_P2.50mm_Horizontal +Connector_JST:JST_XH_S6B-XH-A_1x06_P2.50mm_Horizontal +Connector_JST:JST_XH_S6B-XH-SM4-TB_1x06-1MP_P2.50mm_Horizontal +Connector_JST:JST_XH_S7B-XH-A-1_1x07_P2.50mm_Horizontal +Connector_JST:JST_XH_S7B-XH-A_1x07_P2.50mm_Horizontal +Connector_JST:JST_XH_S8B-XH-A-1_1x08_P2.50mm_Horizontal +Connector_JST:JST_XH_S8B-XH-A_1x08_P2.50mm_Horizontal +Connector_JST:JST_XH_S9B-XH-A-1_1x09_P2.50mm_Horizontal +Connector_JST:JST_XH_S9B-XH-A_1x09_P2.50mm_Horizontal +Connector_JST:JST_ZE_B02B-ZESK-1D_1x02_P1.50mm_Vertical +Connector_JST:JST_ZE_B03B-ZESK-1D_1x03_P1.50mm_Vertical +Connector_JST:JST_ZE_B03B-ZESK-D_1x03_P1.50mm_Vertical +Connector_JST:JST_ZE_B04B-ZESK-1D_1x04_P1.50mm_Vertical +Connector_JST:JST_ZE_B04B-ZESK-D_1x04_P1.50mm_Vertical +Connector_JST:JST_ZE_B05B-ZESK-1D_1x05_P1.50mm_Vertical +Connector_JST:JST_ZE_B05B-ZESK-D_1x05_P1.50mm_Vertical +Connector_JST:JST_ZE_B06B-ZESK-1D_1x06_P1.50mm_Vertical +Connector_JST:JST_ZE_B06B-ZESK-D_1x06_P1.50mm_Vertical +Connector_JST:JST_ZE_B07B-ZESK-1D_1x07_P1.50mm_Vertical +Connector_JST:JST_ZE_B07B-ZESK-D_1x07_P1.50mm_Vertical +Connector_JST:JST_ZE_B08B-ZESK-1D_1x08_P1.50mm_Vertical +Connector_JST:JST_ZE_B08B-ZESK-D_1x08_P1.50mm_Vertical +Connector_JST:JST_ZE_B09B-ZESK-1D_1x09_P1.50mm_Vertical +Connector_JST:JST_ZE_B09B-ZESK-D_1x09_P1.50mm_Vertical +Connector_JST:JST_ZE_B10B-ZESK-1D_1x10_P1.50mm_Vertical +Connector_JST:JST_ZE_B10B-ZESK-D_1x10_P1.50mm_Vertical +Connector_JST:JST_ZE_B11B-ZESK-1D_1x11_P1.50mm_Vertical +Connector_JST:JST_ZE_B11B-ZESK-D_1x11_P1.50mm_Vertical +Connector_JST:JST_ZE_B12B-ZESK-1D_1x12_P1.50mm_Vertical +Connector_JST:JST_ZE_B12B-ZESK-D_1x12_P1.50mm_Vertical +Connector_JST:JST_ZE_B13B-ZESK-1D_1x13_P1.50mm_Vertical +Connector_JST:JST_ZE_B13B-ZESK-D_1x13_P1.50mm_Vertical +Connector_JST:JST_ZE_B14B-ZESK-1D_1x14_P1.50mm_Vertical +Connector_JST:JST_ZE_B14B-ZESK-D_1x14_P1.50mm_Vertical +Connector_JST:JST_ZE_B15B-ZESK-1D_1x15_P1.50mm_Vertical +Connector_JST:JST_ZE_B15B-ZESK-D_1x15_P1.50mm_Vertical +Connector_JST:JST_ZE_B16B-ZESK-1D_1x16_P1.50mm_Vertical +Connector_JST:JST_ZE_B16B-ZESK-D_1x16_P1.50mm_Vertical +Connector_JST:JST_ZE_BM02B-ZESS-TBT_1x02-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM03B-ZESS-TBT_1x03-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM04B-ZESS-TBT_1x04-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM05B-ZESS-TBT_1x05-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM06B-ZESS-TBT_1x06-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM07B-ZESS-TBT_1x07-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM08B-ZESS-TBT_1x08-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM09B-ZESS-TBT_1x09-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM10B-ZESS-TBT_1x10-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM11B-ZESS-TBT_1x11-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM12B-ZESS-TBT_1x12-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM13B-ZESS-TBT_1x13-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM14B-ZESS-TBT_1x14-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM15B-ZESS-TBT_1x15-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_BM16B-ZESS-TBT_1x16-1MP_P1.50mm_Vertical +Connector_JST:JST_ZE_S02B-ZESK-2D_1x02_P1.50mm_Horizontal +Connector_JST:JST_ZE_S03B-ZESK-2D_1x03_P1.50mm_Horizontal +Connector_JST:JST_ZE_S04B-ZESK-2D_1x04_P1.50mm_Horizontal +Connector_JST:JST_ZE_S05B-ZESK-2D_1x05_P1.50mm_Horizontal +Connector_JST:JST_ZE_S06B-ZESK-2D_1x06_P1.50mm_Horizontal +Connector_JST:JST_ZE_S07B-ZESK-2D_1x07_P1.50mm_Horizontal +Connector_JST:JST_ZE_S08B-ZESK-2D_1x08_P1.50mm_Horizontal +Connector_JST:JST_ZE_S09B-ZESK-2D_1x09_P1.50mm_Horizontal +Connector_JST:JST_ZE_S10B-ZESK-2D_1x10_P1.50mm_Horizontal +Connector_JST:JST_ZE_S11B-ZESK-2D_1x11_P1.50mm_Horizontal +Connector_JST:JST_ZE_S12B-ZESK-2D_1x12_P1.50mm_Horizontal +Connector_JST:JST_ZE_S13B-ZESK-2D_1x13_P1.50mm_Horizontal +Connector_JST:JST_ZE_S14B-ZESK-2D_1x14_P1.50mm_Horizontal +Connector_JST:JST_ZE_S15B-ZESK-2D_1x15_P1.50mm_Horizontal +Connector_JST:JST_ZE_S16B-ZESK-2D_1x16_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM02B-ZESS-TB_1x02-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM03B-ZESS-TB_1x03-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM04B-ZESS-TB_1x04-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM05B-ZESS-TB_1x05-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM06B-ZESS-TB_1x06-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM07B-ZESS-TB_1x07-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM08B-ZESS-TB_1x08-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM09B-ZESS-TB_1x09-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM10B-ZESS-TB_1x10-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM11B-ZESS-TB_1x11-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM12B-ZESS-TB_1x12-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM13B-ZESS-TB_1x13-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM14B-ZESS-TB_1x14-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM15B-ZESS-TB_1x15-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZE_SM16B-ZESS-TB_1x16-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_B10B-ZR-SM4-TF_1x10-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B10B-ZR_1x10_P1.50mm_Vertical +Connector_JST:JST_ZH_B11B-ZR-SM4-TF_1x11-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B11B-ZR_1x11_P1.50mm_Vertical +Connector_JST:JST_ZH_B12B-ZR-SM4-TF_1x12-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B12B-ZR_1x12_P1.50mm_Vertical +Connector_JST:JST_ZH_B13B-ZR-SM4-TF_1x13-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B2B-ZR-SM4-TF_1x02-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B2B-ZR_1x02_P1.50mm_Vertical +Connector_JST:JST_ZH_B3B-ZR-SM4-TF_1x03-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B3B-ZR_1x03_P1.50mm_Vertical +Connector_JST:JST_ZH_B4B-ZR-SM4-TF_1x04-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B4B-ZR_1x04_P1.50mm_Vertical +Connector_JST:JST_ZH_B5B-ZR-SM4-TF_1x05-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B5B-ZR_1x05_P1.50mm_Vertical +Connector_JST:JST_ZH_B6B-ZR-SM4-TF_1x06-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B6B-ZR_1x06_P1.50mm_Vertical +Connector_JST:JST_ZH_B7B-ZR-SM4-TF_1x07-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B7B-ZR_1x07_P1.50mm_Vertical +Connector_JST:JST_ZH_B8B-ZR-SM4-TF_1x08-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B8B-ZR_1x08_P1.50mm_Vertical +Connector_JST:JST_ZH_B9B-ZR-SM4-TF_1x09-1MP_P1.50mm_Vertical +Connector_JST:JST_ZH_B9B-ZR_1x09_P1.50mm_Vertical +Connector_JST:JST_ZH_S10B-ZR-SM4A-TF_1x10-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S11B-ZR-SM4A-TF_1x11-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S12B-ZR-SM4A-TF_1x12-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S13B-ZR-SM4A-TF_1x13-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S2B-ZR-SM4A-TF_1x02-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S3B-ZR-SM4A-TF_1x03-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S4B-ZR-SM4A-TF_1x04-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S5B-ZR-SM4A-TF_1x05-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S6B-ZR-SM4A-TF_1x06-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S7B-ZR-SM4A-TF_1x07-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S8B-ZR-SM4A-TF_1x08-1MP_P1.50mm_Horizontal +Connector_JST:JST_ZH_S9B-ZR-SM4A-TF_1x09-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502382-0270_1x02-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-0370_1x03-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-0470_1x04-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-0570_1x05-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-0670_1x06-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-0770_1x07-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-0870_1x08-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-0970_1x09-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-1070_1x10-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-1170_1x11-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-1270_1x12-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-1370_1x13-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-1470_1x14-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502382-1570_1x15-1MP_P1.25mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502386-0270_1x02-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-0370_1x03-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-0470_1x04-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-0570_1x05-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-0670_1x06-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-0770_1x07-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-0870_1x08-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-0970_1x09-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-1070_1x10-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-1170_1x11-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-1270_1x12-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-1370_1x13-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-1470_1x14-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502386-1570_1x15-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502443-0270_1x02-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-0370_1x03-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-0470_1x04-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-0570_1x05-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-0670_1x06-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-0770_1x07-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-0870_1x08-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-0970_1x09-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-1270_1x12-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-1370_1x13-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-1470_1x14-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502443-1570_1x15-1MP_P2.00mm_Vertical +Connector_Molex:Molex_CLIK-Mate_502494-0270_1x02-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-0370_1x03-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-0470_1x04-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-0670_1x06-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-0870_1x08-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-1070_1x10-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-1270_1x12-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-1370_1x13-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-1470_1x14-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502494-1570_1x15-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-0270_1x02-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-0370_1x03-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-0470_1x04-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-0570_1x05-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-0670_1x06-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-0770_1x07-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-0870_1x08-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-0970_1x09-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-1070_1x10-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-1170_1x11-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-1270_1x12-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-1370_1x13-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-1470_1x14-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_502585-1570_1x15-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_CLIK-Mate_505405-0270_1x02-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-0370_1x03-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-0470_1x04-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-0570_1x05-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-0670_1x06-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-0770_1x07-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-0870_1x08-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-0970_1x09-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-1070_1x10-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-1170_1x11-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-1270_1x12-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-1370_1x13-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-1470_1x14-1MP_P1.50mm_Vertical +Connector_Molex:Molex_CLIK-Mate_505405-1570_1x15-1MP_P1.50mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-02A_1x02_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-03A_1x03_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-04A_1x04_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-05A_1x05_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-06A_1x06_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-07A_1x07_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-08A_1x08_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-09A_1x09_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-10A_1x10_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-11A_1x11_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-12A_1x12_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-13A_1x13_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-14A_1x14_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-15A_1x15_P2.54mm_Vertical +Connector_Molex:Molex_KK-254_AE-6410-16A_1x16_P2.54mm_Vertical +Connector_Molex:Molex_KK-396_5273-02A_1x02_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-03A_1x03_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-04A_1x04_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-05A_1x05_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-06A_1x06_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-07A_1x07_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-08A_1x08_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-09A_1x09_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-10A_1x10_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-11A_1x11_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_5273-12A_1x12_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0002_1x02_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0003_1x03_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0004_1x04_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0005_1x05_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0006_1x06_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0007_1x07_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0008_1x08_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0009_1x09_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0010_1x10_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0011_1x11_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0012_1x12_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0013_1x13_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0014_1x14_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0015_1x15_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0016_1x16_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0017_1x17_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41791-0018_1x18_P3.96mm_Vertical +Connector_Molex:Molex_KK-396_A-41792-0002_1x02_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0003_1x03_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0004_1x04_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0005_1x05_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0006_1x06_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0007_1x07_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0008_1x08_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0009_1x09_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0010_1x10_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0011_1x11_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0012_1x12_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0013_1x13_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0014_1x14_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0015_1x15_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0016_1x16_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0017_1x17_P3.96mm_Horizontal +Connector_Molex:Molex_KK-396_A-41792-0018_1x18_P3.96mm_Horizontal +Connector_Molex:Molex_Mega-Fit_76825-0002_2x01_P5.70mm_Horizontal +Connector_Molex:Molex_Mega-Fit_76825-0004_2x02_P5.70mm_Horizontal +Connector_Molex:Molex_Mega-Fit_76825-0006_2x03_P5.70mm_Horizontal +Connector_Molex:Molex_Mega-Fit_76825-0008_2x04_P5.70mm_Horizontal +Connector_Molex:Molex_Mega-Fit_76825-0010_2x05_P5.70mm_Horizontal +Connector_Molex:Molex_Mega-Fit_76825-0012_2x06_P5.70mm_Horizontal +Connector_Molex:Molex_Mega-Fit_76829-0002_2x01_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0004_2x02_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0006_2x03_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0008_2x04_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0010_2x05_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0012_2x06_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0102_2x01_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0104_2x02_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0106_2x03_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0108_2x04_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0110_2x05_P5.70mm_Vertical +Connector_Molex:Molex_Mega-Fit_76829-0112_2x06_P5.70mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0200_2x01_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0210_2x01-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0212_2x01_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0215_2x01_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0218_2x01-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0221_2x01-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0400_2x02_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0410_2x02-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0412_2x02_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0415_2x02_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0418_2x02-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0421_2x02-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0600_2x03_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0610_2x03-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0612_2x03_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0615_2x03_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0618_2x03-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0621_2x03-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0800_2x04_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0810_2x04-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-0812_2x04_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0815_2x04_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0818_2x04-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-0821_2x04-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1000_2x05_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1010_2x05-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1012_2x05_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1015_2x05_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1018_2x05-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1021_2x05-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1200_2x06_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1210_2x06-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1212_2x06_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1215_2x06_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1218_2x06-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1221_2x06-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1400_2x07_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1410_2x07-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1412_2x07_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1415_2x07_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1418_2x07-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1421_2x07-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1600_2x08_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1610_2x08-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1612_2x08_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1615_2x08_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1618_2x08-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1621_2x08-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1800_2x09_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1810_2x09-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-1812_2x09_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1815_2x09_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1818_2x09-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-1821_2x09-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2000_2x10_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2010_2x10-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2012_2x10_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2015_2x10_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2018_2x10-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2021_2x10-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2200_2x11_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2210_2x11-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2212_2x11_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2215_2x11_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2218_2x11-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2221_2x11-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2400_2x12_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2410_2x12-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43045-2412_2x12_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2415_2x12_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2418_2x12-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43045-2421_2x12-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0200_1x02_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0210_1x02-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0210_1x02-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-0215_1x02_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0221_1x02_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0224_1x02-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0300_1x03_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0310_1x03-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0310_1x03-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-0315_1x03_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0321_1x03_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0324_1x03-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0400_1x04_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0410_1x04-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0410_1x04-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-0415_1x04_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0421_1x04_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0424_1x04-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0500_1x05_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0510_1x05-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0510_1x05-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-0515_1x05_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0521_1x05_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0524_1x05-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0600_1x06_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0610_1x06-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0610_1x06-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-0615_1x06_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0621_1x06_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0624_1x06-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0700_1x07_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0710_1x07-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0710_1x07-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-0715_1x07_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0721_1x07_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0724_1x07-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0800_1x08_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0810_1x08-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0810_1x08-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-0815_1x08_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0821_1x08_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0824_1x08-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0900_1x09_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0910_1x09-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-0910_1x09-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-0915_1x09_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0921_1x09_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-0924_1x09-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1000_1x10_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-1010_1x10-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-1010_1x10-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-1015_1x10_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1021_1x10_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1024_1x10-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1100_1x11_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-1110_1x11-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-1110_1x11-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-1115_1x11_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1121_1x11_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1124_1x11-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1200_1x12_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-1210_1x12-1MP_P3.00mm_Horizontal +Connector_Molex:Molex_Micro-Fit_3.0_43650-1210_1x12-1MP_P3.00mm_Horizontal_PnP +Connector_Molex:Molex_Micro-Fit_3.0_43650-1215_1x12_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1221_1x12_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Fit_3.0_43650-1224_1x12-1MP_P3.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-0270_1x02_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-0370_1x03_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-0470_1x04_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-0570_1x05_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-0670_1x06_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-0770_1x07_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-0870_1x08_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-0970_1x09_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-1070_1x10_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-1170_1x11_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-1270_1x12_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-1370_1x13_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-1470_1x14_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53253-1570_1x15_P2.00mm_Vertical +Connector_Molex:Molex_Micro-Latch_53254-0270_1x02_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-0370_1x03_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-0470_1x04_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-0570_1x05_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-0670_1x06_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-0770_1x07_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-0870_1x08_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-0970_1x09_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-1070_1x10_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-1170_1x11_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-1270_1x12_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-1370_1x13_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-1470_1x14_P2.00mm_Horizontal +Connector_Molex:Molex_Micro-Latch_53254-1570_1x15_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55932-0210_1x02_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0230_1x02_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0310_1x03_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0330_1x03_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0410_1x04_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0430_1x04_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0510_1x05_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0530_1x05_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0610_1x06_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0630_1x06_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0710_1x07_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0730_1x07_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0810_1x08_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0830_1x08_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0910_1x09_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-0930_1x09_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1010_1x10_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1030_1x10_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1110_1x11_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1130_1x11_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1210_1x12_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1230_1x12_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1310_1x13_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1330_1x13_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1410_1x14_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1430_1x14_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1510_1x15_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55932-1530_1x15_P2.00mm_Vertical +Connector_Molex:Molex_MicroClasp_55935-0210_1x02_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0230_1x02_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0310_1x03_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0330_1x03_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0410_1x04_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0430_1x04_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0510_1x05_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0530_1x05_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0610_1x06_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0630_1x06_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0710_1x07_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0730_1x07_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0810_1x08_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0830_1x08_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0910_1x09_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-0930_1x09_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1010_1x10_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1030_1x10_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1110_1x11_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1130_1x11_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1210_1x12_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1230_1x12_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1310_1x13_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1330_1x13_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1410_1x14_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1430_1x14_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1510_1x15_P2.00mm_Horizontal +Connector_Molex:Molex_MicroClasp_55935-1530_1x15_P2.00mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5566-02A2_2x01_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-02A_2x01_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-04A2_2x02_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-04A_2x02_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-06A2_2x03_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-06A_2x03_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-08A2_2x04_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-08A_2x04_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-10A2_2x05_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-10A_2x05_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-12A2_2x06_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-12A_2x06_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-14A2_2x07_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-14A_2x07_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-16A2_2x08_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-16A_2x08_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-18A2_2x09_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-18A_2x09_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-20A2_2x10_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-20A_2x10_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-22A2_2x11_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-22A_2x11_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-24A2_2x12_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5566-24A_2x12_P4.20mm_Vertical +Connector_Molex:Molex_Mini-Fit_Jr_5569-02A1_2x01_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-02A2_2x01_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-04A1_2x02_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-04A2_2x02_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-06A1_2x03_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-06A2_2x03_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-08A1_2x04_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-08A2_2x04_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-10A1_2x05_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-10A2_2x05_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-12A1_2x06_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-12A2_2x06_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-14A1_2x07_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-14A2_2x07_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-16A1_2x08_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-16A2_2x08_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-18A1_2x09_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-18A2_2x09_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-20A1_2x10_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-20A2_2x10_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-22A1_2x11_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-22A2_2x11_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-24A1_2x12_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Jr_5569-24A2_2x12_P4.20mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Sr_42819-22XX_1x02_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_42819-22XX_1x02_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42819-32XX_1x03_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_42819-32XX_1x03_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42819-42XX_1x04_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_42819-42XX_1x04_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42819-52XX_1x05_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_42819-52XX_1x05_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42819-62XX_1x06_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_42819-62XX_1x06_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42820-22XX_1x02_P10.00mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Sr_42820-22XX_1x02_P10.00mm_Horizontal_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42820-32XX_1x03_P10.00mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Sr_42820-32XX_1x03_P10.00mm_Horizontal_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42820-42XX_1x04_P10.00mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Sr_42820-42XX_1x04_P10.00mm_Horizontal_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42820-52XX_1x05_P10.00mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Sr_42820-52XX_1x05_P10.00mm_Horizontal_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_42820-62XX_1x06_P10.00mm_Horizontal +Connector_Molex:Molex_Mini-Fit_Sr_42820-62XX_1x06_P10.00mm_Horizontal_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx06_2x03_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx06_2x03_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx08_2x04_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx08_2x04_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx10_2x05_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx10_2x05_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx12_2x06_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx12_2x06_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx14_2x07_P10.00mm_Vertical +Connector_Molex:Molex_Mini-Fit_Sr_43915-xx14_2x07_P10.00mm_Vertical_ThermalVias +Connector_Molex:Molex_Nano-Fit_105309-xx02_1x02_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105309-xx03_1x03_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105309-xx04_1x04_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105309-xx05_1x05_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105309-xx06_1x06_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105309-xx07_1x07_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105309-xx08_1x08_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105310-xx04_2x02_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105310-xx06_2x03_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105310-xx08_2x04_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105310-xx10_2x05_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105310-xx12_2x06_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105310-xx14_2x07_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105310-xx16_2x08_P2.50mm_Vertical +Connector_Molex:Molex_Nano-Fit_105313-xx02_1x02_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105313-xx03_1x03_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105313-xx04_1x04_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105313-xx05_1x05_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105313-xx06_1x06_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105313-xx07_1x07_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105313-xx08_1x08_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105314-xx04_2x02_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105314-xx06_2x03_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105314-xx08_2x04_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105314-xx10_2x05_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105314-xx12_2x06_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105314-xx14_2x07_P2.50mm_Horizontal +Connector_Molex:Molex_Nano-Fit_105314-xx16_2x08_P2.50mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-0270_1x02-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-0370_1x03-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-0470_1x04-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-0570_1x05-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-0670_1x06-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-0770_1x07-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-0870_1x08-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-0970_1x09-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-1070_1x10-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-1270_1x12-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-1470_1x14-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-1570_1x15-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-1870_1x18-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Panelmate_53780-3070_1x30-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-0207_1x02-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-0307_1x03-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-0407_1x04-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-0507_1x05-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-0607_1x06-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-0707_1x07-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-0807_1x08-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-0907_1x09-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-1007_1x10-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-1107_1x11-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-1207_1x12-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-1307_1x13-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-1407_1x14-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_202396-1507_1x15-1MP_P1.00mm_Horizontal +Connector_Molex:Molex_Pico-Clasp_501331-0207_1x02-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-0307_1x03-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-0407_1x04-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-0507_1x05-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-0607_1x06-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-0707_1x07-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-0807_1x08-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-0907_1x09-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-1007_1x10-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-1107_1x11-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-1207_1x12-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-1307_1x13-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-1407_1x14-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-Clasp_501331-1507_1x15-1MP_P1.00mm_Vertical +Connector_Molex:Molex_Pico-EZmate_78171-0002_1x02-1MP_P1.20mm_Vertical +Connector_Molex:Molex_Pico-EZmate_78171-0003_1x03-1MP_P1.20mm_Vertical +Connector_Molex:Molex_Pico-EZmate_78171-0004_1x04-1MP_P1.20mm_Vertical +Connector_Molex:Molex_Pico-EZmate_78171-0005_1x05-1MP_P1.20mm_Vertical +Connector_Molex:Molex_Pico-EZmate_Slim_202656-0021_1x02-1MP_P1.20mm_Vertical +Connector_Molex:Molex_Pico-Lock_205338-0002_1x02-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_Pico-Lock_205338-0004_1x04-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_Pico-Lock_205338-0006_1x06-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_Pico-Lock_205338-0008_1x08-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_Pico-Lock_205338-0010_1x10-1MP_P2.00mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-0291_1x02-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-0391_1x03-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-0491_1x04-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-0591_1x05-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-0691_1x06-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-0791_1x07-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-0891_1x08-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-0991_1x09-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-1091_1x10-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-1191_1x11-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-Lock_504050-1291_1x12-1MP_P1.50mm_Horizontal +Connector_Molex:Molex_Pico-SPOX_87437-1443_1x14-P1.5mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-0210_1x02_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-0310_1x03_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-0410_1x04_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-0510_1x05_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-0610_1x06_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-0710_1x07_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-0810_1x08_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-0910_1x09_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-1010_1x10_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-1110_1x11_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-1210_1x12_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-1310_1x13_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-1410_1x14_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53047-1510_1x15_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53048-0210_1x02_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-0310_1x03_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-0410_1x04_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-0510_1x05_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-0610_1x06_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-0710_1x07_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-0810_1x08_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-0910_1x09_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-1010_1x10_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-1110_1x11_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-1210_1x12_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-1310_1x13_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-1410_1x14_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53048-1510_1x15_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-0271_1x02-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-0371_1x03-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-0471_1x04-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-0571_1x05-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-0671_1x06-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-0771_1x07-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-0871_1x08-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-0971_1x09-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-1071_1x10-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-1171_1x11-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-1271_1x12-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-1371_1x13-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-1471_1x14-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-1571_1x15-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53261-1771_1x17-1MP_P1.25mm_Horizontal +Connector_Molex:Molex_PicoBlade_53398-0271_1x02-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-0371_1x03-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-0471_1x04-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-0571_1x05-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-0671_1x06-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-0771_1x07-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-0871_1x08-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-0971_1x09-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-1071_1x10-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-1171_1x11-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-1271_1x12-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-1371_1x13-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-1471_1x14-1MP_P1.25mm_Vertical +Connector_Molex:Molex_PicoBlade_53398-1571_1x15-1MP_P1.25mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0004_2x02_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0006_2x03_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0008_2x04_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0010_2x05_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0012_2x06_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0014_2x07_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0016_2x08_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0018_2x09_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0020_2x10_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0022_2x11_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0024_2x12_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90325-0026_2x13_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0004_2x02_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0006_2x03_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0008_2x04_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0010_2x05_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0012_2x06_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0014_2x07_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0016_2x08_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0018_2x09_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0020_2x10_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0022_2x11_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0024_2x12_P1.27mm_Vertical +Connector_Molex:Molex_Picoflex_90814-0026_2x13_P1.27mm_Vertical +Connector_Molex:Molex_Sabre_43160-0102_1x02_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-0102_1x02_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-0103_1x03_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-0103_1x03_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-0104_1x04_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-0104_1x04_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-0105_1x05_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-0105_1x05_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-0106_1x06_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-0106_1x06_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-1102_1x02_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_43160-1102_1x02_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_43160-1103_1x03_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_43160-1103_1x03_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_43160-1104_1x04_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_43160-1104_1x04_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_43160-1105_1x05_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_43160-1105_1x05_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_43160-1106_1x06_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_43160-1106_1x06_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_43160-2102_1x02_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-2102_1x02_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-2103_1x03_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-2103_1x03_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-2104_1x04_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-2104_1x04_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-2105_1x05_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-2105_1x05_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_43160-2106_1x06_P7.49mm_Vertical +Connector_Molex:Molex_Sabre_43160-2106_1x06_P7.49mm_Vertical_ThermalVias +Connector_Molex:Molex_Sabre_46007-1102_1x02_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_46007-1102_1x02_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_46007-1103_1x03_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_46007-1103_1x03_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_46007-1104_1x04_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_46007-1104_1x04_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_46007-1105_1x05_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_46007-1105_1x05_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_Sabre_46007-1106_1x06_P7.49mm_Horizontal +Connector_Molex:Molex_Sabre_46007-1106_1x06_P7.49mm_Horizontal_ThermalVias +Connector_Molex:Molex_SlimStack_501920-3001_2x15_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_501920-4001_2x20_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_501920-5001_2x25_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_502426-0810_2x04_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-1410_2x07_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-2010_2x10_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-2210_2x11_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-2410_2x12_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-2610_2x13_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-3010_2x15_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-3210_2x16_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-3410_2x17_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-4010_2x20_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-4410_2x22_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-5010_2x25_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-6010_2x30_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-6410_2x32_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502426-8010_2x40_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-0820_2x04_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-1410_2x07_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-2010_2x10_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-2210_2x11_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-2410_2x12_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-2610_2x13_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-3010_2x15_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-3210_2x16_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-3410_2x17_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-4010_2x20_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-4410_2x22_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-5010_2x25_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-6010_2x30_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-6410_2x32_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_502430-8010_2x40_P0.40mm_Vertical +Connector_Molex:Molex_SlimStack_52991-0208_2x10_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_52991-0308_2x15_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_52991-0408_2x20_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_52991-0508_2x25_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_52991-0608_2x30_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_52991-0708_2x35_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_52991-0808_2x40_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_53748-0208_2x10_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_53748-0308_2x15_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_53748-0408_2x20_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_53748-0608_2x30_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_53748-0708_2x35_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_53748-0808_2x40_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0164_2x08_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0204_2x10_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0224_2x11_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0244_2x12_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0304_2x15_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0344_2x17_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0404_2x20_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0504_2x25_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0604_2x30_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_54722-0804_2x40_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0161_2x08_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0201_2x10_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0221_2x11_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0241_2x12_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0301_2x15_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0341_2x17_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0401_2x20_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0501_2x25_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0601_2x30_P0.50mm_Vertical +Connector_Molex:Molex_SlimStack_55560-0801_2x40_P0.50mm_Vertical +Connector_Molex:Molex_SL_171971-0002_1x02_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0003_1x03_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0004_1x04_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0005_1x05_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0006_1x06_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0007_1x07_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0008_1x08_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0009_1x09_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0010_1x10_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0011_1x11_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0012_1x12_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0013_1x13_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0014_1x14_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0015_1x15_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0016_1x16_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0017_1x17_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0018_1x18_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0019_1x19_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0020_1x20_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0021_1x21_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0022_1x22_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0023_1x23_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0024_1x24_P2.54mm_Vertical +Connector_Molex:Molex_SL_171971-0025_1x25_P2.54mm_Vertical +Connector_Molex:Molex_SPOX_5267-02A_1x02_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-03A_1x03_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-04A_1x04_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-05A_1x05_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-06A_1x06_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-07A_1x07_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-08A_1x08_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-09A_1x09_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-10A_1x10_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-11A_1x11_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-12A_1x12_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-13A_1x13_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-14A_1x14_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5267-15A_1x15_P2.50mm_Vertical +Connector_Molex:Molex_SPOX_5268-02A_1x02_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-03A_1x03_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-04A_1x04_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-05A_1x05_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-06A_1x06_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-07A_1x07_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-08A_1x08_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-09A_1x09_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-10A_1x10_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-11A_1x11_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-12A_1x12_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-13A_1x13_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-14A_1x14_P2.50mm_Horizontal +Connector_Molex:Molex_SPOX_5268-15A_1x15_P2.50mm_Horizontal +Connector_PCBEdge:4UCON_10156_2x40_P1.27mm_Socket_Horizontal +Connector_PCBEdge:BUS_AT +Connector_PCBEdge:BUS_PCI +Connector_PCBEdge:BUS_PCIexpress_x1 +Connector_PCBEdge:BUS_PCIexpress_x16 +Connector_PCBEdge:BUS_PCIexpress_x4 +Connector_PCBEdge:BUS_PCIexpress_x8 +Connector_PCBEdge:BUS_PCI_Express_Mini +Connector_PCBEdge:BUS_PCI_Express_Mini_Dual +Connector_PCBEdge:BUS_PCI_Express_Mini_Full +Connector_PCBEdge:BUS_PCI_Express_Mini_Half +Connector_PCBEdge:JAE_MM60-EZH039-Bx_BUS_PCI_Express_Holder +Connector_PCBEdge:JAE_MM60-EZH059-Bx_BUS_PCI_Express_Holder +Connector_PCBEdge:molex_EDGELOCK_2-CKT +Connector_PCBEdge:molex_EDGELOCK_4-CKT +Connector_PCBEdge:molex_EDGELOCK_6-CKT +Connector_PCBEdge:molex_EDGELOCK_8-CKT +Connector_PCBEdge:Samtec_MECF-05-01-L-DV-WT_2x05_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-05-01-L-DV_2x05_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-05-01-NP-L-DV-WT_2x05_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-05-01-NP-L-DV_2x05_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-05-02-L-DV-WT_2x05_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-05-02-L-DV_2x05_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-05-02-NP-L-DV-WT_2x05_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-05-02-NP-L-DV_2x05_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-05-0_-L-DV_2x05_P1.27mm_Polarized_Edge +Connector_PCBEdge:Samtec_MECF-05-0_-NP-L-DV_2x05_P1.27mm_Edge +Connector_PCBEdge:Samtec_MECF-08-01-L-DV-WT_2x08_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-08-01-L-DV_2x08_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-08-01-NP-L-DV-WT_2x08_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-08-01-NP-L-DV_2x08_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-08-02-L-DV-WT_2x08_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-08-02-L-DV_2x08_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-08-02-NP-L-DV-WT_2x08_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-08-02-NP-L-DV_2x08_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-08-0_-L-DV_2x08_P1.27mm_Polarized_Edge +Connector_PCBEdge:Samtec_MECF-08-0_-NP-L-DV_2x08_P1.27mm_Edge +Connector_PCBEdge:Samtec_MECF-20-01-L-DV-WT_2x20_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-20-01-L-DV_2x20_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-20-01-NP-L-DV-WT_2x20_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-20-01-NP-L-DV_2x20_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-20-02-L-DV-WT_2x20_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-20-02-L-DV_2x20_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-20-02-NP-L-DV-WT_2x20_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-20-02-NP-L-DV_2x20_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-20-0_-L-DV_2x20_P1.27mm_Polarized_Edge +Connector_PCBEdge:Samtec_MECF-20-0_-NP-L-DV_2x20_P1.27mm_Edge +Connector_PCBEdge:Samtec_MECF-30-01-L-DV-WT_2x30_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-30-01-L-DV_2x30_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-30-01-NP-L-DV-WT_2x30_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-30-01-NP-L-DV_2x30_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-30-02-L-DV-WT_2x30_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-30-02-L-DV_2x30_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-30-02-NP-L-DV-WT_2x30_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-30-02-NP-L-DV_2x30_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-30-0_-L-DV_2x30_P1.27mm_Polarized_Edge +Connector_PCBEdge:Samtec_MECF-30-0_-NP-L-DV_2x30_P1.27mm_Edge +Connector_PCBEdge:Samtec_MECF-40-01-L-DV-WT_2x40_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-40-01-L-DV_2x40_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-40-01-NP-L-DV-WT_2x40_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-40-01-NP-L-DV_2x40_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-40-02-L-DV-WT_2x40_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-40-02-L-DV_2x40_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-40-02-NP-L-DV-WT_2x40_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-40-02-NP-L-DV_2x40_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-40-0_-L-DV_2x40_P1.27mm_Polarized_Edge +Connector_PCBEdge:Samtec_MECF-40-0_-NP-L-DV_2x40_P1.27mm_Edge +Connector_PCBEdge:Samtec_MECF-50-01-L-DV-WT_2x50_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-50-01-L-DV_2x50_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-50-01-NP-L-DV-WT_2x50_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-50-01-NP-L-DV_2x50_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-50-02-L-DV-WT_2x50_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-50-02-L-DV_2x50_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-50-02-NP-L-DV-WT_2x50_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-50-02-NP-L-DV_2x50_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-50-0_-L-DV_2x50_P1.27mm_Polarized_Edge +Connector_PCBEdge:Samtec_MECF-50-0_-NP-L-DV_2x50_P1.27mm_Edge +Connector_PCBEdge:Samtec_MECF-60-01-L-DV-WT_2x60_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-60-01-L-DV_2x60_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-60-01-NP-L-DV-WT_2x60_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-60-01-NP-L-DV_2x60_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-60-02-L-DV-WT_2x60_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-60-02-L-DV_2x60_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-60-02-NP-L-DV-WT_2x60_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-60-02-NP-L-DV_2x60_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-60-0_-L-DV_2x60_P1.27mm_Polarized_Edge +Connector_PCBEdge:Samtec_MECF-60-0_-NP-L-DV_2x60_P1.27mm_Edge +Connector_PCBEdge:Samtec_MECF-70-01-L-DV-WT_2x70_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-70-01-L-DV_2x70_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-70-01-NP-L-DV-WT_2x70_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-70-01-NP-L-DV_2x70_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-70-02-L-DV-WT_2x70_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-70-02-L-DV_2x70_P1.27mm_Polarized_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-70-02-NP-L-DV-WT_2x70_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-70-02-NP-L-DV_2x70_P1.27mm_Socket_Horizontal +Connector_PCBEdge:Samtec_MECF-70-0_-L-DV_2x70_P1.27mm_Polarized_Edge +Connector_PCBEdge:Samtec_MECF-70-0_-NP-L-DV_2x70_P1.27mm_Edge +Connector_PCBEdge:SODIMM-200_1.8V_Card_edge +Connector_PCBEdge:SODIMM-200_2.5V_Card_edge +Connector_PCBEdge:SODIMM-260_DDR4_H4.0-5.2_OrientationStd_Socket +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_10-G-7,62_1x10_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_10-G_1x10_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_11-G-7,62_1x11_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_11-G_1x11_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_12-G-7,62_1x12_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_12-G_1x12_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_2-G-7,62_1x02_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_2-G_1x02_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_3-G-7,62_1x03_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_3-G_1x03_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_4-G-7,62_1x04_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_4-G_1x04_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_5-G-7,62_1x05_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_5-G_1x05_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_6-G-7,62_1x06_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_6-G_1x06_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_7-G-7,62_1x07_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_7-G_1x07_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_8-G-7,62_1x08_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_8-G_1x08_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_9-G-7,62_1x09_P7.62mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBA_2,5_9-G_1x09_P7.50mm_Horizontal +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_10-G-7,62_1x10_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_10-G_1x10_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_11-G-7,62_1x11_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_11-G_1x11_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_12-G-7,62_1x12_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_12-G_1x12_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_2-G-7,62_1x02_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_2-G_1x02_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_3-G-7,62_1x03_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_3-G_1x03_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_4-G-7,62_1x04_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_4-G_1x04_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_5-G-7,62_1x05_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_5-G_1x05_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_6-G-7,62_1x06_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_6-G_1x06_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_7-G-7,62_1x07_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_7-G_1x07_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_8-G-7,62_1x08_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_8-G_1x08_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_9-G-7,62_1x09_P7.62mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBVA_2,5_9-G_1x09_P7.50mm_Vertical +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_10-GF-7,62_1x10_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_10-GF-7,62_1x10_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_11-GF-7,62_1x11_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_11-GF-7,62_1x11_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_12-GF-7,62_1x12_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_12-GF-7,62_1x12_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_2-GF-7,62_1x02_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_2-GF-7,62_1x02_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_3-GF-7,62_1x03_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_3-GF-7,62_1x03_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_4-GF-7,62_1x04_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_4-GF-7,62_1x04_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_5-GF-7,62_1x05_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_5-GF-7,62_1x05_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_6-GF-7,62_1x06_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_6-GF-7,62_1x06_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_7-GF-7,62_1x07_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_7-GF-7,62_1x07_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_8-GF-7,62_1x08_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_8-GF-7,62_1x08_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_9-GF-7,62_1x09_P7.62mm_Vertical_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTBV_2,5_9-GF-7,62_1x09_P7.62mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_10-GF-7,62_1x10_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_10-GF-7,62_1x10_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_11-GF-7,62_1x11_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_11-GF-7,62_1x11_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_12-GF-7,62_1x12_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_12-GF-7,62_1x12_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_2-GF-7,62_1x02_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_2-GF-7,62_1x02_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_3-GF-7,62_1x03_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_3-GF-7,62_1x03_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_4-GF-7,62_1x04_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_4-GF-7,62_1x04_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_5-GF-7,62_1x05_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_5-GF-7,62_1x05_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_6-GF-7,62_1x06_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_6-GF-7,62_1x06_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_7-GF-7,62_1x07_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_7-GF-7,62_1x07_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_8-GF-7,62_1x08_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_8-GF-7,62_1x08_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_9-GF-7,62_1x09_P7.62mm_Horizontal_ThreadedFlange +Connector_Phoenix_GMSTB:PhoenixContact_GMSTB_2,5_9-GF-7,62_1x09_P7.62mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_10-G-3.5_1x10_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_10-G-3.81_1x10_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_10-GF-3.5_1x10_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_10-GF-3.5_1x10_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_10-GF-3.81_1x10_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_10-GF-3.81_1x10_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_11-G-3.5_1x11_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_11-G-3.81_1x11_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_11-GF-3.5_1x11_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_11-GF-3.5_1x11_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_11-GF-3.81_1x11_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_11-GF-3.81_1x11_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_12-G-3.5_1x12_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_12-G-3.81_1x12_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_12-GF-3.5_1x12_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_12-GF-3.5_1x12_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_12-GF-3.81_1x12_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_12-GF-3.81_1x12_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_13-G-3.5_1x13_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_13-G-3.81_1x13_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_13-GF-3.5_1x13_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_13-GF-3.5_1x13_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_13-GF-3.81_1x13_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_13-GF-3.81_1x13_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_14-G-3.5_1x14_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_14-G-3.81_1x14_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_14-GF-3.5_1x14_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_14-GF-3.5_1x14_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_14-GF-3.81_1x14_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_14-GF-3.81_1x14_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_15-G-3.5_1x15_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_15-G-3.81_1x15_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_15-GF-3.5_1x15_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_15-GF-3.5_1x15_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_15-GF-3.81_1x15_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_15-GF-3.81_1x15_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_16-G-3.5_1x16_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_16-G-3.81_1x16_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_16-GF-3.5_1x16_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_16-GF-3.5_1x16_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_16-GF-3.81_1x16_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_16-GF-3.81_1x16_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_2-G-3.5_1x02_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_2-G-3.81_1x02_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_2-GF-3.5_1x02_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_2-GF-3.5_1x02_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_2-GF-3.81_1x02_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_2-GF-3.81_1x02_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_3-G-3.5_1x03_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_3-G-3.81_1x03_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_3-GF-3.5_1x03_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_3-GF-3.5_1x03_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_3-GF-3.81_1x03_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_3-GF-3.81_1x03_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_4-G-3.5_1x04_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_4-G-3.81_1x04_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_4-GF-3.5_1x04_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_4-GF-3.5_1x04_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_4-GF-3.81_1x04_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_4-GF-3.81_1x04_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_5-G-3.5_1x05_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_5-G-3.81_1x05_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_5-GF-3.5_1x05_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_5-GF-3.5_1x05_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_5-GF-3.81_1x05_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_5-GF-3.81_1x05_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_6-G-3.5_1x06_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_6-G-3.81_1x06_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_6-GF-3.5_1x06_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_6-GF-3.5_1x06_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_6-GF-3.81_1x06_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_6-GF-3.81_1x06_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_7-G-3.5_1x07_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_7-G-3.81_1x07_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_7-GF-3.5_1x07_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_7-GF-3.5_1x07_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_7-GF-3.81_1x07_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_7-GF-3.81_1x07_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_8-G-3.5_1x08_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_8-G-3.81_1x08_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_8-GF-3.5_1x08_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_8-GF-3.5_1x08_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_8-GF-3.81_1x08_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_8-GF-3.81_1x08_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_9-G-3.5_1x09_P3.50mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_9-G-3.81_1x09_P3.81mm_Vertical +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_9-GF-3.5_1x09_P3.50mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_9-GF-3.5_1x09_P3.50mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_9-GF-3.81_1x09_P3.81mm_Vertical_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MCV_1,5_9-GF-3.81_1x09_P3.81mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_10-G-3.5_1x10_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_10-G-3.81_1x10_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_10-GF-3.5_1x10_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_10-GF-3.5_1x10_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_10-GF-3.81_1x10_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_10-GF-3.81_1x10_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_11-G-3.5_1x11_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_11-G-3.81_1x11_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_11-GF-3.5_1x11_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_11-GF-3.5_1x11_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_11-GF-3.81_1x11_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_11-GF-3.81_1x11_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_12-G-3.5_1x12_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_12-G-3.81_1x12_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_12-GF-3.5_1x12_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_12-GF-3.5_1x12_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_12-GF-3.81_1x12_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_12-GF-3.81_1x12_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_13-G-3.5_1x13_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_13-G-3.81_1x13_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_13-GF-3.5_1x13_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_13-GF-3.5_1x13_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_13-GF-3.81_1x13_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_13-GF-3.81_1x13_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_14-G-3.5_1x14_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_14-G-3.81_1x14_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_14-GF-3.5_1x14_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_14-GF-3.5_1x14_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_14-GF-3.81_1x14_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_14-GF-3.81_1x14_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_15-G-3.5_1x15_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_15-G-3.81_1x15_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_15-GF-3.5_1x15_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_15-GF-3.5_1x15_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_15-GF-3.81_1x15_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_15-GF-3.81_1x15_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_16-G-3.5_1x16_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_16-G-3.81_1x16_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_16-GF-3.5_1x16_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_16-GF-3.5_1x16_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_16-GF-3.81_1x16_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_16-GF-3.81_1x16_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_2-G-3.5_1x02_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_2-G-3.81_1x02_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_2-GF-3.5_1x02_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_2-GF-3.5_1x02_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_2-GF-3.81_1x02_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_2-GF-3.81_1x02_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_3-G-3.5_1x03_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_3-G-3.81_1x03_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_3-GF-3.5_1x03_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_3-GF-3.5_1x03_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_3-GF-3.81_1x03_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_3-GF-3.81_1x03_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_4-G-3.5_1x04_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_4-G-3.81_1x04_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_4-GF-3.5_1x04_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_4-GF-3.5_1x04_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_4-GF-3.81_1x04_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_4-GF-3.81_1x04_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_5-G-3.5_1x05_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_5-G-3.81_1x05_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_5-GF-3.5_1x05_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_5-GF-3.5_1x05_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_5-GF-3.81_1x05_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_5-GF-3.81_1x05_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_6-G-3.5_1x06_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_6-G-3.81_1x06_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_6-GF-3.5_1x06_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_6-GF-3.5_1x06_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_6-GF-3.81_1x06_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_6-GF-3.81_1x06_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_7-G-3.5_1x07_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_7-G-3.81_1x07_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_7-GF-3.5_1x07_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_7-GF-3.5_1x07_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_7-GF-3.81_1x07_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_7-GF-3.81_1x07_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_8-G-3.5_1x08_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_8-G-3.81_1x08_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_8-GF-3.5_1x08_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_8-GF-3.5_1x08_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_8-GF-3.81_1x08_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_8-GF-3.81_1x08_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_9-G-3.5_1x09_P3.50mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_9-G-3.81_1x09_P3.81mm_Horizontal +Connector_Phoenix_MC:PhoenixContact_MC_1,5_9-GF-3.5_1x09_P3.50mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_9-GF-3.5_1x09_P3.50mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC:PhoenixContact_MC_1,5_9-GF-3.81_1x09_P3.81mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC:PhoenixContact_MC_1,5_9-GF-3.81_1x09_P3.81mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_10-G-5.08_1x10_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_10-GF-5.08_1x10_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_10-GF-5.08_1x10_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_11-G-5.08_1x11_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_11-GF-5.08_1x11_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_11-GF-5.08_1x11_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_12-G-5.08_1x12_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_12-GF-5.08_1x12_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_12-GF-5.08_1x12_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_2-G-5.08_1x02_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_2-GF-5.08_1x02_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_2-GF-5.08_1x02_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_3-G-5.08_1x03_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_3-GF-5.08_1x03_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_3-GF-5.08_1x03_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_4-G-5.08_1x04_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_4-GF-5.08_1x04_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_4-GF-5.08_1x04_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_5-G-5.08_1x05_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_5-GF-5.08_1x05_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_5-GF-5.08_1x05_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_6-G-5.08_1x06_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_6-GF-5.08_1x06_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_6-GF-5.08_1x06_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_7-G-5.08_1x07_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_7-GF-5.08_1x07_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_7-GF-5.08_1x07_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_8-G-5.08_1x08_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_8-GF-5.08_1x08_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_8-GF-5.08_1x08_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_9-G-5.08_1x09_P5.08mm_Vertical +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_9-GF-5.08_1x09_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MCV_1,5_9-GF-5.08_1x09_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_10-G-5.08_1x10_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_10-GF-5.08_1x10_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_10-GF-5.08_1x10_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_11-G-5.08_1x11_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_11-GF-5.08_1x11_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_11-GF-5.08_1x11_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_12-G-5.08_1x12_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_12-GF-5.08_1x12_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_12-GF-5.08_1x12_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_2-G-5.08_1x02_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_2-GF-5.08_1x02_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_2-GF-5.08_1x02_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_3-G-5.08_1x03_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_3-GF-5.08_1x03_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_3-GF-5.08_1x03_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_4-G-5.08_1x04_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_4-GF-5.08_1x04_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_4-GF-5.08_1x04_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_5-G-5.08_1x05_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_5-GF-5.08_1x05_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_5-GF-5.08_1x05_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_6-G-5.08_1x06_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_6-GF-5.08_1x06_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_6-GF-5.08_1x06_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_7-G-5.08_1x07_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_7-GF-5.08_1x07_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_7-GF-5.08_1x07_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_8-G-5.08_1x08_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_8-GF-5.08_1x08_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_8-GF-5.08_1x08_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_9-G-5.08_1x09_P5.08mm_Horizontal +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_9-GF-5.08_1x09_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MC_HighVoltage:PhoenixContact_MC_1,5_9-GF-5.08_1x09_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_10-G-5,08_1x10_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_10-G_1x10_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_11-G-5,08_1x11_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_11-G_1x11_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_12-G-5,08_1x12_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_12-G_1x12_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_13-G-5,08_1x13_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_13-G_1x13_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_14-G-5,08_1x14_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_14-G_1x14_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_15-G-5,08_1x15_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_15-G_1x15_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_16-G-5,08_1x16_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_16-G_1x16_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_2-G-5,08_1x02_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_2-G_1x02_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_3-G-5,08_1x03_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_3-G_1x03_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_4-G-5,08_1x04_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_4-G_1x04_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_5-G-5,08_1x05_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_5-G_1x05_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_6-G-5,08_1x06_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_6-G_1x06_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_7-G-5,08_1x07_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_7-G_1x07_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_8-G-5,08_1x08_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_8-G_1x08_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_9-G-5,08_1x09_P5.08mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_9-G_1x09_P5.00mm_Horizontal +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_10-G-5,08_1x10_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_10-G_1x10_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_11-G-5,08_1x11_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_11-G_1x11_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_12-G-5,08_1x12_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_12-G_1x12_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_13-G-5,08_1x13_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_13-G_1x13_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_14-G-5,08_1x14_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_14-G_1x14_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_15-G-5,08_1x15_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_15-G_1x15_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_16-G-5,08_1x16_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_16-G_1x16_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_2-G-5,08_1x02_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_2-G_1x02_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_3-G-5,08_1x03_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_3-G_1x03_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_4-G-5,08_1x04_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_4-G_1x04_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_5-G-5,08_1x05_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_5-G_1x05_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_6-G-5,08_1x06_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_6-G_1x06_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_7-G-5,08_1x07_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_7-G_1x07_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_8-G-5,08_1x08_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_8-G_1x08_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_9-G-5,08_1x09_P5.08mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBVA_2,5_9-G_1x09_P5.00mm_Vertical +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_10-GF-5,08_1x10_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_10-GF-5,08_1x10_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_10-GF_1x10_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_10-GF_1x10_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_11-GF-5,08_1x11_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_11-GF-5,08_1x11_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_11-GF_1x11_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_11-GF_1x11_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_12-GF-5,08_1x12_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_12-GF-5,08_1x12_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_12-GF_1x12_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_12-GF_1x12_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_13-GF-5,08_1x13_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_13-GF-5,08_1x13_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_13-GF_1x13_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_13-GF_1x13_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_14-GF-5,08_1x14_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_14-GF-5,08_1x14_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_14-GF_1x14_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_14-GF_1x14_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_15-GF-5,08_1x15_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_15-GF-5,08_1x15_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_15-GF_1x15_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_15-GF_1x15_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_16-GF-5,08_1x16_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_16-GF-5,08_1x16_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_16-GF_1x16_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_16-GF_1x16_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_2-GF-5,08_1x02_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_2-GF-5,08_1x02_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_2-GF_1x02_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_2-GF_1x02_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_3-GF-5,08_1x03_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_3-GF-5,08_1x03_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_3-GF_1x03_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_3-GF_1x03_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_4-GF-5,08_1x04_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_4-GF-5,08_1x04_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_4-GF_1x04_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_4-GF_1x04_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_5-GF-5,08_1x05_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_5-GF-5,08_1x05_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_5-GF_1x05_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_5-GF_1x05_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_6-GF-5,08_1x06_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_6-GF-5,08_1x06_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_6-GF_1x06_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_6-GF_1x06_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_7-GF-5,08_1x07_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_7-GF-5,08_1x07_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_7-GF_1x07_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_7-GF_1x07_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_8-GF-5,08_1x08_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_8-GF-5,08_1x08_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_8-GF_1x08_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_8-GF_1x08_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_9-GF-5,08_1x09_P5.08mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_9-GF-5,08_1x09_P5.08mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_9-GF_1x09_P5.00mm_Vertical_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTBV_2,5_9-GF_1x09_P5.00mm_Vertical_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_10-GF-5,08_1x10_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_10-GF-5,08_1x10_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_10-GF_1x10_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_10-GF_1x10_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_11-GF-5,08_1x11_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_11-GF-5,08_1x11_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_11-GF_1x11_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_11-GF_1x11_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_12-GF-5,08_1x12_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_12-GF-5,08_1x12_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_12-GF_1x12_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_12-GF_1x12_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_13-GF-5,08_1x13_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_13-GF-5,08_1x13_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_13-GF_1x13_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_13-GF_1x13_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_14-GF-5,08_1x14_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_14-GF-5,08_1x14_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_14-GF_1x14_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_14-GF_1x14_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_15-GF-5,08_1x15_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_15-GF-5,08_1x15_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_15-GF_1x15_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_15-GF_1x15_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_16-GF-5,08_1x16_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_16-GF-5,08_1x16_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_16-GF_1x16_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_16-GF_1x16_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_2-GF-5,08_1x02_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_2-GF-5,08_1x02_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_2-GF_1x02_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_2-GF_1x02_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_3-GF-5,08_1x03_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_3-GF-5,08_1x03_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_3-GF_1x03_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_3-GF_1x03_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_4-GF-5,08_1x04_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_4-GF-5,08_1x04_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_4-GF_1x04_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_4-GF_1x04_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_5-GF-5,08_1x05_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_5-GF-5,08_1x05_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_5-GF_1x05_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_5-GF_1x05_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_6-GF-5,08_1x06_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_6-GF-5,08_1x06_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_6-GF_1x06_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_6-GF_1x06_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_7-GF-5,08_1x07_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_7-GF-5,08_1x07_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_7-GF_1x07_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_7-GF_1x07_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_8-GF-5,08_1x08_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_8-GF-5,08_1x08_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_8-GF_1x08_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_8-GF_1x08_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_9-GF-5,08_1x09_P5.08mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_9-GF-5,08_1x09_P5.08mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_9-GF_1x09_P5.00mm_Horizontal_ThreadedFlange +Connector_Phoenix_MSTB:PhoenixContact_MSTB_2,5_9-GF_1x09_P5.00mm_Horizontal_ThreadedFlange_MountHole +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_10-H-3.5_1x10_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_11-H-3.5_1x11_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_12-H-3.5_1x12_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_2-H-3.5_1x02_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_3-H-3.5_1x03_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_4-H-3.5_1x04_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_5-H-3.5_1x05_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_6-H-3.5_1x06_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_7-H-3.5_1x07_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_8-H-3.5_1x08_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_1.5_9-H-3.5_1x09_P3.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_1-H-5.0_1x01_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_10-H-5.0-EX_1x10_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_10-H-5.0_1x10_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_10-V-5.0-EX_1x10_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_11-H-5.0-EX_1x11_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_11-H-5.0_1x11_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_11-V-5.0-EX_1x11_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_12-H-5.0-EX_1x12_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_12-H-5.0_1x12_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_12-V-5.0-EX_1x12_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_2-H-5.0-EX_1x02_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_2-H-5.0_1x02_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_2-V-5.0-EX_1x02_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_3-H-5.0-EX_1x03_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_3-H-5.0_1x03_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_3-V-5.0-EX_1x03_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_4-H-5.0-EX_1x04_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_4-H-5.0_1x04_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_4-V-5.0-EX_1x04_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_5-H-5.0-EX_1x05_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_5-H-5.0_1x05_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_5-V-5.0-EX_1x05_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_6-H-5.0-EX_1x06_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_6-H-5.0_1x06_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_6-V-5.0-EX_1x06_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_7-H-5.0-EX_1x07_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_7-H-5.0_1x07_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_7-V-5.0-EX_1x07_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_8-H-5.0-EX_1x08_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_8-H-5.0_1x08_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_8-V-5.0-EX_1x08_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_9-H-5.0-EX_1x09_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_9-H-5.0_1x09_P5.0mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_2.5_9-V-5.0-EX_1x09_P5.0mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_1-H-7.5_1x01_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_1-V-7.5_1x01_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_10-H-7.5-ZB_1x10_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_10-V-7.5-ZB_1x10_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_11-H-7.5-ZB_1x11_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_11-V-7.5-ZB_1x11_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_12-H-7.5-ZB_1x12_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_12-V-7.5-ZB_1x12_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_2-H-7.5-ZB_1x02_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_2-V-7.5_1x02_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_3-H-7.5-ZB_1x03_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_3-H-7.5_1x03_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_3-V-7.5-ZB_1x03_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_4-H-7.5-ZB_1x04_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_5-H-7.5-ZB_1x05_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_5-V-7.5-ZB_1x05_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_6-H-7.5-ZB_1x06_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_6-V-7.5-ZB_1x06_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_7-H-7.5-ZB_1x07_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_7-V-7.5-ZB_1x07_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_8-H-7.5-ZB_1x08_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_8-V-7.5-ZB_1x08_P7.5mm_Vertical +Connector_Phoenix_SPT:PhoenixContact_SPT_5_9-H-7.5-ZB_1x09_P7.5mm_Horizontal +Connector_Phoenix_SPT:PhoenixContact_SPT_5_9-V-7.5-ZB_1x09_P7.5mm_Vertical +Connector_Pin:Pin_D0.7mm_L6.5mm_W1.8mm_FlatFork +Connector_Pin:Pin_D0.9mm_L10.0mm_W2.4mm_FlatFork +Connector_Pin:Pin_D1.0mm_L10.0mm +Connector_Pin:Pin_D1.0mm_L10.0mm_LooseFit +Connector_Pin:Pin_D1.1mm_L10.2mm_W3.5mm_Flat +Connector_Pin:Pin_D1.1mm_L8.5mm_W2.5mm_FlatFork +Connector_Pin:Pin_D1.2mm_L10.2mm_W2.9mm_FlatFork +Connector_Pin:Pin_D1.2mm_L11.3mm_W3.0mm_Flat +Connector_Pin:Pin_D1.3mm_L10.0mm_W3.5mm_Flat +Connector_Pin:Pin_D1.3mm_L11.0mm +Connector_Pin:Pin_D1.3mm_L11.0mm_LooseFit +Connector_Pin:Pin_D1.3mm_L11.3mm_W2.8mm_Flat +Connector_Pin:Pin_D1.4mm_L8.5mm_W2.8mm_FlatFork +Connector_PinHeader_1.00mm:PinHeader_1x01_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x01_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x02_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x02_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x02_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x02_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x03_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x03_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x03_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x03_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x04_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x04_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x04_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x04_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x05_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x05_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x05_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x05_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x06_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x06_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x06_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x06_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x07_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x07_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x07_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x07_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x08_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x08_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x08_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x08_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x09_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x09_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x09_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x09_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x10_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x10_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x10_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x10_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x11_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x11_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x11_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x11_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x12_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x12_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x12_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x12_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x13_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x13_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x13_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x13_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x14_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x14_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x14_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x14_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x15_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x15_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x15_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x15_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x16_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x16_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x16_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x16_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x17_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x17_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x17_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x17_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x18_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x18_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x18_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x18_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x19_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x19_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x19_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x19_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x20_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x20_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x20_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x20_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x21_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x21_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x21_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x21_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x22_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x22_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x22_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x22_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x23_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x23_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x23_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x23_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x24_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x24_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x24_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x24_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x25_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x25_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x25_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x25_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x26_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x26_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x26_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x26_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x27_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x27_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x27_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x27_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x28_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x28_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x28_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x28_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x29_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x29_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x29_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x29_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x30_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x30_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x30_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x30_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x31_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x31_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x31_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x31_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x32_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x32_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x32_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x32_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x33_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x33_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x33_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x33_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x34_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x34_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x34_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x34_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x35_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x35_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x35_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x35_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x36_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x36_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x36_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x36_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x37_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x37_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x37_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x37_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x38_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x38_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x38_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x38_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x39_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x39_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x39_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x39_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_1x40_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_1x40_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_1x40_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.00mm:PinHeader_1x40_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.00mm:PinHeader_2x01_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x01_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x01_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x02_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x02_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x02_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x03_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x03_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x03_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x04_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x04_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x04_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x05_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x05_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x05_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x06_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x06_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x06_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x07_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x07_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x07_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x08_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x08_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x08_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x09_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x09_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x09_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x10_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x10_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x10_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x11_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x11_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x11_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x12_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x12_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x12_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x13_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x13_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x13_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x14_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x14_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x14_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x15_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x15_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x15_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x16_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x16_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x16_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x17_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x17_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x17_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x18_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x18_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x18_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x19_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x19_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x19_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x20_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x20_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x20_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x21_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x21_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x21_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x22_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x22_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x22_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x23_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x23_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x23_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x24_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x24_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x24_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x25_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x25_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x25_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x26_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x26_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x26_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x27_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x27_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x27_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x28_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x28_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x28_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x29_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x29_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x29_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x30_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x30_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x30_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x31_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x31_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x31_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x32_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x32_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x32_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x33_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x33_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x33_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x34_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x34_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x34_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x35_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x35_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x35_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x36_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x36_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x36_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x37_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x37_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x37_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x38_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x38_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x38_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x39_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x39_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x39_P1.00mm_Vertical_SMD +Connector_PinHeader_1.00mm:PinHeader_2x40_P1.00mm_Horizontal +Connector_PinHeader_1.00mm:PinHeader_2x40_P1.00mm_Vertical +Connector_PinHeader_1.00mm:PinHeader_2x40_P1.00mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_1x01_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x01_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x02_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x02_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x02_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x02_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x03_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x03_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x03_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x03_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x04_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x04_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x04_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x04_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x05_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x05_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x05_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x05_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x06_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x06_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x06_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x06_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x07_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x07_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x07_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x07_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x08_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x08_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x08_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x08_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x09_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x09_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x09_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x09_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x10_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x10_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x10_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x10_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x11_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x11_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x11_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x11_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x12_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x12_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x12_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x12_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x13_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x13_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x13_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x13_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x14_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x14_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x14_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x14_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x15_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x15_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x15_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x15_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x16_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x16_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x16_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x16_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x17_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x17_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x17_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x17_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x18_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x18_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x18_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x18_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x19_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x19_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x19_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x19_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x20_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x20_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x20_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x20_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x21_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x21_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x21_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x21_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x22_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x22_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x22_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x22_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x23_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x23_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x23_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x23_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x24_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x24_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x24_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x24_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x25_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x25_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x25_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x25_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x26_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x26_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x26_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x26_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x27_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x27_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x27_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x27_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x28_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x28_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x28_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x28_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x29_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x29_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x29_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x29_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x30_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x30_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x30_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x30_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x31_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x31_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x31_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x31_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x32_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x32_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x32_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x32_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x33_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x33_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x33_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x33_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x34_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x34_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x34_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x34_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x35_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x35_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x35_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x35_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x36_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x36_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x36_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x36_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x37_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x37_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x37_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x37_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x38_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x38_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x38_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x38_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x39_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x39_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x39_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x39_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_1x40_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_1x40_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_1x40_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinHeader_1.27mm:PinHeader_1x40_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinHeader_1.27mm:PinHeader_2x01_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x01_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x01_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x02_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x02_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x02_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x03_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x03_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x03_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x04_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x04_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x04_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x05_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x05_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x05_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x06_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x06_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x06_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x07_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x07_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x07_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x08_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x08_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x08_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x09_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x09_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x09_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x10_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x10_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x10_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x11_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x11_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x11_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x12_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x12_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x12_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x13_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x13_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x13_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x14_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x14_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x14_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x15_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x15_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x15_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x16_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x16_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x16_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x17_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x17_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x17_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x18_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x18_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x18_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x19_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x19_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x19_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x20_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x20_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x20_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x21_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x21_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x21_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x22_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x22_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x22_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x23_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x23_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x23_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x24_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x24_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x24_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x25_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x25_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x25_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x26_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x26_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x26_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x27_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x27_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x27_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x28_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x28_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x28_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x29_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x29_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x29_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x30_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x30_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x30_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x31_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x31_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x31_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x32_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x32_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x32_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x33_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x33_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x33_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x34_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x34_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x34_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x35_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x35_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x35_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x36_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x36_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x36_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x37_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x37_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x37_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x38_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x38_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x38_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x39_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x39_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x39_P1.27mm_Vertical_SMD +Connector_PinHeader_1.27mm:PinHeader_2x40_P1.27mm_Horizontal +Connector_PinHeader_1.27mm:PinHeader_2x40_P1.27mm_Vertical +Connector_PinHeader_1.27mm:PinHeader_2x40_P1.27mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_1x01_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x01_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x02_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x02_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x02_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x02_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x03_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x03_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x03_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x03_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x04_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x04_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x04_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x04_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x05_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x05_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x05_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x05_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x06_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x06_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x06_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x06_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x07_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x07_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x07_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x07_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x08_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x08_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x08_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x08_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x09_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x09_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x09_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x09_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x10_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x10_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x10_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x10_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x11_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x11_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x11_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x11_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x12_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x12_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x12_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x12_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x13_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x13_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x13_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x13_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x14_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x14_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x14_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x14_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x15_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x15_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x15_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x15_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x16_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x16_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x16_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x16_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x17_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x17_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x17_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x17_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x18_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x18_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x18_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x18_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x19_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x19_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x19_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x19_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x20_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x20_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x20_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x20_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x21_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x21_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x21_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x21_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x22_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x22_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x22_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x22_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x23_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x23_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x23_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x23_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x24_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x24_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x24_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x24_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x25_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x25_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x25_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x25_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x26_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x26_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x26_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x26_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x27_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x27_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x27_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x27_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x28_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x28_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x28_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x28_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x29_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x29_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x29_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x29_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x30_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x30_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x30_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x30_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x31_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x31_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x31_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x31_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x32_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x32_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x32_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x32_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x33_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x33_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x33_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x33_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x34_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x34_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x34_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x34_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x35_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x35_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x35_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x35_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x36_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x36_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x36_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x36_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x37_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x37_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x37_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x37_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x38_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x38_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x38_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x38_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x39_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x39_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x39_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x39_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_1x40_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_1x40_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_1x40_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.00mm:PinHeader_1x40_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.00mm:PinHeader_2x01_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x01_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x01_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x02_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x02_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x02_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x03_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x03_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x03_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x04_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x04_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x04_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x05_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x05_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x05_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x06_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x06_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x06_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x07_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x07_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x07_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x08_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x08_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x08_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x09_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x09_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x09_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x10_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x10_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x10_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x11_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x11_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x11_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x12_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x12_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x12_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x13_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x13_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x13_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x14_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x14_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x14_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x15_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x15_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x15_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x16_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x16_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x16_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x17_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x17_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x17_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x18_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x18_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x18_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x19_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x19_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x19_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x20_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x20_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x20_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x21_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x21_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x21_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x22_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x22_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x22_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x23_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x23_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x23_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x24_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x24_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x24_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x25_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x25_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x25_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x26_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x26_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x26_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x27_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x27_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x27_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x28_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x28_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x28_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x29_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x29_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x29_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x30_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x30_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x30_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x31_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x31_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x31_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x32_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x32_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x32_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x33_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x33_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x33_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x34_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x34_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x34_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x35_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x35_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x35_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x36_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x36_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x36_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x37_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x37_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x37_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x38_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x38_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x38_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x39_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x39_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x39_P2.00mm_Vertical_SMD +Connector_PinHeader_2.00mm:PinHeader_2x40_P2.00mm_Horizontal +Connector_PinHeader_2.00mm:PinHeader_2x40_P2.00mm_Vertical +Connector_PinHeader_2.00mm:PinHeader_2x40_P2.00mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x06_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x06_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x06_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x06_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x07_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x07_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x07_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x07_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x08_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x08_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x08_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x08_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x09_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x09_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x09_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x09_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x10_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x10_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x10_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x10_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x11_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x11_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x11_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x11_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x12_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x12_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x12_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x12_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x13_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x13_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x13_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x13_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x14_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x14_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x14_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x14_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x15_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x15_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x15_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x15_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x16_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x16_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x16_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x16_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x17_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x17_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x17_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x17_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x18_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x18_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x18_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x18_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x19_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x19_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x19_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x19_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x20_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x20_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x20_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x20_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x21_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x21_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x21_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x21_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x22_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x22_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x22_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x22_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x23_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x23_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x23_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x23_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x24_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x24_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x24_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x24_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x25_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x25_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x25_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x25_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x26_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x26_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x26_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x26_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x27_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x27_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x27_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x27_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x28_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x28_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x28_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x28_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x29_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x29_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x29_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x29_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x30_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x30_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x30_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x30_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x31_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x31_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x31_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x31_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x32_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x32_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x32_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x32_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x33_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x33_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x33_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x33_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x34_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x34_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x34_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x34_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x35_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x35_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x35_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x35_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x36_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x36_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x36_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x36_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x37_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x37_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x37_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x37_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x38_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x38_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x38_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x38_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x39_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x39_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x39_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x39_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_1x40_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_1x40_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_1x40_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinHeader_2.54mm:PinHeader_1x40_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinHeader_2.54mm:PinHeader_2x01_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x01_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x01_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x02_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x02_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x02_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x04_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x04_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x04_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x05_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x05_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x05_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x06_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x06_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x06_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x07_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x07_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x07_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x08_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x08_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x08_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x09_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x09_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x09_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x11_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x11_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x11_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x12_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x12_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x12_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x13_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x13_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x13_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x14_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x14_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x14_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x15_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x15_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x15_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x16_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x16_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x16_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x17_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x17_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x17_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x18_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x18_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x18_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x19_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x19_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x19_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x20_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x20_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x20_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x21_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x21_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x21_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x22_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x22_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x22_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x23_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x23_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x23_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x24_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x24_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x24_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x25_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x25_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x25_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x26_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x26_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x26_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x27_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x27_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x27_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x28_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x28_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x28_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x29_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x29_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x29_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x30_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x30_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x30_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x31_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x31_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x31_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x32_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x32_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x32_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x33_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x33_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x33_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x34_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x34_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x34_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x35_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x35_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x35_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x36_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x36_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x36_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x37_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x37_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x37_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x38_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x38_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x38_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x39_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x39_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x39_P2.54mm_Vertical_SMD +Connector_PinHeader_2.54mm:PinHeader_2x40_P2.54mm_Horizontal +Connector_PinHeader_2.54mm:PinHeader_2x40_P2.54mm_Vertical +Connector_PinHeader_2.54mm:PinHeader_2x40_P2.54mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_1x02_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x02_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x02_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x03_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x03_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x03_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x04_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x04_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x04_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x05_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x05_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x05_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x06_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x06_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x06_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x07_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x07_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x07_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x08_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x08_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x08_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x09_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x09_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x09_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x10_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x10_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x10_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x11_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x11_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x11_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x12_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x12_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x12_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x13_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x13_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x13_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x14_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x14_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x14_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x15_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x15_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x15_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x16_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x16_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x16_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x17_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x17_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x17_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x18_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x18_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x18_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x19_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x19_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x19_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x20_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x20_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x20_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x21_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x21_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x21_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x22_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x22_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x22_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x23_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x23_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x23_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x24_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x24_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x24_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x25_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x25_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x25_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x26_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x26_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x26_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x27_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x27_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x27_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x28_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x28_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x28_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x29_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x29_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x29_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x30_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x30_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x30_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x31_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x31_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x31_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x32_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x32_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x32_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x33_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x33_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x33_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x34_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x34_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x34_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x35_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x35_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x35_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x36_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x36_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x36_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x37_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x37_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x37_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x38_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x38_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x38_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x39_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x39_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x39_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_1x40_P1.00mm_Vertical +Connector_PinSocket_1.00mm:PinSocket_1x40_P1.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.00mm:PinSocket_1x40_P1.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.00mm:PinSocket_2x02_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x03_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x04_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x05_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x06_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x07_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x08_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x09_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x10_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x11_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x12_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x13_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x14_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x15_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x16_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x17_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x18_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x19_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x20_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x21_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x22_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x23_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x24_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x25_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x26_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x27_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x28_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x29_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x30_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x31_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x32_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x33_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x34_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x35_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x36_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x37_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x38_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x39_P1.00mm_Vertical_SMD +Connector_PinSocket_1.00mm:PinSocket_2x40_P1.00mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_1x01_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x02_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x02_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x02_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x03_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x03_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x03_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x04_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x04_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x04_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x05_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x05_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x05_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x06_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x06_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x06_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x07_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x07_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x07_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x08_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x08_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x08_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x09_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x09_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x09_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x10_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x10_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x10_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x11_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x11_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x11_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x12_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x12_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x12_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x13_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x13_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x13_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x14_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x14_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x14_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x15_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x15_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x15_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x16_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x16_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x16_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x17_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x17_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x17_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x18_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x18_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x18_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x19_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x19_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x19_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x20_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x20_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x20_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x21_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x21_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x21_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x22_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x22_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x22_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x23_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x23_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x23_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x24_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x24_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x24_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x25_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x25_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x25_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x26_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x26_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x26_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x27_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x27_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x27_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x28_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x28_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x28_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x29_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x29_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x29_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x30_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x30_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x30_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x31_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x31_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x31_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x32_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x32_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x32_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x33_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x33_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x33_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x34_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x34_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x34_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x35_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x35_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x35_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x36_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x36_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x36_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x37_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x37_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x37_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x38_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x38_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x38_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x39_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x39_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x39_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_1x40_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_1x40_P1.27mm_Vertical_SMD_Pin1Left +Connector_PinSocket_1.27mm:PinSocket_1x40_P1.27mm_Vertical_SMD_Pin1Right +Connector_PinSocket_1.27mm:PinSocket_2x01_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x01_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x02_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x02_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x03_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x03_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x03_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x04_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x04_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x04_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x05_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x05_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x05_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x06_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x06_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x06_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x07_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x07_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x07_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x08_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x08_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x08_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x09_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x09_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x09_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x10_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x10_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x10_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x11_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x11_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x11_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x12_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x12_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x12_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x13_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x13_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x13_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x14_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x14_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x14_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x15_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x15_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x15_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x16_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x16_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x16_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x17_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x17_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x17_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x18_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x18_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x18_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x19_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x19_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x19_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x20_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x20_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x20_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x21_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x21_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x21_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x22_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x22_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x22_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x23_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x23_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x23_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x24_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x24_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x24_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x25_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x25_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x25_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x26_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x26_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x26_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x27_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x27_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x27_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x28_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x28_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x28_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x29_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x29_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x29_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x30_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x30_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x30_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x31_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x31_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x31_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x32_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x32_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x32_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x33_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x33_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x33_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x34_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x34_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x34_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x35_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x35_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x35_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x36_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x36_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x36_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x37_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x37_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x37_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x38_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x38_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x38_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x39_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x39_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x39_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x40_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x40_P1.27mm_Vertical +Connector_PinSocket_1.27mm:PinSocket_2x40_P1.27mm_Vertical_SMD +Connector_PinSocket_1.27mm:PinSocket_2x41_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x42_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x43_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x44_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x45_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x46_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x47_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x48_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x49_P1.27mm_Horizontal +Connector_PinSocket_1.27mm:PinSocket_2x50_P1.27mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x01_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x01_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x02_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x02_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x02_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x02_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x03_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x03_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x03_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x03_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x04_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x04_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x04_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x04_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x05_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x05_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x05_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x05_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x06_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x06_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x06_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x06_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x07_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x07_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x07_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x07_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x08_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x08_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x08_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x08_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x09_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x09_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x09_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x09_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x10_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x10_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x10_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x10_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x11_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x11_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x11_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x11_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x12_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x12_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x12_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x12_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x13_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x13_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x13_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x13_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x14_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x14_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x14_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x14_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x15_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x15_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x15_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x15_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x16_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x16_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x16_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x16_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x17_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x17_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x17_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x17_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x18_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x18_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x18_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x18_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x19_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x19_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x19_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x19_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x20_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x20_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x20_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x20_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x21_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x21_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x21_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x21_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x22_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x22_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x22_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x22_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x23_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x23_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x23_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x23_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x24_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x24_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x24_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x24_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x25_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x25_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x25_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x25_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x26_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x26_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x26_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x26_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x27_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x27_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x27_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x27_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x28_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x28_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x28_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x28_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x29_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x29_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x29_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x29_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x30_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x30_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x30_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x30_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x31_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x31_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x31_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x31_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x32_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x32_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x32_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x32_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x33_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x33_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x33_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x33_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x34_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x34_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x34_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x34_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x35_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x35_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x35_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x35_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x36_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x36_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x36_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x36_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x37_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x37_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x37_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x37_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x38_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x38_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x38_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x38_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x39_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x39_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x39_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x39_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_1x40_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_1x40_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_1x40_P2.00mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.00mm:PinSocket_1x40_P2.00mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.00mm:PinSocket_2x01_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x01_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x01_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x02_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x02_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x02_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x03_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x03_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x03_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x04_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x04_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x04_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x05_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x05_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x05_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x06_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x06_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x06_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x07_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x07_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x07_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x08_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x08_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x08_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x09_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x09_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x09_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x10_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x10_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x10_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x11_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x11_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x11_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x12_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x12_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x12_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x13_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x13_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x13_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x14_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x14_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x14_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x15_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x15_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x15_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x16_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x16_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x16_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x17_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x17_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x17_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x18_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x18_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x18_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x19_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x19_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x19_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x20_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x20_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x20_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x21_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x21_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x21_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x22_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x22_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x22_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x23_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x23_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x23_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x24_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x24_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x24_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x25_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x25_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x25_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x26_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x26_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x26_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x27_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x27_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x27_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x28_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x28_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x28_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x29_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x29_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x29_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x30_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x30_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x30_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x31_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x31_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x31_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x32_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x32_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x32_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x33_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x33_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x33_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x34_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x34_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x34_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x35_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x35_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x35_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x36_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x36_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x36_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x37_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x37_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x37_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x38_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x38_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x38_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x39_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x39_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x39_P2.00mm_Vertical_SMD +Connector_PinSocket_2.00mm:PinSocket_2x40_P2.00mm_Horizontal +Connector_PinSocket_2.00mm:PinSocket_2x40_P2.00mm_Vertical +Connector_PinSocket_2.00mm:PinSocket_2x40_P2.00mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_1x01_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x01_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x02_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x02_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x02_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x02_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x03_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x03_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x03_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x03_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x04_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x04_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x04_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x04_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x05_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x05_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x05_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x05_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x06_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x06_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x06_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x06_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x07_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x07_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x07_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x07_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x08_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x08_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x08_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x08_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x09_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x09_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x09_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x09_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x10_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x10_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x10_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x10_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x11_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x11_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x11_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x11_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x12_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x12_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x12_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x12_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x13_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x13_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x13_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x13_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x14_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x14_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x14_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x14_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x15_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x15_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x15_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x15_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x16_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x16_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x16_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x16_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x17_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x17_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x17_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x17_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x18_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x18_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x18_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x18_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x19_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x19_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x19_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x19_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x20_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x20_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x20_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x20_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x21_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x21_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x21_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x21_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x22_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x22_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x22_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x22_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x23_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x23_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x23_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x23_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x24_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x24_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x24_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x24_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x25_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x25_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x25_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x25_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x26_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x26_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x26_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x26_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x27_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x27_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x27_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x27_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x28_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x28_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x28_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x28_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x29_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x29_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x29_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x29_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x30_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x30_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x30_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x30_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x31_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x31_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x31_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x31_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x32_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x32_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x32_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x32_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x33_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x33_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x33_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x33_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x34_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x34_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x34_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x34_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x35_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x35_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x35_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x35_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x36_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x36_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x36_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x36_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x37_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x37_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x37_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x37_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x38_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x38_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x38_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x38_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x39_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x39_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x39_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x39_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_1x40_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_1x40_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_1x40_P2.54mm_Vertical_SMD_Pin1Left +Connector_PinSocket_2.54mm:PinSocket_1x40_P2.54mm_Vertical_SMD_Pin1Right +Connector_PinSocket_2.54mm:PinSocket_2x01_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x01_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x01_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x02_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x02_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x02_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x03_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x03_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x03_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x04_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x04_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x04_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x05_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x05_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x05_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x06_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x06_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x06_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x07_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x07_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x07_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x08_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x08_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x08_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x09_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x09_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x09_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x10_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x10_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x10_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x11_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x11_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x11_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x12_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x12_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x12_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x13_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x13_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x13_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x14_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x14_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x14_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x15_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x15_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x15_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x16_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x16_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x16_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x17_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x17_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x17_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x18_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x18_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x18_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x19_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x19_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x19_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x20_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x20_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x20_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x21_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x21_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x21_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x22_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x22_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x22_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x23_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x23_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x23_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x24_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x24_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x24_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x25_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x25_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x25_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x26_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x26_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x26_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x27_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x27_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x27_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x28_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x28_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x28_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x29_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x29_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x29_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x30_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x30_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x30_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x31_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x31_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x31_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x32_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x32_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x32_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x33_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x33_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x33_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x34_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x34_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x34_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x35_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x35_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x35_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x36_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x36_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x36_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x37_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x37_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x37_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x38_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x38_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x38_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x39_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x39_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x39_P2.54mm_Vertical_SMD +Connector_PinSocket_2.54mm:PinSocket_2x40_P2.54mm_Horizontal +Connector_PinSocket_2.54mm:PinSocket_2x40_P2.54mm_Vertical +Connector_PinSocket_2.54mm:PinSocket_2x40_P2.54mm_Vertical_SMD +Connector_RJ:RJ12_Amphenol_54601-x06_Horizontal +Connector_RJ:RJ14_Connfly_DS1133-S4_Horizontal +Connector_RJ:RJ25_Wayconn_MJEA-660X1_Horizontal +Connector_RJ:RJ45_Abracon_ARJP11A-MA_Horizontal +Connector_RJ:RJ45_Amphenol_54602-x08_Horizontal +Connector_RJ:RJ45_Amphenol_RJHSE5380-08 +Connector_RJ:RJ45_Amphenol_RJHSE5380 +Connector_RJ:RJ45_Amphenol_RJHSE538X-02 +Connector_RJ:RJ45_Amphenol_RJHSE538X-04 +Connector_RJ:RJ45_Amphenol_RJHSE538X +Connector_RJ:RJ45_Amphenol_RJMG1BD3B8K1ANR +Connector_RJ:RJ45_Bel_SI-60062-F +Connector_RJ:RJ45_BEL_SS74301-00x_Vertical +Connector_RJ:RJ45_Bel_V895-1001-AW_Vertical +Connector_RJ:RJ45_Cetus_J1B1211CCD_Horizontal +Connector_RJ:RJ45_Connfly_DS1128-09-S8xx-S_Horizontal +Connector_RJ:RJ45_HALO_HFJ11-x2450E-LxxRL_Horizontal +Connector_RJ:RJ45_HALO_HFJ11-x2450ERL_Horizontal +Connector_RJ:RJ45_HALO_HFJ11-x2450HRL_Horizontal +Connector_RJ:RJ45_Hanrun_HR911105A_Horizontal +Connector_RJ:RJ45_Kycon_G7LX-A88S7-BP-xx_Horizontal +Connector_RJ:RJ45_Molex_0855135013_Vertical +Connector_RJ:RJ45_Molex_9346520x_Horizontal +Connector_RJ:RJ45_Ninigi_GE +Connector_RJ:RJ45_OST_PJ012-8P8CX_Vertical +Connector_RJ:RJ45_Plug_Metz_AJP92A8813 +Connector_RJ:RJ45_Pulse_JK00177NL_Horizontal +Connector_RJ:RJ45_Pulse_JK0654219NL_Horizontal +Connector_RJ:RJ45_Pulse_JXD6-0001NL_Horizontal +Connector_RJ:RJ45_RCH_RC01937 +Connector_RJ:RJ45_UDE_RB1-125B8G1A +Connector_RJ:RJ45_Wuerth_74980111211_Horizontal +Connector_RJ:RJ45_Wuerth_7499010001A_Horizontal +Connector_RJ:RJ45_Wuerth_7499010121A_Horizontal +Connector_RJ:RJ45_Wuerth_7499010211A_Horizontal +Connector_RJ:RJ45_Wuerth_7499111446_Horizontal +Connector_RJ:RJ45_Wuerth_7499151120_Horizontal +Connector_RJ:RJ9_Evercom_5301-440xxx_Horizontal +Connector_Samtec:Samtec_FMC_ASP-134486-01_10x40_P1.27mm_Vertical +Connector_Samtec:Samtec_FMC_ASP-134602-01_10x40_P1.27mm_Vertical +Connector_Samtec:Samtec_FMC_ASP-134604-01_4x40_Vertical +Connector_Samtec:Samtec_LSHM-105-xx.x-x-DV-N_2x05_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-105-xx.x-x-DV-S_2x05-1SH_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-110-xx.x-x-DV-N_2x10_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-110-xx.x-x-DV-S_2x10-1SH_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-120-xx.x-x-DV-N_2x20_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-120-xx.x-x-DV-S_2x20-1SH_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-130-xx.x-x-DV-N_2x30_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-130-xx.x-x-DV-S_2x30-1SH_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-140-xx.x-x-DV-N_2x40_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-140-xx.x-x-DV-S_2x40-1SH_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-150-xx.x-x-DV-N_2x50_P0.50mm_Vertical +Connector_Samtec:Samtec_LSHM-150-xx.x-x-DV-S_2x50-1SH_P0.50mm_Vertical +Connector_Samtec_HLE_SMD:Samtec_HLE-102-02-xxx-DV-BE-LC_2x02_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-102-02-xxx-DV-BE_2x02_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-102-02-xxx-DV-LC_2x02_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-102-02-xxx-DV_2x02_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-103-02-xxx-DV-BE-LC_2x03_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-103-02-xxx-DV-BE_2x03_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-103-02-xxx-DV-LC_2x03_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-103-02-xxx-DV_2x03_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-104-02-xxx-DV-A_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-104-02-xxx-DV-BE-A_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-104-02-xxx-DV-BE-LC_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-104-02-xxx-DV-BE_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-104-02-xxx-DV-LC_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-104-02-xxx-DV_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-105-02-xxx-DV-A_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-105-02-xxx-DV-BE-A_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-105-02-xxx-DV-BE-LC_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-105-02-xxx-DV-BE_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-105-02-xxx-DV-LC_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-105-02-xxx-DV_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-106-02-xxx-DV-A_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-106-02-xxx-DV-BE-A_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-106-02-xxx-DV-BE-LC_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-106-02-xxx-DV-BE_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-106-02-xxx-DV-LC_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-106-02-xxx-DV_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-107-02-xxx-DV-A_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-107-02-xxx-DV-BE-A_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-107-02-xxx-DV-BE-LC_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-107-02-xxx-DV-BE_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-107-02-xxx-DV-LC_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-107-02-xxx-DV_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-108-02-xxx-DV-A_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-108-02-xxx-DV-BE-A_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-108-02-xxx-DV-BE-LC_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-108-02-xxx-DV-BE_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-108-02-xxx-DV-LC_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-108-02-xxx-DV_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-109-02-xxx-DV-A_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-109-02-xxx-DV-BE-A_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-109-02-xxx-DV-BE-LC_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-109-02-xxx-DV-BE_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-109-02-xxx-DV-LC_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-109-02-xxx-DV_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-110-02-xxx-DV-A_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-110-02-xxx-DV-BE-A_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-110-02-xxx-DV-BE-LC_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-110-02-xxx-DV-BE_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-110-02-xxx-DV-LC_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-110-02-xxx-DV_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-111-02-xxx-DV-A_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-111-02-xxx-DV-BE-A_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-111-02-xxx-DV-BE-LC_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-111-02-xxx-DV-BE_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-111-02-xxx-DV-LC_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-111-02-xxx-DV_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-112-02-xxx-DV-A_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-112-02-xxx-DV-BE-A_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-112-02-xxx-DV-BE-LC_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-112-02-xxx-DV-BE_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-112-02-xxx-DV-LC_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-112-02-xxx-DV_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-113-02-xxx-DV-A_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-113-02-xxx-DV-BE-A_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-113-02-xxx-DV-BE-LC_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-113-02-xxx-DV-BE_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-113-02-xxx-DV-LC_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-113-02-xxx-DV_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-114-02-xxx-DV-A_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-114-02-xxx-DV-BE-A_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-114-02-xxx-DV-BE-LC_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-114-02-xxx-DV-BE_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-114-02-xxx-DV-LC_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-114-02-xxx-DV_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-115-02-xxx-DV-A_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-115-02-xxx-DV-BE-A_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-115-02-xxx-DV-BE-LC_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-115-02-xxx-DV-BE_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-115-02-xxx-DV-LC_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-115-02-xxx-DV_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-116-02-xxx-DV-A_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-116-02-xxx-DV-BE-A_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-116-02-xxx-DV-BE-LC_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-116-02-xxx-DV-BE_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-116-02-xxx-DV-LC_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-116-02-xxx-DV_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-117-02-xxx-DV-A_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-117-02-xxx-DV-BE-A_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-117-02-xxx-DV-BE-LC_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-117-02-xxx-DV-BE_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-117-02-xxx-DV-LC_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-117-02-xxx-DV_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-118-02-xxx-DV-A_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-118-02-xxx-DV-BE-A_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-118-02-xxx-DV-BE-LC_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-118-02-xxx-DV-BE_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-118-02-xxx-DV-LC_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-118-02-xxx-DV_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-119-02-xxx-DV-A_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-119-02-xxx-DV-BE-A_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-119-02-xxx-DV-BE-LC_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-119-02-xxx-DV-BE_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-119-02-xxx-DV-LC_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-119-02-xxx-DV_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-120-02-xxx-DV-A_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-120-02-xxx-DV-BE-A_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-120-02-xxx-DV-BE-LC_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-120-02-xxx-DV-BE_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-120-02-xxx-DV-LC_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-120-02-xxx-DV_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-121-02-xxx-DV-A_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-121-02-xxx-DV-BE-A_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-121-02-xxx-DV-BE-LC_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-121-02-xxx-DV-BE_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-121-02-xxx-DV-LC_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-121-02-xxx-DV_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-122-02-xxx-DV-A_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-122-02-xxx-DV-BE-A_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-122-02-xxx-DV-BE-LC_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-122-02-xxx-DV-BE_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-122-02-xxx-DV-LC_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-122-02-xxx-DV_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-123-02-xxx-DV-A_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-123-02-xxx-DV-BE-A_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-123-02-xxx-DV-BE-LC_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-123-02-xxx-DV-BE_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-123-02-xxx-DV-LC_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-123-02-xxx-DV_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-124-02-xxx-DV-A_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-124-02-xxx-DV-BE-A_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-124-02-xxx-DV-BE-LC_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-124-02-xxx-DV-BE_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-124-02-xxx-DV-LC_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-124-02-xxx-DV_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-125-02-xxx-DV-A_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-125-02-xxx-DV-BE-A_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-125-02-xxx-DV-BE-LC_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-125-02-xxx-DV-BE_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-125-02-xxx-DV-LC_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-125-02-xxx-DV_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-126-02-xxx-DV-A_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-126-02-xxx-DV-BE-A_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-126-02-xxx-DV-BE-LC_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-126-02-xxx-DV-BE_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-126-02-xxx-DV-LC_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-126-02-xxx-DV_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-127-02-xxx-DV-A_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-127-02-xxx-DV-BE-A_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-127-02-xxx-DV-BE-LC_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-127-02-xxx-DV-BE_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-127-02-xxx-DV-LC_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-127-02-xxx-DV_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-128-02-xxx-DV-A_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-128-02-xxx-DV-BE-A_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-128-02-xxx-DV-BE-LC_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-128-02-xxx-DV-BE_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-128-02-xxx-DV-LC_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-128-02-xxx-DV_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-129-02-xxx-DV-A_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-129-02-xxx-DV-BE-A_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-129-02-xxx-DV-BE-LC_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-129-02-xxx-DV-BE_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-129-02-xxx-DV-LC_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-129-02-xxx-DV_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-130-02-xxx-DV-A_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-130-02-xxx-DV-BE-A_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-130-02-xxx-DV-BE-LC_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-130-02-xxx-DV-BE_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-130-02-xxx-DV-LC_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-130-02-xxx-DV_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-131-02-xxx-DV-A_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-131-02-xxx-DV-BE-A_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-131-02-xxx-DV-BE-LC_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-131-02-xxx-DV-BE_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-131-02-xxx-DV-LC_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-131-02-xxx-DV_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-132-02-xxx-DV-A_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-132-02-xxx-DV-BE-A_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-132-02-xxx-DV-BE-LC_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-132-02-xxx-DV-BE_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-132-02-xxx-DV-LC_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-132-02-xxx-DV_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-133-02-xxx-DV-A_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-133-02-xxx-DV-BE-A_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-133-02-xxx-DV-BE-LC_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-133-02-xxx-DV-BE_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-133-02-xxx-DV-LC_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-133-02-xxx-DV_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-134-02-xxx-DV-A_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-134-02-xxx-DV-BE-A_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-134-02-xxx-DV-BE-LC_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-134-02-xxx-DV-BE_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-134-02-xxx-DV-LC_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-134-02-xxx-DV_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-135-02-xxx-DV-A_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-135-02-xxx-DV-BE-A_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-135-02-xxx-DV-BE-LC_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-135-02-xxx-DV-BE_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-135-02-xxx-DV-LC_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-135-02-xxx-DV_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-136-02-xxx-DV-A_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-136-02-xxx-DV-BE-A_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-136-02-xxx-DV-BE-LC_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-136-02-xxx-DV-BE_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-136-02-xxx-DV-LC_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-136-02-xxx-DV_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-137-02-xxx-DV-A_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-137-02-xxx-DV-BE-A_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-137-02-xxx-DV-BE-LC_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-137-02-xxx-DV-BE_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-137-02-xxx-DV-LC_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-137-02-xxx-DV_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-138-02-xxx-DV-A_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-138-02-xxx-DV-BE-A_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-138-02-xxx-DV-BE-LC_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-138-02-xxx-DV-BE_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-138-02-xxx-DV-LC_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-138-02-xxx-DV_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-139-02-xxx-DV-A_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-139-02-xxx-DV-BE-A_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-139-02-xxx-DV-BE-LC_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-139-02-xxx-DV-BE_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-139-02-xxx-DV-LC_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-139-02-xxx-DV_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-140-02-xxx-DV-A_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-140-02-xxx-DV-BE-A_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-140-02-xxx-DV-BE-LC_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-140-02-xxx-DV-BE_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-140-02-xxx-DV-LC_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-140-02-xxx-DV_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-141-02-xxx-DV-A_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-141-02-xxx-DV-BE-A_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-141-02-xxx-DV-BE-LC_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-141-02-xxx-DV-BE_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-141-02-xxx-DV-LC_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-141-02-xxx-DV_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-142-02-xxx-DV-A_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-142-02-xxx-DV-BE-A_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-142-02-xxx-DV-BE-LC_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-142-02-xxx-DV-BE_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-142-02-xxx-DV-LC_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-142-02-xxx-DV_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-143-02-xxx-DV-A_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-143-02-xxx-DV-BE-A_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-143-02-xxx-DV-BE-LC_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-143-02-xxx-DV-BE_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-143-02-xxx-DV-LC_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-143-02-xxx-DV_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-144-02-xxx-DV-A_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-144-02-xxx-DV-BE-A_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-144-02-xxx-DV-BE-LC_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-144-02-xxx-DV-BE_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-144-02-xxx-DV-LC_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-144-02-xxx-DV_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-145-02-xxx-DV-A_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-145-02-xxx-DV-BE-A_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-145-02-xxx-DV-BE-LC_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-145-02-xxx-DV-BE_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-145-02-xxx-DV-LC_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-145-02-xxx-DV_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-146-02-xxx-DV-A_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-146-02-xxx-DV-BE-A_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-146-02-xxx-DV-BE-LC_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-146-02-xxx-DV-BE_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-146-02-xxx-DV-LC_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-146-02-xxx-DV_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-147-02-xxx-DV-A_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-147-02-xxx-DV-BE-A_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-147-02-xxx-DV-BE-LC_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-147-02-xxx-DV-BE_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-147-02-xxx-DV-LC_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-147-02-xxx-DV_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-148-02-xxx-DV-A_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-148-02-xxx-DV-BE-A_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-148-02-xxx-DV-BE-LC_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-148-02-xxx-DV-BE_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-148-02-xxx-DV-LC_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-148-02-xxx-DV_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-149-02-xxx-DV-A_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-149-02-xxx-DV-BE-A_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-149-02-xxx-DV-BE-LC_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-149-02-xxx-DV-BE_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-149-02-xxx-DV-LC_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-149-02-xxx-DV_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-150-02-xxx-DV-A_2x50_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-150-02-xxx-DV-BE-A_2x50_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-150-02-xxx-DV-BE-LC_2x50_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-150-02-xxx-DV-BE_2x50_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-150-02-xxx-DV-LC_2x50_P2.54mm_Horizontal +Connector_Samtec_HLE_SMD:Samtec_HLE-150-02-xxx-DV_2x50_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-104-02-xx-DV-PE-LC_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-104-02-xx-DV-PE_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-104-02-xx-DV-TE_2x04_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-105-02-xx-DV-PE-LC_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-105-02-xx-DV-PE_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-105-02-xx-DV-TE_2x05_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-106-02-xx-DV-PE-LC_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-106-02-xx-DV-PE_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-106-02-xx-DV-TE_2x06_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-107-02-xx-DV-PE-LC_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-107-02-xx-DV-PE_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-107-02-xx-DV-TE_2x07_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-108-02-xx-DV-PE-LC_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-108-02-xx-DV-PE_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-108-02-xx-DV-TE_2x08_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-109-02-xx-DV-PE-LC_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-109-02-xx-DV-PE_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-109-02-xx-DV-TE_2x09_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-110-02-xx-DV-PE-LC_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-110-02-xx-DV-PE_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-110-02-xx-DV-TE_2x10_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-111-02-xx-DV-PE-LC_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-111-02-xx-DV-PE_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-111-02-xx-DV-TE_2x11_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-112-02-xx-DV-PE-LC_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-112-02-xx-DV-PE_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-112-02-xx-DV-TE_2x12_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-113-02-xx-DV-PE-LC_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-113-02-xx-DV-PE_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-113-02-xx-DV-TE_2x13_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-114-02-xx-DV-PE-LC_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-114-02-xx-DV-PE_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-114-02-xx-DV-TE_2x14_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-115-02-xx-DV-PE-LC_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-115-02-xx-DV-PE_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-115-02-xx-DV-TE_2x15_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-116-02-xx-DV-PE-LC_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-116-02-xx-DV-PE_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-116-02-xx-DV-TE_2x16_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-117-02-xx-DV-PE-LC_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-117-02-xx-DV-PE_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-117-02-xx-DV-TE_2x17_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-118-02-xx-DV-PE-LC_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-118-02-xx-DV-PE_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-118-02-xx-DV-TE_2x18_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-119-02-xx-DV-PE-LC_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-119-02-xx-DV-PE_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-119-02-xx-DV-TE_2x19_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-120-02-xx-DV-PE-LC_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-120-02-xx-DV-PE_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-120-02-xx-DV-TE_2x20_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-121-02-xx-DV-PE-LC_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-121-02-xx-DV-PE_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-121-02-xx-DV-TE_2x21_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-122-02-xx-DV-PE-LC_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-122-02-xx-DV-PE_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-122-02-xx-DV-TE_2x22_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-123-02-xx-DV-PE-LC_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-123-02-xx-DV-PE_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-123-02-xx-DV-TE_2x23_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-124-02-xx-DV-PE-LC_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-124-02-xx-DV-PE_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-124-02-xx-DV-TE_2x24_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-125-02-xx-DV-PE-LC_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-125-02-xx-DV-PE_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-125-02-xx-DV-TE_2x25_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-126-02-xx-DV-PE-LC_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-126-02-xx-DV-PE_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-126-02-xx-DV-TE_2x26_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-127-02-xx-DV-PE-LC_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-127-02-xx-DV-PE_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-127-02-xx-DV-TE_2x27_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-128-02-xx-DV-PE-LC_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-128-02-xx-DV-PE_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-128-02-xx-DV-TE_2x28_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-129-02-xx-DV-PE-LC_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-129-02-xx-DV-PE_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-129-02-xx-DV-TE_2x29_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-130-02-xx-DV-PE-LC_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-130-02-xx-DV-PE_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-130-02-xx-DV-TE_2x30_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-131-02-xx-DV-PE-LC_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-131-02-xx-DV-PE_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-131-02-xx-DV-TE_2x31_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-132-02-xx-DV-PE-LC_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-132-02-xx-DV-PE_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-132-02-xx-DV-TE_2x32_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-133-02-xx-DV-PE-LC_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-133-02-xx-DV-PE_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-133-02-xx-DV-TE_2x33_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-134-02-xx-DV-PE-LC_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-134-02-xx-DV-PE_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-134-02-xx-DV-TE_2x34_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-135-02-xx-DV-PE-LC_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-135-02-xx-DV-PE_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-135-02-xx-DV-TE_2x35_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-136-02-xx-DV-PE-LC_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-136-02-xx-DV-PE_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-136-02-xx-DV-TE_2x36_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-137-02-xx-DV-PE-LC_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-137-02-xx-DV-PE_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-137-02-xx-DV-TE_2x37_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-138-02-xx-DV-PE-LC_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-138-02-xx-DV-PE_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-138-02-xx-DV-TE_2x38_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-139-02-xx-DV-PE-LC_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-139-02-xx-DV-PE_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-139-02-xx-DV-TE_2x39_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-140-02-xx-DV-PE-LC_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-140-02-xx-DV-PE_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-140-02-xx-DV-TE_2x40_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-141-02-xx-DV-PE-LC_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-141-02-xx-DV-PE_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-141-02-xx-DV-TE_2x41_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-142-02-xx-DV-PE-LC_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-142-02-xx-DV-PE_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-142-02-xx-DV-TE_2x42_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-143-02-xx-DV-PE-LC_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-143-02-xx-DV-PE_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-143-02-xx-DV-TE_2x43_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-144-02-xx-DV-PE-LC_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-144-02-xx-DV-PE_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-144-02-xx-DV-TE_2x44_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-145-02-xx-DV-PE-LC_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-145-02-xx-DV-PE_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-145-02-xx-DV-TE_2x45_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-146-02-xx-DV-PE-LC_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-146-02-xx-DV-PE_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-146-02-xx-DV-TE_2x46_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-147-02-xx-DV-PE-LC_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-147-02-xx-DV-PE_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-147-02-xx-DV-TE_2x47_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-148-02-xx-DV-PE-LC_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-148-02-xx-DV-PE_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-148-02-xx-DV-TE_2x48_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-149-02-xx-DV-PE-LC_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-149-02-xx-DV-PE_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-149-02-xx-DV-TE_2x49_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-150-02-xx-DV-PE-LC_2x50_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-150-02-xx-DV-PE_2x50_P2.54mm_Horizontal +Connector_Samtec_HLE_THT:Samtec_HLE-150-02-xx-DV-TE_2x50_P2.54mm_Horizontal +Connector_Samtec_HPM_THT:Samtec_HPM-01-01-x-S_Straight_1x01_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-01-05-x-S_Straight_1x01_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-02-01-x-S_Straight_1x02_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-02-05-x-S_Straight_1x02_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-03-01-x-S_Straight_1x03_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-03-05-x-S_Straight_1x03_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-04-01-x-S_Straight_1x04_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-04-05-x-S_Straight_1x04_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-05-01-x-S_Straight_1x05_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-05-05-x-S_Straight_1x05_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-06-01-x-S_Straight_1x06_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-06-05-x-S_Straight_1x06_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-07-01-x-S_Straight_1x07_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-07-05-x-S_Straight_1x07_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-08-01-x-S_Straight_1x08_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-08-05-x-S_Straight_1x08_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-09-01-x-S_Straight_1x09_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-09-05-x-S_Straight_1x09_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-10-01-x-S_Straight_1x10_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-10-05-x-S_Straight_1x10_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-11-01-x-S_Straight_1x11_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-11-05-x-S_Straight_1x11_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-12-01-x-S_Straight_1x12_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-12-05-x-S_Straight_1x12_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-13-01-x-S_Straight_1x13_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-13-05-x-S_Straight_1x13_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-14-01-x-S_Straight_1x14_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-14-05-x-S_Straight_1x14_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-15-01-x-S_Straight_1x15_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-15-05-x-S_Straight_1x15_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-16-01-x-S_Straight_1x16_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-16-05-x-S_Straight_1x16_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-17-01-x-S_Straight_1x17_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-17-05-x-S_Straight_1x17_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-18-01-x-S_Straight_1x18_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-18-05-x-S_Straight_1x18_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-19-01-x-S_Straight_1x19_Pitch5.08mm +Connector_Samtec_HPM_THT:Samtec_HPM-19-05-x-S_Straight_1x19_Pitch5.08mm +Connector_Samtec_HSEC8:Samtec_HSEC8-109-01-X-DV-A-BL_2x09_P0.8mm_Pol04_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-109-01-X-DV-A-WT_2x09_P0.8mm_Pol04_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-109-X-X-DV-BL_2x09_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-109-X-X-DV_2x09_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-109-X-X-DV_2x09_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-110-01-X-DV-A-BL_2x10_P0.8mm_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-110-01-X-DV-A-WT_2x10_P0.8mm_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-110-01-X-DV-A_2x10_P0.8mm_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-110-01-X-DV_2x10_P0.8mm_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-110-03-X-DV-A-WT_2x10_P0.8mm_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-110-03-X-DV-A_2x10_P0.8mm_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-110-03-X-DV_2x10_P0.8mm_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-110-X-X-DV-BL_2x10_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-110-X-X-DV_2x10_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-110-X-X-DV_2x10_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-01-X-DV-A-BL_2x100_P0.8mm_Pol32_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-01-X-DV-A-WT_2x100_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-01-X-DV-A_2x100_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-01-X-DV_2x100_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-03-X-DV-A-WT_2x100_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-03-X-DV-A_2x100_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-03-X-DV_2x100_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-X-X-DV-BL_2x100_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-X-X-DV_2x100_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-1100-X-X-DV_2x100_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-113-01-X-DV-A-BL_2x13_P0.8mm_Pol06_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-113-01-X-DV-A-WT_2x13_P0.8mm_Pol06_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-113-01-X-DV-A_2x13_P0.8mm_Pol06_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-113-01-X-DV_2x13_P0.8mm_Pol06_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-113-X-X-DV-BL_2x13_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-113-X-X-DV_2x13_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-113-X-X-DV_2x13_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-120-01-X-DV-A-BL_2x20_P0.8mm_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-120-01-X-DV-A-WT_2x20_P0.8mm_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-120-01-X-DV-A_2x20_P0.8mm_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-120-01-X-DV_2x20_P0.8mm_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-120-03-X-DV-A-WT_2x20_P0.8mm_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-120-03-X-DV-A_2x20_P0.8mm_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-120-03-X-DV_2x20_P0.8mm_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-120-X-X-DV-BL_2x20_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-120-X-X-DV_2x20_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-120-X-X-DV_2x20_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-125-01-X-DV-A-BL_2x25_P0.8mm_Pol06_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-125-01-X-DV-A-WT_2x25_P0.8mm_Pol06_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-125-01-X-DV-A_2x25_P0.8mm_Pol06_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-125-01-X-DV_2x25_P0.8mm_Pol06_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-125-X-X-DV-BL_2x25_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-125-X-X-DV_2x25_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-125-X-X-DV_2x25_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-130-01-X-DV-A-BL_2x30_P0.8mm_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-130-01-X-DV-A-WT_2x30_P0.8mm_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-130-01-X-DV-A_2x30_P0.8mm_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-130-01-X-DV_2x30_P0.8mm_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-130-03-X-DV-A-WT_2x30_P0.8mm_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-130-03-X-DV-A_2x30_P0.8mm_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-130-03-X-DV_2x30_P0.8mm_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-130-X-X-DV-BL_2x30_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-130-X-X-DV_2x30_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-130-X-X-DV_2x30_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-137-01-X-DV-A-BL_2x37_P0.8mm_Pol21_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-137-01-X-DV-A-WT_2x37_P0.8mm_Pol21_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-137-01-X-DV-A_2x37_P0.8mm_Pol21_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-137-01-X-DV_2x37_P0.8mm_Pol21_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-137-X-X-DV-BL_2x37_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-137-X-X-DV_2x37_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-137-X-X-DV_2x37_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-140-01-X-DV-A-BL_2x40_P0.8mm_Pol22_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-140-01-X-DV-A-WT_2x40_P0.8mm_Pol22_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-140-01-X-DV-A_2x40_P0.8mm_Pol22_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-140-01-X-DV_2x40_P0.8mm_Pol22_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-140-03-X-DV-A-WT_2x40_P0.8mm_Pol22_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-140-03-X-DV-A_2x40_P0.8mm_Pol22_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-140-03-X-DV_2x40_P0.8mm_Pol22_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-140-X-X-DV-BL_2x40_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-140-X-X-DV_2x40_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-140-X-X-DV_2x40_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-149-01-X-DV-A-BL_2x49_P0.8mm_Pol27_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-149-01-X-DV-A-WT_2x49_P0.8mm_Pol27_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-149-01-X-DV-A_2x49_P0.8mm_Pol27_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-149-01-X-DV_2x49_P0.8mm_Pol27_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-149-X-X-DV-BL_2x49_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-149-X-X-DV_2x49_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-149-X-X-DV_2x49_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-150-01-X-DV-A-BL_2x50_P0.8mm_Pol27_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-150-01-X-DV-A-WT_2x50_P0.8mm_Pol27_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-150-01-X-DV-A_2x50_P0.8mm_Pol27_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-150-01-X-DV_2x50_P0.8mm_Pol27_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-150-03-X-DV-A-WT_2x50_P0.8mm_Pol27_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-150-03-X-DV-A_2x50_P0.8mm_Pol27_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-150-03-X-DV_2x50_P0.8mm_Pol27_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-150-X-X-DV-BL_2x50_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-150-X-X-DV_2x50_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-150-X-X-DV_2x50_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-160-01-X-DV-A-BL_2x60_P0.8mm_Pol32_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-160-01-X-DV-A-WT_2x60_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-160-01-X-DV-A_2x60_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-160-01-X-DV_2x60_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-160-03-X-DV-A-WT_2x60_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-160-03-X-DV-A_2x60_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-160-03-X-DV_2x60_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-160-X-X-DV-BL_2x60_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-160-X-X-DV_2x60_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-160-X-X-DV_2x60_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-170-01-X-DV-A-BL_2x70_P0.8mm_Pol32_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-170-01-X-DV-A-WT_2x70_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-170-01-X-DV-A_2x70_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-170-01-X-DV_2x70_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-170-03-X-DV-A-WT_2x70_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-170-03-X-DV-A_2x70_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-170-03-X-DV_2x70_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-170-X-X-DV-BL_2x70_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-170-X-X-DV_2x70_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-170-X-X-DV_2x70_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-180-01-X-DV-A-BL_2x80_P0.8mm_Pol32_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-180-01-X-DV-A-WT_2x80_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-180-01-X-DV-A_2x80_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-180-01-X-DV_2x80_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-180-03-X-DV-A-WT_2x80_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-180-03-X-DV-A_2x80_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-180-03-X-DV_2x80_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-180-X-X-DV-BL_2x80_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-180-X-X-DV_2x80_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-180-X-X-DV_2x80_P0.8mm_Wing_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-190-01-X-DV-A-BL_2x90_P0.8mm_Pol32_Socket_WeldTabs_BoardLocks +Connector_Samtec_HSEC8:Samtec_HSEC8-190-01-X-DV-A-WT_2x90_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-190-01-X-DV-A_2x90_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-190-01-X-DV_2x90_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-190-03-X-DV-A-WT_2x90_P0.8mm_Pol32_Socket_WeldTabs +Connector_Samtec_HSEC8:Samtec_HSEC8-190-03-X-DV-A_2x90_P0.8mm_Pol32_Socket_AlignmentPins +Connector_Samtec_HSEC8:Samtec_HSEC8-190-03-X-DV_2x90_P0.8mm_Pol32_Socket +Connector_Samtec_HSEC8:Samtec_HSEC8-190-X-X-DV-BL_2x90_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-190-X-X-DV_2x90_P0.8mm_Edge +Connector_Samtec_HSEC8:Samtec_HSEC8-190-X-X-DV_2x90_P0.8mm_Wing_Edge +Connector_Samtec_MicroMate:Samtec_T1M-02-X-S-RA_1x02-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-02-X-S-V_1x02-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-02-X-SH-L_1x02-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-02-X-SV-L_1x02-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-03-X-S-RA_1x03-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-03-X-S-V_1x03-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-03-X-SH-L_1x03-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-03-X-SV-L_1x03-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-04-X-S-RA_1x04-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-04-X-S-V_1x04-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-04-X-SH-L_1x04-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-04-X-SV-L_1x04-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-05-X-S-RA_1x05-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-05-X-S-V_1x05-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-05-X-SH-L_1x05-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-05-X-SV-L_1x05-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-06-X-S-RA_1x06-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-06-X-S-V_1x06-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-06-X-SH-L_1x06-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-06-X-SV-L_1x06-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-07-X-S-RA_1x07-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-07-X-S-V_1x07-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-07-X-SH-L_1x07-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-07-X-SV-L_1x07-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-08-X-S-RA_1x08-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-08-X-S-V_1x08-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-08-X-SH-L_1x08-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-08-X-SV-L_1x08-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-09-X-S-RA_1x09-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-09-X-S-V_1x09-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-09-X-SH-L_1x09-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-09-X-SV-L_1x09-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-10-X-S-RA_1x10-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-10-X-S-V_1x10-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-10-X-SH-L_1x10-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-10-X-SV-L_1x10-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-11-X-S-RA_1x11-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-11-X-S-V_1x11-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-11-X-SH-L_1x11-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-11-X-SV-L_1x11-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-12-X-S-RA_1x12-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-12-X-S-V_1x12-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-12-X-SH-L_1x12-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-12-X-SV-L_1x12-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-13-X-S-RA_1x13-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-13-X-S-V_1x13-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-13-X-SH-L_1x13-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-13-X-SV-L_1x13-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-14-X-S-RA_1x14-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-14-X-S-V_1x14-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-14-X-SH-L_1x14-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-14-X-SV-L_1x14-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-15-X-S-RA_1x15-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-15-X-S-V_1x15-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-15-X-SH-L_1x15-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-15-X-SV-L_1x15-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-16-X-S-RA_1x16-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-16-X-S-V_1x16-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-16-X-SH-L_1x16-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-16-X-SV-L_1x16-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-17-X-S-RA_1x17-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-17-X-S-V_1x17-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-17-X-SH-L_1x17-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-17-X-SV-L_1x17-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-18-X-S-RA_1x18-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-18-X-S-V_1x18-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-18-X-SH-L_1x18-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-18-X-SV-L_1x18-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-19-X-S-RA_1x19-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-19-X-S-V_1x19-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-19-X-SH-L_1x19-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-19-X-SV-L_1x19-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroMate:Samtec_T1M-20-X-S-RA_1x20-1MP_P1.0mm_Terminal_Horizontal +Connector_Samtec_MicroMate:Samtec_T1M-20-X-S-V_1x20-1MP_P1.0mm_Terminal_Vertical +Connector_Samtec_MicroMate:Samtec_T1M-20-X-SH-L_1x20-1MP_P1.0mm_Terminal_Horizontal_Latch +Connector_Samtec_MicroMate:Samtec_T1M-20-X-SV-L_1x20-1MP_P1.0mm_Terminal_Vertical_Latch +Connector_Samtec_MicroPower:Samtec_UMPS-02-XX.X-X-V-S-W_1x02-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-02-XX.X-X-V-S_1x02_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPS-03-XX.X-X-V-S-W_1x03-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-03-XX.X-X-V-S_1x03_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPS-04-XX.X-X-V-S-W_1x04-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-04-XX.X-X-V-S_1x04_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPS-05-XX.X-X-V-S-W_1x05-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-05-XX.X-X-V-S_1x05_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPS-06-XX.X-X-V-S-W_1x06-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-06-XX.X-X-V-S_1x06_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPS-07-XX.X-X-V-S-W_1x07-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-07-XX.X-X-V-S_1x07_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPS-08-XX.X-X-V-S-W_1x08-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-08-XX.X-X-V-S_1x08_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPS-09-XX.X-X-V-S-W_1x09-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-09-XX.X-X-V-S_1x09_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPS-10-XX.X-X-V-S-W_1x10-1MP_P2.0mm_Socket_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPS-10-XX.X-X-V-S_1x10_P2.0mm_Socket +Connector_Samtec_MicroPower:Samtec_UMPT-02-XX.X-X-V-S-W_1x02-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-02-XX.X-X-V-S_1x02_P2.0mm_Terminal +Connector_Samtec_MicroPower:Samtec_UMPT-03-XX.X-X-V-S-W_1x03-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-03-XX.X-X-V-S_1x03_P2.0mm_Terminal +Connector_Samtec_MicroPower:Samtec_UMPT-04-XX.X-X-V-S-W_1x04-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-04-XX.X-X-V-S_1x04_P2.0mm_Terminal +Connector_Samtec_MicroPower:Samtec_UMPT-05-XX.X-X-V-S-W_1x05-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-05-XX.X-X-V-S_1x05_P2.0mm_Terminal +Connector_Samtec_MicroPower:Samtec_UMPT-06-XX.X-X-V-S-W_1x06-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-06-XX.X-X-V-S_1x06_P2.0mm_Terminal +Connector_Samtec_MicroPower:Samtec_UMPT-07-XX.X-X-V-S-W_1x07-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-07-XX.X-X-V-S_1x07_P2.0mm_Terminal +Connector_Samtec_MicroPower:Samtec_UMPT-08-XX.X-X-V-S-W_1x08-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-08-XX.X-X-V-S_1x08_P2.0mm_Terminal +Connector_Samtec_MicroPower:Samtec_UMPT-09-XX.X-X-V-S-W_1x09-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-09-XX.X-X-V-S_1x09_P2.0mm_Terminal +Connector_Samtec_MicroPower:Samtec_UMPT-10-XX.X-X-V-S-W_1x10-1MP_P2.0mm_Terminal_WeldTab +Connector_Samtec_MicroPower:Samtec_UMPT-10-XX.X-X-V-S_1x10_P2.0mm_Terminal +Connector_SATA_SAS:SAS-mini_TEConnectivity_1888174_Vertical +Connector_SATA_SAS:SATA_Amphenol_10029364-001LF_Horizontal +Connector_Stocko:Stocko_MKS_1651-6-0-202_1x2_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1652-6-0-202_1x2_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1653-6-0-303_1x3_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1654-6-0-404_1x4_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1655-6-0-505_1x5_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1656-6-0-606_1x6_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1657-6-0-707_1x7_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1658-6-0-808_1x8_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1659-6-0-909_1x9_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1660-6-0-1010_1x10_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1661-6-0-1111_1x11_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1662-6-0-1212_1x12_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1663-6-0-1313_1x13_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1664-6-0-1414_1x14_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1665-6-0-1515_1x15_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1666-6-0-1616_1x16_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1667-6-0-1717_1x17_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1668-6-0-1818_1x18_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1669-6-0-1919_1x19_P2.50mm_Vertical +Connector_Stocko:Stocko_MKS_1670-6-0-2020_1x20_P2.50mm_Vertical +Connector_TE-Connectivity:TE_1-826576-3_1x13_P3.96mm_Vertical +Connector_TE-Connectivity:TE_1-826576-5_1x15_P3.96mm_Vertical +Connector_TE-Connectivity:TE_1-826576-6_1x16_P3.96mm_Vertical +Connector_TE-Connectivity:TE_1-826576-7_1x17_P3.96mm_Vertical +Connector_TE-Connectivity:TE_1-826576-8_1x18_P3.96mm_Vertical +Connector_TE-Connectivity:TE_2-826576-0_1x20_P3.96mm_Vertical +Connector_TE-Connectivity:TE_2834006-1_1x01_P4.0mm_Horizontal +Connector_TE-Connectivity:TE_2834006-2_1x02_P4.0mm_Horizontal +Connector_TE-Connectivity:TE_2834006-3_1x03_P4.0mm_Horizontal +Connector_TE-Connectivity:TE_2834006-4_1x04_P4.0mm_Horizontal +Connector_TE-Connectivity:TE_2834006-5_1x05_P4.0mm_Horizontal +Connector_TE-Connectivity:TE_3-826576-6_1x36_P3.96mm_Vertical +Connector_TE-Connectivity:TE_440054-2_1x02_P2.00mm_Vertical +Connector_TE-Connectivity:TE_440055-2_1x02_P2.00mm_Horizontal +Connector_TE-Connectivity:TE_5767171-1_2x19_P0.635mm_Vertical +Connector_TE-Connectivity:TE_826576-2_1x02_P3.96mm_Vertical +Connector_TE-Connectivity:TE_826576-3_1x03_P3.96mm_Vertical +Connector_TE-Connectivity:TE_826576-5_1x05_P3.96mm_Vertical +Connector_TE-Connectivity:TE_826576-6_1x06_P3.96mm_Vertical +Connector_TE-Connectivity:TE_826576-7_1x07_P3.96mm_Vertical +Connector_TE-Connectivity:TE_826576-8_1x08_P3.96mm_Vertical +Connector_TE-Connectivity:TE_826576-9_1x09_P3.96mm_Vertical +Connector_TE-Connectivity:TE_AMPSEAL_1-776087-x_3Rows_23_P0.4mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770182-x_3x03_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770186-x_3x04_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770190-x_3x05_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770621-x_2x06_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770858-x_2x05_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770866-x_1x02_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770870-x_1x03_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770874-x_2x02_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770875-x_2x03_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770966-x_1x02_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770967-x_1x03_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770968-x_2x02_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770969-x_2x03_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770970-x_2x04_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770971-x_2x05_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770972-x_2x06_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770973-x_2x07_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-770974-x_2x08_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794067-x_2x07_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794068-x_2x08_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794069-x_2x09_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794070-x_2x10_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794071-x_2x11_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794072-x_2x12_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794073-x_2x04_P4.14mm_Vertical +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794105-x_2x09_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794106-x_2x10_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794107-x_2x11_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794108-x_2x12_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_1-794374-x_1x01_P4.14mm_Horizontal +Connector_TE-Connectivity:TE_MATE-N-LOK_350211-1_1x04_P5.08mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_1-215079-0_2x05_P1.27mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_1-215079-2_2x06_P1.27mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_1-215079-4_2x07_P1.27mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_1-215079-6_2x08_P1.27mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_1-215079-8_2x09_P1.27mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_2-215079-0_2x10_P1.27mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_215079-4_2x02_P1.27mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_215079-6_2x03_P1.27mm_Vertical +Connector_TE-Connectivity:TE_Micro-MaTch_215079-8_2x04_P1.27mm_Vertical +Connector_TE-Connectivity:TE_T4041037031-000_M8_03_Socket_Straight +Connector_TE-Connectivity:TE_T4041037041-000_M8_04_Socket_Straight +Connector_USB:USB3_A_Molex_48393-001 +Connector_USB:USB3_A_Molex_48406-0001_Horizontal_Stacked +Connector_USB:USB3_A_Plug_Wuerth_692112030100_Horizontal +Connector_USB:USB3_A_Receptacle_Wuerth_692122030100 +Connector_USB:USB3_Micro-B_Connfly_DS1104-01 +Connector_USB:USB_A_CNCTech_1001-011-01101_Horizontal +Connector_USB:USB_A_Connfly_DS1095 +Connector_USB:USB_A_Connfly_DS1098_Horizontal +Connector_USB:USB_A_CUI_UJ2-ADH-TH_Horizontal_Stacked +Connector_USB:USB_A_Kycon_KUSBX-AS1N-B_Horizontal +Connector_USB:USB_A_Molex_105057_Vertical +Connector_USB:USB_A_Molex_48037-2200_Horizontal +Connector_USB:USB_A_Molex_67643_Horizontal +Connector_USB:USB_A_Receptacle_GCT_USB1046 +Connector_USB:USB_A_Receptacle_XKB_U231-091N-4BLRA00-S +Connector_USB:USB_A_Stewart_SS-52100-001_Horizontal +Connector_USB:USB_A_TE_292303-7_Horizontal +Connector_USB:USB_A_Wuerth_614004134726_Horizontal +Connector_USB:USB_A_Wuerth_61400826021_Horizontal_Stacked +Connector_USB:USB_B_Amphenol_MUSB-D511_Vertical_Rugged +Connector_USB:USB_B_Lumberg_2411_02_Horizontal +Connector_USB:USB_B_OST_USB-B1HSxx_Horizontal +Connector_USB:USB_B_TE_5787834_Vertical +Connector_USB:USB_C_Plug_JAE_DX07P024AJ1 +Connector_USB:USB_C_Plug_Molex_105444 +Connector_USB:USB_C_Plug_ShenzhenJingTuoJin_918-118A2021Y40002_Vertical +Connector_USB:USB_C_Receptacle_Amphenol_12401548E4-2A +Connector_USB:USB_C_Receptacle_Amphenol_12401548E4-2A_CircularHoles +Connector_USB:USB_C_Receptacle_Amphenol_12401610E4-2A +Connector_USB:USB_C_Receptacle_Amphenol_12401610E4-2A_CircularHoles +Connector_USB:USB_C_Receptacle_Amphenol_12401948E412A +Connector_USB:USB_C_Receptacle_Amphenol_124019772112A +Connector_USB:USB_C_Receptacle_CNCTech_C-ARA1-AK51X +Connector_USB:USB_C_Receptacle_G-Switch_GT-USB-7010ASV +Connector_USB:USB_C_Receptacle_G-Switch_GT-USB-7025 +Connector_USB:USB_C_Receptacle_G-Switch_GT-USB-7051x +Connector_USB:USB_C_Receptacle_GCT_USB4085 +Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal +Connector_USB:USB_C_Receptacle_GCT_USB4110 +Connector_USB:USB_C_Receptacle_GCT_USB4115-03-C +Connector_USB:USB_C_Receptacle_GCT_USB4125-xx-x-0190_6P_TopMnt_Horizontal +Connector_USB:USB_C_Receptacle_GCT_USB4125-xx-x_6P_TopMnt_Horizontal +Connector_USB:USB_C_Receptacle_GCT_USB4135-GF-A_6P_TopMnt_Horizontal +Connector_USB:USB_C_Receptacle_HCTL_HC-TYPE-C-16P-01A +Connector_USB:USB_C_Receptacle_HRO_TYPE-C-31-M-12 +Connector_USB:USB_C_Receptacle_HRO_TYPE-C-31-M-17 +Connector_USB:USB_C_Receptacle_JAE_DX07S016JA1R1500 +Connector_USB:USB_C_Receptacle_JAE_DX07S024WJ1R350 +Connector_USB:USB_C_Receptacle_JAE_DX07S024WJ3R400 +Connector_USB:USB_C_Receptacle_Molex_105450-0101 +Connector_USB:USB_C_Receptacle_Palconn_UTC16-G +Connector_USB:USB_C_Receptacle_XKB_U262-16XN-4BVC11 +Connector_USB:USB_Micro-AB_Molex_47590-0001 +Connector_USB:USB_Micro-B_Amphenol_10103594-0001LF_Horizontal +Connector_USB:USB_Micro-B_Amphenol_10104110_Horizontal +Connector_USB:USB_Micro-B_Amphenol_10118193-0001LF_Horizontal +Connector_USB:USB_Micro-B_Amphenol_10118193-0002LF_Horizontal +Connector_USB:USB_Micro-B_Amphenol_10118194-0001LF_Horizontal +Connector_USB:USB_Micro-B_Amphenol_10118194_Horizontal +Connector_USB:USB_Micro-B_GCT_USB3076-30-A +Connector_USB:USB_Micro-B_Molex-105017-0001 +Connector_USB:USB_Micro-B_Molex-105133-0001 +Connector_USB:USB_Micro-B_Molex-105133-0031 +Connector_USB:USB_Micro-B_Molex_47346-0001 +Connector_USB:USB_Micro-B_Technik_TWP-4002D-H3 +Connector_USB:USB_Micro-B_Wuerth_614105150721_Vertical +Connector_USB:USB_Micro-B_Wuerth_614105150721_Vertical_CircularHoles +Connector_USB:USB_Micro-B_Wuerth_629105150521 +Connector_USB:USB_Micro-B_Wuerth_629105150521_CircularHoles +Connector_USB:USB_Micro-B_XKB_U254-051T-4BH83-F1S +Connector_USB:USB_Mini-B_AdamTech_MUSB-B5-S-VT-TSMT-1_SMD_Vertical +Connector_USB:USB_Mini-B_Lumberg_2486_01_Horizontal +Connector_USB:USB_Mini-B_Tensility_54-00023_Vertical +Connector_USB:USB_Mini-B_Tensility_54-00023_Vertical_CircularHoles +Connector_USB:USB_Mini-B_Wuerth_65100516121_Horizontal +Connector_Video:DVI-D_Molex_74320-4004_Horizontal +Connector_Video:DVI-I_Molex_74320-1004_Horizontal +Connector_Video:HDMI_A_Amphenol_10029449-x01xLF_Horizontal +Connector_Video:HDMI_A_Contact_Technology_HDMI-19APL2_Horizontal +Connector_Video:HDMI_A_Kycon_KDMIX-SL1-NS-WS-B15_VerticalRightAngle +Connector_Video:HDMI_A_Molex_208658-1001_Horizontal +Connector_Video:HDMI_Micro-D_Molex_46765-0x01 +Connector_Video:HDMI_Micro-D_Molex_46765-1x01 +Connector_Video:HDMI_Micro-D_Molex_46765-2x0x +Connector_Wago:Wago_734-132_1x02_P3.50mm_Vertical +Connector_Wago:Wago_734-133_1x03_P3.50mm_Vertical +Connector_Wago:Wago_734-134_1x04_P3.50mm_Vertical +Connector_Wago:Wago_734-135_1x05_P3.50mm_Vertical +Connector_Wago:Wago_734-136_1x06_P3.50mm_Vertical +Connector_Wago:Wago_734-137_1x07_P3.50mm_Vertical +Connector_Wago:Wago_734-138_1x08_P3.50mm_Vertical +Connector_Wago:Wago_734-139_1x09_P3.50mm_Vertical +Connector_Wago:Wago_734-140_1x10_P3.50mm_Vertical +Connector_Wago:Wago_734-141_1x11_P3.50mm_Vertical +Connector_Wago:Wago_734-142_1x12_P3.50mm_Vertical +Connector_Wago:Wago_734-143_1x13_P3.50mm_Vertical +Connector_Wago:Wago_734-144_1x14_P3.50mm_Vertical +Connector_Wago:Wago_734-146_1x16_P3.50mm_Vertical +Connector_Wago:Wago_734-148_1x18_P3.50mm_Vertical +Connector_Wago:Wago_734-150_1x20_P3.50mm_Vertical +Connector_Wago:Wago_734-154_1x24_P3.50mm_Vertical +Connector_Wago:Wago_734-162_1x02_P3.50mm_Horizontal +Connector_Wago:Wago_734-163_1x03_P3.50mm_Horizontal +Connector_Wago:Wago_734-164_1x04_P3.50mm_Horizontal +Connector_Wago:Wago_734-165_1x05_P3.50mm_Horizontal +Connector_Wago:Wago_734-166_1x06_P3.50mm_Horizontal +Connector_Wago:Wago_734-167_1x07_P3.50mm_Horizontal +Connector_Wago:Wago_734-168_1x08_P3.50mm_Horizontal +Connector_Wago:Wago_734-169_1x09_P3.50mm_Horizontal +Connector_Wago:Wago_734-170_1x10_P3.50mm_Horizontal +Connector_Wago:Wago_734-171_1x11_P3.50mm_Horizontal +Connector_Wago:Wago_734-172_1x12_P3.50mm_Horizontal +Connector_Wago:Wago_734-173_1x13_P3.50mm_Horizontal +Connector_Wago:Wago_734-174_1x14_P3.50mm_Horizontal +Connector_Wago:Wago_734-176_1x16_P3.50mm_Horizontal +Connector_Wago:Wago_734-178_1x18_P3.50mm_Horizontal +Connector_Wago:Wago_734-180_1x20_P3.50mm_Horizontal +Connector_Wago:Wago_734-184_1x24_P3.50mm_Horizontal +Connector_Wire:SolderWire-0.127sqmm_1x01_D0.48mm_OD1mm +Connector_Wire:SolderWire-0.127sqmm_1x01_D0.48mm_OD1mm_Relief +Connector_Wire:SolderWire-0.127sqmm_1x01_D0.48mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.127sqmm_1x02_P3.7mm_D0.48mm_OD1mm +Connector_Wire:SolderWire-0.127sqmm_1x02_P3.7mm_D0.48mm_OD1mm_Relief +Connector_Wire:SolderWire-0.127sqmm_1x02_P3.7mm_D0.48mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.127sqmm_1x03_P3.7mm_D0.48mm_OD1mm +Connector_Wire:SolderWire-0.127sqmm_1x03_P3.7mm_D0.48mm_OD1mm_Relief +Connector_Wire:SolderWire-0.127sqmm_1x03_P3.7mm_D0.48mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.127sqmm_1x04_P3.7mm_D0.48mm_OD1mm +Connector_Wire:SolderWire-0.127sqmm_1x04_P3.7mm_D0.48mm_OD1mm_Relief +Connector_Wire:SolderWire-0.127sqmm_1x04_P3.7mm_D0.48mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.127sqmm_1x05_P3.7mm_D0.48mm_OD1mm +Connector_Wire:SolderWire-0.127sqmm_1x05_P3.7mm_D0.48mm_OD1mm_Relief +Connector_Wire:SolderWire-0.127sqmm_1x05_P3.7mm_D0.48mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.127sqmm_1x06_P3.7mm_D0.48mm_OD1mm +Connector_Wire:SolderWire-0.127sqmm_1x06_P3.7mm_D0.48mm_OD1mm_Relief +Connector_Wire:SolderWire-0.127sqmm_1x06_P3.7mm_D0.48mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.15sqmm_1x01_D0.5mm_OD1.5mm +Connector_Wire:SolderWire-0.15sqmm_1x01_D0.5mm_OD1.5mm_Relief +Connector_Wire:SolderWire-0.15sqmm_1x01_D0.5mm_OD1.5mm_Relief2x +Connector_Wire:SolderWire-0.15sqmm_1x02_P4mm_D0.5mm_OD1.5mm +Connector_Wire:SolderWire-0.15sqmm_1x02_P4mm_D0.5mm_OD1.5mm_Relief +Connector_Wire:SolderWire-0.15sqmm_1x02_P4mm_D0.5mm_OD1.5mm_Relief2x +Connector_Wire:SolderWire-0.15sqmm_1x03_P4mm_D0.5mm_OD1.5mm +Connector_Wire:SolderWire-0.15sqmm_1x03_P4mm_D0.5mm_OD1.5mm_Relief +Connector_Wire:SolderWire-0.15sqmm_1x03_P4mm_D0.5mm_OD1.5mm_Relief2x +Connector_Wire:SolderWire-0.15sqmm_1x04_P4mm_D0.5mm_OD1.5mm +Connector_Wire:SolderWire-0.15sqmm_1x04_P4mm_D0.5mm_OD1.5mm_Relief +Connector_Wire:SolderWire-0.15sqmm_1x04_P4mm_D0.5mm_OD1.5mm_Relief2x +Connector_Wire:SolderWire-0.15sqmm_1x05_P4mm_D0.5mm_OD1.5mm +Connector_Wire:SolderWire-0.15sqmm_1x05_P4mm_D0.5mm_OD1.5mm_Relief +Connector_Wire:SolderWire-0.15sqmm_1x05_P4mm_D0.5mm_OD1.5mm_Relief2x +Connector_Wire:SolderWire-0.15sqmm_1x06_P4mm_D0.5mm_OD1.5mm +Connector_Wire:SolderWire-0.15sqmm_1x06_P4mm_D0.5mm_OD1.5mm_Relief +Connector_Wire:SolderWire-0.15sqmm_1x06_P4mm_D0.5mm_OD1.5mm_Relief2x +Connector_Wire:SolderWire-0.1sqmm_1x01_D0.4mm_OD1mm +Connector_Wire:SolderWire-0.1sqmm_1x01_D0.4mm_OD1mm_Relief +Connector_Wire:SolderWire-0.1sqmm_1x01_D0.4mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.1sqmm_1x02_P3.6mm_D0.4mm_OD1mm +Connector_Wire:SolderWire-0.1sqmm_1x02_P3.6mm_D0.4mm_OD1mm_Relief +Connector_Wire:SolderWire-0.1sqmm_1x02_P3.6mm_D0.4mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.1sqmm_1x03_P3.6mm_D0.4mm_OD1mm +Connector_Wire:SolderWire-0.1sqmm_1x03_P3.6mm_D0.4mm_OD1mm_Relief +Connector_Wire:SolderWire-0.1sqmm_1x03_P3.6mm_D0.4mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.1sqmm_1x04_P3.6mm_D0.4mm_OD1mm +Connector_Wire:SolderWire-0.1sqmm_1x04_P3.6mm_D0.4mm_OD1mm_Relief +Connector_Wire:SolderWire-0.1sqmm_1x04_P3.6mm_D0.4mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.1sqmm_1x05_P3.6mm_D0.4mm_OD1mm +Connector_Wire:SolderWire-0.1sqmm_1x05_P3.6mm_D0.4mm_OD1mm_Relief +Connector_Wire:SolderWire-0.1sqmm_1x05_P3.6mm_D0.4mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.1sqmm_1x06_P3.6mm_D0.4mm_OD1mm +Connector_Wire:SolderWire-0.1sqmm_1x06_P3.6mm_D0.4mm_OD1mm_Relief +Connector_Wire:SolderWire-0.1sqmm_1x06_P3.6mm_D0.4mm_OD1mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x01_D0.65mm_OD1.7mm +Connector_Wire:SolderWire-0.25sqmm_1x01_D0.65mm_OD1.7mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x01_D0.65mm_OD1.7mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x01_D0.65mm_OD2mm +Connector_Wire:SolderWire-0.25sqmm_1x01_D0.65mm_OD2mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x01_D0.65mm_OD2mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x02_P4.2mm_D0.65mm_OD1.7mm +Connector_Wire:SolderWire-0.25sqmm_1x02_P4.2mm_D0.65mm_OD1.7mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x02_P4.2mm_D0.65mm_OD1.7mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x02_P4.5mm_D0.65mm_OD2mm +Connector_Wire:SolderWire-0.25sqmm_1x02_P4.5mm_D0.65mm_OD2mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x02_P4.5mm_D0.65mm_OD2mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x03_P4.2mm_D0.65mm_OD1.7mm +Connector_Wire:SolderWire-0.25sqmm_1x03_P4.2mm_D0.65mm_OD1.7mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x03_P4.2mm_D0.65mm_OD1.7mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x03_P4.5mm_D0.65mm_OD2mm +Connector_Wire:SolderWire-0.25sqmm_1x03_P4.5mm_D0.65mm_OD2mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x03_P4.5mm_D0.65mm_OD2mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x04_P4.2mm_D0.65mm_OD1.7mm +Connector_Wire:SolderWire-0.25sqmm_1x04_P4.2mm_D0.65mm_OD1.7mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x04_P4.2mm_D0.65mm_OD1.7mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x04_P4.5mm_D0.65mm_OD2mm +Connector_Wire:SolderWire-0.25sqmm_1x04_P4.5mm_D0.65mm_OD2mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x04_P4.5mm_D0.65mm_OD2mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x05_P4.2mm_D0.65mm_OD1.7mm +Connector_Wire:SolderWire-0.25sqmm_1x05_P4.2mm_D0.65mm_OD1.7mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x05_P4.2mm_D0.65mm_OD1.7mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x05_P4.5mm_D0.65mm_OD2mm +Connector_Wire:SolderWire-0.25sqmm_1x05_P4.5mm_D0.65mm_OD2mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x05_P4.5mm_D0.65mm_OD2mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x06_P4.2mm_D0.65mm_OD1.7mm +Connector_Wire:SolderWire-0.25sqmm_1x06_P4.2mm_D0.65mm_OD1.7mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x06_P4.2mm_D0.65mm_OD1.7mm_Relief2x +Connector_Wire:SolderWire-0.25sqmm_1x06_P4.5mm_D0.65mm_OD2mm +Connector_Wire:SolderWire-0.25sqmm_1x06_P4.5mm_D0.65mm_OD2mm_Relief +Connector_Wire:SolderWire-0.25sqmm_1x06_P4.5mm_D0.65mm_OD2mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x01_D0.9mm_OD2.1mm +Connector_Wire:SolderWire-0.5sqmm_1x01_D0.9mm_OD2.1mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x01_D0.9mm_OD2.1mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x01_D0.9mm_OD2.3mm +Connector_Wire:SolderWire-0.5sqmm_1x01_D0.9mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x01_D0.9mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x02_P4.6mm_D0.9mm_OD2.1mm +Connector_Wire:SolderWire-0.5sqmm_1x02_P4.6mm_D0.9mm_OD2.1mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x02_P4.6mm_D0.9mm_OD2.1mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x02_P4.8mm_D0.9mm_OD2.3mm +Connector_Wire:SolderWire-0.5sqmm_1x02_P4.8mm_D0.9mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x02_P4.8mm_D0.9mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x03_P4.6mm_D0.9mm_OD2.1mm +Connector_Wire:SolderWire-0.5sqmm_1x03_P4.6mm_D0.9mm_OD2.1mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x03_P4.6mm_D0.9mm_OD2.1mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x03_P4.8mm_D0.9mm_OD2.3mm +Connector_Wire:SolderWire-0.5sqmm_1x03_P4.8mm_D0.9mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x03_P4.8mm_D0.9mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x04_P4.6mm_D0.9mm_OD2.1mm +Connector_Wire:SolderWire-0.5sqmm_1x04_P4.6mm_D0.9mm_OD2.1mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x04_P4.6mm_D0.9mm_OD2.1mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x04_P4.8mm_D0.9mm_OD2.3mm +Connector_Wire:SolderWire-0.5sqmm_1x04_P4.8mm_D0.9mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x04_P4.8mm_D0.9mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x05_P4.6mm_D0.9mm_OD2.1mm +Connector_Wire:SolderWire-0.5sqmm_1x05_P4.6mm_D0.9mm_OD2.1mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x05_P4.6mm_D0.9mm_OD2.1mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x05_P4.8mm_D0.9mm_OD2.3mm +Connector_Wire:SolderWire-0.5sqmm_1x05_P4.8mm_D0.9mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x05_P4.8mm_D0.9mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x06_P4.6mm_D0.9mm_OD2.1mm +Connector_Wire:SolderWire-0.5sqmm_1x06_P4.6mm_D0.9mm_OD2.1mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x06_P4.6mm_D0.9mm_OD2.1mm_Relief2x +Connector_Wire:SolderWire-0.5sqmm_1x06_P4.8mm_D0.9mm_OD2.3mm +Connector_Wire:SolderWire-0.5sqmm_1x06_P4.8mm_D0.9mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.5sqmm_1x06_P4.8mm_D0.9mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x01_D1.25mm_OD2.3mm +Connector_Wire:SolderWire-0.75sqmm_1x01_D1.25mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x01_D1.25mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x01_D1.25mm_OD3.5mm +Connector_Wire:SolderWire-0.75sqmm_1x01_D1.25mm_OD3.5mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x01_D1.25mm_OD3.5mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x02_P4.8mm_D1.25mm_OD2.3mm +Connector_Wire:SolderWire-0.75sqmm_1x02_P4.8mm_D1.25mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x02_P4.8mm_D1.25mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x02_P7mm_D1.25mm_OD3.5mm +Connector_Wire:SolderWire-0.75sqmm_1x02_P7mm_D1.25mm_OD3.5mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x02_P7mm_D1.25mm_OD3.5mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x03_P4.8mm_D1.25mm_OD2.3mm +Connector_Wire:SolderWire-0.75sqmm_1x03_P4.8mm_D1.25mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x03_P4.8mm_D1.25mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x03_P7mm_D1.25mm_OD3.5mm +Connector_Wire:SolderWire-0.75sqmm_1x03_P7mm_D1.25mm_OD3.5mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x03_P7mm_D1.25mm_OD3.5mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x04_P4.8mm_D1.25mm_OD2.3mm +Connector_Wire:SolderWire-0.75sqmm_1x04_P4.8mm_D1.25mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x04_P4.8mm_D1.25mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x04_P7mm_D1.25mm_OD3.5mm +Connector_Wire:SolderWire-0.75sqmm_1x04_P7mm_D1.25mm_OD3.5mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x04_P7mm_D1.25mm_OD3.5mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x05_P4.8mm_D1.25mm_OD2.3mm +Connector_Wire:SolderWire-0.75sqmm_1x05_P4.8mm_D1.25mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x05_P4.8mm_D1.25mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x05_P7mm_D1.25mm_OD3.5mm +Connector_Wire:SolderWire-0.75sqmm_1x05_P7mm_D1.25mm_OD3.5mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x05_P7mm_D1.25mm_OD3.5mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x06_P4.8mm_D1.25mm_OD2.3mm +Connector_Wire:SolderWire-0.75sqmm_1x06_P4.8mm_D1.25mm_OD2.3mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x06_P4.8mm_D1.25mm_OD2.3mm_Relief2x +Connector_Wire:SolderWire-0.75sqmm_1x06_P7mm_D1.25mm_OD3.5mm +Connector_Wire:SolderWire-0.75sqmm_1x06_P7mm_D1.25mm_OD3.5mm_Relief +Connector_Wire:SolderWire-0.75sqmm_1x06_P7mm_D1.25mm_OD3.5mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x01_D1.7mm_OD3.9mm +Connector_Wire:SolderWire-1.5sqmm_1x01_D1.7mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x01_D1.7mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x01_D1.7mm_OD3mm +Connector_Wire:SolderWire-1.5sqmm_1x01_D1.7mm_OD3mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x01_D1.7mm_OD3mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x02_P6mm_D1.7mm_OD3mm +Connector_Wire:SolderWire-1.5sqmm_1x02_P6mm_D1.7mm_OD3mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x02_P6mm_D1.7mm_OD3mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x02_P7.8mm_D1.7mm_OD3.9mm +Connector_Wire:SolderWire-1.5sqmm_1x02_P7.8mm_D1.7mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x02_P7.8mm_D1.7mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x03_P6mm_D1.7mm_OD3mm +Connector_Wire:SolderWire-1.5sqmm_1x03_P6mm_D1.7mm_OD3mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x03_P6mm_D1.7mm_OD3mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x03_P7.8mm_D1.7mm_OD3.9mm +Connector_Wire:SolderWire-1.5sqmm_1x03_P7.8mm_D1.7mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x03_P7.8mm_D1.7mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x04_P6mm_D1.7mm_OD3mm +Connector_Wire:SolderWire-1.5sqmm_1x04_P6mm_D1.7mm_OD3mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x04_P6mm_D1.7mm_OD3mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x04_P7.8mm_D1.7mm_OD3.9mm +Connector_Wire:SolderWire-1.5sqmm_1x04_P7.8mm_D1.7mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x04_P7.8mm_D1.7mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x05_P6mm_D1.7mm_OD3mm +Connector_Wire:SolderWire-1.5sqmm_1x05_P6mm_D1.7mm_OD3mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x05_P6mm_D1.7mm_OD3mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x05_P7.8mm_D1.7mm_OD3.9mm +Connector_Wire:SolderWire-1.5sqmm_1x05_P7.8mm_D1.7mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x05_P7.8mm_D1.7mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x06_P6mm_D1.7mm_OD3mm +Connector_Wire:SolderWire-1.5sqmm_1x06_P6mm_D1.7mm_OD3mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x06_P6mm_D1.7mm_OD3mm_Relief2x +Connector_Wire:SolderWire-1.5sqmm_1x06_P7.8mm_D1.7mm_OD3.9mm +Connector_Wire:SolderWire-1.5sqmm_1x06_P7.8mm_D1.7mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1.5sqmm_1x06_P7.8mm_D1.7mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x01_D1.4mm_OD2.7mm +Connector_Wire:SolderWire-1sqmm_1x01_D1.4mm_OD2.7mm_Relief +Connector_Wire:SolderWire-1sqmm_1x01_D1.4mm_OD2.7mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x01_D1.4mm_OD3.9mm +Connector_Wire:SolderWire-1sqmm_1x01_D1.4mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1sqmm_1x01_D1.4mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x02_P5.4mm_D1.4mm_OD2.7mm +Connector_Wire:SolderWire-1sqmm_1x02_P5.4mm_D1.4mm_OD2.7mm_Relief +Connector_Wire:SolderWire-1sqmm_1x02_P5.4mm_D1.4mm_OD2.7mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x02_P7.8mm_D1.4mm_OD3.9mm +Connector_Wire:SolderWire-1sqmm_1x02_P7.8mm_D1.4mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1sqmm_1x02_P7.8mm_D1.4mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x03_P5.4mm_D1.4mm_OD2.7mm +Connector_Wire:SolderWire-1sqmm_1x03_P5.4mm_D1.4mm_OD2.7mm_Relief +Connector_Wire:SolderWire-1sqmm_1x03_P5.4mm_D1.4mm_OD2.7mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x03_P7.8mm_D1.4mm_OD3.9mm +Connector_Wire:SolderWire-1sqmm_1x03_P7.8mm_D1.4mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1sqmm_1x03_P7.8mm_D1.4mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x04_P5.4mm_D1.4mm_OD2.7mm +Connector_Wire:SolderWire-1sqmm_1x04_P5.4mm_D1.4mm_OD2.7mm_Relief +Connector_Wire:SolderWire-1sqmm_1x04_P5.4mm_D1.4mm_OD2.7mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x04_P7.8mm_D1.4mm_OD3.9mm +Connector_Wire:SolderWire-1sqmm_1x04_P7.8mm_D1.4mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1sqmm_1x04_P7.8mm_D1.4mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x05_P5.4mm_D1.4mm_OD2.7mm +Connector_Wire:SolderWire-1sqmm_1x05_P5.4mm_D1.4mm_OD2.7mm_Relief +Connector_Wire:SolderWire-1sqmm_1x05_P5.4mm_D1.4mm_OD2.7mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x05_P7.8mm_D1.4mm_OD3.9mm +Connector_Wire:SolderWire-1sqmm_1x05_P7.8mm_D1.4mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1sqmm_1x05_P7.8mm_D1.4mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x06_P5.4mm_D1.4mm_OD2.7mm +Connector_Wire:SolderWire-1sqmm_1x06_P5.4mm_D1.4mm_OD2.7mm_Relief +Connector_Wire:SolderWire-1sqmm_1x06_P5.4mm_D1.4mm_OD2.7mm_Relief2x +Connector_Wire:SolderWire-1sqmm_1x06_P7.8mm_D1.4mm_OD3.9mm +Connector_Wire:SolderWire-1sqmm_1x06_P7.8mm_D1.4mm_OD3.9mm_Relief +Connector_Wire:SolderWire-1sqmm_1x06_P7.8mm_D1.4mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x01_D2.4mm_OD3.6mm +Connector_Wire:SolderWire-2.5sqmm_1x01_D2.4mm_OD3.6mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x01_D2.4mm_OD3.6mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x01_D2.4mm_OD4.4mm +Connector_Wire:SolderWire-2.5sqmm_1x01_D2.4mm_OD4.4mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x01_D2.4mm_OD4.4mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x02_P7.2mm_D2.4mm_OD3.6mm +Connector_Wire:SolderWire-2.5sqmm_1x02_P7.2mm_D2.4mm_OD3.6mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x02_P7.2mm_D2.4mm_OD3.6mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x02_P8.8mm_D2.4mm_OD4.4mm +Connector_Wire:SolderWire-2.5sqmm_1x02_P8.8mm_D2.4mm_OD4.4mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x02_P8.8mm_D2.4mm_OD4.4mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x03_P7.2mm_D2.4mm_OD3.6mm +Connector_Wire:SolderWire-2.5sqmm_1x03_P7.2mm_D2.4mm_OD3.6mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x03_P7.2mm_D2.4mm_OD3.6mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x03_P8.8mm_D2.4mm_OD4.4mm +Connector_Wire:SolderWire-2.5sqmm_1x03_P8.8mm_D2.4mm_OD4.4mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x03_P8.8mm_D2.4mm_OD4.4mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x04_P7.2mm_D2.4mm_OD3.6mm +Connector_Wire:SolderWire-2.5sqmm_1x04_P7.2mm_D2.4mm_OD3.6mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x04_P7.2mm_D2.4mm_OD3.6mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x04_P8.8mm_D2.4mm_OD4.4mm +Connector_Wire:SolderWire-2.5sqmm_1x04_P8.8mm_D2.4mm_OD4.4mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x04_P8.8mm_D2.4mm_OD4.4mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x05_P7.2mm_D2.4mm_OD3.6mm +Connector_Wire:SolderWire-2.5sqmm_1x05_P7.2mm_D2.4mm_OD3.6mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x05_P7.2mm_D2.4mm_OD3.6mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x05_P8.8mm_D2.4mm_OD4.4mm +Connector_Wire:SolderWire-2.5sqmm_1x05_P8.8mm_D2.4mm_OD4.4mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x05_P8.8mm_D2.4mm_OD4.4mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x06_P7.2mm_D2.4mm_OD3.6mm +Connector_Wire:SolderWire-2.5sqmm_1x06_P7.2mm_D2.4mm_OD3.6mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x06_P7.2mm_D2.4mm_OD3.6mm_Relief2x +Connector_Wire:SolderWire-2.5sqmm_1x06_P8.8mm_D2.4mm_OD4.4mm +Connector_Wire:SolderWire-2.5sqmm_1x06_P8.8mm_D2.4mm_OD4.4mm_Relief +Connector_Wire:SolderWire-2.5sqmm_1x06_P8.8mm_D2.4mm_OD4.4mm_Relief2x +Connector_Wire:SolderWire-2sqmm_1x01_D2mm_OD3.9mm +Connector_Wire:SolderWire-2sqmm_1x01_D2mm_OD3.9mm_Relief +Connector_Wire:SolderWire-2sqmm_1x01_D2mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-2sqmm_1x02_P7.8mm_D2mm_OD3.9mm +Connector_Wire:SolderWire-2sqmm_1x02_P7.8mm_D2mm_OD3.9mm_Relief +Connector_Wire:SolderWire-2sqmm_1x02_P7.8mm_D2mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-2sqmm_1x03_P7.8mm_D2mm_OD3.9mm +Connector_Wire:SolderWire-2sqmm_1x03_P7.8mm_D2mm_OD3.9mm_Relief +Connector_Wire:SolderWire-2sqmm_1x03_P7.8mm_D2mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-2sqmm_1x04_P7.8mm_D2mm_OD3.9mm +Connector_Wire:SolderWire-2sqmm_1x04_P7.8mm_D2mm_OD3.9mm_Relief +Connector_Wire:SolderWire-2sqmm_1x04_P7.8mm_D2mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-2sqmm_1x05_P7.8mm_D2mm_OD3.9mm +Connector_Wire:SolderWire-2sqmm_1x05_P7.8mm_D2mm_OD3.9mm_Relief +Connector_Wire:SolderWire-2sqmm_1x05_P7.8mm_D2mm_OD3.9mm_Relief2x +Connector_Wire:SolderWire-2sqmm_1x06_P7.8mm_D2mm_OD3.9mm +Connector_Wire:SolderWire-2sqmm_1x06_P7.8mm_D2mm_OD3.9mm_Relief +Connector_Wire:SolderWire-2sqmm_1x06_P7.8mm_D2mm_OD3.9mm_Relief2x +Connector_Wire:SolderWirePad_1x01_SMD_1x2mm +Connector_Wire:SolderWirePad_1x01_SMD_5x10mm +Connector_Wuerth:Wuerth_WR-PHD_610004243021_SMD_2x02_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610006243021_SMD_2x03_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610008243021_SMD_2x04_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610010243021_SMD_2x05_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610012243021_SMD_2x06_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610016243021_SMD_2x08_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610018243021_SMD_2x09_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610020243021_SMD_2x10_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610022243021_SMD_2x11_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610024243021_SMD_2x12_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610026243021_SMD_2x13_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610032243021_SMD_2x16_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_610034243021_SMD_2x17_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613004216921_Large_2x02_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61300425721_Standard_2x02_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613006216921_Large_2x03_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61300625721_Standard_2x03_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613008216921_Large_2x04_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61300825721_Standard_2x04_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613010216921_Large_2x05_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61301025721_Standard_2x05_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613012216921_Large_2x06_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61301225721_Standard_2x06_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613016216921_Large_2x08_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61301625721_Standard_2x08_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613018216921_Large_2x09_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613020216921_Large_2x10_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61302025721_Standard_2x10_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613022216921_Large_2x11_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613024216921_Large_2x12_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61302425721_Standard_2x12_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613026216921_Large_2x13_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61302625721_Standard_2x13_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613032216921_Large_2x16_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61303225721_Standard_2x16_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_613034216921_Large_2x17_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-PHD_61303425721_Standard_2x17_P2.54mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64800211622_1x02_P1.50mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64800311622_1x03_P1.50mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64800411622_1x04_P1.50mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64800511622_1x05_P1.50mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64800611622_1x06_P1.50mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64800711622_1x07_P1.50mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64800811622_1x08_P1.50mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64800911622_1x09_P1.50mm_Vertical +Connector_Wuerth:Wuerth_WR-WTB_64801011622_1x10_P1.50mm_Vertical +Converter_ACDC:Converter_ACDC_CUI_PBO-3-Sxx_THT_Vertical +Converter_ACDC:Converter_ACDC_Hahn_HS-400xx_THT +Converter_ACDC:Converter_ACDC_Hi-Link_HLK-10Mxx +Converter_ACDC:Converter_ACDC_Hi-Link_HLK-12MxxA +Converter_ACDC:Converter_ACDC_Hi-Link_HLK-20Mxx +Converter_ACDC:Converter_ACDC_Hi-Link_HLK-2Mxx +Converter_ACDC:Converter_ACDC_Hi-Link_HLK-30Mxx +Converter_ACDC:Converter_ACDC_Hi-Link_HLK-5Mxx +Converter_ACDC:Converter_ACDC_Hi-Link_HLK-PMxx +Converter_ACDC:Converter_ACDC_MeanWell_IRM-02-xx_SMD +Converter_ACDC:Converter_ACDC_MeanWell_IRM-02-xx_THT +Converter_ACDC:Converter_ACDC_MeanWell_IRM-03-xx_SMD +Converter_ACDC:Converter_ACDC_MeanWell_IRM-03-xx_THT +Converter_ACDC:Converter_ACDC_MeanWell_IRM-05-xx_THT +Converter_ACDC:Converter_ACDC_MeanWell_IRM-10-xx_THT +Converter_ACDC:Converter_ACDC_MeanWell_IRM-20-xx_THT +Converter_ACDC:Converter_ACDC_MeanWell_IRM-60-xx_THT +Converter_ACDC:Converter_ACDC_MeanWell_MFM-10-xx_THT +Converter_ACDC:Converter_ACDC_MeanWell_MFM-15-xx_THT +Converter_ACDC:Converter_ACDC_Murata_BAC05SxxDC_THT +Converter_ACDC:Converter_ACDC_RECOM_RAC01-xxSGB_THT +Converter_ACDC:Converter_ACDC_RECOM_RAC04-xxSGx_THT +Converter_ACDC:Converter_ACDC_RECOM_RAC05-xxSK_THT +Converter_ACDC:Converter_ACDC_Recom_RAC20-xxDK_THT +Converter_ACDC:Converter_ACDC_Recom_RAC20-xxSK_THT +Converter_ACDC:Converter_ACDC_TRACO_TMF_051xx_THT +Converter_ACDC:Converter_ACDC_TRACO_TMF_101xx_THT +Converter_ACDC:Converter_ACDC_TRACO_TMF_201xx_THT +Converter_ACDC:Converter_ACDC_TRACO_TMF_301xx_THT +Converter_ACDC:Converter_ACDC_TRACO_TMG-15_THT +Converter_ACDC:Converter_ACDC_TRACO_TMLM-04_THT +Converter_ACDC:Converter_ACDC_TRACO_TMLM-05_THT +Converter_ACDC:Converter_ACDC_TRACO_TMLM-10-20_THT +Converter_ACDC:Converter_ACDC_TRACO_TPP-15-1xx-D_THT +Converter_ACDC:Converter_ACDC_Vigortronix_VTX-214-010-xxx_THT +Converter_ACDC:Converter_ACDC_Vigortronix_VTX-214-015-1xx_THT +Converter_ACDC:Converter_ACDC_ZETTLER_ZPI03Sxx00WC_THT +Converter_DCDC:Converter_DCDC_Artesyn_ATA_SMD +Converter_DCDC:Converter_DCDC_Bothhand_CFUDxxxx_THT +Converter_DCDC:Converter_DCDC_Bothhand_CFUSxxxxEH_THT +Converter_DCDC:Converter_DCDC_Bothhand_CFUSxxxx_THT +Converter_DCDC:Converter_DCDC_Cincon_EC5BExx_Dual_THT +Converter_DCDC:Converter_DCDC_Cincon_EC5BExx_Single_THT +Converter_DCDC:Converter_DCDC_Cincon_EC6Cxx_Dual-Triple_THT +Converter_DCDC:Converter_DCDC_Cincon_EC6Cxx_Single_THT +Converter_DCDC:Converter_DCDC_Cyntec_MUN12AD01-SH +Converter_DCDC:Converter_DCDC_Cyntec_MUN12AD03-SH +Converter_DCDC:Converter_DCDC_MeanWell_NID30_THT +Converter_DCDC:Converter_DCDC_MeanWell_NID60_THT +Converter_DCDC:Converter_DCDC_MeanWell_NSD10_THT +Converter_DCDC:Converter_DCDC_Murata_CRE1xxxxxx3C_THT +Converter_DCDC:Converter_DCDC_Murata_CRE1xxxxxxDC_THT +Converter_DCDC:Converter_DCDC_Murata_CRE1xxxxxxSC_THT +Converter_DCDC:Converter_DCDC_Murata_MEE1SxxxxSC_THT +Converter_DCDC:Converter_DCDC_Murata_MEE3SxxxxSC_THT +Converter_DCDC:Converter_DCDC_muRata_MEJ1DxxxxSC_THT +Converter_DCDC:Converter_DCDC_muRata_MEJ1SxxxxSC_THT +Converter_DCDC:Converter_DCDC_Murata_MGJ2DxxxxxxSC_THT +Converter_DCDC:Converter_DCDC_Murata_MGJ3 +Converter_DCDC:Converter_DCDC_Murata_MYRxP +Converter_DCDC:Converter_DCDC_Murata_NCS1SxxxxSC_THT +Converter_DCDC:Converter_DCDC_Murata_NMAxxxxDC_THT +Converter_DCDC:Converter_DCDC_Murata_NMAxxxxSC_THT +Converter_DCDC:Converter_DCDC_Murata_NXExSxxxxMC_SMD +Converter_DCDC:Converter_DCDC_Murata_OKI-78SR_Horizontal +Converter_DCDC:Converter_DCDC_Murata_OKI-78SR_Vertical +Converter_DCDC:Converter_DCDC_RECOM_R-78B-2.0_THT +Converter_DCDC:Converter_DCDC_RECOM_R-78E-0.5_THT +Converter_DCDC:Converter_DCDC_RECOM_R-78HB-0.5L_THT +Converter_DCDC:Converter_DCDC_RECOM_R-78HB-0.5_THT +Converter_DCDC:Converter_DCDC_RECOM_R-78S-0.1_THT +Converter_DCDC:Converter_DCDC_RECOM_R5xxxDA_THT +Converter_DCDC:Converter_DCDC_RECOM_R5xxxPA_THT +Converter_DCDC:Converter_DCDC_RECOM_RCD-24_THT +Converter_DCDC:Converter_DCDC_RECOM_RPA60-xxxxSFW +Converter_DCDC:Converter_DCDC_RECOM_RPMx.x-x.0 +Converter_DCDC:Converter_DCDC_Silvertel_Ag54xx +Converter_DCDC:Converter_DCDC_Silvertel_Ag5810 +Converter_DCDC:Converter_DCDC_Silvertel_Ag99xxLP_THT +Converter_DCDC:Converter_DCDC_TRACO_TBA1-xxxxE_Dual_THT +Converter_DCDC:Converter_DCDC_TRACO_TBA1-xxxxE_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_TBA2-xxxx_Dual_THT +Converter_DCDC:Converter_DCDC_TRACO_TBA2-xxxx_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_TDN_5-xxxxWISM_SMD +Converter_DCDC:Converter_DCDC_TRACO_TDN_5-xxxxWI_THT +Converter_DCDC:Converter_DCDC_TRACO_TDU1-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_TEA1-xxxxE_THT +Converter_DCDC:Converter_DCDC_TRACO_TEA1-xxxxHI_THT +Converter_DCDC:Converter_DCDC_TRACO_TEA1-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_TEC3-24xxUI_THT +Converter_DCDC:Converter_DCDC_TRACO_TEL12-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_TEN10-110xxWIRH_THT +Converter_DCDC:Converter_DCDC_TRACO_TEN10-xxxx_Dual_THT +Converter_DCDC:Converter_DCDC_TRACO_TEN10-xxxx_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_TEN10-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_TEN20-110xxWIRH_THT +Converter_DCDC:Converter_DCDC_TRACO_TEN20-xxxx-N4_THT +Converter_DCDC:Converter_DCDC_TRACO_TEN20-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_TEN40-110xxWIRH_THT +Converter_DCDC:Converter_DCDC_TRACO_THB10-xxxx_Dual_THT +Converter_DCDC:Converter_DCDC_TRACO_THB10-xxxx_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_THD_15-xxxxWIN_THT +Converter_DCDC:Converter_DCDC_TRACO_THN30-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_THR40-72xxWI_THT +Converter_DCDC:Converter_DCDC_TRACO_TMA-05xxD_12xxD_Dual_THT +Converter_DCDC:Converter_DCDC_TRACO_TMA-05xxS_12xxS_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_TMA-15xxD_24xxD_Dual_THT +Converter_DCDC:Converter_DCDC_TRACO_TMA-15xxS_24xxS_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_TME_03xxS_05xxS_12xxS_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_TME_24xxS_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_TMR-1-xxxx_Dual_THT +Converter_DCDC:Converter_DCDC_TRACO_TMR-1-xxxx_Single_THT +Converter_DCDC:Converter_DCDC_TRACO_TMR-1SM_SMD +Converter_DCDC:Converter_DCDC_TRACO_TMR-2xxxxWI_THT +Converter_DCDC:Converter_DCDC_TRACO_TMR-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_TMR4-xxxxWI_THT +Converter_DCDC:Converter_DCDC_TRACO_TMU3-05xx_12xx_THT +Converter_DCDC:Converter_DCDC_TRACO_TMU3-24xx_THT +Converter_DCDC:Converter_DCDC_TRACO_TOS06-05SIL_THT +Converter_DCDC:Converter_DCDC_TRACO_TOS06-12SIL_THT +Converter_DCDC:Converter_DCDC_TRACO_TRA3-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_TRI1-xxxx_THT +Converter_DCDC:Converter_DCDC_TRACO_TSR-1_THT +Converter_DCDC:Converter_DCDC_TRACO_TSR0.6-48xxWI_TSR0.6-48xxxWI_THT +Converter_DCDC:Converter_DCDC_TRACO_TSR1-xxxxE_THT +Converter_DCDC:Converter_DCDC_TRACO_TSR2-24xxN_TSR2-24xxxN_THT +Converter_DCDC:Converter_DCDC_TRACO_TSR2-xxxx_THT +Converter_DCDC:Converter_DCDC_XP_POWER-IA48xxD_THT +Converter_DCDC:Converter_DCDC_XP_POWER-IA48xxS_THT +Converter_DCDC:Converter_DCDC_XP_POWER-IAxxxxD_THT +Converter_DCDC:Converter_DCDC_XP_POWER-IAxxxxS_THT +Converter_DCDC:Converter_DCDC_XP_POWER-IHxxxxDH_THT +Converter_DCDC:Converter_DCDC_XP_POWER-IHxxxxD_THT +Converter_DCDC:Converter_DCDC_XP_POWER-IHxxxxSH_THT +Converter_DCDC:Converter_DCDC_XP_POWER-IHxxxxS_THT +Converter_DCDC:Converter_DCDC_XP_POWER-ISU02_SMD +Converter_DCDC:Converter_DCDC_XP_POWER-ITQxxxxS-H_THT +Converter_DCDC:Converter_DCDC_XP_POWER-ITXxxxxSA_THT +Converter_DCDC:Converter_DCDC_XP_POWER-ITxxxxxS_THT +Converter_DCDC:Converter_DCDC_XP_POWER_JTDxxxxxxx_THT +Converter_DCDC:Converter_DCDC_XP_POWER_JTExxxxDxx_THT +Crystal:Crystal_AT310_D3.0mm_L10.0mm_Horizontal +Crystal:Crystal_AT310_D3.0mm_L10.0mm_Horizontal_1EP_style1 +Crystal:Crystal_AT310_D3.0mm_L10.0mm_Horizontal_1EP_style2 +Crystal:Crystal_AT310_D3.0mm_L10.0mm_Vertical +Crystal:Crystal_C26-LF_D2.1mm_L6.5mm_Horizontal +Crystal:Crystal_C26-LF_D2.1mm_L6.5mm_Horizontal_1EP_style1 +Crystal:Crystal_C26-LF_D2.1mm_L6.5mm_Horizontal_1EP_style2 +Crystal:Crystal_C26-LF_D2.1mm_L6.5mm_Vertical +Crystal:Crystal_C38-LF_D3.0mm_L8.0mm_Horizontal +Crystal:Crystal_C38-LF_D3.0mm_L8.0mm_Horizontal_1EP_style1 +Crystal:Crystal_C38-LF_D3.0mm_L8.0mm_Horizontal_1EP_style2 +Crystal:Crystal_C38-LF_D3.0mm_L8.0mm_Vertical +Crystal:Crystal_DS10_D1.0mm_L4.3mm_Horizontal +Crystal:Crystal_DS10_D1.0mm_L4.3mm_Horizontal_1EP_style1 +Crystal:Crystal_DS10_D1.0mm_L4.3mm_Horizontal_1EP_style2 +Crystal:Crystal_DS10_D1.0mm_L4.3mm_Vertical +Crystal:Crystal_DS15_D1.5mm_L5.0mm_Horizontal +Crystal:Crystal_DS15_D1.5mm_L5.0mm_Horizontal_1EP_style1 +Crystal:Crystal_DS15_D1.5mm_L5.0mm_Horizontal_1EP_style2 +Crystal:Crystal_DS15_D1.5mm_L5.0mm_Vertical +Crystal:Crystal_DS26_D2.0mm_L6.0mm_Horizontal +Crystal:Crystal_DS26_D2.0mm_L6.0mm_Horizontal_1EP_style1 +Crystal:Crystal_DS26_D2.0mm_L6.0mm_Horizontal_1EP_style2 +Crystal:Crystal_DS26_D2.0mm_L6.0mm_Vertical +Crystal:Crystal_HC18-U_Horizontal +Crystal:Crystal_HC18-U_Horizontal_1EP_style1 +Crystal:Crystal_HC18-U_Horizontal_1EP_style2 +Crystal:Crystal_HC18-U_Vertical +Crystal:Crystal_HC33-U_Horizontal +Crystal:Crystal_HC33-U_Horizontal_1EP_style1 +Crystal:Crystal_HC33-U_Horizontal_1EP_style2 +Crystal:Crystal_HC33-U_Vertical +Crystal:Crystal_HC35-U +Crystal:Crystal_HC49-4H_Vertical +Crystal:Crystal_HC49-U-3Pin_Vertical +Crystal:Crystal_HC49-U_Horizontal +Crystal:Crystal_HC49-U_Horizontal_1EP_style1 +Crystal:Crystal_HC49-U_Horizontal_1EP_style2 +Crystal:Crystal_HC49-U_Vertical +Crystal:Crystal_HC50_Horizontal +Crystal:Crystal_HC50_Horizontal_1EP_style1 +Crystal:Crystal_HC50_Horizontal_1EP_style2 +Crystal:Crystal_HC50_Vertical +Crystal:Crystal_HC51-U_Vertical +Crystal:Crystal_HC51_Horizontal +Crystal:Crystal_HC51_Horizontal_1EP_style1 +Crystal:Crystal_HC51_Horizontal_1EP_style2 +Crystal:Crystal_HC52-6mm_Horizontal +Crystal:Crystal_HC52-6mm_Horizontal_1EP_style1 +Crystal:Crystal_HC52-6mm_Horizontal_1EP_style2 +Crystal:Crystal_HC52-6mm_Vertical +Crystal:Crystal_HC52-8mm_Horizontal +Crystal:Crystal_HC52-8mm_Horizontal_1EP_style1 +Crystal:Crystal_HC52-8mm_Horizontal_1EP_style2 +Crystal:Crystal_HC52-8mm_Vertical +Crystal:Crystal_HC52-U-3Pin_Vertical +Crystal:Crystal_HC52-U_Horizontal +Crystal:Crystal_HC52-U_Horizontal_1EP_style1 +Crystal:Crystal_HC52-U_Horizontal_1EP_style2 +Crystal:Crystal_HC52-U_Vertical +Crystal:Crystal_Round_D1.0mm_Vertical +Crystal:Crystal_Round_D1.5mm_Vertical +Crystal:Crystal_Round_D2.0mm_Vertical +Crystal:Crystal_Round_D3.0mm_Vertical +Crystal:Crystal_SMD_0603-2Pin_6.0x3.5mm +Crystal:Crystal_SMD_0603-2Pin_6.0x3.5mm_HandSoldering +Crystal:Crystal_SMD_0603-4Pin_6.0x3.5mm +Crystal:Crystal_SMD_0603-4Pin_6.0x3.5mm_HandSoldering +Crystal:Crystal_SMD_1210-4Pin_1.2x1.0mm +Crystal:Crystal_SMD_2012-2Pin_2.0x1.2mm +Crystal:Crystal_SMD_2012-2Pin_2.0x1.2mm_HandSoldering +Crystal:Crystal_SMD_2016-4Pin_2.0x1.6mm +Crystal:Crystal_SMD_2520-4Pin_2.5x2.0mm +Crystal:Crystal_SMD_3215-2Pin_3.2x1.5mm +Crystal:Crystal_SMD_3225-4Pin_3.2x2.5mm +Crystal:Crystal_SMD_3225-4Pin_3.2x2.5mm_HandSoldering +Crystal:Crystal_SMD_5032-2Pin_5.0x3.2mm +Crystal:Crystal_SMD_5032-2Pin_5.0x3.2mm_HandSoldering +Crystal:Crystal_SMD_5032-4Pin_5.0x3.2mm +Crystal:Crystal_SMD_7050-2Pin_7.0x5.0mm +Crystal:Crystal_SMD_7050-2Pin_7.0x5.0mm_HandSoldering +Crystal:Crystal_SMD_7050-4Pin_7.0x5.0mm +Crystal:Crystal_SMD_Abracon_ABM10-4Pin_2.5x2.0mm +Crystal:Crystal_SMD_Abracon_ABM3-2Pin_5.0x3.2mm +Crystal:Crystal_SMD_Abracon_ABM3-2Pin_5.0x3.2mm_HandSoldering +Crystal:Crystal_SMD_Abracon_ABM3B-4Pin_5.0x3.2mm +Crystal:Crystal_SMD_Abracon_ABM3C-4Pin_5.0x3.2mm +Crystal:Crystal_SMD_Abracon_ABM7-2Pin_6.0x3.5mm +Crystal:Crystal_SMD_Abracon_ABM8AIG-4Pin_3.2x2.5mm +Crystal:Crystal_SMD_Abracon_ABM8G-4Pin_3.2x2.5mm +Crystal:Crystal_SMD_Abracon_ABS25-4Pin_8.0x3.8mm +Crystal:Crystal_SMD_ECS_CSM3X-2Pin_7.6x4.1mm +Crystal:Crystal_SMD_EuroQuartz_EQ161-2Pin_3.2x1.5mm +Crystal:Crystal_SMD_EuroQuartz_EQ161-2Pin_3.2x1.5mm_HandSoldering +Crystal:Crystal_SMD_EuroQuartz_MJ-4Pin_5.0x3.2mm +Crystal:Crystal_SMD_EuroQuartz_MJ-4Pin_5.0x3.2mm_HandSoldering +Crystal:Crystal_SMD_EuroQuartz_MQ-4Pin_7.0x5.0mm +Crystal:Crystal_SMD_EuroQuartz_MQ-4Pin_7.0x5.0mm_HandSoldering +Crystal:Crystal_SMD_EuroQuartz_MQ2-2Pin_7.0x5.0mm +Crystal:Crystal_SMD_EuroQuartz_MQ2-2Pin_7.0x5.0mm_HandSoldering +Crystal:Crystal_SMD_EuroQuartz_MT-4Pin_3.2x2.5mm +Crystal:Crystal_SMD_EuroQuartz_MT-4Pin_3.2x2.5mm_HandSoldering +Crystal:Crystal_SMD_EuroQuartz_X22-4Pin_2.5x2.0mm +Crystal:Crystal_SMD_EuroQuartz_X22-4Pin_2.5x2.0mm_HandSoldering +Crystal:Crystal_SMD_FOX_FE-2Pin_7.5x5.0mm +Crystal:Crystal_SMD_FOX_FE-2Pin_7.5x5.0mm_HandSoldering +Crystal:Crystal_SMD_FOX_FQ7050-2Pin_7.0x5.0mm +Crystal:Crystal_SMD_FOX_FQ7050-2Pin_7.0x5.0mm_HandSoldering +Crystal:Crystal_SMD_FOX_FQ7050-4Pin_7.0x5.0mm +Crystal:Crystal_SMD_FrontierElectronics_FM206 +Crystal:Crystal_SMD_G8-2Pin_3.2x1.5mm +Crystal:Crystal_SMD_G8-2Pin_3.2x1.5mm_HandSoldering +Crystal:Crystal_SMD_HC49-SD +Crystal:Crystal_SMD_HC49-SD_HandSoldering +Crystal:Crystal_SMD_MicroCrystal_CC1V-T1A-2Pin_8.0x3.7mm +Crystal:Crystal_SMD_MicroCrystal_CC1V-T1A-2Pin_8.0x3.7mm_HandSoldering +Crystal:Crystal_SMD_MicroCrystal_CC4V-T1A-2Pin_5.0x1.9mm +Crystal:Crystal_SMD_MicroCrystal_CC4V-T1A-2Pin_5.0x1.9mm_HandSoldering +Crystal:Crystal_SMD_MicroCrystal_CC5V-T1A-2Pin_4.1x1.5mm +Crystal:Crystal_SMD_MicroCrystal_CC5V-T1A-2Pin_4.1x1.5mm_HandSoldering +Crystal:Crystal_SMD_MicroCrystal_CC7V-T1A-2Pin_3.2x1.5mm +Crystal:Crystal_SMD_MicroCrystal_CC7V-T1A-2Pin_3.2x1.5mm_HandSoldering +Crystal:Crystal_SMD_MicroCrystal_CC8V-T1A-2Pin_2.0x1.2mm +Crystal:Crystal_SMD_MicroCrystal_CC8V-T1A-2Pin_2.0x1.2mm_HandSoldering +Crystal:Crystal_SMD_MicroCrystal_CM9V-T1A-2Pin_1.6x1.0mm +Crystal:Crystal_SMD_MicroCrystal_CM9V-T1A-2Pin_1.6x1.0mm_HandSoldering +Crystal:Crystal_SMD_MicroCrystal_MS1V-T1K +Crystal:Crystal_SMD_MicroCrystal_MS3V-T1R +Crystal:Crystal_SMD_Qantek_QC5CB-2Pin_5x3.2mm +Crystal:Crystal_SMD_SeikoEpson_FA128-4Pin_2.0x1.6mm +Crystal:Crystal_SMD_SeikoEpson_FA238-4Pin_3.2x2.5mm +Crystal:Crystal_SMD_SeikoEpson_FA238-4Pin_3.2x2.5mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_FA238V-4Pin_3.2x2.5mm +Crystal:Crystal_SMD_SeikoEpson_FA238V-4Pin_3.2x2.5mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_MA406-4Pin_11.7x4.0mm +Crystal:Crystal_SMD_SeikoEpson_MA406-4Pin_11.7x4.0mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_MA505-2Pin_12.7x5.1mm +Crystal:Crystal_SMD_SeikoEpson_MA505-2Pin_12.7x5.1mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_MA506-4Pin_12.7x5.1mm +Crystal:Crystal_SMD_SeikoEpson_MA506-4Pin_12.7x5.1mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_MC146-4Pin_6.7x1.5mm +Crystal:Crystal_SMD_SeikoEpson_MC146-4Pin_6.7x1.5mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_MC156-4Pin_7.1x2.5mm +Crystal:Crystal_SMD_SeikoEpson_MC156-4Pin_7.1x2.5mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_MC306-4Pin_8.0x3.2mm +Crystal:Crystal_SMD_SeikoEpson_MC306-4Pin_8.0x3.2mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_MC405-2Pin_9.6x4.1mm +Crystal:Crystal_SMD_SeikoEpson_MC405-2Pin_9.6x4.1mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_MC406-4Pin_9.6x4.1mm +Crystal:Crystal_SMD_SeikoEpson_MC406-4Pin_9.6x4.1mm_HandSoldering +Crystal:Crystal_SMD_SeikoEpson_TSX3225-4Pin_3.2x2.5mm +Crystal:Crystal_SMD_SeikoEpson_TSX3225-4Pin_3.2x2.5mm_HandSoldering +Crystal:Crystal_SMD_TXC_7A-2Pin_5x3.2mm +Crystal:Crystal_SMD_TXC_7M-4Pin_3.2x2.5mm +Crystal:Crystal_SMD_TXC_7M-4Pin_3.2x2.5mm_HandSoldering +Crystal:Crystal_SMD_TXC_9HT11-2Pin_2.0x1.2mm +Crystal:Crystal_SMD_TXC_9HT11-2Pin_2.0x1.2mm_HandSoldering +Crystal:Crystal_SMD_TXC_AX_8045-2Pin_8.0x4.5mm +Crystal:Resonator-2Pin_W10.0mm_H5.0mm +Crystal:Resonator-2Pin_W6.0mm_H3.0mm +Crystal:Resonator-2Pin_W7.0mm_H2.5mm +Crystal:Resonator-2Pin_W8.0mm_H3.5mm +Crystal:Resonator-3Pin_W10.0mm_H5.0mm +Crystal:Resonator-3Pin_W6.0mm_H3.0mm +Crystal:Resonator-3Pin_W7.0mm_H2.5mm +Crystal:Resonator-3Pin_W8.0mm_H3.5mm +Crystal:Resonator_Murata_CSTLSxxxG-3Pin_W8.0mm_H3.0mm +Crystal:Resonator_Murata_CSTLSxxxX-3Pin_W5.5mm_H3.0mm +Crystal:Resonator_Murata_DSN6-3Pin_W7.0mm_H2.5mm +Crystal:Resonator_Murata_DSS6-3Pin_W7.0mm_H2.5mm +Crystal:Resonator_SMD-3Pin_7.2x3.0mm +Crystal:Resonator_SMD-3Pin_7.2x3.0mm_HandSoldering +Crystal:Resonator_SMD_Murata_CDSCB-2Pin_4.5x2.0mm +Crystal:Resonator_SMD_Murata_CDSCB-2Pin_4.5x2.0mm_HandSoldering +Crystal:Resonator_SMD_Murata_CSTCR_4.5x2x1.15mm +Crystal:Resonator_SMD_Murata_CSTxExxV-3Pin_3.0x1.1mm +Crystal:Resonator_SMD_Murata_CSTxExxV-3Pin_3.0x1.1mm_HandSoldering +Crystal:Resonator_SMD_Murata_SFECV-3Pin_6.9x2.9mm +Crystal:Resonator_SMD_Murata_SFECV-3Pin_6.9x2.9mm_HandSoldering +Crystal:Resonator_SMD_Murata_SFSKA-3Pin_7.9x3.8mm +Crystal:Resonator_SMD_Murata_SFSKA-3Pin_7.9x3.8mm_HandSoldering +Crystal:Resonator_SMD_Murata_TPSKA-3Pin_7.9x3.8mm +Crystal:Resonator_SMD_Murata_TPSKA-3Pin_7.9x3.8mm_HandSoldering +Diode_SMD:Diode_Bridge_Bourns_CD-DF4xxS +Diode_SMD:Diode_Bridge_Diotec_ABS +Diode_SMD:Diode_Bridge_Diotec_MicroDil_3.0x3.0x1.8mm +Diode_SMD:Diode_Bridge_Diotec_SO-DIL-Slim +Diode_SMD:Diode_Bridge_OnSemi_SDIP-4L +Diode_SMD:Diode_Bridge_Vishay_DFS +Diode_SMD:Diode_Bridge_Vishay_DFSFlat +Diode_SMD:Diode_Bridge_Vishay_MBLS +Diode_SMD:D_01005_0402Metric +Diode_SMD:D_01005_0402Metric_Pad0.57x0.30mm_HandSolder +Diode_SMD:D_0201_0603Metric +Diode_SMD:D_0201_0603Metric_Pad0.64x0.40mm_HandSolder +Diode_SMD:D_0402_1005Metric +Diode_SMD:D_0402_1005Metric_Pad0.77x0.64mm_HandSolder +Diode_SMD:D_0603_1608Metric +Diode_SMD:D_0603_1608Metric_Pad1.05x0.95mm_HandSolder +Diode_SMD:D_0805_2012Metric +Diode_SMD:D_0805_2012Metric_Pad1.15x1.40mm_HandSolder +Diode_SMD:D_1206_3216Metric +Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder +Diode_SMD:D_1210_3225Metric +Diode_SMD:D_1210_3225Metric_Pad1.42x2.65mm_HandSolder +Diode_SMD:D_1812_4532Metric +Diode_SMD:D_1812_4532Metric_Pad1.30x3.40mm_HandSolder +Diode_SMD:D_2010_5025Metric +Diode_SMD:D_2010_5025Metric_Pad1.52x2.65mm_HandSolder +Diode_SMD:D_2114_3652Metric +Diode_SMD:D_2114_3652Metric_Pad1.85x3.75mm_HandSolder +Diode_SMD:D_2512_6332Metric +Diode_SMD:D_2512_6332Metric_Pad1.52x3.35mm_HandSolder +Diode_SMD:D_3220_8050Metric +Diode_SMD:D_3220_8050Metric_Pad2.65x5.15mm_HandSolder +Diode_SMD:D_MELF-RM10_Universal_Handsoldering +Diode_SMD:D_MELF +Diode_SMD:D_MELF_Handsoldering +Diode_SMD:D_MicroMELF +Diode_SMD:D_MicroMELF_Handsoldering +Diode_SMD:D_MicroSMP_AK +Diode_SMD:D_MicroSMP_KA +Diode_SMD:D_MiniMELF +Diode_SMD:D_MiniMELF_Handsoldering +Diode_SMD:D_PowerDI-123 +Diode_SMD:D_PowerDI-5 +Diode_SMD:D_Powermite2_AK +Diode_SMD:D_Powermite2_KA +Diode_SMD:D_Powermite3 +Diode_SMD:D_Powermite_AK +Diode_SMD:D_Powermite_KA +Diode_SMD:D_QFN_3.3x3.3mm_P0.65mm +Diode_SMD:D_SC-80 +Diode_SMD:D_SC-80_HandSoldering +Diode_SMD:D_SMA-SMB_Universal_Handsoldering +Diode_SMD:D_SMA +Diode_SMD:D_SMA_Handsoldering +Diode_SMD:D_SMB-SMC_Universal_Handsoldering +Diode_SMD:D_SMB +Diode_SMD:D_SMB_Handsoldering +Diode_SMD:D_SMB_Modified +Diode_SMD:D_SMC-RM10_Universal_Handsoldering +Diode_SMD:D_SMC +Diode_SMD:D_SMC_Handsoldering +Diode_SMD:D_SMF +Diode_SMD:D_SMP_DO-220AA +Diode_SMD:D_SOD-110 +Diode_SMD:D_SOD-123 +Diode_SMD:D_SOD-123F +Diode_SMD:D_SOD-128 +Diode_SMD:D_SOD-323 +Diode_SMD:D_SOD-323F +Diode_SMD:D_SOD-323_HandSoldering +Diode_SMD:D_SOD-523 +Diode_SMD:D_SOD-882 +Diode_SMD:D_SOD-882D +Diode_SMD:D_SOD-923 +Diode_SMD:D_TUMD2 +Diode_SMD:Infineon_SG-WLL-2-3_0.58x0.28_P0.36mm +Diode_SMD:Littelfuse_PolyZen-LS +Diode_SMD:Nexperia_CFP3_SOD-123W +Diode_SMD:Nexperia_DSN0603-2_0.6x0.3mm_P0.4mm +Diode_SMD:Nexperia_DSN1608-2_1.6x0.8mm +Diode_SMD:OnSemi_751EP_SOIC-4_3.9x4.725mm_P2.54mm +Diode_SMD:ST_D_SMC +Diode_SMD:ST_QFN-2L_1.6x1.0mm +Diode_SMD:Vishay_SMPA +Diode_THT:Diode_Bridge_15.1x15.1x6.3mm_P10.9mm +Diode_THT:Diode_Bridge_15.2x15.2x6.3mm_P10.9mm +Diode_THT:Diode_Bridge_15.7x15.7x6.3mm_P10.8mm +Diode_THT:Diode_Bridge_16.7x16.7x6.3mm_P10.8mm +Diode_THT:Diode_Bridge_19.0x19.0x6.8mm_P12.7mm +Diode_THT:Diode_Bridge_19.0x3.5x10.0mm_P5.0mm +Diode_THT:Diode_Bridge_28.6x28.6x7.3mm_P18.0mm_P11.6mm +Diode_THT:Diode_Bridge_32.0x5.6x17.0mm_P10.0mm_P7.5mm +Diode_THT:Diode_Bridge_Comchip_SCVB-L +Diode_THT:Diode_Bridge_DIGITRON_KBPC_T +Diode_THT:Diode_Bridge_DIP-4_W5.08mm_P2.54mm +Diode_THT:Diode_Bridge_DIP-4_W7.62mm_P5.08mm +Diode_THT:Diode_Bridge_GeneSiC_KBPC_T +Diode_THT:Diode_Bridge_GeneSiC_KBPC_W +Diode_THT:Diode_Bridge_IXYS_GUFP +Diode_THT:Diode_Bridge_Round_D8.9mm +Diode_THT:Diode_Bridge_Round_D9.0mm +Diode_THT:Diode_Bridge_Round_D9.8mm +Diode_THT:Diode_Bridge_Vishay_GBL +Diode_THT:Diode_Bridge_Vishay_GBU +Diode_THT:Diode_Bridge_Vishay_KBL +Diode_THT:Diode_Bridge_Vishay_KBPC1 +Diode_THT:Diode_Bridge_Vishay_KBPC6 +Diode_THT:Diode_Bridge_Vishay_KBPM +Diode_THT:Diode_Bridge_Vishay_KBU +Diode_THT:D_5KPW_P12.70mm_Horizontal +Diode_THT:D_5KPW_P7.62mm_Vertical_AnodeUp +Diode_THT:D_5KPW_P7.62mm_Vertical_KathodeUp +Diode_THT:D_5KP_P10.16mm_Horizontal +Diode_THT:D_5KP_P12.70mm_Horizontal +Diode_THT:D_5KP_P7.62mm_Vertical_AnodeUp +Diode_THT:D_5KP_P7.62mm_Vertical_KathodeUp +Diode_THT:D_5W_P10.16mm_Horizontal +Diode_THT:D_5W_P12.70mm_Horizontal +Diode_THT:D_5W_P5.08mm_Vertical_AnodeUp +Diode_THT:D_5W_P5.08mm_Vertical_KathodeUp +Diode_THT:D_A-405_P10.16mm_Horizontal +Diode_THT:D_A-405_P12.70mm_Horizontal +Diode_THT:D_A-405_P2.54mm_Vertical_AnodeUp +Diode_THT:D_A-405_P2.54mm_Vertical_KathodeUp +Diode_THT:D_A-405_P5.08mm_Vertical_AnodeUp +Diode_THT:D_A-405_P5.08mm_Vertical_KathodeUp +Diode_THT:D_A-405_P7.62mm_Horizontal +Diode_THT:D_DO-15_P10.16mm_Horizontal +Diode_THT:D_DO-15_P12.70mm_Horizontal +Diode_THT:D_DO-15_P15.24mm_Horizontal +Diode_THT:D_DO-15_P2.54mm_Vertical_AnodeUp +Diode_THT:D_DO-15_P2.54mm_Vertical_KathodeUp +Diode_THT:D_DO-15_P3.81mm_Vertical_AnodeUp +Diode_THT:D_DO-15_P3.81mm_Vertical_KathodeUp +Diode_THT:D_DO-15_P5.08mm_Vertical_AnodeUp +Diode_THT:D_DO-15_P5.08mm_Vertical_KathodeUp +Diode_THT:D_DO-201AD_P12.70mm_Horizontal +Diode_THT:D_DO-201AD_P15.24mm_Horizontal +Diode_THT:D_DO-201AD_P3.81mm_Vertical_AnodeUp +Diode_THT:D_DO-201AD_P3.81mm_Vertical_KathodeUp +Diode_THT:D_DO-201AD_P5.08mm_Vertical_AnodeUp +Diode_THT:D_DO-201AD_P5.08mm_Vertical_KathodeUp +Diode_THT:D_DO-201AE_P12.70mm_Horizontal +Diode_THT:D_DO-201AE_P15.24mm_Horizontal +Diode_THT:D_DO-201AE_P3.81mm_Vertical_AnodeUp +Diode_THT:D_DO-201AE_P3.81mm_Vertical_KathodeUp +Diode_THT:D_DO-201AE_P5.08mm_Vertical_AnodeUp +Diode_THT:D_DO-201AE_P5.08mm_Vertical_KathodeUp +Diode_THT:D_DO-201_P12.70mm_Horizontal +Diode_THT:D_DO-201_P15.24mm_Horizontal +Diode_THT:D_DO-201_P3.81mm_Vertical_AnodeUp +Diode_THT:D_DO-201_P3.81mm_Vertical_KathodeUp +Diode_THT:D_DO-201_P5.08mm_Vertical_AnodeUp +Diode_THT:D_DO-201_P5.08mm_Vertical_KathodeUp +Diode_THT:D_DO-247_Horizontal_TabDown +Diode_THT:D_DO-247_Horizontal_TabUp +Diode_THT:D_DO-247_Vertical +Diode_THT:D_DO-27_P12.70mm_Horizontal +Diode_THT:D_DO-27_P15.24mm_Horizontal +Diode_THT:D_DO-27_P5.08mm_Vertical_AnodeUp +Diode_THT:D_DO-27_P5.08mm_Vertical_KathodeUp +Diode_THT:D_DO-34_SOD68_P10.16mm_Horizontal +Diode_THT:D_DO-34_SOD68_P12.70mm_Horizontal +Diode_THT:D_DO-34_SOD68_P2.54mm_Vertical_AnodeUp +Diode_THT:D_DO-34_SOD68_P2.54mm_Vertical_KathodeUp +Diode_THT:D_DO-34_SOD68_P5.08mm_Vertical_AnodeUp +Diode_THT:D_DO-34_SOD68_P5.08mm_Vertical_KathodeUp +Diode_THT:D_DO-34_SOD68_P7.62mm_Horizontal +Diode_THT:D_DO-35_SOD27_P10.16mm_Horizontal +Diode_THT:D_DO-35_SOD27_P12.70mm_Horizontal +Diode_THT:D_DO-35_SOD27_P2.54mm_Vertical_AnodeUp +Diode_THT:D_DO-35_SOD27_P2.54mm_Vertical_KathodeUp +Diode_THT:D_DO-35_SOD27_P3.81mm_Vertical_AnodeUp +Diode_THT:D_DO-35_SOD27_P3.81mm_Vertical_KathodeUp +Diode_THT:D_DO-35_SOD27_P5.08mm_Vertical_AnodeUp +Diode_THT:D_DO-35_SOD27_P5.08mm_Vertical_KathodeUp +Diode_THT:D_DO-35_SOD27_P7.62mm_Horizontal +Diode_THT:D_DO-41_SOD81_P10.16mm_Horizontal +Diode_THT:D_DO-41_SOD81_P12.70mm_Horizontal +Diode_THT:D_DO-41_SOD81_P2.54mm_Vertical_AnodeUp +Diode_THT:D_DO-41_SOD81_P2.54mm_Vertical_KathodeUp +Diode_THT:D_DO-41_SOD81_P3.81mm_Vertical_AnodeUp +Diode_THT:D_DO-41_SOD81_P3.81mm_Vertical_KathodeUp +Diode_THT:D_DO-41_SOD81_P5.08mm_Vertical_AnodeUp +Diode_THT:D_DO-41_SOD81_P5.08mm_Vertical_KathodeUp +Diode_THT:D_DO-41_SOD81_P7.62mm_Horizontal +Diode_THT:D_P600_R-6_P12.70mm_Horizontal +Diode_THT:D_P600_R-6_P20.00mm_Horizontal +Diode_THT:D_P600_R-6_P7.62mm_Vertical_AnodeUp +Diode_THT:D_P600_R-6_P7.62mm_Vertical_KathodeUp +Diode_THT:D_T-1_P10.16mm_Horizontal +Diode_THT:D_T-1_P12.70mm_Horizontal +Diode_THT:D_T-1_P2.54mm_Vertical_AnodeUp +Diode_THT:D_T-1_P2.54mm_Vertical_KathodeUp +Diode_THT:D_T-1_P5.08mm_Horizontal +Display:Adafruit_SSD1306 +Display:Adafruit_SSD1306_No_Mounting_Holes +Display:AG12864E +Display:CR2013-MI2120 +Display:EA-eDIP128B-XXX +Display:EA_DOGL128-6 +Display:EA_DOGM128-6 +Display:EA_DOGS104X-A +Display:EA_DOGXL160-7 +Display:EA_DOGXL160-7_Backlight +Display:EA_eDIP160-XXX +Display:EA_eDIP240-XXX +Display:EA_eDIP320X-XXX +Display:EA_eDIPTFT32-XXX +Display:EA_eDIPTFT43-ATC +Display:EA_eDIPTFT43-XXX +Display:EA_eDIPTFT57-XXX +Display:EA_eDIPTFT70-ATC +Display:EA_eDIPTFT70-XXX +Display:EA_T123X-I2C +Display:ER-OLED0.42-1W_Folded +Display:ERM19264 +Display:HDSM-441B_HDSM-443B +Display:HDSM-541B_HDSM-543B +Display:HDSP-4830 +Display:HDSP-4832 +Display:HDSP-4836 +Display:HDSP-4840 +Display:HDSP-4850 +Display:HDSP-48xx +Display:HLCP-J100 +Display:HY1602E +Display:LCD-016N002L +Display:LM16255 +Display:NHD-0420H1Z +Display:NHD-C0220BiZ-FSRGB +Display:NHD-C0220BiZ +Display:NHD-C12832A1Z-FSRGB +Display:OLED-128O064D +Display:RC1602A +Display:WC1602A +Display_7Segment:7SEGMENT-LED__HDSM531_HDSM533_SMD +Display_7Segment:7SegmentLED_LTS6760_LTS6780 +Display_7Segment:AD-121F2 +Display_7Segment:AFF_2x7SEG-DIGIT_10mm +Display_7Segment:CA56-12CGKWA +Display_7Segment:CA56-12EWA +Display_7Segment:CA56-12SEKWA +Display_7Segment:CA56-12SRWA +Display_7Segment:CA56-12SURKWA +Display_7Segment:CA56-12SYKWA +Display_7Segment:CC56-12GWA +Display_7Segment:CC56-12YWA +Display_7Segment:D1X8K +Display_7Segment:DA04-11CGKWA +Display_7Segment:DA04-11SEKWA +Display_7Segment:DA04-11SURKWA +Display_7Segment:DA04-11SYKWA +Display_7Segment:DA56-11CGKWA +Display_7Segment:DA56-11SEKWA +Display_7Segment:DA56-11SURKWA +Display_7Segment:DA56-11SYKWA +Display_7Segment:DE113-XX-XX +Display_7Segment:DE114-RS-20 +Display_7Segment:DE119-XX-XX +Display_7Segment:DE122-XX-XX +Display_7Segment:DE152-XX-XX +Display_7Segment:DE170-XX-XX +Display_7Segment:ELD_426XXXX +Display_7Segment:HDSP-7401 +Display_7Segment:HDSP-7507 +Display_7Segment:HDSP-7801 +Display_7Segment:HDSP-7807 +Display_7Segment:HDSP-A151 +Display_7Segment:HDSP-A401 +Display_7Segment:KCSC02-105 +Display_7Segment:KCSC02-106 +Display_7Segment:KCSC02-107 +Display_7Segment:KCSC02-123 +Display_7Segment:KCSC02-136 +Display_7Segment:LTC-4627Jx +Display_7Segment:MAN3410A +Display_7Segment:MAN3420A +Display_7Segment:MAN3610A +Display_7Segment:MAN3620A +Display_7Segment:MAN3630A +Display_7Segment:MAN3810A +Display_7Segment:MAN3820A +Display_7Segment:MAN71A +Display_7Segment:MAN72A +Display_7Segment:MAN73A +Display_7Segment:SA15-11xxx +Display_7Segment:SBC18-11SURKCGKWA +Display_7Segment:Sx39-1xxxxx +Ferrite_THT:LairdTech_28C0236-0JW-10 +Fiducial:Fiducial_0.5mm_Mask1.5mm +Fiducial:Fiducial_0.5mm_Mask1mm +Fiducial:Fiducial_0.75mm_Mask1.5mm +Fiducial:Fiducial_0.75mm_Mask2.25mm +Fiducial:Fiducial_1.5mm_Mask3mm +Fiducial:Fiducial_1.5mm_Mask4.5mm +Fiducial:Fiducial_1mm_Mask2mm +Fiducial:Fiducial_1mm_Mask3mm +Filter:Filter_1109-5_1.1x0.9mm +Filter:Filter_1411-5_1.4x1.1mm +Filter:Filter_Bourns_SRF0905_6.0x9.2mm +Filter:Filter_FILTERCON_1FPxx +Filter:Filter_KEMET_PZB300_24.0x12.5mm_P10.0mm +Filter:Filter_Mini-Circuits_FV1206-1 +Filter:Filter_Mini-Circuits_FV1206-4 +Filter:Filter_Mini-Circuits_FV1206-5 +Filter:Filter_Mini-Circuits_FV1206-6 +Filter:Filter_Mini-Circuits_FV1206-7 +Filter:Filter_Mini-Circuits_FV1206 +Filter:Filter_Murata_BNX025 +Filter:Filter_Murata_BNX025_ThermalVias +Filter:Filter_Murata_SFECF-6 +Filter:Filter_Murata_SFECF-6_HandSoldering +Filter:Filter_SAW-6_3.8x3.8mm +Filter:Filter_SAW-8_3.8x3.8mm +Filter:Filter_SAW_Epcos_DCC6C_3x3mm +Filter:Filter_Schaffner_FN405 +Filter:Filter_Schaffner_FN406 +Fuse:FuseHolder_Blade_ATO_Littelfuse_FLR_178.6165 +Fuse:Fuseholder_Blade_ATO_Littelfuse_Pudenz_2_Pin +Fuse:Fuseholder_Blade_Mini_Keystone_3568 +Fuse:Fuseholder_Clip-5x20mm_Bel_FC-203-22_Lateral_P17.80x5.00mm_D1.17mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Eaton_1A5601-01_Inline_P20.80x6.76mm_D1.70mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Keystone_3512P_Inline_P23.62x7.27mm_D1.02x2.41x1.02x1.57mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Keystone_3512_Inline_P23.62x7.27mm_D1.02x1.57mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Keystone_3517_Inline_P23.11x6.76mm_D1.70mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Keystone_3518P_Inline_P23.11x6.76mm_D2.44x1.70mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Littelfuse_100_Inline_P20.50x4.60mm_D1.30mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Littelfuse_111_Inline_P20.00x5.00mm_D1.05mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Littelfuse_111_Lateral_P18.80x5.00mm_D1.17mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Littelfuse_445-030_Inline_P20.50x5.20mm_D1.30mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Littelfuse_519_Inline_P20.60x5.00mm_D1.00mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Littelfuse_520_Inline_P20.50x5.80mm_D1.30mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Littelfuse_521_Lateral_P17.00x5.00mm_D1.30mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Schurter_CQM_Inline_P20.60x5.00mm_D1.00mm_Horizontal +Fuse:Fuseholder_Clip-5x20mm_Schurter_OG_Lateral_P15.00x5.00mm_D1.3mm_Horizontal +Fuse:Fuseholder_Clip-6.3x32mm_Littelfuse_102071_Inline_P34.70x7.60mm_D2.00mm_Horizontal +Fuse:Fuseholder_Clip-6.3x32mm_Littelfuse_102_122_Inline_P34.21x7.62mm_D1.98mm_Horizontal +Fuse:Fuseholder_Clip-6.3x32mm_Littelfuse_102_Inline_P34.21x7.62mm_D2.54mm_Horizontal +Fuse:Fuseholder_Clip-6.3x32mm_Littelfuse_122_Inline_P34.21x7.62mm_D2.54mm_Horizontal +Fuse:Fuseholder_Cylinder-5x20mm_Bulgin_FX0456_Vertical_Closed +Fuse:Fuseholder_Cylinder-5x20mm_Bulgin_FX0457_Horizontal_Closed +Fuse:Fuseholder_Cylinder-5x20mm_EATON_H15-V-1_Vertical_Closed +Fuse:Fuseholder_Cylinder-5x20mm_EATON_HBV_Vertical_Closed +Fuse:Fuseholder_Cylinder-5x20mm_EATON_HBW_Vertical_Closed +Fuse:Fuseholder_Cylinder-5x20mm_Schurter_0031_8201_Horizontal_Open +Fuse:Fuseholder_Cylinder-5x20mm_Schurter_FAB_0031-355x_Horizontal_Closed +Fuse:Fuseholder_Cylinder-5x20mm_Schurter_FPG4_Vertical_Closed +Fuse:Fuseholder_Cylinder-5x20mm_Schurter_FUP_0031.2510_Horizontal_Closed +Fuse:Fuseholder_Cylinder-5x20mm_Schurter_OGN-SMD_Horizontal_Open +Fuse:Fuseholder_Cylinder-5x20mm_Stelvio-Kontek_PTF78_Horizontal_Open +Fuse:Fuseholder_Cylinder-5x20mm_Wuerth_696103101002-SMD_Horizontal_Open +Fuse:Fuseholder_Cylinder-6.3x32mm_Schurter_0031-8002_Horizontal_Open +Fuse:Fuseholder_Cylinder-6.3x32mm_Schurter_FUP_0031.2520_Horizontal_Closed +Fuse:Fuseholder_Keystone_3555-2 +Fuse:Fuseholder_Littelfuse_100_series_5x20mm +Fuse:Fuseholder_Littelfuse_100_series_5x25mm +Fuse:Fuseholder_Littelfuse_100_series_5x30mm +Fuse:Fuseholder_Littelfuse_445_030_series_5x20mm +Fuse:Fuseholder_Littelfuse_445_030_series_5x25mm +Fuse:Fuseholder_Littelfuse_445_030_series_5x30mm +Fuse:Fuseholder_Littelfuse_Nano2_154x +Fuse:Fuseholder_Littelfuse_Nano2_157x +Fuse:Fuseholder_TR5_Littelfuse_No560_No460 +Fuse:Fuse_0402_1005Metric +Fuse:Fuse_0402_1005Metric_Pad0.77x0.64mm_HandSolder +Fuse:Fuse_0603_1608Metric +Fuse:Fuse_0603_1608Metric_Pad1.05x0.95mm_HandSolder +Fuse:Fuse_0805_2012Metric +Fuse:Fuse_0805_2012Metric_Pad1.15x1.40mm_HandSolder +Fuse:Fuse_1206_3216Metric +Fuse:Fuse_1206_3216Metric_Pad1.42x1.75mm_HandSolder +Fuse:Fuse_1210_3225Metric +Fuse:Fuse_1210_3225Metric_Pad1.42x2.65mm_HandSolder +Fuse:Fuse_1812_4532Metric +Fuse:Fuse_1812_4532Metric_Pad1.30x3.40mm_HandSolder +Fuse:Fuse_2010_5025Metric +Fuse:Fuse_2010_5025Metric_Pad1.52x2.65mm_HandSolder +Fuse:Fuse_2512_6332Metric +Fuse:Fuse_2512_6332Metric_Pad1.52x3.35mm_HandSolder +Fuse:Fuse_2920_7451Metric +Fuse:Fuse_2920_7451Metric_Pad2.10x5.45mm_HandSolder +Fuse:Fuse_BelFuse_0ZRE0005FF_L8.3mm_W3.8mm +Fuse:Fuse_BelFuse_0ZRE0008FF_L8.3mm_W3.8mm +Fuse:Fuse_BelFuse_0ZRE0012FF_L8.3mm_W3.8mm +Fuse:Fuse_BelFuse_0ZRE0016FF_L9.9mm_W3.8mm +Fuse:Fuse_BelFuse_0ZRE0025FF_L9.6mm_W3.8mm +Fuse:Fuse_BelFuse_0ZRE0033FF_L11.4mm_W3.8mm +Fuse:Fuse_BelFuse_0ZRE0040FF_L11.5mm_W3.8mm +Fuse:Fuse_BelFuse_0ZRE0055FF_L14.0mm_W4.1mm +Fuse:Fuse_BelFuse_0ZRE0075FF_L11.5mm_W4.8mm +Fuse:Fuse_BelFuse_0ZRE0100FF_L18.7mm_W5.1mm +Fuse:Fuse_BelFuse_0ZRE0125FF_L21.2mm_W5.3mm +Fuse:Fuse_BelFuse_0ZRE0150FF_L23.4mm_W5.3mm +Fuse:Fuse_BelFuse_0ZRE0200FF_L24.9mm_W6.1mm +Fuse:Fuse_Blade_ATO_directSolder +Fuse:Fuse_Blade_Mini_directSolder +Fuse:Fuse_Bourns_MF-RG1000 +Fuse:Fuse_Bourns_MF-RG1100 +Fuse:Fuse_Bourns_MF-RG300 +Fuse:Fuse_Bourns_MF-RG400 +Fuse:Fuse_Bourns_MF-RG500 +Fuse:Fuse_Bourns_MF-RG600 +Fuse:Fuse_Bourns_MF-RG650 +Fuse:Fuse_Bourns_MF-RG700 +Fuse:Fuse_Bourns_MF-RG800 +Fuse:Fuse_Bourns_MF-RG900 +Fuse:Fuse_Bourns_MF-RHT050 +Fuse:Fuse_Bourns_MF-RHT070 +Fuse:Fuse_Bourns_MF-RHT100 +Fuse:Fuse_Bourns_MF-RHT1000 +Fuse:Fuse_Bourns_MF-RHT1100 +Fuse:Fuse_Bourns_MF-RHT1300 +Fuse:Fuse_Bourns_MF-RHT200 +Fuse:Fuse_Bourns_MF-RHT300 +Fuse:Fuse_Bourns_MF-RHT400 +Fuse:Fuse_Bourns_MF-RHT500 +Fuse:Fuse_Bourns_MF-RHT550 +Fuse:Fuse_Bourns_MF-RHT600 +Fuse:Fuse_Bourns_MF-RHT650 +Fuse:Fuse_Bourns_MF-RHT700 +Fuse:Fuse_Bourns_MF-RHT750 +Fuse:Fuse_Bourns_MF-RHT800 +Fuse:Fuse_Bourns_MF-RHT900 +Fuse:Fuse_Bourns_MF-SM_7.98x5.44mm +Fuse:Fuse_Bourns_MF-SM_9.5x6.71mm +Fuse:Fuse_Bourns_TBU-CA +Fuse:Fuse_Littelfuse-LVR100 +Fuse:Fuse_Littelfuse-LVR125 +Fuse:Fuse_Littelfuse-LVR200 +Fuse:Fuse_Littelfuse-NANO2-451_453 +Fuse:Fuse_Littelfuse-NANO2-462 +Fuse:Fuse_Littelfuse-NANO2-885 +Fuse:Fuse_Littelfuse_372_D8.50mm +Fuse:Fuse_Littelfuse_395Series +Fuse:Fuse_Schurter_UMT250 +Fuse:Fuse_Schurter_UMZ250 +Fuse:Fuse_SunFuse-6HP +Heatsink:Heatsink_125x35x50mm_3xFixationM3 +Heatsink:Heatsink_35x26mm_1xFixation3mm_Fischer-SK486-35 +Heatsink:Heatsink_38x38mm_SpringFixation +Heatsink:Heatsink_62x40mm_2xFixation3mm +Heatsink:Heatsink_AAVID_573300D00010G_TO-263 +Heatsink:Heatsink_AAVID_576802B03900G +Heatsink:Heatsink_AAVID_590302B03600G +Heatsink:Heatsink_AAVID_TV5G_TO220_Horizontal +Heatsink:Heatsink_Fischer_FK224xx2201_25x8.3mm +Heatsink:Heatsink_Fischer_FK24413D2PAK_26x13mm +Heatsink:Heatsink_Fischer_FK24413DPAK_23x13mm +Heatsink:Heatsink_Fischer_SK104-STC-STIC_35x13mm_2xDrill2.5mm +Heatsink:Heatsink_Fischer_SK104-STCB_35x13mm__2xDrill3.5mm_ScrewM3 +Heatsink:Heatsink_Fischer_SK129-STS_42x25mm_2xDrill2.5mm +Heatsink:Heatsink_SheetType_50x7mm_2Fixations +Heatsink:Heatsink_Stonecold_HS-130_30x12mm_2xFixation2.5mm +Heatsink:Heatsink_Stonecold_HS-132_32x14mm_2xFixation1.5mm +Heatsink:Heatsink_Stonecold_HS-S01_13.21x6.35mm +Heatsink:Heatsink_Stonecold_HS-S02_13.21x9.53mm +Heatsink:Heatsink_Stonecold_HS-S03_13.21x12.7mm +Inductor_SMD:L_01005_0402Metric +Inductor_SMD:L_01005_0402Metric_Pad0.57x0.30mm_HandSolder +Inductor_SMD:L_0201_0603Metric +Inductor_SMD:L_0201_0603Metric_Pad0.64x0.40mm_HandSolder +Inductor_SMD:L_0402_1005Metric +Inductor_SMD:L_0402_1005Metric_Pad0.77x0.64mm_HandSolder +Inductor_SMD:L_0603_1608Metric +Inductor_SMD:L_0603_1608Metric_Pad1.05x0.95mm_HandSolder +Inductor_SMD:L_0805_2012Metric +Inductor_SMD:L_0805_2012Metric_Pad1.05x1.20mm_HandSolder +Inductor_SMD:L_0805_2012Metric_Pad1.15x1.40mm_HandSolder +Inductor_SMD:L_10.4x10.4_H4.8 +Inductor_SMD:L_1008_2520Metric +Inductor_SMD:L_1008_2520Metric_Pad1.43x2.20mm_HandSolder +Inductor_SMD:L_1206_3216Metric +Inductor_SMD:L_1206_3216Metric_Pad1.22x1.90mm_HandSolder +Inductor_SMD:L_1206_3216Metric_Pad1.42x1.75mm_HandSolder +Inductor_SMD:L_1210_3225Metric +Inductor_SMD:L_1210_3225Metric_Pad1.42x2.65mm_HandSolder +Inductor_SMD:L_12x12mm_H4.5mm +Inductor_SMD:L_12x12mm_H6mm +Inductor_SMD:L_12x12mm_H8mm +Inductor_SMD:L_1806_4516Metric +Inductor_SMD:L_1806_4516Metric_Pad1.45x1.90mm_HandSolder +Inductor_SMD:L_1812_4532Metric +Inductor_SMD:L_1812_4532Metric_Pad1.30x3.40mm_HandSolder +Inductor_SMD:L_2010_5025Metric +Inductor_SMD:L_2010_5025Metric_Pad1.52x2.65mm_HandSolder +Inductor_SMD:L_2512_6332Metric +Inductor_SMD:L_2512_6332Metric_Pad1.52x3.35mm_HandSolder +Inductor_SMD:L_6.3x6.3_H3 +Inductor_SMD:L_7.3x7.3_H3.5 +Inductor_SMD:L_7.3x7.3_H4.5 +Inductor_SMD:L_Abracon_ASPI-0425 +Inductor_SMD:L_Abracon_ASPI-0630LR +Inductor_SMD:L_Abracon_ASPI-3012S +Inductor_SMD:L_Abracon_ASPI-4030S +Inductor_SMD:L_Abracon_ASPIAIG-F4020 +Inductor_SMD:L_APV_ANR252010 +Inductor_SMD:L_APV_ANR252012 +Inductor_SMD:L_APV_ANR3010 +Inductor_SMD:L_APV_ANR3012 +Inductor_SMD:L_APV_ANR3015 +Inductor_SMD:L_APV_ANR4010 +Inductor_SMD:L_APV_ANR4012 +Inductor_SMD:L_APV_ANR4018 +Inductor_SMD:L_APV_ANR4020 +Inductor_SMD:L_APV_ANR4026 +Inductor_SMD:L_APV_ANR4030 +Inductor_SMD:L_APV_ANR5012 +Inductor_SMD:L_APV_ANR5020 +Inductor_SMD:L_APV_ANR5040 +Inductor_SMD:L_APV_ANR5045 +Inductor_SMD:L_APV_ANR6020 +Inductor_SMD:L_APV_ANR6028 +Inductor_SMD:L_APV_ANR6045 +Inductor_SMD:L_APV_ANR8040 +Inductor_SMD:L_APV_ANR8065 +Inductor_SMD:L_APV_APH0412 +Inductor_SMD:L_APV_APH0420 +Inductor_SMD:L_APV_APH0518 +Inductor_SMD:L_APV_APH0530 +Inductor_SMD:L_APV_APH0615 +Inductor_SMD:L_APV_APH0618 +Inductor_SMD:L_APV_APH0620 +Inductor_SMD:L_APV_APH0624 +Inductor_SMD:L_APV_APH0630 +Inductor_SMD:L_APV_APH0640 +Inductor_SMD:L_APV_APH0650 +Inductor_SMD:L_APV_APH0840 +Inductor_SMD:L_APV_APH0850 +Inductor_SMD:L_APV_APH1030 +Inductor_SMD:L_APV_APH1040 +Inductor_SMD:L_APV_APH1050 +Inductor_SMD:L_APV_APH1240 +Inductor_SMD:L_APV_APH1250 +Inductor_SMD:L_APV_APH1260 +Inductor_SMD:L_APV_APH1265 +Inductor_SMD:L_APV_APH1770 +Inductor_SMD:L_APV_APH2213 +Inductor_SMD:L_AVX_LMLP07A7 +Inductor_SMD:L_Bourns-SRN1060 +Inductor_SMD:L_Bourns-SRN4018 +Inductor_SMD:L_Bourns-SRN6028 +Inductor_SMD:L_Bourns-SRN8040_8x8.15mm +Inductor_SMD:L_Bourns-SRR1005 +Inductor_SMD:L_Bourns-SRU1028_10.0x10.0mm +Inductor_SMD:L_Bourns-SRU8028_8.0x8.0mm +Inductor_SMD:L_Bourns-SRU8043 +Inductor_SMD:L_Bourns_SDR0604 +Inductor_SMD:L_Bourns_SDR1806 +Inductor_SMD:L_Bourns_SRF1260 +Inductor_SMD:L_Bourns_SRN6045TA +Inductor_SMD:L_Bourns_SRN8040TA +Inductor_SMD:L_Bourns_SRP1038C_10.0x10.0mm +Inductor_SMD:L_Bourns_SRP1050WA +Inductor_SMD:L_Bourns_SRP1245A +Inductor_SMD:L_Bourns_SRP1770TA_16.9x16.9mm +Inductor_SMD:L_Bourns_SRP2313AA +Inductor_SMD:L_Bourns_SRP5030T +Inductor_SMD:L_Bourns_SRP6060FA +Inductor_SMD:L_Bourns_SRP7028A_7.3x6.6mm +Inductor_SMD:L_Bourns_SRR1208_12.7x12.7mm +Inductor_SMD:L_Bourns_SRR1210A +Inductor_SMD:L_Bourns_SRR1260 +Inductor_SMD:L_Bourns_SRU5016_5.2x5.2mm +Inductor_SMD:L_Cenker_CKCS201610 +Inductor_SMD:L_Cenker_CKCS252010 +Inductor_SMD:L_Cenker_CKCS252012 +Inductor_SMD:L_Cenker_CKCS3012 +Inductor_SMD:L_Cenker_CKCS3015 +Inductor_SMD:L_Cenker_CKCS4018 +Inductor_SMD:L_Cenker_CKCS4020 +Inductor_SMD:L_Cenker_CKCS4030 +Inductor_SMD:L_Cenker_CKCS5020 +Inductor_SMD:L_Cenker_CKCS5040 +Inductor_SMD:L_Cenker_CKCS6020 +Inductor_SMD:L_Cenker_CKCS6028 +Inductor_SMD:L_Cenker_CKCS6045 +Inductor_SMD:L_Cenker_CKCS8040 +Inductor_SMD:L_Cenker_CKCS8060 +Inductor_SMD:L_Cenker_CKCS8080 +Inductor_SMD:L_Changjiang_FNR252010S +Inductor_SMD:L_Changjiang_FNR252012S +Inductor_SMD:L_Changjiang_FNR3010S +Inductor_SMD:L_Changjiang_FNR3012S +Inductor_SMD:L_Changjiang_FNR3015S +Inductor_SMD:L_Changjiang_FNR3021S +Inductor_SMD:L_Changjiang_FNR4010S +Inductor_SMD:L_Changjiang_FNR4012S +Inductor_SMD:L_Changjiang_FNR4015S +Inductor_SMD:L_Changjiang_FNR4018S +Inductor_SMD:L_Changjiang_FNR4020S +Inductor_SMD:L_Changjiang_FNR4026S +Inductor_SMD:L_Changjiang_FNR4030S +Inductor_SMD:L_Changjiang_FNR5012S +Inductor_SMD:L_Changjiang_FNR5015S +Inductor_SMD:L_Changjiang_FNR5020S +Inductor_SMD:L_Changjiang_FNR5030S +Inductor_SMD:L_Changjiang_FNR5040S +Inductor_SMD:L_Changjiang_FNR5045S +Inductor_SMD:L_Changjiang_FNR6020S +Inductor_SMD:L_Changjiang_FNR6028S +Inductor_SMD:L_Changjiang_FNR6040S +Inductor_SMD:L_Changjiang_FNR6045S +Inductor_SMD:L_Changjiang_FNR8040S +Inductor_SMD:L_Changjiang_FNR8050S +Inductor_SMD:L_Changjiang_FNR8065S +Inductor_SMD:L_Changjiang_FXL0412 +Inductor_SMD:L_Changjiang_FXL0420 +Inductor_SMD:L_Changjiang_FXL0518 +Inductor_SMD:L_Changjiang_FXL0530 +Inductor_SMD:L_Changjiang_FXL0615 +Inductor_SMD:L_Changjiang_FXL0618 +Inductor_SMD:L_Changjiang_FXL0624 +Inductor_SMD:L_Changjiang_FXL0630 +Inductor_SMD:L_Changjiang_FXL0640 +Inductor_SMD:L_Changjiang_FXL0650 +Inductor_SMD:L_Changjiang_FXL0840 +Inductor_SMD:L_Changjiang_FXL1030 +Inductor_SMD:L_Changjiang_FXL1040 +Inductor_SMD:L_Changjiang_FXL1050 +Inductor_SMD:L_Changjiang_FXL1340 +Inductor_SMD:L_Changjiang_FXL1350 +Inductor_SMD:L_Changjiang_FXL1360 +Inductor_SMD:L_Changjiang_FXL1365 +Inductor_SMD:L_Changjiang_FXL1770 +Inductor_SMD:L_Changjiang_FXL2213 +Inductor_SMD:L_Chilisin_BMRA00040415 +Inductor_SMD:L_Chilisin_BMRA00040420 +Inductor_SMD:L_Chilisin_BMRA00050520 +Inductor_SMD:L_Chilisin_BMRA00050530 +Inductor_SMD:L_Chilisin_BMRB00050512 +Inductor_SMD:L_Chilisin_BMRB00050518-B +Inductor_SMD:L_Chilisin_BMRB00050518 +Inductor_SMD:L_Chilisin_BMRB00060612 +Inductor_SMD:L_Chilisin_BMRB00060618 +Inductor_SMD:L_Chilisin_BMRB00060624 +Inductor_SMD:L_Chilisin_BMRB00060650 +Inductor_SMD:L_Chilisin_BMRF00101040 +Inductor_SMD:L_Chilisin_BMRF00131350 +Inductor_SMD:L_Chilisin_BMRF00131360 +Inductor_SMD:L_Chilisin_BMRF00171770 +Inductor_SMD:L_Chilisin_BMRG00101030 +Inductor_SMD:L_Chilisin_BMRG00131360 +Inductor_SMD:L_Chilisin_BMRx00040412 +Inductor_SMD:L_Chilisin_BMRx00050512-B +Inductor_SMD:L_Chilisin_BMRx00050515 +Inductor_SMD:L_Chilisin_BMRx00060615 +Inductor_SMD:L_Chilisin_BMRx00060630 +Inductor_SMD:L_Coilcraft_1515SQ-47N +Inductor_SMD:L_Coilcraft_1515SQ-68N +Inductor_SMD:L_Coilcraft_1515SQ-82N +Inductor_SMD:L_Coilcraft_2222SQ-111 +Inductor_SMD:L_Coilcraft_2222SQ-131 +Inductor_SMD:L_Coilcraft_2222SQ-161 +Inductor_SMD:L_Coilcraft_2222SQ-181 +Inductor_SMD:L_Coilcraft_2222SQ-221 +Inductor_SMD:L_Coilcraft_2222SQ-271 +Inductor_SMD:L_Coilcraft_2222SQ-301 +Inductor_SMD:L_Coilcraft_2222SQ-90N +Inductor_SMD:L_Coilcraft_2929SQ-331 +Inductor_SMD:L_Coilcraft_2929SQ-361 +Inductor_SMD:L_Coilcraft_2929SQ-391 +Inductor_SMD:L_Coilcraft_2929SQ-431 +Inductor_SMD:L_Coilcraft_2929SQ-501 +Inductor_SMD:L_Coilcraft_LPS3010 +Inductor_SMD:L_Coilcraft_LPS3314 +Inductor_SMD:L_Coilcraft_LPS4018 +Inductor_SMD:L_Coilcraft_LPS4414 +Inductor_SMD:L_Coilcraft_LPS5030 +Inductor_SMD:L_Coilcraft_MOS6020-XXX +Inductor_SMD:L_Coilcraft_MSS1038-XXX +Inductor_SMD:L_Coilcraft_MSS1038T-XXX +Inductor_SMD:L_Coilcraft_MSS1048-XXX +Inductor_SMD:L_Coilcraft_MSS1048T-XXX +Inductor_SMD:L_Coilcraft_MSS1210-XXX +Inductor_SMD:L_Coilcraft_MSS1210H-XXX +Inductor_SMD:L_Coilcraft_MSS1246-XXX +Inductor_SMD:L_Coilcraft_MSS1246H-XXX +Inductor_SMD:L_Coilcraft_MSS1246T-XXX +Inductor_SMD:L_Coilcraft_MSS1260-XXX +Inductor_SMD:L_Coilcraft_MSS1260H-XXX +Inductor_SMD:L_Coilcraft_MSS1260T-XXX +Inductor_SMD:L_Coilcraft_MSS1278-XXX +Inductor_SMD:L_Coilcraft_MSS1278H-XXX +Inductor_SMD:L_Coilcraft_MSS1278T-XXX +Inductor_SMD:L_Coilcraft_MSS1514V-XXX +Inductor_SMD:L_Coilcraft_MSS1583-XXX +Inductor_SMD:L_Coilcraft_MSS1812T-XXX +Inductor_SMD:L_Coilcraft_MSS7348-XXX +Inductor_SMD:L_Coilcraft_XAL1010-XXX +Inductor_SMD:L_Coilcraft_XAL1030-XXX +Inductor_SMD:L_Coilcraft_XAL1060-XXX +Inductor_SMD:L_Coilcraft_XAL1350-XXX +Inductor_SMD:L_Coilcraft_XAL1510-103 +Inductor_SMD:L_Coilcraft_XAL1510-153 +Inductor_SMD:L_Coilcraft_XAL1510-223 +Inductor_SMD:L_Coilcraft_XAL1510-333 +Inductor_SMD:L_Coilcraft_XAL1510-472 +Inductor_SMD:L_Coilcraft_XAL1510-682 +Inductor_SMD:L_Coilcraft_XAL1510-822 +Inductor_SMD:L_Coilcraft_XAL1513-153 +Inductor_SMD:L_Coilcraft_XAL1580-102 +Inductor_SMD:L_Coilcraft_XAL1580-132 +Inductor_SMD:L_Coilcraft_XAL1580-182 +Inductor_SMD:L_Coilcraft_XAL1580-202 +Inductor_SMD:L_Coilcraft_XAL1580-302 +Inductor_SMD:L_Coilcraft_XAL1580-401 +Inductor_SMD:L_Coilcraft_XAL1580-452 +Inductor_SMD:L_Coilcraft_XAL1580-532 +Inductor_SMD:L_Coilcraft_XAL1580-612 +Inductor_SMD:L_Coilcraft_XAL1580-741 +Inductor_SMD:L_Coilcraft_XAL4020-XXX +Inductor_SMD:L_Coilcraft_XAL4030-XXX +Inductor_SMD:L_Coilcraft_XAL4040-XXX +Inductor_SMD:L_Coilcraft_XAL5020-XXX +Inductor_SMD:L_Coilcraft_XAL5030-XXX +Inductor_SMD:L_Coilcraft_XAL5050-XXX +Inductor_SMD:L_Coilcraft_XAL6020-XXX +Inductor_SMD:L_Coilcraft_XAL6030-XXX +Inductor_SMD:L_Coilcraft_XAL6060-XXX +Inductor_SMD:L_Coilcraft_XAL7020-102 +Inductor_SMD:L_Coilcraft_XAL7020-122 +Inductor_SMD:L_Coilcraft_XAL7020-151 +Inductor_SMD:L_Coilcraft_XAL7020-152 +Inductor_SMD:L_Coilcraft_XAL7020-222 +Inductor_SMD:L_Coilcraft_XAL7020-271 +Inductor_SMD:L_Coilcraft_XAL7020-331 +Inductor_SMD:L_Coilcraft_XAL7020-471 +Inductor_SMD:L_Coilcraft_XAL7020-681 +Inductor_SMD:L_Coilcraft_XAL7030-102 +Inductor_SMD:L_Coilcraft_XAL7030-103 +Inductor_SMD:L_Coilcraft_XAL7030-152 +Inductor_SMD:L_Coilcraft_XAL7030-161 +Inductor_SMD:L_Coilcraft_XAL7030-222 +Inductor_SMD:L_Coilcraft_XAL7030-272 +Inductor_SMD:L_Coilcraft_XAL7030-301 +Inductor_SMD:L_Coilcraft_XAL7030-332 +Inductor_SMD:L_Coilcraft_XAL7030-472 +Inductor_SMD:L_Coilcraft_XAL7030-562 +Inductor_SMD:L_Coilcraft_XAL7030-601 +Inductor_SMD:L_Coilcraft_XAL7030-682 +Inductor_SMD:L_Coilcraft_XAL7030-822 +Inductor_SMD:L_Coilcraft_XAL7050-XXX +Inductor_SMD:L_Coilcraft_XAL7070-XXX +Inductor_SMD:L_Coilcraft_XAL8050-223 +Inductor_SMD:L_Coilcraft_XAL8080-XXX +Inductor_SMD:L_Coilcraft_XFL2010 +Inductor_SMD:L_Coilcraft_XxL4020 +Inductor_SMD:L_Coilcraft_XxL4030 +Inductor_SMD:L_Coilcraft_XxL4040 +Inductor_SMD:L_CommonModeChoke_Coilcraft_0603USB +Inductor_SMD:L_CommonModeChoke_Coilcraft_0805USB +Inductor_SMD:L_CommonModeChoke_Coilcraft_1812CAN +Inductor_SMD:L_CommonModeChoke_Murata_DLW5BTMxxxSQ2x_5x5mm +Inductor_SMD:L_CommonModeChoke_TDK_ACM2520-2P +Inductor_SMD:L_CommonModeChoke_TDK_ACM2520-3P +Inductor_SMD:L_CommonModeChoke_TDK_ACM7060 +Inductor_SMD:L_CommonModeChoke_Wuerth_WE-SL5 +Inductor_SMD:L_CommonMode_Delevan_4222 +Inductor_SMD:L_CommonMode_Wuerth_WE-SL2 +Inductor_SMD:L_CommonMode_Wurth_WE-CNSW-1206 +Inductor_SMD:L_Eaton_MCL2012V1 +Inductor_SMD:L_Fastron_PISN +Inductor_SMD:L_Fastron_PISN_Handsoldering +Inductor_SMD:L_Fastron_PISR +Inductor_SMD:L_Fastron_PISR_Handsoldering +Inductor_SMD:L_Ferrocore_DLG-0302 +Inductor_SMD:L_Ferrocore_DLG-0403 +Inductor_SMD:L_Ferrocore_DLG-0504 +Inductor_SMD:L_Ferrocore_DLG-0703 +Inductor_SMD:L_Ferrocore_DLG-0705 +Inductor_SMD:L_Ferrocore_DLG-1004 +Inductor_SMD:L_Ferrocore_DLG-1005 +Inductor_SMD:L_KOHERelec_MDA5030 +Inductor_SMD:L_KOHERelec_MDA7030 +Inductor_SMD:L_Murata_DEM35xxC +Inductor_SMD:L_Murata_DFE201610P +Inductor_SMD:L_Murata_LQH2MCNxxxx02_2.0x1.6mm +Inductor_SMD:L_Murata_LQH55DN_5.7x5.0mm +Inductor_SMD:L_Neosid_Air-Coil_SML_1turn_HDM0131A +Inductor_SMD:L_Neosid_Air-Coil_SML_2turn_HAM0231A +Inductor_SMD:L_Neosid_Air-Coil_SML_2turn_HDM0231A +Inductor_SMD:L_Neosid_Air-Coil_SML_3turn_HAM0331A +Inductor_SMD:L_Neosid_Air-Coil_SML_3turn_HDM0331A +Inductor_SMD:L_Neosid_Air-Coil_SML_4turn_HAM0431A +Inductor_SMD:L_Neosid_Air-Coil_SML_4turn_HDM0431A +Inductor_SMD:L_Neosid_Air-Coil_SML_5turn_HAM0531A +Inductor_SMD:L_Neosid_Air-Coil_SML_5turn_HDM0531A +Inductor_SMD:L_Neosid_Air-Coil_SML_6-10turn_HAM0631A-HAM1031A +Inductor_SMD:L_Neosid_Air-Coil_SML_6-10turn_HDM0431A-HDM1031A +Inductor_SMD:L_Neosid_Air-Coil_SML_6turn_HAM0631A +Inductor_SMD:L_Neosid_MicroCoil_Ms36-L +Inductor_SMD:L_Neosid_Ms42 +Inductor_SMD:L_Neosid_Ms50 +Inductor_SMD:L_Neosid_Ms50T +Inductor_SMD:L_Neosid_Ms85 +Inductor_SMD:L_Neosid_Ms85T +Inductor_SMD:L_Neosid_Ms95 +Inductor_SMD:L_Neosid_Ms95a +Inductor_SMD:L_Neosid_Ms95T +Inductor_SMD:L_Neosid_SM-NE127 +Inductor_SMD:L_Neosid_SM-NE127_HandSoldering +Inductor_SMD:L_Neosid_SM-NE150 +Inductor_SMD:L_Neosid_SM-NE95H +Inductor_SMD:L_Neosid_SM-PIC0512H +Inductor_SMD:L_Neosid_SM-PIC0602H +Inductor_SMD:L_Neosid_SM-PIC0612H +Inductor_SMD:L_Neosid_SM-PIC1004H +Inductor_SMD:L_Neosid_SMS-ME3010 +Inductor_SMD:L_Neosid_SMS-ME3015 +Inductor_SMD:L_Neosid_SMs42 +Inductor_SMD:L_Neosid_SMs50 +Inductor_SMD:L_Neosid_SMs85 +Inductor_SMD:L_Neosid_SMs95_SMs95p +Inductor_SMD:L_Pulse_P059x +Inductor_SMD:L_Pulse_PA4320 +Inductor_SMD:L_Pulse_PA4332 +Inductor_SMD:L_Pulse_PA4340 +Inductor_SMD:L_Pulse_PA4341 +Inductor_SMD:L_Pulse_PA4344 +Inductor_SMD:L_Pulse_PA4349 +Inductor_SMD:L_Pulse_PA5402 +Inductor_SMD:L_Sagami_CER1242B +Inductor_SMD:L_Sagami_CER1257B +Inductor_SMD:L_Sagami_CER1277B +Inductor_SMD:L_Sagami_CWR1242C +Inductor_SMD:L_Sagami_CWR1257C +Inductor_SMD:L_Sagami_CWR1277C +Inductor_SMD:L_SigTra_SC3316F +Inductor_SMD:L_SOREDE_SNR.1050_10x10x5mm +Inductor_SMD:L_Sumida_CDMC6D28_7.25x6.5mm +Inductor_SMD:L_Sumida_CR75 +Inductor_SMD:L_Sunlord_MWSA0402S +Inductor_SMD:L_Sunlord_MWSA0412S +Inductor_SMD:L_Sunlord_MWSA0503S +Inductor_SMD:L_Sunlord_MWSA0518S +Inductor_SMD:L_Sunlord_MWSA0602S +Inductor_SMD:L_Sunlord_MWSA0603S +Inductor_SMD:L_Sunlord_MWSA0604S +Inductor_SMD:L_Sunlord_MWSA0605S +Inductor_SMD:L_Sunlord_MWSA0615S +Inductor_SMD:L_Sunlord_MWSA0618S +Inductor_SMD:L_Sunlord_MWSA0624S +Inductor_SMD:L_Sunlord_MWSA0804S +Inductor_SMD:L_Sunlord_MWSA1003S +Inductor_SMD:L_Sunlord_MWSA1004S +Inductor_SMD:L_Sunlord_MWSA1005S +Inductor_SMD:L_Sunlord_MWSA1204S-100 +Inductor_SMD:L_Sunlord_MWSA1204S-150 +Inductor_SMD:L_Sunlord_MWSA1204S-1R0 +Inductor_SMD:L_Sunlord_MWSA1204S-1R5 +Inductor_SMD:L_Sunlord_MWSA1204S-220 +Inductor_SMD:L_Sunlord_MWSA1204S-2R2 +Inductor_SMD:L_Sunlord_MWSA1204S-3R3 +Inductor_SMD:L_Sunlord_MWSA1204S-4R7 +Inductor_SMD:L_Sunlord_MWSA1204S-6R8 +Inductor_SMD:L_Sunlord_MWSA1204S-R22 +Inductor_SMD:L_Sunlord_MWSA1204S-R47 +Inductor_SMD:L_Sunlord_MWSA1204S-R68 +Inductor_SMD:L_Sunlord_MWSA1204S-R82 +Inductor_SMD:L_Sunlord_MWSA1205S-100 +Inductor_SMD:L_Sunlord_MWSA1205S-150 +Inductor_SMD:L_Sunlord_MWSA1205S-1R0 +Inductor_SMD:L_Sunlord_MWSA1205S-1R5 +Inductor_SMD:L_Sunlord_MWSA1205S-220 +Inductor_SMD:L_Sunlord_MWSA1205S-2R2 +Inductor_SMD:L_Sunlord_MWSA1205S-330 +Inductor_SMD:L_Sunlord_MWSA1205S-3R3 +Inductor_SMD:L_Sunlord_MWSA1205S-470 +Inductor_SMD:L_Sunlord_MWSA1205S-4R7 +Inductor_SMD:L_Sunlord_MWSA1205S-6R8 +Inductor_SMD:L_Sunlord_MWSA1205S-R22 +Inductor_SMD:L_Sunlord_MWSA1205S-R36 +Inductor_SMD:L_Sunlord_MWSA1205S-R50 +Inductor_SMD:L_Sunlord_MWSA1205S-R68 +Inductor_SMD:L_Sunlord_MWSA1205S-R82 +Inductor_SMD:L_Sunlord_MWSA1206S-100 +Inductor_SMD:L_Sunlord_MWSA1206S-101 +Inductor_SMD:L_Sunlord_MWSA1206S-120 +Inductor_SMD:L_Sunlord_MWSA1206S-121 +Inductor_SMD:L_Sunlord_MWSA1206S-150 +Inductor_SMD:L_Sunlord_MWSA1206S-151 +Inductor_SMD:L_Sunlord_MWSA1206S-180 +Inductor_SMD:L_Sunlord_MWSA1206S-1R5 +Inductor_SMD:L_Sunlord_MWSA1206S-220 +Inductor_SMD:L_Sunlord_MWSA1206S-270 +Inductor_SMD:L_Sunlord_MWSA1206S-2R2 +Inductor_SMD:L_Sunlord_MWSA1206S-330 +Inductor_SMD:L_Sunlord_MWSA1206S-3R3 +Inductor_SMD:L_Sunlord_MWSA1206S-470 +Inductor_SMD:L_Sunlord_MWSA1206S-4R7 +Inductor_SMD:L_Sunlord_MWSA1206S-5R6 +Inductor_SMD:L_Sunlord_MWSA1206S-680 +Inductor_SMD:L_Sunlord_MWSA1206S-6R8 +Inductor_SMD:L_Sunlord_MWSA1206S-8R2 +Inductor_SMD:L_Sunlord_MWSA1206S-R68 +Inductor_SMD:L_Sunlord_MWSA1265S +Inductor_SMD:L_Sunlord_MWSA1707S +Inductor_SMD:L_Sunlord_MWSA2213S +Inductor_SMD:L_Sunlord_SWPA252010S +Inductor_SMD:L_Sunlord_SWPA252012S +Inductor_SMD:L_Sunlord_SWPA3010S +Inductor_SMD:L_Sunlord_SWPA3012S +Inductor_SMD:L_Sunlord_SWPA3015S +Inductor_SMD:L_Sunlord_SWPA4010S +Inductor_SMD:L_Sunlord_SWPA4012S +Inductor_SMD:L_Sunlord_SWPA4018S +Inductor_SMD:L_Sunlord_SWPA4020S +Inductor_SMD:L_Sunlord_SWPA4026S +Inductor_SMD:L_Sunlord_SWPA4030S +Inductor_SMD:L_Sunlord_SWPA5012S +Inductor_SMD:L_Sunlord_SWPA5020S +Inductor_SMD:L_Sunlord_SWPA5040S +Inductor_SMD:L_Sunlord_SWPA6020S +Inductor_SMD:L_Sunlord_SWPA6028S +Inductor_SMD:L_Sunlord_SWPA6040S +Inductor_SMD:L_Sunlord_SWPA6045S +Inductor_SMD:L_Sunlord_SWPA8040S +Inductor_SMD:L_Sunlord_SWRB1204S +Inductor_SMD:L_Sunlord_SWRB1205S +Inductor_SMD:L_Sunlord_SWRB1207S +Inductor_SMD:L_SXN_SMDRI124 +Inductor_SMD:L_SXN_SMDRI125 +Inductor_SMD:L_SXN_SMDRI127 +Inductor_SMD:L_SXN_SMDRI62 +Inductor_SMD:L_SXN_SMDRI64 +Inductor_SMD:L_SXN_SMDRI73 +Inductor_SMD:L_SXN_SMDRI74 +Inductor_SMD:L_TaiTech_TMPC1265_13.5x12.5mm +Inductor_SMD:L_Taiyo-Yuden_BK_Array_1206_3216Metric +Inductor_SMD:L_Taiyo-Yuden_MD-1616 +Inductor_SMD:L_Taiyo-Yuden_MD-2020 +Inductor_SMD:L_Taiyo-Yuden_MD-3030 +Inductor_SMD:L_Taiyo-Yuden_MD-4040 +Inductor_SMD:L_Taiyo-Yuden_MD-5050 +Inductor_SMD:L_Taiyo-Yuden_NR-10050_9.8x10.0mm +Inductor_SMD:L_Taiyo-Yuden_NR-10050_9.8x10.0mm_HandSoldering +Inductor_SMD:L_Taiyo-Yuden_NR-20xx +Inductor_SMD:L_Taiyo-Yuden_NR-20xx_HandSoldering +Inductor_SMD:L_Taiyo-Yuden_NR-24xx +Inductor_SMD:L_Taiyo-Yuden_NR-24xx_HandSoldering +Inductor_SMD:L_Taiyo-Yuden_NR-30xx +Inductor_SMD:L_Taiyo-Yuden_NR-30xx_HandSoldering +Inductor_SMD:L_Taiyo-Yuden_NR-40xx +Inductor_SMD:L_Taiyo-Yuden_NR-40xx_HandSoldering +Inductor_SMD:L_Taiyo-Yuden_NR-50xx +Inductor_SMD:L_Taiyo-Yuden_NR-50xx_HandSoldering +Inductor_SMD:L_Taiyo-Yuden_NR-60xx +Inductor_SMD:L_Taiyo-Yuden_NR-60xx_HandSoldering +Inductor_SMD:L_Taiyo-Yuden_NR-80xx +Inductor_SMD:L_Taiyo-Yuden_NR-80xx_HandSoldering +Inductor_SMD:L_TDK_MLZ1608 +Inductor_SMD:L_TDK_MLZ2012_h0.85mm +Inductor_SMD:L_TDK_MLZ2012_h1.25mm +Inductor_SMD:L_TDK_NLV25_2.5x2.0mm +Inductor_SMD:L_TDK_NLV32_3.2x2.5mm +Inductor_SMD:L_TDK_SLF10145 +Inductor_SMD:L_TDK_SLF10165 +Inductor_SMD:L_TDK_SLF12555 +Inductor_SMD:L_TDK_SLF12565 +Inductor_SMD:L_TDK_SLF12575 +Inductor_SMD:L_TDK_SLF6025 +Inductor_SMD:L_TDK_SLF6028 +Inductor_SMD:L_TDK_SLF6045 +Inductor_SMD:L_TDK_SLF7032 +Inductor_SMD:L_TDK_SLF7045 +Inductor_SMD:L_TDK_SLF7055 +Inductor_SMD:L_TDK_VLF10040 +Inductor_SMD:L_TDK_VLP8040 +Inductor_SMD:L_TDK_VLS6045EX_VLS6045AF +Inductor_SMD:L_TracoPower_TCK-047_5.2x5.8mm +Inductor_SMD:L_TracoPower_TCK-141 +Inductor_SMD:L_Vishay_IFSC-1515AH_4x4x1.8mm +Inductor_SMD:L_Vishay_IHLP-1212 +Inductor_SMD:L_Vishay_IHLP-1616 +Inductor_SMD:L_Vishay_IHLP-2020 +Inductor_SMD:L_Vishay_IHLP-2525 +Inductor_SMD:L_Vishay_IHLP-4040 +Inductor_SMD:L_Vishay_IHLP-5050 +Inductor_SMD:L_Vishay_IHLP-6767 +Inductor_SMD:L_Vishay_IHSM-3825 +Inductor_SMD:L_Vishay_IHSM-4825 +Inductor_SMD:L_Vishay_IHSM-5832 +Inductor_SMD:L_Vishay_IHSM-7832 +Inductor_SMD:L_Walsin_WLFM201209x +Inductor_SMD:L_Walsin_WLFM201609x +Inductor_SMD:L_Walsin_WLFM252009x +Inductor_SMD:L_Wuerth_HCF-2013 +Inductor_SMD:L_Wuerth_HCF-2815 +Inductor_SMD:L_Wuerth_HCF-2818 +Inductor_SMD:L_Wuerth_HCI-1030 +Inductor_SMD:L_Wuerth_HCI-1040 +Inductor_SMD:L_Wuerth_HCI-1050 +Inductor_SMD:L_Wuerth_HCI-1335 +Inductor_SMD:L_Wuerth_HCI-1350 +Inductor_SMD:L_Wuerth_HCI-1365 +Inductor_SMD:L_Wuerth_HCI-1890 +Inductor_SMD:L_Wuerth_HCI-2212 +Inductor_SMD:L_Wuerth_HCI-5040 +Inductor_SMD:L_Wuerth_HCI-7030 +Inductor_SMD:L_Wuerth_HCI-7040 +Inductor_SMD:L_Wuerth_HCI-7050 +Inductor_SMD:L_Wuerth_HCM-1050 +Inductor_SMD:L_Wuerth_HCM-1052 +Inductor_SMD:L_Wuerth_HCM-1070 +Inductor_SMD:L_Wuerth_HCM-1078 +Inductor_SMD:L_Wuerth_HCM-1190 +Inductor_SMD:L_Wuerth_HCM-1240 +Inductor_SMD:L_Wuerth_HCM-1350 +Inductor_SMD:L_Wuerth_HCM-1390 +Inductor_SMD:L_Wuerth_HCM-7050 +Inductor_SMD:L_Wuerth_HCM-7070 +Inductor_SMD:L_Wuerth_MAPI-1610 +Inductor_SMD:L_Wuerth_MAPI-2010 +Inductor_SMD:L_Wuerth_MAPI-2506 +Inductor_SMD:L_Wuerth_MAPI-2508 +Inductor_SMD:L_Wuerth_MAPI-2510 +Inductor_SMD:L_Wuerth_MAPI-2512 +Inductor_SMD:L_Wuerth_MAPI-3010 +Inductor_SMD:L_Wuerth_MAPI-3012 +Inductor_SMD:L_Wuerth_MAPI-3015 +Inductor_SMD:L_Wuerth_MAPI-3020 +Inductor_SMD:L_Wuerth_MAPI-4020 +Inductor_SMD:L_Wuerth_MAPI-4030 +Inductor_SMD:L_Wuerth_WE-DD-Typ-L-Typ-XL-Typ-XXL +Inductor_SMD:L_Wuerth_WE-DD-Typ-M-Typ-S +Inductor_SMD:L_Wuerth_WE-GF-1210 +Inductor_SMD:L_Wuerth_WE-PD-Typ-7345 +Inductor_SMD:L_Wuerth_WE-PD-Typ-LS +Inductor_SMD:L_Wuerth_WE-PD-Typ-LS_Handsoldering +Inductor_SMD:L_Wuerth_WE-PD-Typ-M-Typ-S +Inductor_SMD:L_Wuerth_WE-PD-Typ-M-Typ-S_Handsoldering +Inductor_SMD:L_Wuerth_WE-PD2-Typ-L +Inductor_SMD:L_Wuerth_WE-PD2-Typ-MS +Inductor_SMD:L_Wuerth_WE-PD2-Typ-XL +Inductor_SMD:L_Wuerth_WE-PD4-Typ-X +Inductor_SMD:L_Wuerth_WE-PDF +Inductor_SMD:L_Wuerth_WE-PDF_Handsoldering +Inductor_SMD:L_Wuerth_WE-TPC-3816 +Inductor_SMD:L_Wuerth_WE-XHMI-8080 +Inductor_SMD:L_Wurth_WE-CAIR-5910 +Inductor_SMD_Wurth:L_Wurth_WE-LQSH-2010 +Inductor_SMD_Wurth:L_Wurth_WE-LQSH-2512 +Inductor_SMD_Wurth:L_Wurth_WE-LQSH-3012 +Inductor_SMD_Wurth:L_Wurth_WE-LQSH-4020 +Inductor_THT:Choke_EPCOS_B82722A +Inductor_THT:Choke_Schaffner_RN102-04-14.0x14.0mm +Inductor_THT:Choke_Schaffner_RN112-04-17.7x17.1mm +Inductor_THT:Choke_Schaffner_RN114-04-22.5x21.5mm +Inductor_THT:Choke_Schaffner_RN116-04-22.5x21.5mm +Inductor_THT:Choke_Schaffner_RN122-04-28.0x27.0mm +Inductor_THT:Choke_Schaffner_RN142-04-33.1x32.5mm +Inductor_THT:Choke_Schaffner_RN143-04-33.1x32.5mm +Inductor_THT:Choke_Schaffner_RN152-04-43.0x41.8mm +Inductor_THT:Choke_Schaffner_RN202-04-8.8x18.2mm +Inductor_THT:Choke_Schaffner_RN204-04-9.0x14.0mm +Inductor_THT:Choke_Schaffner_RN212-04-12.5x18.0mm +Inductor_THT:Choke_Schaffner_RN214-04-15.5x23.0mm +Inductor_THT:Choke_Schaffner_RN216-04-15.5x23.0mm +Inductor_THT:Choke_Schaffner_RN218-04-12.5x18.0mm +Inductor_THT:Choke_Schaffner_RN222-04-18.0x31.0mm +Inductor_THT:Choke_Schaffner_RN232-04-18.0x31.0mm +Inductor_THT:Choke_Schaffner_RN242-04-18.0x31.0mm +Inductor_THT:L_Axial_L11.0mm_D4.5mm_P15.24mm_Horizontal_Fastron_MECC +Inductor_THT:L_Axial_L11.0mm_D4.5mm_P5.08mm_Vertical_Fastron_MECC +Inductor_THT:L_Axial_L11.0mm_D4.5mm_P7.62mm_Vertical_Fastron_MECC +Inductor_THT:L_Axial_L12.0mm_D5.0mm_P15.24mm_Horizontal_Fastron_MISC +Inductor_THT:L_Axial_L12.0mm_D5.0mm_P5.08mm_Vertical_Fastron_MISC +Inductor_THT:L_Axial_L12.0mm_D5.0mm_P7.62mm_Vertical_Fastron_MISC +Inductor_THT:L_Axial_L12.8mm_D5.8mm_P20.32mm_Horizontal_Fastron_HBCC +Inductor_THT:L_Axial_L12.8mm_D5.8mm_P25.40mm_Horizontal_Fastron_HBCC +Inductor_THT:L_Axial_L12.8mm_D5.8mm_P5.08mm_Vertical_Fastron_HBCC +Inductor_THT:L_Axial_L12.8mm_D5.8mm_P7.62mm_Vertical_Fastron_HBCC +Inductor_THT:L_Axial_L13.0mm_D4.5mm_P15.24mm_Horizontal_Fastron_HCCC +Inductor_THT:L_Axial_L13.0mm_D4.5mm_P5.08mm_Vertical_Fastron_HCCC +Inductor_THT:L_Axial_L13.0mm_D4.5mm_P7.62mm_Vertical_Fastron_HCCC +Inductor_THT:L_Axial_L14.0mm_D4.5mm_P15.24mm_Horizontal_Fastron_LACC +Inductor_THT:L_Axial_L14.0mm_D4.5mm_P5.08mm_Vertical_Fastron_LACC +Inductor_THT:L_Axial_L14.0mm_D4.5mm_P7.62mm_Vertical_Fastron_LACC +Inductor_THT:L_Axial_L14.5mm_D5.8mm_P20.32mm_Horizontal_Fastron_HBCC +Inductor_THT:L_Axial_L14.5mm_D5.8mm_P25.40mm_Horizontal_Fastron_HBCC +Inductor_THT:L_Axial_L14.5mm_D5.8mm_P5.08mm_Vertical_Fastron_HBCC +Inductor_THT:L_Axial_L14.5mm_D5.8mm_P7.62mm_Vertical_Fastron_HBCC +Inductor_THT:L_Axial_L16.0mm_D6.3mm_P20.32mm_Horizontal_Fastron_VHBCC +Inductor_THT:L_Axial_L16.0mm_D6.3mm_P25.40mm_Horizontal_Fastron_VHBCC +Inductor_THT:L_Axial_L16.0mm_D6.3mm_P5.08mm_Vertical_Fastron_VHBCC +Inductor_THT:L_Axial_L16.0mm_D6.3mm_P7.62mm_Vertical_Fastron_VHBCC +Inductor_THT:L_Axial_L16.0mm_D7.5mm_P20.32mm_Horizontal_Fastron_XHBCC +Inductor_THT:L_Axial_L16.0mm_D7.5mm_P25.40mm_Horizontal_Fastron_XHBCC +Inductor_THT:L_Axial_L16.0mm_D7.5mm_P5.08mm_Vertical_Fastron_XHBCC +Inductor_THT:L_Axial_L16.0mm_D7.5mm_P7.62mm_Vertical_Fastron_XHBCC +Inductor_THT:L_Axial_L16.0mm_D9.5mm_P20.32mm_Horizontal_Vishay_IM-10-37 +Inductor_THT:L_Axial_L16.0mm_D9.5mm_P5.08mm_Vertical_Vishay_IM-10-37 +Inductor_THT:L_Axial_L17.5mm_D12.0mm_P20.32mm_Horizontal_Vishay_IM-10-46 +Inductor_THT:L_Axial_L17.5mm_D12.0mm_P7.62mm_Vertical_Vishay_IM-10-46 +Inductor_THT:L_Axial_L20.0mm_D8.0mm_P25.40mm_Horizontal +Inductor_THT:L_Axial_L20.0mm_D8.0mm_P5.08mm_Vertical +Inductor_THT:L_Axial_L20.0mm_D8.0mm_P7.62mm_Vertical +Inductor_THT:L_Axial_L20.3mm_D12.1mm_P28.50mm_Horizontal_Vishay_IHA-101 +Inductor_THT:L_Axial_L20.3mm_D12.1mm_P7.62mm_Vertical_Vishay_IHA-101 +Inductor_THT:L_Axial_L20.3mm_D12.7mm_P25.40mm_Horizontal_Vishay_IHA-201 +Inductor_THT:L_Axial_L20.3mm_D12.7mm_P7.62mm_Vertical_Vishay_IHA-201 +Inductor_THT:L_Axial_L23.4mm_D12.7mm_P32.00mm_Horizontal_Vishay_IHA-203 +Inductor_THT:L_Axial_L23.4mm_D12.7mm_P7.62mm_Vertical_Vishay_IHA-203 +Inductor_THT:L_Axial_L24.0mm_D7.1mm_P30.48mm_Horizontal_Vishay_IM-10-28 +Inductor_THT:L_Axial_L24.0mm_D7.1mm_P5.08mm_Vertical_Vishay_IM-10-28 +Inductor_THT:L_Axial_L24.0mm_D7.5mm_P27.94mm_Horizontal_Fastron_MESC +Inductor_THT:L_Axial_L24.0mm_D7.5mm_P5.08mm_Vertical_Fastron_MESC +Inductor_THT:L_Axial_L24.0mm_D7.5mm_P7.62mm_Vertical_Fastron_MESC +Inductor_THT:L_Axial_L26.0mm_D10.0mm_P30.48mm_Horizontal_Fastron_77A +Inductor_THT:L_Axial_L26.0mm_D10.0mm_P5.08mm_Vertical_Fastron_77A +Inductor_THT:L_Axial_L26.0mm_D10.0mm_P7.62mm_Vertical_Fastron_77A +Inductor_THT:L_Axial_L26.0mm_D11.0mm_P30.48mm_Horizontal_Fastron_77A +Inductor_THT:L_Axial_L26.0mm_D11.0mm_P5.08mm_Vertical_Fastron_77A +Inductor_THT:L_Axial_L26.0mm_D11.0mm_P7.62mm_Vertical_Fastron_77A +Inductor_THT:L_Axial_L26.0mm_D9.0mm_P30.48mm_Horizontal_Fastron_77A +Inductor_THT:L_Axial_L26.0mm_D9.0mm_P5.08mm_Vertical_Fastron_77A +Inductor_THT:L_Axial_L26.0mm_D9.0mm_P7.62mm_Vertical_Fastron_77A +Inductor_THT:L_Axial_L26.7mm_D12.1mm_P35.00mm_Horizontal_Vishay_IHA-103 +Inductor_THT:L_Axial_L26.7mm_D12.1mm_P7.62mm_Vertical_Vishay_IHA-103 +Inductor_THT:L_Axial_L26.7mm_D14.0mm_P35.00mm_Horizontal_Vishay_IHA-104 +Inductor_THT:L_Axial_L26.7mm_D14.0mm_P7.62mm_Vertical_Vishay_IHA-104 +Inductor_THT:L_Axial_L29.9mm_D14.0mm_P38.00mm_Horizontal_Vishay_IHA-105 +Inductor_THT:L_Axial_L29.9mm_D14.0mm_P7.62mm_Vertical_Vishay_IHA-105 +Inductor_THT:L_Axial_L30.0mm_D8.0mm_P35.56mm_Horizontal_Fastron_77A +Inductor_THT:L_Axial_L30.0mm_D8.0mm_P5.08mm_Vertical_Fastron_77A +Inductor_THT:L_Axial_L30.0mm_D8.0mm_P7.62mm_Vertical_Fastron_77A +Inductor_THT:L_Axial_L5.0mm_D3.6mm_P10.00mm_Horizontal_Murata_BL01RN1A2A2 +Inductor_THT:L_Axial_L5.3mm_D2.2mm_P10.16mm_Horizontal_Vishay_IM-1 +Inductor_THT:L_Axial_L5.3mm_D2.2mm_P2.54mm_Vertical_Vishay_IM-1 +Inductor_THT:L_Axial_L5.3mm_D2.2mm_P7.62mm_Horizontal_Vishay_IM-1 +Inductor_THT:L_Axial_L6.6mm_D2.7mm_P10.16mm_Horizontal_Vishay_IM-2 +Inductor_THT:L_Axial_L6.6mm_D2.7mm_P2.54mm_Vertical_Vishay_IM-2 +Inductor_THT:L_Axial_L7.0mm_D3.3mm_P10.16mm_Horizontal_Fastron_MICC +Inductor_THT:L_Axial_L7.0mm_D3.3mm_P12.70mm_Horizontal_Fastron_MICC +Inductor_THT:L_Axial_L7.0mm_D3.3mm_P2.54mm_Vertical_Fastron_MICC +Inductor_THT:L_Axial_L7.0mm_D3.3mm_P5.08mm_Vertical_Fastron_MICC +Inductor_THT:L_Axial_L9.5mm_D4.0mm_P12.70mm_Horizontal_Fastron_SMCC +Inductor_THT:L_Axial_L9.5mm_D4.0mm_P15.24mm_Horizontal_Fastron_SMCC +Inductor_THT:L_Axial_L9.5mm_D4.0mm_P2.54mm_Vertical_Fastron_SMCC +Inductor_THT:L_Axial_L9.5mm_D4.0mm_P5.08mm_Vertical_Fastron_SMCC +Inductor_THT:L_CommonMode_PulseElectronics_PH9455x105NL_1 +Inductor_THT:L_CommonMode_PulseElectronics_PH9455x155NL_1 +Inductor_THT:L_CommonMode_PulseElectronics_PH9455x205NL_1 +Inductor_THT:L_CommonMode_PulseElectronics_PH9455x405NL_1 +Inductor_THT:L_CommonMode_PulseElectronics_PH9455x705NL_1 +Inductor_THT:L_CommonMode_PulseElectronics_PH9455xxx6NL_2 +Inductor_THT:L_CommonMode_TDK_B82746S4143A040 +Inductor_THT:L_CommonMode_TDK_B82746S6702A040 +Inductor_THT:L_CommonMode_TDK_B82747E6163A040 +Inductor_THT:L_CommonMode_TDK_B82747E6203A040 +Inductor_THT:L_CommonMode_TDK_B82747E6253A040 +Inductor_THT:L_CommonMode_TDK_B82747E6353A040 +Inductor_THT:L_CommonMode_TDK_B82767S4123N030 +Inductor_THT:L_CommonMode_TDK_B82767S4193N030 +Inductor_THT:L_CommonMode_TDK_B82767S4263N030 +Inductor_THT:L_CommonMode_Toroid_Vertical_L19.3mm_W10.8mm_Px6.35mm_Py15.24mm_Bourns_8100 +Inductor_THT:L_CommonMode_Toroid_Vertical_L21.0mm_W10.0mm_Px5.08mm_Py12.70mm_Murata_5100 +Inductor_THT:L_CommonMode_Toroid_Vertical_L24.0mm_W16.3mm_Px10.16mm_Py20.32mm_Murata_5200 +Inductor_THT:L_CommonMode_Toroid_Vertical_L30.5mm_W15.2mm_Px10.16mm_Py20.32mm_Bourns_8100 +Inductor_THT:L_CommonMode_Toroid_Vertical_L34.3mm_W20.3mm_Px15.24mm_Py22.86mm_Bourns_8100 +Inductor_THT:L_CommonMode_Toroid_Vertical_L36.8mm_W20.3mm_Px15.24mm_Py22.86mm_Bourns_8100 +Inductor_THT:L_CommonMode_Toroid_Vertical_L38.1mm_W20.3mm_Px15.24mm_Py22.86mm_Bourns_8100 +Inductor_THT:L_CommonMode_Toroid_Vertical_L39.4mm_W20.3mm_Px15.24mm_Py22.86mm_Bourns_8100 +Inductor_THT:L_CommonMode_Toroid_Vertical_L41.9mm_W20.3mm_Px15.24mm_Py22.86mm_Bourns_8100 +Inductor_THT:L_CommonMode_Toroid_Vertical_L43.2mm_W22.9mm_Px17.78mm_Py30.48mm_Bourns_8100 +Inductor_THT:L_CommonMode_VAC_T60405-S6123-X140 +Inductor_THT:L_CommonMode_VAC_T60405-S6123-X240 +Inductor_THT:L_CommonMode_VAC_T60405-S6123-X402 +Inductor_THT:L_CommonMode_Wuerth_WE-CMB-L +Inductor_THT:L_CommonMode_Wuerth_WE-CMB-M +Inductor_THT:L_CommonMode_Wuerth_WE-CMB-S +Inductor_THT:L_CommonMode_Wuerth_WE-CMB-XL +Inductor_THT:L_CommonMode_Wuerth_WE-CMB-XS +Inductor_THT:L_CommonMode_Wuerth_WE-CMB-XXL +Inductor_THT:L_Mount_Lodestone_VTM120 +Inductor_THT:L_Mount_Lodestone_VTM160 +Inductor_THT:L_Mount_Lodestone_VTM254 +Inductor_THT:L_Mount_Lodestone_VTM280 +Inductor_THT:L_Mount_Lodestone_VTM950-6 +Inductor_THT:L_Radial_D10.0mm_P5.00mm_Fastron_07M +Inductor_THT:L_Radial_D10.0mm_P5.00mm_Fastron_07P +Inductor_THT:L_Radial_D10.0mm_P5.00mm_Neosid_SD12k_style3 +Inductor_THT:L_Radial_D10.0mm_P5.00mm_Neosid_SD12_style3 +Inductor_THT:L_Radial_D10.5mm_P4.00x5.00mm_Murata_1200RS +Inductor_THT:L_Radial_D10.5mm_P5.00mm_Abacron_AISR-01 +Inductor_THT:L_Radial_D12.0mm_P10.00mm_Neosid_SD12k_style1 +Inductor_THT:L_Radial_D12.0mm_P10.00mm_Neosid_SD12_style1 +Inductor_THT:L_Radial_D12.0mm_P5.00mm_Fastron_11P +Inductor_THT:L_Radial_D12.0mm_P5.00mm_Neosid_SD12k_style2 +Inductor_THT:L_Radial_D12.0mm_P5.00mm_Neosid_SD12_style2 +Inductor_THT:L_Radial_D12.0mm_P6.00mm_Murata_1900R +Inductor_THT:L_Radial_D12.5mm_P7.00mm_Fastron_09HCP +Inductor_THT:L_Radial_D12.5mm_P9.00mm_Fastron_09HCP +Inductor_THT:L_Radial_D13.5mm_P7.00mm_Fastron_09HCP +Inductor_THT:L_Radial_D14.2mm_P10.00mm_Neosid_SD14 +Inductor_THT:L_Radial_D16.0mm_P10.00mm_Panasonic_15E-L +Inductor_THT:L_Radial_D16.8mm_P11.43mm_Vishay_IHB-1 +Inductor_THT:L_Radial_D16.8mm_P12.07mm_Vishay_IHB-1 +Inductor_THT:L_Radial_D16.8mm_P12.70mm_Vishay_IHB-1 +Inductor_THT:L_Radial_D18.0mm_P10.00mm +Inductor_THT:L_Radial_D21.0mm_P14.61mm_Vishay_IHB-2 +Inductor_THT:L_Radial_D21.0mm_P15.00mm_Vishay_IHB-2 +Inductor_THT:L_Radial_D21.0mm_P15.24mm_Vishay_IHB-2 +Inductor_THT:L_Radial_D21.0mm_P15.75mm_Vishay_IHB-2 +Inductor_THT:L_Radial_D21.0mm_P19.00mm +Inductor_THT:L_Radial_D24.0mm_P24.00mm +Inductor_THT:L_Radial_D24.4mm_P22.90mm_Murata_1400series +Inductor_THT:L_Radial_D24.4mm_P23.10mm_Murata_1400series +Inductor_THT:L_Radial_D24.4mm_P23.40mm_Murata_1400series +Inductor_THT:L_Radial_D24.4mm_P23.70mm_Murata_1400series +Inductor_THT:L_Radial_D24.4mm_P23.90mm_Murata_1400series +Inductor_THT:L_Radial_D27.9mm_P18.29mm_Vishay_IHB-3 +Inductor_THT:L_Radial_D27.9mm_P19.05mm_Vishay_IHB-3 +Inductor_THT:L_Radial_D27.9mm_P20.07mm_Vishay_IHB-3 +Inductor_THT:L_Radial_D28.0mm_P29.20mm +Inductor_THT:L_Radial_D29.8mm_P28.30mm_Murata_1400series +Inductor_THT:L_Radial_D29.8mm_P28.50mm_Murata_1400series +Inductor_THT:L_Radial_D29.8mm_P28.80mm_Murata_1400series +Inductor_THT:L_Radial_D29.8mm_P29.00mm_Murata_1400series +Inductor_THT:L_Radial_D29.8mm_P29.30mm_Murata_1400series +Inductor_THT:L_Radial_D40.6mm_P26.16mm_Vishay_IHB-5 +Inductor_THT:L_Radial_D40.6mm_P27.18mm_Vishay_IHB-4 +Inductor_THT:L_Radial_D40.6mm_P27.94mm_Vishay_IHB-4 +Inductor_THT:L_Radial_D40.6mm_P27.94mm_Vishay_IHB-5 +Inductor_THT:L_Radial_D40.6mm_P28.70mm_Vishay_IHB-5 +Inductor_THT:L_Radial_D50.8mm_P33.27mm_Vishay_IHB-6 +Inductor_THT:L_Radial_D50.8mm_P34.29mm_Vishay_IHB-6 +Inductor_THT:L_Radial_D50.8mm_P35.81mm_Vishay_IHB-6 +Inductor_THT:L_Radial_D50.8mm_P36.32mm_Vishay_IHB-6 +Inductor_THT:L_Radial_D50.8mm_P38.86mm_Vishay_IHB-6 +Inductor_THT:L_Radial_D6.0mm_P4.00mm +Inductor_THT:L_Radial_D7.0mm_P3.00mm +Inductor_THT:L_Radial_D7.2mm_P3.00mm_Murata_1700 +Inductor_THT:L_Radial_D7.5mm_P3.50mm_Fastron_07P +Inductor_THT:L_Radial_D7.5mm_P5.00mm_Fastron_07P +Inductor_THT:L_Radial_D7.8mm_P5.00mm_Fastron_07HCP +Inductor_THT:L_Radial_D8.7mm_P5.00mm_Fastron_07HCP +Inductor_THT:L_Radial_D9.5mm_P5.00mm_Fastron_07HVP +Inductor_THT:L_Radial_L10.2mm_W10.2mm_Px7.62mm_Py7.62mm_Pulse_LP-30 +Inductor_THT:L_Radial_L11.5mm_W11.5mm_Px6.00mm_Py6.00mm_Neosid_NE-CPB-11EN_Drill1.3mm +Inductor_THT:L_Radial_L11.5mm_W11.5mm_Px6.00mm_Py6.00mm_Neosid_NE-CPB-11EN_Drill1.5mm +Inductor_THT:L_Radial_L11.5mm_W11.5mm_Px6.00mm_Py6.00mm_Neosid_NE-CPB-11EN_Drill1.7mm +Inductor_THT:L_Radial_L11.5mm_W11.5mm_Px6.00mm_Py6.00mm_Neosid_NE-CPB-11EN_Drill1.8mm +Inductor_THT:L_Radial_L12.6mm_W12.6mm_Px9.52mm_Py9.52mm_Pulse_LP-37 +Inductor_THT:L_Radial_L16.1mm_W16.1mm_Px7.62mm_Py12.70mm_Pulse_LP-44 +Inductor_THT:L_Radial_L7.5mm_W4.6mm_P5.00mm_Neosid_SD75 +Inductor_THT:L_Radial_L8.0mm_W8.0mm_P5.00mm_Neosid_NE-CPB-07E +Inductor_THT:L_Radial_L8.0mm_W8.0mm_P5.00mm_Neosid_SD8 +Inductor_THT:L_Radial_L9.1mm_W9.1mm_Px6.35mm_Py6.35mm_Pulse_LP-25 +Inductor_THT:L_SELF1408 +Inductor_THT:L_SELF1418 +Inductor_THT:L_Toroid_Horizontal_D11.2mm_P17.00mm_Diameter12-5mm_Amidon-T44 +Inductor_THT:L_Toroid_Horizontal_D12.7mm_P20.00mm_Diameter14-5mm_Amidon-T50 +Inductor_THT:L_Toroid_Horizontal_D16.8mm_P14.70mm_Vishay_TJ3 +Inductor_THT:L_Toroid_Horizontal_D16.8mm_P14.70mm_Vishay_TJ3_BigPads +Inductor_THT:L_Toroid_Horizontal_D17.3mm_P15.24mm_Bourns_2000 +Inductor_THT:L_Toroid_Horizontal_D21.8mm_P19.10mm_Bourns_2100 +Inductor_THT:L_Toroid_Horizontal_D21.8mm_P19.60mm_Bourns_2100 +Inductor_THT:L_Toroid_Horizontal_D22.4mm_P19.80mm_Vishay_TJ4 +Inductor_THT:L_Toroid_Horizontal_D24.1mm_P21.80mm_Bourns_2200 +Inductor_THT:L_Toroid_Horizontal_D24.1mm_P23.10mm_Bourns_2200 +Inductor_THT:L_Toroid_Horizontal_D25.4mm_P22.90mm_Vishay_TJ5 +Inductor_THT:L_Toroid_Horizontal_D25.4mm_P22.90mm_Vishay_TJ5_BigPads +Inductor_THT:L_Toroid_Horizontal_D26.0mm_P5.08mm +Inductor_THT:L_Toroid_Horizontal_D28.0mm_P25.10mm_Bourns_2200 +Inductor_THT:L_Toroid_Horizontal_D28.0mm_P26.67mm_Bourns_2200 +Inductor_THT:L_Toroid_Horizontal_D3.2mm_P6.40mm_Diameter3-5mm_Amidon-T12 +Inductor_THT:L_Toroid_Horizontal_D32.5mm_P28.90mm_Bourns_2300 +Inductor_THT:L_Toroid_Horizontal_D32.5mm_P30.00mm_Bourns_2300 +Inductor_THT:L_Toroid_Horizontal_D35.1mm_P31.00mm_Vishay_TJ6 +Inductor_THT:L_Toroid_Horizontal_D4.1mm_P8.00mm_Diameter4-5mm_Amidon-T16 +Inductor_THT:L_Toroid_Horizontal_D40.0mm_P48.26mm +Inductor_THT:L_Toroid_Horizontal_D41.9mm_P37.60mm_Vishay_TJ7 +Inductor_THT:L_Toroid_Horizontal_D49.3mm_P44.60mm_Vishay_TJ8 +Inductor_THT:L_Toroid_Horizontal_D5.1mm_P9.00mm_Diameter6-5mm_Amidon-T20 +Inductor_THT:L_Toroid_Horizontal_D6.5mm_P10.00mm_Diameter7-5mm_Amidon-T25 +Inductor_THT:L_Toroid_Horizontal_D69.1mm_P63.20mm_Vishay_TJ9 +Inductor_THT:L_Toroid_Horizontal_D7.8mm_P13.00mm_Diameter9-5mm_Amidon-T30 +Inductor_THT:L_Toroid_Horizontal_D9.5mm_P15.00mm_Diameter10-5mm_Amidon-T37 +Inductor_THT:L_Toroid_Vertical_L10.0mm_W5.0mm_P5.08mm +Inductor_THT:L_Toroid_Vertical_L13.0mm_W6.5mm_P5.60mm +Inductor_THT:L_Toroid_Vertical_L14.0mm_W5.6mm_P5.30mm_Bourns_5700 +Inductor_THT:L_Toroid_Vertical_L14.0mm_W6.3mm_P4.57mm_Pulse_A +Inductor_THT:L_Toroid_Vertical_L14.7mm_W8.6mm_P5.58mm_Pulse_KM-1 +Inductor_THT:L_Toroid_Vertical_L16.0mm_W8.0mm_P7.62mm +Inductor_THT:L_Toroid_Vertical_L16.3mm_W7.1mm_P7.11mm_Pulse_H +Inductor_THT:L_Toroid_Vertical_L16.4mm_W7.6mm_P6.60mm_Vishay_TJ3 +Inductor_THT:L_Toroid_Vertical_L16.5mm_W11.4mm_P7.62mm_Pulse_KM-2 +Inductor_THT:L_Toroid_Vertical_L16.8mm_W9.2mm_P7.10mm_Vishay_TJ3 +Inductor_THT:L_Toroid_Vertical_L16.8mm_W9.2mm_P7.10mm_Vishay_TJ3_BigPads +Inductor_THT:L_Toroid_Vertical_L17.8mm_W8.1mm_P7.62mm_Bourns_5700 +Inductor_THT:L_Toroid_Vertical_L17.8mm_W9.7mm_P7.11mm_Pulse_B +Inductor_THT:L_Toroid_Vertical_L19.1mm_W8.1mm_P7.10mm_Bourns_5700 +Inductor_THT:L_Toroid_Vertical_L21.6mm_W11.4mm_P7.62mm_Pulse_KM-3 +Inductor_THT:L_Toroid_Vertical_L21.6mm_W8.4mm_P8.38mm_Pulse_G +Inductor_THT:L_Toroid_Vertical_L21.6mm_W9.1mm_P8.40mm_Bourns_5700 +Inductor_THT:L_Toroid_Vertical_L21.6mm_W9.5mm_P7.11mm_Pulse_C +Inductor_THT:L_Toroid_Vertical_L22.4mm_W10.2mm_P7.90mm_Vishay_TJ4 +Inductor_THT:L_Toroid_Vertical_L24.6mm_W15.5mm_P11.44mm_Pulse_KM-4 +Inductor_THT:L_Toroid_Vertical_L25.4mm_W14.7mm_P12.20mm_Vishay_TJ5 +Inductor_THT:L_Toroid_Vertical_L25.4mm_W14.7mm_P12.20mm_Vishay_TJ5_BigPads +Inductor_THT:L_Toroid_Vertical_L26.7mm_W14.0mm_P10.16mm_Pulse_D +Inductor_THT:L_Toroid_Vertical_L28.6mm_W14.3mm_P11.43mm_Bourns_5700 +Inductor_THT:L_Toroid_Vertical_L31.8mm_W15.9mm_P13.50mm_Bourns_5700 +Inductor_THT:L_Toroid_Vertical_L33.0mm_W17.8mm_P12.70mm_Pulse_KM-5 +Inductor_THT:L_Toroid_Vertical_L35.1mm_W21.1mm_P18.50mm_Vishay_TJ6 +Inductor_THT:L_Toroid_Vertical_L35.6mm_W17.8mm_P12.70mm_Pulse_E +Inductor_THT:L_Toroid_Vertical_L41.9mm_W17.8mm_P12.70mm_Pulse_F +Inductor_THT:L_Toroid_Vertical_L41.9mm_W19.1mm_P15.80mm_Vishay_TJ7 +Inductor_THT:L_Toroid_Vertical_L46.0mm_W19.1mm_P21.80mm_Bourns_5700 +Inductor_THT:L_Toroid_Vertical_L48.8mm_W25.4mm_P20.80mm_Vishay_TJ8 +Inductor_THT:L_Toroid_Vertical_L54.0mm_W23.8mm_P20.10mm_Bourns_5700 +Inductor_THT:L_Toroid_Vertical_L67.6mm_W36.1mm_P31.80mm_Vishay_TJ9 +Inductor_THT_Wurth:L_Wurth_WE-HCFT-2012_LeadDiameter1.2mm +Inductor_THT_Wurth:L_Wurth_WE-HCFT-2012_LeadDiameter1.5mm +Inductor_THT_Wurth:L_Wurth_WE-HCFT-2504 +Inductor_THT_Wurth:L_Wurth_WE-HCFT-3521 +Inductor_THT_Wurth:L_Wurth_WE-HCFT-3533_LeadDiameter1.8mm +Inductor_THT_Wurth:L_Wurth_WE-HCFT-3533_LeadDiameter2.0mm +Inductor_THT_Wurth:L_Wurth_WE-HCFT-3540_LeadDiameter0.8mm +Inductor_THT_Wurth:L_Wurth_WE-HCFT-3540_LeadDiameter1.3mm +Inductor_THT_Wurth:L_Wurth_WE-HCFT-3540_LeadDiameter1.5mm +Inductor_THT_Wurth:L_Wurth_WE-HCFT-3540_LeadDiameter2.0mm +Jumper:SolderJumper-2_P1.3mm_Bridged2Bar_Pad1.0x1.5mm +Jumper:SolderJumper-2_P1.3mm_Bridged2Bar_RoundedPad1.0x1.5mm +Jumper:SolderJumper-2_P1.3mm_Bridged_Pad1.0x1.5mm +Jumper:SolderJumper-2_P1.3mm_Bridged_RoundedPad1.0x1.5mm +Jumper:SolderJumper-2_P1.3mm_Open_Pad1.0x1.5mm +Jumper:SolderJumper-2_P1.3mm_Open_RoundedPad1.0x1.5mm +Jumper:SolderJumper-2_P1.3mm_Open_TrianglePad1.0x1.5mm +Jumper:SolderJumper-3_P1.3mm_Bridged12_Pad1.0x1.5mm +Jumper:SolderJumper-3_P1.3mm_Bridged12_Pad1.0x1.5mm_NumberLabels +Jumper:SolderJumper-3_P1.3mm_Bridged12_RoundedPad1.0x1.5mm +Jumper:SolderJumper-3_P1.3mm_Bridged12_RoundedPad1.0x1.5mm_NumberLabels +Jumper:SolderJumper-3_P1.3mm_Bridged2Bar12_Pad1.0x1.5mm +Jumper:SolderJumper-3_P1.3mm_Bridged2Bar12_Pad1.0x1.5mm_NumberLabels +Jumper:SolderJumper-3_P1.3mm_Bridged2Bar12_RoundedPad1.0x1.5mm +Jumper:SolderJumper-3_P1.3mm_Bridged2Bar12_RoundedPad1.0x1.5mm_NumberLabels +Jumper:SolderJumper-3_P1.3mm_Open_Pad1.0x1.5mm +Jumper:SolderJumper-3_P1.3mm_Open_Pad1.0x1.5mm_NumberLabels +Jumper:SolderJumper-3_P1.3mm_Open_RoundedPad1.0x1.5mm +Jumper:SolderJumper-3_P1.3mm_Open_RoundedPad1.0x1.5mm_NumberLabels +Jumper:SolderJumper-3_P2.0mm_Open_TrianglePad1.0x1.5mm +Jumper:SolderJumper-3_P2.0mm_Open_TrianglePad1.0x1.5mm_NumberLabels +LED_SMD:LED-APA102-2020 +LED_SMD:LED-L1T2_LUMILEDS +LED_SMD:LED_0201_0603Metric +LED_SMD:LED_0201_0603Metric_Pad0.64x0.40mm_HandSolder +LED_SMD:LED_0402_1005Metric +LED_SMD:LED_0402_1005Metric_Pad0.77x0.64mm_HandSolder +LED_SMD:LED_0603_1608Metric +LED_SMD:LED_0603_1608Metric_Pad1.05x0.95mm_HandSolder +LED_SMD:LED_0805_2012Metric +LED_SMD:LED_0805_2012Metric_Pad1.15x1.40mm_HandSolder +LED_SMD:LED_1206_3216Metric +LED_SMD:LED_1206_3216Metric_Pad1.42x1.75mm_HandSolder +LED_SMD:LED_1206_3216Metric_ReverseMount_Hole1.8x2.4mm +LED_SMD:LED_1210_3225Metric +LED_SMD:LED_1210_3225Metric_Pad1.42x2.65mm_HandSolder +LED_SMD:LED_1812_4532Metric +LED_SMD:LED_1812_4532Metric_Pad1.30x3.40mm_HandSolder +LED_SMD:LED_1W_3W_R8 +LED_SMD:LED_2010_5025Metric +LED_SMD:LED_2010_5025Metric_Pad1.52x2.65mm_HandSolder +LED_SMD:LED_2512_6332Metric +LED_SMD:LED_2512_6332Metric_Pad1.52x3.35mm_HandSolder +LED_SMD:LED_ASMB-KTF0-0A306 +LED_SMD:LED_Avago_PLCC4_3.2x2.8mm_CW +LED_SMD:LED_Avago_PLCC6_3x2.8mm +LED_SMD:LED_Cree-PLCC4_2x2mm_CW +LED_SMD:LED_Cree-PLCC4_3.2x2.8mm_CCW +LED_SMD:LED_Cree-PLCC4_5x5mm_CW +LED_SMD:LED_Cree-PLCC6_4.7x1.5mm +LED_SMD:LED_Cree-XB +LED_SMD:LED_Cree-XH +LED_SMD:LED_Cree-XHP35 +LED_SMD:LED_Cree-XHP50_12V +LED_SMD:LED_Cree-XHP50_6V +LED_SMD:LED_Cree-XHP70_12V +LED_SMD:LED_Cree-XHP70_6V +LED_SMD:LED_Cree-XP-G +LED_SMD:LED_Cree-XP +LED_SMD:LED_Cree-XQ +LED_SMD:LED_Cree-XQ_HandSoldering +LED_SMD:LED_CSP_Samsung_LH181B_2.36x2.36mm +LED_SMD:LED_Dialight_591 +LED_SMD:LED_Everlight-SMD3528_3.5x2.8mm_67-21ST +LED_SMD:LED_Inolux_IN-P55TATRGB_PLCC6_5.0x5.5mm_P1.8mm +LED_SMD:LED_Inolux_IN-PI554FCH_PLCC4_5.0x5.0mm_P3.2mm +LED_SMD:LED_Kingbright_AAA3528ESGCT +LED_SMD:LED_Kingbright_APA1606_1.6x0.6mm_Horizontal +LED_SMD:LED_Kingbright_APDA3020VBCD +LED_SMD:LED_Kingbright_APFA3010_3x1.5mm_Horizontal +LED_SMD:LED_Kingbright_APHBM2012_2x1.25mm +LED_SMD:LED_Kingbright_KPA-3010_3x2x1mm +LED_SMD:LED_Kingbright_KPBD-3224 +LED_SMD:LED_LiteOn_LTST-C19HE1WT +LED_SMD:LED_LiteOn_LTST-C235KGKRKT +LED_SMD:LED_LiteOn_LTST-C295K_1.6x0.8mm +LED_SMD:LED_LiteOn_LTST-E563C_PLCC4_5.0x5.0mm_P3.2mm +LED_SMD:LED_LiteOn_LTST-E563C_PLCC4_5.0x5.0mm_P3.2mm_HandSoldering +LED_SMD:LED_LiteOn_LTST-S326 +LED_SMD:LED_Lumex_SML-LX0303SIUPGUSB +LED_SMD:LED_Lumex_SML-LX0404SIUPGUSB +LED_SMD:LED_Luminus_MP-3030-1100_3.0x3.0mm +LED_SMD:LED_miniPLCC_2315 +LED_SMD:LED_miniPLCC_2315_Handsoldering +LED_SMD:LED_OPSCO_SK6812_PLCC4_5.0x5.0mm_P3.1mm +LED_SMD:LED_Osram_Lx_P47F_D2mm_ReverseMount +LED_SMD:LED_PLCC-2_3.4x3.0mm_AK +LED_SMD:LED_PLCC-2_3.4x3.0mm_KA +LED_SMD:LED_PLCC-2_3x2mm_AK +LED_SMD:LED_PLCC-2_3x2mm_KA +LED_SMD:LED_PLCC_2835 +LED_SMD:LED_PLCC_2835_Handsoldering +LED_SMD:LED_RGB_1210 +LED_SMD:LED_RGB_5050-6 +LED_SMD:LED_RGB_Cree-PLCC-6_6x5mm_P2.1mm +LED_SMD:LED_RGB_Everlight_EASV3015RGBA0_Horizontal +LED_SMD:LED_RGB_Getian_GT-P6PRGB4303 +LED_SMD:LED_RGB_Lumex_SML-LXT0805SIUGUBW +LED_SMD:LED_RGB_PLCC-6 +LED_SMD:LED_RGB_Wuerth-PLCC4_3.2x2.8mm_150141M173100 +LED_SMD:LED_RGB_Wuerth_150080M153000 +LED_SMD:LED_ROHM_SMLVN6 +LED_SMD:LED_SK6805_PLCC4_2.4x2.7mm_P1.3mm +LED_SMD:LED_SK6812MINI_PLCC4_3.5x3.5mm_P1.75mm +LED_SMD:LED_SK6812_EC15_1.5x1.5mm +LED_SMD:LED_SK6812_PLCC4_5.0x5.0mm_P3.2mm +LED_SMD:LED_WS2812B-2020_PLCC4_2.0x2.0mm +LED_SMD:LED_WS2812B-Mini_PLCC4_3.5x3.5mm +LED_SMD:LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm +LED_SMD:LED_WS2812_PLCC6_5.0x5.0mm_P1.6mm +LED_SMD:LED_Wurth_150044M155260 +LED_SMD:LED_Yuji_5730 +LED_THT:LED_BL-FL7680RGB +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O1.27mm_Z1.6mm +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O1.27mm_Z4.9mm +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O1.27mm_Z8.2mm +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O3.81mm_Z1.6mm +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O3.81mm_Z4.9mm +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O3.81mm_Z8.2mm +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O6.35mm_Z1.6mm +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O6.35mm_Z4.9mm +LED_THT:LED_D1.8mm_W1.8mm_H2.4mm_Horizontal_O6.35mm_Z8.2mm +LED_THT:LED_D1.8mm_W3.3mm_H2.4mm +LED_THT:LED_D10.0mm-3 +LED_THT:LED_D10.0mm +LED_THT:LED_D2.0mm_W4.0mm_H2.8mm_FlatTop +LED_THT:LED_D2.0mm_W4.8mm_H2.5mm_FlatTop +LED_THT:LED_D20.0mm +LED_THT:LED_D3.0mm-3 +LED_THT:LED_D3.0mm +LED_THT:LED_D3.0mm_Clear +LED_THT:LED_D3.0mm_FlatTop +LED_THT:LED_D3.0mm_Horizontal_O1.27mm_Z10.0mm +LED_THT:LED_D3.0mm_Horizontal_O1.27mm_Z2.0mm +LED_THT:LED_D3.0mm_Horizontal_O1.27mm_Z2.0mm_Clear +LED_THT:LED_D3.0mm_Horizontal_O1.27mm_Z2.0mm_IRBlack +LED_THT:LED_D3.0mm_Horizontal_O1.27mm_Z2.0mm_IRGrey +LED_THT:LED_D3.0mm_Horizontal_O1.27mm_Z6.0mm +LED_THT:LED_D3.0mm_Horizontal_O3.81mm_Z10.0mm +LED_THT:LED_D3.0mm_Horizontal_O3.81mm_Z2.0mm +LED_THT:LED_D3.0mm_Horizontal_O3.81mm_Z6.0mm +LED_THT:LED_D3.0mm_Horizontal_O6.35mm_Z10.0mm +LED_THT:LED_D3.0mm_Horizontal_O6.35mm_Z2.0mm +LED_THT:LED_D3.0mm_Horizontal_O6.35mm_Z6.0mm +LED_THT:LED_D3.0mm_IRBlack +LED_THT:LED_D3.0mm_IRGrey +LED_THT:LED_D4.0mm +LED_THT:LED_D5.0mm-3 +LED_THT:LED_D5.0mm-3_Horizontal_O3.81mm_Z3.0mm +LED_THT:LED_D5.0mm-4_RGB +LED_THT:LED_D5.0mm-4_RGB_Staggered_Pins +LED_THT:LED_D5.0mm-4_RGB_Wide_Pins +LED_THT:LED_D5.0mm +LED_THT:LED_D5.0mm_Clear +LED_THT:LED_D5.0mm_FlatTop +LED_THT:LED_D5.0mm_Horizontal_O1.27mm_Z15.0mm +LED_THT:LED_D5.0mm_Horizontal_O1.27mm_Z3.0mm +LED_THT:LED_D5.0mm_Horizontal_O1.27mm_Z3.0mm_Clear +LED_THT:LED_D5.0mm_Horizontal_O1.27mm_Z3.0mm_IRBlack +LED_THT:LED_D5.0mm_Horizontal_O1.27mm_Z3.0mm_IRGrey +LED_THT:LED_D5.0mm_Horizontal_O1.27mm_Z9.0mm +LED_THT:LED_D5.0mm_Horizontal_O3.81mm_Z15.0mm +LED_THT:LED_D5.0mm_Horizontal_O3.81mm_Z3.0mm +LED_THT:LED_D5.0mm_Horizontal_O3.81mm_Z9.0mm +LED_THT:LED_D5.0mm_Horizontal_O6.35mm_Z15.0mm +LED_THT:LED_D5.0mm_Horizontal_O6.35mm_Z3.0mm +LED_THT:LED_D5.0mm_Horizontal_O6.35mm_Z9.0mm +LED_THT:LED_D5.0mm_IRBlack +LED_THT:LED_D5.0mm_IRGrey +LED_THT:LED_D8.0mm-3 +LED_THT:LED_D8.0mm +LED_THT:LED_Oval_W5.2mm_H3.8mm +LED_THT:LED_Rectangular_W3.0mm_H2.0mm +LED_THT:LED_Rectangular_W3.9mm_H1.8mm +LED_THT:LED_Rectangular_W3.9mm_H1.9mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm-3Pins +LED_THT:LED_Rectangular_W5.0mm_H2.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O1.27mm_Z1.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O1.27mm_Z3.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O1.27mm_Z5.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O3.81mm_Z1.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O3.81mm_Z3.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O3.81mm_Z5.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O6.35mm_Z1.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O6.35mm_Z3.0mm +LED_THT:LED_Rectangular_W5.0mm_H2.0mm_Horizontal_O6.35mm_Z5.0mm +LED_THT:LED_Rectangular_W5.0mm_H5.0mm +LED_THT:LED_Rectangular_W7.62mm_H4.55mm_P5.08mm_R3 +LED_THT:LED_SideEmitter_Rectangular_W4.5mm_H1.6mm +LED_THT:LED_VCCLite_5381H1_6.35x6.35mm +LED_THT:LED_VCCLite_5381H3_6.35x6.35mm +LED_THT:LED_VCCLite_5381H5_6.35x6.35mm +LED_THT:LED_VCCLite_5381H7_6.35x6.35mm +Module:A20_OLINUXINO_LIME2 +Module:Adafruit_Feather +Module:Adafruit_Feather_32u4_FONA +Module:Adafruit_Feather_32u4_FONA_WithMountingHoles +Module:Adafruit_Feather_32u4_RFM +Module:Adafruit_Feather_32u4_RFM_WithMountingHoles +Module:Adafruit_Feather_M0_RFM +Module:Adafruit_Feather_M0_RFM_WithMountingHoles +Module:Adafruit_Feather_M0_Wifi +Module:Adafruit_Feather_M0_Wifi_WithMountingHoles +Module:Adafruit_Feather_WICED +Module:Adafruit_Feather_WICED_WithMountingHoles +Module:Adafruit_Feather_WithMountingHoles +Module:Adafruit_HUZZAH_ESP8266_breakout +Module:Adafruit_HUZZAH_ESP8266_breakout_WithMountingHoles +Module:Arduino_Nano +Module:Arduino_Nano_WithMountingHoles +Module:Arduino_UNO_R2 +Module:Arduino_UNO_R2_WithMountingHoles +Module:Arduino_UNO_R3 +Module:Arduino_UNO_R3_WithMountingHoles +Module:BeagleBoard_PocketBeagle +Module:Carambola2 +Module:Electrosmith_Daisy_Seed +Module:Flipper_Zero_Angled +Module:Flipper_Zero_Straight +Module:Google_Coral_SMT_TPU_Module +Module:Maple_Mini +Module:Olimex_MOD-WIFI-ESP8266-DEV +Module:Onion_Omega2+ +Module:Onion_Omega2S +Module:Pololu_Breakout-16_15.2x20.3mm +Module:RaspberryPi_Pico_Common_SMD +Module:RaspberryPi_Pico_Common_THT +Module:RaspberryPi_Pico_Common_Unspecified +Module:RaspberryPi_Pico_SMD +Module:RaspberryPi_Pico_SMD_HandSolder +Module:RaspberryPi_Pico_W_SMD +Module:RaspberryPi_Pico_W_SMD_HandSolder +Module:Raspberry_Pi_Zero_Socketed_THT_FaceDown_MountingHoles +Module:Sipeed-M1 +Module:Sipeed-M1W +Module:ST_Morpho_Connector_144_STLink +Module:ST_Morpho_Connector_144_STLink_MountingHoles +Module:Texas_EUK_R-PDSS-T7_THT +Module:Texas_EUS_R-PDSS-T5_THT +Module:Texas_EUW_R-PDSS-T7_THT +Motors:Vybronics_VZ30C1T8219732L +MountingEquipment:DINRailAdapter_3xM3_PhoenixContact_1201578 +MountingHole:MountingHole_2.1mm +MountingHole:MountingHole_2.2mm_M2 +MountingHole:MountingHole_2.2mm_M2_DIN965 +MountingHole:MountingHole_2.2mm_M2_DIN965_Pad +MountingHole:MountingHole_2.2mm_M2_DIN965_Pad_TopBottom +MountingHole:MountingHole_2.2mm_M2_DIN965_Pad_TopOnly +MountingHole:MountingHole_2.2mm_M2_ISO14580 +MountingHole:MountingHole_2.2mm_M2_ISO14580_Pad +MountingHole:MountingHole_2.2mm_M2_ISO14580_Pad_TopBottom +MountingHole:MountingHole_2.2mm_M2_ISO14580_Pad_TopOnly +MountingHole:MountingHole_2.2mm_M2_ISO7380 +MountingHole:MountingHole_2.2mm_M2_ISO7380_Pad +MountingHole:MountingHole_2.2mm_M2_ISO7380_Pad_TopBottom +MountingHole:MountingHole_2.2mm_M2_ISO7380_Pad_TopOnly +MountingHole:MountingHole_2.2mm_M2_Pad +MountingHole:MountingHole_2.2mm_M2_Pad_TopBottom +MountingHole:MountingHole_2.2mm_M2_Pad_TopOnly +MountingHole:MountingHole_2.2mm_M2_Pad_Via +MountingHole:MountingHole_2.5mm +MountingHole:MountingHole_2.5mm_Pad +MountingHole:MountingHole_2.5mm_Pad_TopBottom +MountingHole:MountingHole_2.5mm_Pad_TopOnly +MountingHole:MountingHole_2.5mm_Pad_Via +MountingHole:MountingHole_2.7mm +MountingHole:MountingHole_2.7mm_M2.5 +MountingHole:MountingHole_2.7mm_M2.5_DIN965 +MountingHole:MountingHole_2.7mm_M2.5_DIN965_Pad +MountingHole:MountingHole_2.7mm_M2.5_DIN965_Pad_TopBottom +MountingHole:MountingHole_2.7mm_M2.5_DIN965_Pad_TopOnly +MountingHole:MountingHole_2.7mm_M2.5_ISO14580 +MountingHole:MountingHole_2.7mm_M2.5_ISO14580_Pad +MountingHole:MountingHole_2.7mm_M2.5_ISO14580_Pad_TopBottom +MountingHole:MountingHole_2.7mm_M2.5_ISO14580_Pad_TopOnly +MountingHole:MountingHole_2.7mm_M2.5_ISO7380 +MountingHole:MountingHole_2.7mm_M2.5_ISO7380_Pad +MountingHole:MountingHole_2.7mm_M2.5_ISO7380_Pad_TopBottom +MountingHole:MountingHole_2.7mm_M2.5_ISO7380_Pad_TopOnly +MountingHole:MountingHole_2.7mm_M2.5_Pad +MountingHole:MountingHole_2.7mm_M2.5_Pad_TopBottom +MountingHole:MountingHole_2.7mm_M2.5_Pad_TopOnly +MountingHole:MountingHole_2.7mm_M2.5_Pad_Via +MountingHole:MountingHole_2.7mm_Pad +MountingHole:MountingHole_2.7mm_Pad_TopBottom +MountingHole:MountingHole_2.7mm_Pad_TopOnly +MountingHole:MountingHole_2.7mm_Pad_Via +MountingHole:MountingHole_2mm +MountingHole:MountingHole_3.2mm_M3 +MountingHole:MountingHole_3.2mm_M3_DIN965 +MountingHole:MountingHole_3.2mm_M3_DIN965_Pad +MountingHole:MountingHole_3.2mm_M3_DIN965_Pad_TopBottom +MountingHole:MountingHole_3.2mm_M3_DIN965_Pad_TopOnly +MountingHole:MountingHole_3.2mm_M3_ISO14580 +MountingHole:MountingHole_3.2mm_M3_ISO14580_Pad +MountingHole:MountingHole_3.2mm_M3_ISO14580_Pad_TopBottom +MountingHole:MountingHole_3.2mm_M3_ISO14580_Pad_TopOnly +MountingHole:MountingHole_3.2mm_M3_ISO7380 +MountingHole:MountingHole_3.2mm_M3_ISO7380_Pad +MountingHole:MountingHole_3.2mm_M3_ISO7380_Pad_TopBottom +MountingHole:MountingHole_3.2mm_M3_ISO7380_Pad_TopOnly +MountingHole:MountingHole_3.2mm_M3_Pad +MountingHole:MountingHole_3.2mm_M3_Pad_TopBottom +MountingHole:MountingHole_3.2mm_M3_Pad_TopOnly +MountingHole:MountingHole_3.2mm_M3_Pad_Via +MountingHole:MountingHole_3.5mm +MountingHole:MountingHole_3.5mm_Pad +MountingHole:MountingHole_3.5mm_Pad_TopBottom +MountingHole:MountingHole_3.5mm_Pad_TopOnly +MountingHole:MountingHole_3.5mm_Pad_Via +MountingHole:MountingHole_3.7mm +MountingHole:MountingHole_3.7mm_Pad +MountingHole:MountingHole_3.7mm_Pad_TopBottom +MountingHole:MountingHole_3.7mm_Pad_TopOnly +MountingHole:MountingHole_3.7mm_Pad_Via +MountingHole:MountingHole_3mm +MountingHole:MountingHole_3mm_Pad +MountingHole:MountingHole_3mm_Pad_TopBottom +MountingHole:MountingHole_3mm_Pad_TopOnly +MountingHole:MountingHole_3mm_Pad_Via +MountingHole:MountingHole_4.3mm_M4 +MountingHole:MountingHole_4.3mm_M4_DIN965 +MountingHole:MountingHole_4.3mm_M4_DIN965_Pad +MountingHole:MountingHole_4.3mm_M4_DIN965_Pad_TopBottom +MountingHole:MountingHole_4.3mm_M4_DIN965_Pad_TopOnly +MountingHole:MountingHole_4.3mm_M4_ISO14580 +MountingHole:MountingHole_4.3mm_M4_ISO14580_Pad +MountingHole:MountingHole_4.3mm_M4_ISO14580_Pad_TopBottom +MountingHole:MountingHole_4.3mm_M4_ISO14580_Pad_TopOnly +MountingHole:MountingHole_4.3mm_M4_ISO7380 +MountingHole:MountingHole_4.3mm_M4_ISO7380_Pad +MountingHole:MountingHole_4.3mm_M4_ISO7380_Pad_TopBottom +MountingHole:MountingHole_4.3mm_M4_ISO7380_Pad_TopOnly +MountingHole:MountingHole_4.3mm_M4_Pad +MountingHole:MountingHole_4.3mm_M4_Pad_TopBottom +MountingHole:MountingHole_4.3mm_M4_Pad_TopOnly +MountingHole:MountingHole_4.3mm_M4_Pad_Via +MountingHole:MountingHole_4.3x6.2mm_M4_Pad +MountingHole:MountingHole_4.3x6.2mm_M4_Pad_Via +MountingHole:MountingHole_4.5mm +MountingHole:MountingHole_4.5mm_Pad +MountingHole:MountingHole_4.5mm_Pad_TopBottom +MountingHole:MountingHole_4.5mm_Pad_TopOnly +MountingHole:MountingHole_4.5mm_Pad_Via +MountingHole:MountingHole_4mm +MountingHole:MountingHole_4mm_Pad +MountingHole:MountingHole_4mm_Pad_TopBottom +MountingHole:MountingHole_4mm_Pad_TopOnly +MountingHole:MountingHole_4mm_Pad_Via +MountingHole:MountingHole_5.3mm_M5 +MountingHole:MountingHole_5.3mm_M5_DIN965 +MountingHole:MountingHole_5.3mm_M5_DIN965_Pad +MountingHole:MountingHole_5.3mm_M5_DIN965_Pad_TopBottom +MountingHole:MountingHole_5.3mm_M5_DIN965_Pad_TopOnly +MountingHole:MountingHole_5.3mm_M5_ISO14580 +MountingHole:MountingHole_5.3mm_M5_ISO14580_Pad +MountingHole:MountingHole_5.3mm_M5_ISO14580_Pad_TopBottom +MountingHole:MountingHole_5.3mm_M5_ISO14580_Pad_TopOnly +MountingHole:MountingHole_5.3mm_M5_ISO7380 +MountingHole:MountingHole_5.3mm_M5_ISO7380_Pad +MountingHole:MountingHole_5.3mm_M5_ISO7380_Pad_TopBottom +MountingHole:MountingHole_5.3mm_M5_ISO7380_Pad_TopOnly +MountingHole:MountingHole_5.3mm_M5_Pad +MountingHole:MountingHole_5.3mm_M5_Pad_TopBottom +MountingHole:MountingHole_5.3mm_M5_Pad_TopOnly +MountingHole:MountingHole_5.3mm_M5_Pad_Via +MountingHole:MountingHole_5.5mm +MountingHole:MountingHole_5.5mm_Pad +MountingHole:MountingHole_5.5mm_Pad_TopBottom +MountingHole:MountingHole_5.5mm_Pad_TopOnly +MountingHole:MountingHole_5.5mm_Pad_Via +MountingHole:MountingHole_5mm +MountingHole:MountingHole_5mm_Pad +MountingHole:MountingHole_5mm_Pad_TopBottom +MountingHole:MountingHole_5mm_Pad_TopOnly +MountingHole:MountingHole_5mm_Pad_Via +MountingHole:MountingHole_6.4mm_M6 +MountingHole:MountingHole_6.4mm_M6_DIN965 +MountingHole:MountingHole_6.4mm_M6_DIN965_Pad +MountingHole:MountingHole_6.4mm_M6_DIN965_Pad_TopBottom +MountingHole:MountingHole_6.4mm_M6_DIN965_Pad_TopOnly +MountingHole:MountingHole_6.4mm_M6_ISO14580 +MountingHole:MountingHole_6.4mm_M6_ISO14580_Pad +MountingHole:MountingHole_6.4mm_M6_ISO14580_Pad_TopBottom +MountingHole:MountingHole_6.4mm_M6_ISO14580_Pad_TopOnly +MountingHole:MountingHole_6.4mm_M6_ISO7380 +MountingHole:MountingHole_6.4mm_M6_ISO7380_Pad +MountingHole:MountingHole_6.4mm_M6_ISO7380_Pad_TopBottom +MountingHole:MountingHole_6.4mm_M6_ISO7380_Pad_TopOnly +MountingHole:MountingHole_6.4mm_M6_Pad +MountingHole:MountingHole_6.4mm_M6_Pad_TopBottom +MountingHole:MountingHole_6.4mm_M6_Pad_TopOnly +MountingHole:MountingHole_6.4mm_M6_Pad_Via +MountingHole:MountingHole_6.5mm +MountingHole:MountingHole_6.5mm_Pad +MountingHole:MountingHole_6.5mm_Pad_TopBottom +MountingHole:MountingHole_6.5mm_Pad_TopOnly +MountingHole:MountingHole_6.5mm_Pad_Via +MountingHole:MountingHole_6mm +MountingHole:MountingHole_6mm_Pad +MountingHole:MountingHole_6mm_Pad_TopBottom +MountingHole:MountingHole_6mm_Pad_TopOnly +MountingHole:MountingHole_6mm_Pad_Via +MountingHole:MountingHole_8.4mm_M8 +MountingHole:MountingHole_8.4mm_M8_Pad +MountingHole:MountingHole_8.4mm_M8_Pad_TopBottom +MountingHole:MountingHole_8.4mm_M8_Pad_TopOnly +MountingHole:MountingHole_8.4mm_M8_Pad_Via +MountingHole:ToolingHole_1.152mm +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H10mm_9771100360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H11mm_9771110360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H12mm_9771120360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H13mm_9771130360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H14mm_9771140360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H15mm_9771150360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H5mm_9771050360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H6mm_9771060360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H7mm_9771070360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H8mm_9771080360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSE-ExternalM3_H9mm_9771090360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H10mm_9774100482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H1mm_9774010482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H2mm_9774020482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H3mm_9774030482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H4mm_9774040482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H5mm_9774050482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H6mm_9774060482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H7mm_9774070482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H8mm_9774080482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-4.5mm_H9mm_9774090482 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H1.5mm_9774015633 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H1mm_9774010633 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H2.5mm_9774025633 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H2.5mm_ThreadDepth1.5mm_97730256332 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H2.5mm_ThreadDepth1.5mm_NoNPTH_97730256330 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H2mm_9774020633 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H3.5mm_ThreadDepth2mm_97730356332 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H3.5mm_ThreadDepth2mm_97730356334 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H3.5mm_ThreadDepth2mm_NoNPTH_97730356330 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H3mm_9774030633 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H3mm_ThreadDepth1.8mm_97730306332 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H3mm_ThreadDepth1.8mm_NoNPTH_97730306330 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H4.5mm_ThreadDepth2mm_97730456332 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H4.5mm_ThreadDepth2mm_97730456334 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H4.5mm_ThreadDepth2mm_NoNPTH_97730456330 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H4mm_ThreadDepth2mm_97730406332 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H4mm_ThreadDepth2mm_97730406334 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H4mm_ThreadDepth2mm_NoNPTH_97730406330 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H5mm_ThreadDepth2mm_97730506332 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H5mm_ThreadDepth2mm_97730506334 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H5mm_ThreadDepth2mm_NoNPTH_97730506330 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H6mm_ThreadDepth2mm_97730606332 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H6mm_ThreadDepth2mm_97730606334 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M1.6_H6mm_ThreadDepth2mm_NoNPTH_97730606330 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H1.5mm_9774015243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H1mm_9774010243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H2.5mm_9774025243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H2mm_9774020243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H3.5mm_9774035243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H3mm_9774030243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H4.5mm_9774045243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H4mm_9774040243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H5mm_9774050243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H6mm_9774060243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H7mm_9774070243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M2_H8mm_9774080243 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H1.5mm_9774015360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H10mm_9774100360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H11mm_9774110360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H12mm_9774120360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H13mm_9774130360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H14mm_9774140360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H15mm_9774150360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H1mm_9774010360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H2.5mm_9774025360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H2mm_9774020360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H3mm_9774030360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H4mm_9774040360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H5mm_9774050360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H6mm_9774060360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H7mm_9774070360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H8mm_9774080360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSI-M3_H9mm_9774090360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H10.6mm_ReverseMount_9775106960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H11.6mm_ReverseMount_9775116960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H2.6mm_ReverseMount_9775026960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H3.1mm_ReverseMount_9775031960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H3.6mm_ReverseMount_9775036960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H4.1mm_ReverseMount_9775041960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H4.6mm_ReverseMount_9775046960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H5.1mm_ReverseMount_9775051960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H5.6mm_ReverseMount_9775056960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H6.6mm_ReverseMount_9775066960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H7.6mm_ReverseMount_9775076960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H8.6mm_ReverseMount_9775086960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-3.2mm_H9.6mm_ReverseMount_9775096960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H10.6mm_ReverseMount_9775106360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H11.6mm_ReverseMount_9775116360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H2.6mm_ReverseMount_9775026360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H3.1mm_ReverseMount_9775031360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H3.6mm_ReverseMount_9775036360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H4.1mm_ReverseMount_9775041360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H4.6mm_ReverseMount_9775046360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H5.1mm_ReverseMount_9775051360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H5.6mm_ReverseMount_9775056360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H6.6mm_ReverseMount_9775066360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H7.6mm_ReverseMount_9775076360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H8.6mm_ReverseMount_9775086360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSR-M3_H9.6mm_ReverseMount_9775096360 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H10mm_SnapRivet_9776100960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H2.5mm_SnapRivet_9776025960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H2mm_SnapRivet_9776020960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H3mm_SnapRivet_9776030960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H4mm_SnapRivet_9776040960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H5mm_SnapRivet_9776050960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H6mm_SnapRivet_9776060960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H7mm_SnapRivet_9776070960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H8mm_SnapRivet_9776080960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMSSR-3.3mm_H9mm_SnapRivet_9776090960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H1.5mm_9774015943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H1mm_9774010943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H2.5mm_9774025943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H2mm_9774020943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H3.5mm_9774035943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H3mm_9774030943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H4.5mm_9774045943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H4mm_9774040943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H5mm_9774050943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H6mm_9774060943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H7mm_9774070943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.25mm_H8mm_9774080943 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H1.5mm_9774015951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H10mm_9774100951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H1mm_9774010951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H2.5mm_9774025951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H2mm_9774020951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H3mm_9774030951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H4mm_9774040951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H5.5mm_9774055951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H5mm_9774050951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H6.5mm_9774065951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H6mm_9774060951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H7mm_9774070951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H8mm_9774080951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-2.7mm_H9mm_9774090951 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H1.5mm_9774015960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H10mm_9774100960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H11mm_9774110960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H12mm_9774120960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H13mm_9774130960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H14mm_9774140960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H15mm_9774150960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H1mm_9774010960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H2.5mm_9774025960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H2mm_9774020960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H3mm_9774030960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H4mm_9774040960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H5mm_9774050960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H6mm_9774060960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H7mm_9774070960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H8mm_9774080960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-3.3mm_H9mm_9774090960 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H10mm_9774100982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H1mm_9774010982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H2mm_9774020982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H3mm_9774030982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H4mm_9774040982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H5mm_9774050982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H6mm_9774060982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H7mm_9774070982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H8mm_9774080982 +Mounting_Wuerth:Mounting_Wuerth_WA-SMST-4.5mm_H9mm_9774090982 +Mounting_Wuerth:Mounting_Wuerth_WP-SMRA-D3.3mm_L7mm_7466300_Horizontal +Mounting_Wuerth:Mounting_Wuerth_WP-SMRA-M3_L7mm_7466303_Horizontal +NetTie:NetTie-2_SMD_Pad0.5mm +NetTie:NetTie-2_SMD_Pad2.0mm +NetTie:NetTie-2_THT_Pad0.3mm +NetTie:NetTie-2_THT_Pad1.0mm +NetTie:NetTie-3_SMD_Pad0.5mm +NetTie:NetTie-3_SMD_Pad2.0mm +NetTie:NetTie-3_THT_Pad0.3mm +NetTie:NetTie-3_THT_Pad1.0mm +NetTie:NetTie-4_SMD_Pad0.5mm +NetTie:NetTie-4_SMD_Pad2.0mm +NetTie:NetTie-4_THT_Pad0.3mm +NetTie:NetTie-4_THT_Pad1.0mm +OptoDevice:ADNS-9800 +OptoDevice:AGILENT_HFBR-152x +OptoDevice:AGILENT_HFBR-252x +OptoDevice:AMS_TSL2550_SMD +OptoDevice:AMS_TSL25911FN +OptoDevice:Broadcom_AFBR-16xxZ_Horizontal +OptoDevice:Broadcom_AFBR-16xxZ_Tilted +OptoDevice:Broadcom_AFBR-16xxZ_Vertical +OptoDevice:Broadcom_APDS-9160-003 +OptoDevice:Broadcom_APDS-9301 +OptoDevice:Broadcom_DFN-6_2x2mm_P0.65mm +OptoDevice:Broadcom_LGA-8_2x2mm_P0.53mm +OptoDevice:Broadcom_LGA-8_2x2mm_P0.5mm +OptoDevice:Everlight_IRM-H6xxT +OptoDevice:Everlight_ITR1201SR10AR +OptoDevice:Everlight_ITR8307 +OptoDevice:Everlight_ITR8307F43 +OptoDevice:Everlight_ITR8307_Reverse +OptoDevice:Everlight_ITR9608-F +OptoDevice:Finder_34.81 +OptoDevice:Hamamatsu_C12880 +OptoDevice:Hamamatsu_S13360-30CS +OptoDevice:Kingbright_KPS-3227 +OptoDevice:Kingbright_KPS-5130 +OptoDevice:Kingbright_KRC011_Horizontal +OptoDevice:Kingbright_KRC011_Vertical +OptoDevice:Kodenshi_LG206D +OptoDevice:Kodenshi_LG206L +OptoDevice:Kodenshi_SG105 +OptoDevice:Kodenshi_SG105F +OptoDevice:Kodenshi_SG105_Reverse +OptoDevice:LaserDiode_TO18-D5.6-3 +OptoDevice:LaserDiode_TO3.3-D3.3-3 +OptoDevice:LaserDiode_TO38ICut-3 +OptoDevice:LaserDiode_TO5-D9-3 +OptoDevice:LaserDiode_TO56-3 +OptoDevice:Lightpipe_Bivar_RLP1-400-650 +OptoDevice:Lightpipe_Bivar_SLP3-150-100-F +OptoDevice:Lightpipe_Bivar_SLP3-150-100-R +OptoDevice:Lightpipe_Bivar_SLP3-150-150-F +OptoDevice:Lightpipe_Bivar_SLP3-150-150-R +OptoDevice:Lightpipe_Bivar_SLP3-150-200-R +OptoDevice:Lightpipe_Bivar_SLP3-150-250-F +OptoDevice:Lightpipe_Bivar_SLP3-150-250-R +OptoDevice:Lightpipe_Bivar_SLP3-150-300-F +OptoDevice:Lightpipe_Bivar_SLP3-150-300-R +OptoDevice:Lightpipe_Bivar_SLP3-150-450-R +OptoDevice:Lightpipe_Dialight_515-1064F +OptoDevice:Lightpipe_LPF-C012303S +OptoDevice:Lightpipe_LPF-C013301S +OptoDevice:Lightpipe_Mentor_1275.x00x +OptoDevice:Lightpipe_Mentor_1276.1004 +OptoDevice:Lightpipe_Mentor_1276.2004 +OptoDevice:Lite-On_LTR-303ALS-01 +OptoDevice:Luna_NSL-32 +OptoDevice:Maxim_OLGA-14_3.3x5.6mm_P0.8mm +OptoDevice:OnSemi_CASE100AQ +OptoDevice:OnSemi_CASE100CY +OptoDevice:ONSemi_QSE15x +OptoDevice:Osram_BP104-SMD +OptoDevice:Osram_BPW34S-SMD +OptoDevice:Osram_BPW82 +OptoDevice:Osram_DIL2_4.3x4.65mm_P5.08mm +OptoDevice:Osram_LPT80A +OptoDevice:Osram_SFH205 +OptoDevice:Osram_SFH2201 +OptoDevice:Osram_SFH225 +OptoDevice:Osram_SFH2430 +OptoDevice:Osram_SFH2440 +OptoDevice:Osram_SFH3710 +OptoDevice:Osram_SFH9x0x +OptoDevice:Osram_SMD-SmartDIL +OptoDevice:Panasonic_APV-AQY_SSOP-4_4.45x2.65mm_P1.27mm +OptoDevice:PerkinElmer_VTL5C +OptoDevice:PerkinElmer_VTL5Cx2 +OptoDevice:Renesas_DFN-6_1.5x1.6mm_P0.5mm +OptoDevice:Rohm_RPR-0720 +OptoDevice:R_LDR_10x8.5mm_P7.6mm_Vertical +OptoDevice:R_LDR_11x9.4mm_P8.2mm_Vertical +OptoDevice:R_LDR_12x10.8mm_P9.0mm_Vertical +OptoDevice:R_LDR_4.9x4.2mm_P2.54mm_Vertical +OptoDevice:R_LDR_5.0x4.1mm_P3mm_Vertical +OptoDevice:R_LDR_5.1x4.3mm_P3.4mm_Vertical +OptoDevice:R_LDR_5.2x5.2mm_P3.5mm_Horizontal +OptoDevice:R_LDR_7x6mm_P5.1mm_Vertical +OptoDevice:R_LDR_D13.8mm_P9.0mm_Vertical +OptoDevice:R_LDR_D20mm_P17.5mm_Vertical +OptoDevice:R_LDR_D6.4mm_P3.4mm_Vertical +OptoDevice:Sharp_GP2S700HCP +OptoDevice:Sharp_GP2Y0A41SK0F +OptoDevice:Sharp_IS471F +OptoDevice:Sharp_IS485 +OptoDevice:Siemens_SFH900 +OptoDevice:ST_VL53L0X +OptoDevice:Toshiba_TORX170_TORX173_TORX193_TORX194 +OptoDevice:Toshiba_TOTX170_TOTX173_TOTX193_TOTX194 +OptoDevice:Vishay_CAST-3Pin +OptoDevice:Vishay_CNY70 +OptoDevice:Vishay_MINICAST-3Pin +OptoDevice:Vishay_MINIMOLD-3Pin +OptoDevice:Vishay_MOLD-3Pin +OptoDevice:Vishay_TCRT5000 +Oscillator:Oscillator_DIP-14 +Oscillator:Oscillator_DIP-14_LargePads +Oscillator:Oscillator_DIP-8 +Oscillator:Oscillator_DIP-8_LargePads +Oscillator:Oscillator_OCXO_Morion_MV267 +Oscillator:Oscillator_OCXO_Morion_MV317 +Oscillator:Oscillator_SeikoEpson_SG-8002DB +Oscillator:Oscillator_SeikoEpson_SG-8002DC +Oscillator:Oscillator_SMD_Abracon_ABLNO +Oscillator:Oscillator_SMD_Abracon_ASCO-4Pin_1.6x1.2mm +Oscillator:Oscillator_SMD_Abracon_ASDMB-4Pin_2.5x2.0mm +Oscillator:Oscillator_SMD_Abracon_ASE-4Pin_3.2x2.5mm +Oscillator:Oscillator_SMD_Abracon_ASE-4Pin_3.2x2.5mm_HandSoldering +Oscillator:Oscillator_SMD_Abracon_ASV-4Pin_7.0x5.1mm +Oscillator:Oscillator_SMD_Abracon_ASV-4Pin_7.0x5.1mm_HandSoldering +Oscillator:Oscillator_SMD_Diodes_FN-4Pin_7.0x5.0mm +Oscillator:Oscillator_SMD_ECS_2520MV-xxx-xx-4Pin_2.5x2.0mm +Oscillator:Oscillator_SMD_EuroQuartz_XO32-4Pin_3.2x2.5mm +Oscillator:Oscillator_SMD_EuroQuartz_XO32-4Pin_3.2x2.5mm_HandSoldering +Oscillator:Oscillator_SMD_EuroQuartz_XO53-4Pin_5.0x3.2mm +Oscillator:Oscillator_SMD_EuroQuartz_XO53-4Pin_5.0x3.2mm_HandSoldering +Oscillator:Oscillator_SMD_EuroQuartz_XO91-4Pin_7.0x5.0mm +Oscillator:Oscillator_SMD_EuroQuartz_XO91-4Pin_7.0x5.0mm_HandSoldering +Oscillator:Oscillator_SMD_Fordahl_DFAS1-6Pin_14.8x9.1mm +Oscillator:Oscillator_SMD_Fordahl_DFAS11-4Pin_7.0x5.0mm +Oscillator:Oscillator_SMD_Fordahl_DFAS11-4Pin_7.0x5.0mm_HandSoldering +Oscillator:Oscillator_SMD_Fordahl_DFAS15-4Pin_5.0x3.2mm +Oscillator:Oscillator_SMD_Fordahl_DFAS15-4Pin_5.0x3.2mm_HandSoldering +Oscillator:Oscillator_SMD_Fordahl_DFAS2-4Pin_7.3x5.1mm +Oscillator:Oscillator_SMD_Fordahl_DFAS2-4Pin_7.3x5.1mm_HandSoldering +Oscillator:Oscillator_SMD_Fordahl_DFAS3-4Pin_9.1x7.2mm +Oscillator:Oscillator_SMD_Fordahl_DFAS3-4Pin_9.1x7.2mm_HandSoldering +Oscillator:Oscillator_SMD_Fordahl_DFAS7-4Pin_19.9x12.9mm +Oscillator:Oscillator_SMD_Fordahl_DFAS7-4Pin_19.9x12.9mm_HandSoldering +Oscillator:Oscillator_SMD_Fox_FT5H_5.0x3.2mm +Oscillator:Oscillator_SMD_IDT_JS6-6_5.0x3.2mm_P1.27mm +Oscillator:Oscillator_SMD_IDT_JU6-6_7.0x5.0mm_P2.54mm +Oscillator:Oscillator_SMD_IQD_IQXO70-4Pin_7.5x5.0mm +Oscillator:Oscillator_SMD_IQD_IQXO70-4Pin_7.5x5.0mm_HandSoldering +Oscillator:Oscillator_SMD_Kyocera_2520-6Pin_2.5x2.0mm +Oscillator:Oscillator_SMD_Kyocera_KC2520Z-4Pin_2.5x2.0mm +Oscillator:Oscillator_SMD_OCXO_ConnorWinfield_OH300 +Oscillator:Oscillator_SMD_SeikoEpson_SG210-4Pin_2.5x2.0mm +Oscillator:Oscillator_SMD_SeikoEpson_SG210-4Pin_2.5x2.0mm_HandSoldering +Oscillator:Oscillator_SMD_SeikoEpson_SG3030CM +Oscillator:Oscillator_SMD_SeikoEpson_SG8002CA-4Pin_7.0x5.0mm +Oscillator:Oscillator_SMD_SeikoEpson_SG8002CA-4Pin_7.0x5.0mm_HandSoldering +Oscillator:Oscillator_SMD_SeikoEpson_SG8002CE-4Pin_3.2x2.5mm +Oscillator:Oscillator_SMD_SeikoEpson_SG8002CE-4Pin_3.2x2.5mm_HandSoldering +Oscillator:Oscillator_SMD_SeikoEpson_SG8002JA-4Pin_14.0x8.7mm +Oscillator:Oscillator_SMD_SeikoEpson_SG8002JA-4Pin_14.0x8.7mm_HandSoldering +Oscillator:Oscillator_SMD_SeikoEpson_SG8002JC-4Pin_10.5x5.0mm +Oscillator:Oscillator_SMD_SeikoEpson_SG8002JC-4Pin_10.5x5.0mm_HandSoldering +Oscillator:Oscillator_SMD_SeikoEpson_SG8002LB-4Pin_5.0x3.2mm +Oscillator:Oscillator_SMD_SeikoEpson_SG8002LB-4Pin_5.0x3.2mm_HandSoldering +Oscillator:Oscillator_SMD_SeikoEpson_TG2520SMN-xxx-xxxxxx-4Pin_2.5x2.0mm +Oscillator:Oscillator_SMD_SI570_SI571_HandSoldering +Oscillator:Oscillator_SMD_SI570_SI571_Standard +Oscillator:Oscillator_SMD_Silicon_Labs_LGA-6_2.5x3.2mm_P1.25mm +Oscillator:Oscillator_SMD_SiTime_PQFD-6L_3.2x2.5mm +Oscillator:Oscillator_SMD_SiTime_SiT9121-6Pin_3.2x2.5mm +Oscillator:Oscillator_SMD_SiT_PQFN-4Pin_2.0x1.6mm +Oscillator:Oscillator_SMD_SiT_PQFN-4Pin_2.5x2.0mm +Oscillator:Oscillator_SMD_SiT_PQFN-4Pin_3.2x2.5mm +Oscillator:Oscillator_SMD_SiT_PQFN-4Pin_5.0x3.2mm +Oscillator:Oscillator_SMD_SiT_PQFN-4Pin_7.0x5.0mm +Oscillator:Oscillator_SMD_TCXO_G158 +Oscillator:Oscillator_SMD_TXC_7C-4Pin_5.0x3.2mm +Oscillator:Oscillator_SMD_TXC_7C-4Pin_5.0x3.2mm_HandSoldering +Package_BGA:Alliance_TFBGA-36_6x8mm_Layout6x8_P0.75mm +Package_BGA:Alliance_TFBGA-54_8x8mm_Layout9x9_P0.8mm +Package_BGA:Analog_BGA-165_11.9x16mm_Layout11x15_P1.0mm +Package_BGA:Analog_BGA-209_9.5x16mm_Layout11x19_P0.8mm +Package_BGA:Analog_BGA-28_4x6.25mm_Layout4x7_P0.8mm +Package_BGA:Analog_BGA-49_6.25x6.25mm_Layout7x7_P0.8mm +Package_BGA:Analog_BGA-77_9x15mm_Layout7x11_P1.27mm +Package_BGA:BGA-100_11.0x11.0mm_Layout10x10_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD +Package_BGA:BGA-100_6.0x6.0mm_Layout11x11_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD +Package_BGA:BGA-1023_33.0x33.0mm_Layout32x32_P1.0mm +Package_BGA:BGA-1156_35.0x35.0mm_Layout34x34_P1.0mm +Package_BGA:BGA-121_9.0x9.0mm_Layout11x11_P0.8mm_Ball0.4mm_Pad0.35mm_NSMD +Package_BGA:BGA-1295_37.5x37.5mm_Layout36x36_P1.0mm +Package_BGA:BGA-132_12x18mm_Layout11x17_P1.0mm +Package_BGA:BGA-144_13.0x13.0mm_Layout12x12_P1.0mm +Package_BGA:BGA-144_7.0x7.0mm_Layout13x13_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD +Package_BGA:BGA-152_14x18mm_Layout13x17_P0.5mm +Package_BGA:BGA-153_8.0x8.0mm_Layout15x15_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD +Package_BGA:BGA-169_11.0x11.0mm_Layout13x13_P0.8mm_Ball0.5mm_Pad0.4mm_NSMD +Package_BGA:BGA-16_1.92x1.92mm_Layout4x4_P0.5mm +Package_BGA:BGA-196_15x15mm_Layout14x14_P1.0mm +Package_BGA:BGA-200_10x14.5mm_Layout12x22_P0.8x0.65mm +Package_BGA:BGA-256_11.0x11.0mm_Layout20x20_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD +Package_BGA:BGA-256_14.0x14.0mm_Layout16x16_P0.8mm_Ball0.45mm_Pad0.32mm_NSMD +Package_BGA:BGA-256_17.0x17.0mm_Layout16x16_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD +Package_BGA:BGA-25_6.35x6.35mm_Layout5x5_P1.27mm +Package_BGA:BGA-324_15.0x15.0mm_Layout18x18_P0.8mm_Ball0.5mm_Pad0.4mm_NSMD +Package_BGA:BGA-324_15x15mm_Layout18x18_P0.8mm +Package_BGA:BGA-324_19.0x19.0mm_Layout18x18_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD +Package_BGA:BGA-352_35.0x35.0mm_Layout26x26_P1.27mm +Package_BGA:BGA-36_3.396x3.466mm_Layout6x6_P0.4mm_Ball0.25mm_Pad0.2mm_NSMD +Package_BGA:BGA-400_21.0x21.0mm_Layout20x20_P1.0mm +Package_BGA:BGA-484_23.0x23.0mm_Layout22x22_P1.0mm +Package_BGA:BGA-48_8.0x9.0mm_Layout6x8_P0.8mm +Package_BGA:BGA-529_19x19mm_Layout23x23_P0.8mm +Package_BGA:BGA-624_21x21mm_Layout25x25_P0.8mm +Package_BGA:BGA-625_21.0x21.0mm_Layout25x25_P0.8mm +Package_BGA:BGA-64_9.0x9.0mm_Layout10x10_P0.8mm +Package_BGA:BGA-672_27.0x27.0mm_Layout26x26_P1.0mm_Ball0.6mm_Pad0.5mm_NSMD +Package_BGA:BGA-676_27.0x27.0mm_Layout26x26_P1.0mm_Ball0.6mm_Pad0.5mm_NSMD +Package_BGA:BGA-68_5.0x5.0mm_Layout9x9_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD +Package_BGA:BGA-81_4.496x4.377mm_Layout9x9_P0.4mm_Ball0.25mm_Pad0.2mm_NSMD +Package_BGA:BGA-90_8.0x13.0mm_Layout2x3x15_P0.8mm +Package_BGA:BGA-96_9.0x13.0mm_Layout2x3x16_P0.8mm +Package_BGA:BGA-9_1.6x1.6mm_Layout3x3_P0.5mm +Package_BGA:EPC_BGA-4_0.9x0.9mm_Layout2x2_P0.45mm +Package_BGA:FB-BGA-484_23.0x23.0mm_Layout22x22_P1.0mm +Package_BGA:FBGA-78_7.5x11mm_Layout2x3x13_P0.8mm +Package_BGA:Fujitsu_WLP-15_2.28x3.092mm_Layout3x5_P0.4mm +Package_BGA:Infineon_LFBGA-292_17x17mm_Layout20x20_P0.8mm +Package_BGA:Infineon_TFBGA-48_6x10mm_Layout6x8_P0.75mm +Package_BGA:Lattice_caBGA-381_17x17mm_Layout20x20_P0.8mm +Package_BGA:Lattice_caBGA-381_17x17mm_Layout20x20_P0.8mm_SMD +Package_BGA:Lattice_caBGA-756_27x27mm_Layout32x32_P0.8mm +Package_BGA:Lattice_iCE40_csBGA-132_8x8mm_Layout14x14_P0.5mm +Package_BGA:LFBGA-100_10x10mm_Layout10x10_P0.8mm +Package_BGA:LFBGA-144_10x10mm_Layout12x12_P0.8mm +Package_BGA:LFBGA-153_11.5x13mm_Layout14x14_P0.5mm +Package_BGA:LFBGA-169_12x16mm_Layout14x28_P0.5mm +Package_BGA:LFBGA-169_12x18mm_Layout14x28_P0.5mm +Package_BGA:LFBGA-169_14x18mm_Layout14x28_P0.5mm +Package_BGA:LFBGA-289_14x14mm_Layout17x17_P0.8mm +Package_BGA:LFBGA-400_16x16mm_Layout20x20_P0.8mm +Package_BGA:LFBGA-484_18x18mm_Layout22x22_P0.8mm +Package_BGA:Linear_BGA-133_15.0x15.0mm_Layout12x12_P1.27mm +Package_BGA:MAPBGA-272_9x9mm_Layout17x17_P0.5mm +Package_BGA:MAPBGA-289_14x14mm_Layout17x17_P0.8mm +Package_BGA:Maxim_WLP-12 +Package_BGA:Maxim_WLP-12_2.008x1.608mm_Layout4x3_P0.4mm +Package_BGA:Maxim_WLP-9_1.595x1.415_Layout3x3_P0.4mm_Ball0.27mm_Pad0.25mm_NSMD +Package_BGA:Microchip_TFBGA-196_11x11mm_Layout14x14_P0.75mm_SMD +Package_BGA:Micron_FBGA-78_7.5x10.6mm_Layout9x13_P0.8mm +Package_BGA:Micron_FBGA-78_8x10.5mm_Layout9x13_P0.8mm +Package_BGA:Micron_FBGA-78_9x10.5mm_Layout9x13_P0.8mm +Package_BGA:Micron_FBGA-96_7.5x13.5mm_Layout9x16_P0.8mm +Package_BGA:Micron_FBGA-96_8x14mm_Layout9x16_P0.8mm +Package_BGA:Micron_FBGA-96_9x14mm_Layout9x16_P0.8mm +Package_BGA:NXP_VFBGA-42_2.6x3mm_Layout6x7_P0.4mm +Package_BGA:ST_LFBGA-354_16x16mm_Layout19x19_P0.8mm +Package_BGA:ST_LFBGA-448_18x18mm_Layout22x22_P0.8mm +Package_BGA:ST_TFBGA-169_7x7mm_Layout13x13_P0.5mm +Package_BGA:ST_TFBGA-225_13x13mm_Layout15x15_P0.8mm +Package_BGA:ST_TFBGA-257_10x10mm_Layout19x19_P0.5mmP0.65mm +Package_BGA:ST_TFBGA-320_11x11mm_Layout21x21_P0.5mm +Package_BGA:ST_TFBGA-361_12x12mm_Layout23x23_P0.5mmP0.65mm +Package_BGA:ST_UFBGA-121_6x6mm_Layout11x11_P0.5mm +Package_BGA:ST_UFBGA-129_7x7mm_Layout13x13_P0.5mm +Package_BGA:ST_UFBGA-59_5x5mm_Layout8x8_P0.5mm +Package_BGA:ST_UFBGA-73_5x5mm_Layout9x9_P0.5mm +Package_BGA:ST_UFBGA-81_5x5mm_Layout9x9_P0.5mm +Package_BGA:ST_uTFBGA-36_3.6x3.6mm_Layout6x6_P0.5mm +Package_BGA:Texas_BGA-289_15x15mm_Layout17x17_P0.8mm +Package_BGA:Texas_DSBGA-10_1.36x1.86mm_Layout3x4_P0.5mm +Package_BGA:Texas_DSBGA-12_1.36x1.86mm_Layout3x4_P0.5mm +Package_BGA:Texas_DSBGA-16_2.39x2.39mm_Layout4x4_P0.5mm +Package_BGA:Texas_DSBGA-28_1.9x3mm_Layout4x7_P0.4mm +Package_BGA:Texas_DSBGA-49_3.33x3.488mm_Layout7x7_P0.4mm +Package_BGA:Texas_DSBGA-5_0.822x1.116mm_Layout2x1x2_P0.4mm +Package_BGA:Texas_DSBGA-5_0.8875x1.3875mm_Layout2x3_P0.5mm +Package_BGA:Texas_DSBGA-5_1.5855x1.6365mm_Layout3x2_P0.5mm +Package_BGA:Texas_DSBGA-64_3.415x3.535mm_Layout8x8_P0.4mm +Package_BGA:Texas_DSBGA-6_0.704x1.054mm_Layout2x3_P0.35mm +Package_BGA:Texas_DSBGA-6_0.757x1.01mm_Layout2x3_P0.35mm +Package_BGA:Texas_DSBGA-6_0.855x1.255mm_Layout2x3_P0.4mm_LevelB +Package_BGA:Texas_DSBGA-6_0.855x1.255mm_Layout2x3_P0.4mm_LevelC +Package_BGA:Texas_DSBGA-6_0.95x1.488mm_Layout2x3_P0.4mm +Package_BGA:Texas_DSBGA-6_0.9x1.4mm_Layout2x3_P0.5mm +Package_BGA:Texas_DSBGA-8_0.705x1.468mm_Layout2x4_P0.4mm +Package_BGA:Texas_DSBGA-8_0.9x1.9mm_Layout2x4_P0.5mm +Package_BGA:Texas_DSBGA-8_1.43x1.41mm_Layout3x3_P0.5mm +Package_BGA:Texas_DSBGA-8_1.5195x1.5195mm_Layout3x3_P0.5mm +Package_BGA:Texas_DSBGA-9_1.4715x1.4715mm_Layout3x3_P0.5mm +Package_BGA:Texas_DSBGA-9_1.62x1.58mm_Layout3x3_P0.5mm +Package_BGA:Texas_MicroStar_Junior_BGA-113_7x7mm_Layout12x12_P0.5mm +Package_BGA:Texas_MicroStar_Junior_BGA-12_2.0x2.5mm_Layout4x3_P0.5mm +Package_BGA:Texas_MicroStar_Junior_BGA-80_5.0x5.0mm_Layout9x9_P0.5mm +Package_BGA:Texas_PicoStar_BGA-4_0.758x0.758mm_Layout2x2_P0.4mm +Package_BGA:Texas_YFP0020_DSBGA-20_1.588x1.988mm_Layout4x5_P0.4mm +Package_BGA:TFBGA-100_5.5x5.5mm_Layout10x10_P0.5mm +Package_BGA:TFBGA-100_8x8mm_Layout10x10_P0.8mm +Package_BGA:TFBGA-100_9.0x9.0mm_Layout10x10_P0.8mm +Package_BGA:TFBGA-121_10x10mm_Layout11x11_P0.8mm +Package_BGA:TFBGA-169_9x9mm_Layout13x13_P0.65mm +Package_BGA:TFBGA-216_13x13mm_Layout15x15_P0.8mm +Package_BGA:TFBGA-225_10x10mm_Layout15x15_P0.65mm +Package_BGA:TFBGA-256_13x13mm_Layout16x16_P0.8mm +Package_BGA:TFBGA-265_14x14mm_Layout17x17_P0.8mm +Package_BGA:TFBGA-289_9x9mm_Layout17x17_P0.5mm +Package_BGA:TFBGA-324_12x12mm_Layout18x18_P0.65mm +Package_BGA:TFBGA-361_13x13mm_Layout19x19_P0.65mm +Package_BGA:TFBGA-48_6x10mm_Layout6x8_P0.75mm +Package_BGA:TFBGA-49_3x3mm_Layout7x7_P0.4mm +Package_BGA:TFBGA-576_16x16mm_Layout24x24_P0.65mm +Package_BGA:TFBGA-644_19x19mm_Layout28x28_P0.65mm +Package_BGA:TFBGA-64_5x5mm_Layout8x8_P0.5mm +Package_BGA:TFBGA-81_5x5mm_Layout9x9_P0.5mm +Package_BGA:UCBGA-36_2.5x2.5mm_Layout6x6_P0.4mm +Package_BGA:UCBGA-49_3x3mm_Layout7x7_P0.4mm +Package_BGA:UCBGA-81_4x4mm_Layout9x9_P0.4mm +Package_BGA:UFBGA-100_7x7mm_Layout12x12_P0.5mm +Package_BGA:UFBGA-132_7x7mm_Layout12x12_P0.5mm +Package_BGA:UFBGA-132_7x7mm_P0.5mm +Package_BGA:UFBGA-144_10x10mm_Layout12x12_P0.8mm +Package_BGA:UFBGA-144_7x7mm_Layout12x12_P0.5mm +Package_BGA:UFBGA-15_3.0x3.0mm_Layout4x4_P0.65mm +Package_BGA:UFBGA-169_7x7mm_Layout13x13_P0.5mm +Package_BGA:UFBGA-201_10x10mm_Layout15x15_P0.65mm +Package_BGA:UFBGA-32_4.0x4.0mm_Layout6x6_P0.5mm +Package_BGA:UFBGA-64_5x5mm_Layout8x8_P0.5mm +Package_BGA:VFBGA-100_7.0x7.0mm_Layout10x10_P0.65mm +Package_BGA:VFBGA-49_5.0x5.0mm_Layout7x7_P0.65mm +Package_BGA:VFBGA-86_6x6mm_Layout10x10_P0.55mm +Package_BGA:WLP-4_0.728x0.728mm_Layout2x2_P0.35mm +Package_BGA:WLP-4_0.83x0.83mm_P0.4mm +Package_BGA:WLP-4_0.86x0.86mm_P0.4mm +Package_BGA:WLP-9_1.468x1.448mm_Layout3x3_P0.4mm +Package_BGA:XBGA-121_10x10mm_Layout11x11_P0.8mm +Package_BGA:XFBGA-121_8x8mm_Layout11x11_P0.65mm +Package_BGA:XFBGA-36_3.5x3.5mm_Layout6x6_P0.5mm +Package_BGA:XFBGA-64_5.0x5.0mm_Layout8x8_P0.5mm +Package_BGA:Xilinx_CLG225 +Package_BGA:Xilinx_CLG400 +Package_BGA:Xilinx_CLG484_CLG485 +Package_BGA:Xilinx_CPG236 +Package_BGA:Xilinx_CPG238 +Package_BGA:Xilinx_CPGA196 +Package_BGA:Xilinx_CSG324 +Package_BGA:Xilinx_CSG325 +Package_BGA:Xilinx_CSGA225 +Package_BGA:Xilinx_CSGA324 +Package_BGA:Xilinx_FBG484 +Package_BGA:Xilinx_FBG676 +Package_BGA:Xilinx_FBG900 +Package_BGA:Xilinx_FFG1156 +Package_BGA:Xilinx_FFG1157_FFG1158 +Package_BGA:Xilinx_FFG1761 +Package_BGA:Xilinx_FFG1926_FFG1927_FFG1928_FFG1930 +Package_BGA:Xilinx_FFG676 +Package_BGA:Xilinx_FFG900_FFG901 +Package_BGA:Xilinx_FFV1761 +Package_BGA:Xilinx_FGG484 +Package_BGA:Xilinx_FGG676 +Package_BGA:Xilinx_FGGA484 +Package_BGA:Xilinx_FGGA676 +Package_BGA:Xilinx_FHG1761 +Package_BGA:Xilinx_FLG1925_FLG1926_FLG1928_FLG1930 +Package_BGA:Xilinx_FTG256 +Package_BGA:Xilinx_FTGB196 +Package_BGA:Xilinx_RB484 +Package_BGA:Xilinx_RB676 +Package_BGA:Xilinx_RF1156 +Package_BGA:Xilinx_RF1157_RF1158 +Package_BGA:Xilinx_RF1761 +Package_BGA:Xilinx_RF1930 +Package_BGA:Xilinx_RF676 +Package_BGA:Xilinx_RF900 +Package_BGA:Xilinx_RFG676 +Package_BGA:Xilinx_RS484 +Package_BGA:Xilinx_SBG484 +Package_BGA:Xilinx_SBG485 +Package_CSP:Analog_LFCSP-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm +Package_CSP:Analog_LFCSP-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm_ThermalVias +Package_CSP:Analog_LFCSP-16-1EP_4x4mm_P0.65mm_EP2.35x2.35mm +Package_CSP:Analog_LFCSP-16-1EP_4x4mm_P0.65mm_EP2.35x2.35mm_ThermalVias +Package_CSP:Analog_LFCSP-8-1EP_3x3mm_P0.5mm_EP1.53x1.85mm +Package_CSP:Analog_LFCSP-UQ-10_1.3x1.6mm_P0.4mm +Package_CSP:Anpec_WLCSP-20_1.76x2.03mm_Layout4x5_P0.4mm +Package_CSP:Dialog_WLCSP-34_4.54x1.66mm_Layout17x4_P0.25x0.34mm +Package_CSP:DiodesInc_GEA20_WLCSP-20_1.7x2.1mm_Layout4x5_P0.4mm +Package_CSP:Efinix_WLCSP-64_3.5353x3.3753mm_Layout8x8_P0.4mm +Package_CSP:Efinix_WLCSP-80_4.4567x3.5569mm_Layout10x8_P0.4mm +Package_CSP:LFCSP-10_2x2mm_P0.5mm +Package_CSP:LFCSP-16-1EP_3x3mm_P0.5mm_EP1.3x1.3mm +Package_CSP:LFCSP-16-1EP_3x3mm_P0.5mm_EP1.3x1.3mm_ThermalVias +Package_CSP:LFCSP-16-1EP_3x3mm_P0.5mm_EP1.5x1.5mm +Package_CSP:LFCSP-16-1EP_3x3mm_P0.5mm_EP1.6x1.6mm +Package_CSP:LFCSP-16-1EP_3x3mm_P0.5mm_EP1.6x1.6mm_ThermalVias +Package_CSP:LFCSP-16-1EP_3x3mm_P0.5mm_EP1.7x1.7mm +Package_CSP:LFCSP-16-1EP_3x3mm_P0.5mm_EP1.7x1.7mm_ThermalVias +Package_CSP:LFCSP-16-1EP_3x3mm_P0.5mm_EP1.854x1.854mm +Package_CSP:LFCSP-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm +Package_CSP:LFCSP-16-1EP_4x4mm_P0.65mm_EP2.4x2.4mm +Package_CSP:LFCSP-16-1EP_4x4mm_P0.65mm_EP2.4x2.4mm_ThermalVias +Package_CSP:LFCSP-16-1EP_4x4mm_P0.65mm_EP2.6x2.6mm +Package_CSP:LFCSP-16-1EP_4x4mm_P0.65mm_EP2.6x2.6mm_ThermalVias +Package_CSP:LFCSP-16_3x3mm_P0.5mm +Package_CSP:LFCSP-20-1EP_4x4mm_P0.5mm_EP2.1x2.1mm +Package_CSP:LFCSP-20-1EP_4x4mm_P0.5mm_EP2.1x2.1mm_ThermalVias +Package_CSP:LFCSP-20-1EP_4x4mm_P0.5mm_EP2.5x2.5mm +Package_CSP:LFCSP-20-1EP_4x4mm_P0.5mm_EP2.5x2.5mm_ThermalVias +Package_CSP:LFCSP-20-1EP_4x4mm_P0.5mm_EP2.6x2.6mm +Package_CSP:LFCSP-20-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias +Package_CSP:LFCSP-24-1EP_4x4mm_P0.5mm_EP0.5x0.5mm +Package_CSP:LFCSP-24-1EP_4x4mm_P0.5mm_EP2.3x2.3mm +Package_CSP:LFCSP-24-1EP_4x4mm_P0.5mm_EP2.3x2.3mm_ThermalVias +Package_CSP:LFCSP-24-1EP_4x4mm_P0.5mm_EP2.5x2.5mm +Package_CSP:LFCSP-24-1EP_4x4mm_P0.5mm_EP2.5x2.5mm_ThermalVias +Package_CSP:LFCSP-28-1EP_5x5mm_P0.5mm_EP3.14x3.14mm +Package_CSP:LFCSP-28-1EP_5x5mm_P0.5mm_EP3.14x3.14mm_ThermalVias +Package_CSP:LFCSP-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm +Package_CSP:LFCSP-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm_ThermalVias +Package_CSP:LFCSP-32-1EP_5x5mm_P0.5mm_EP3.25x3.25mm +Package_CSP:LFCSP-32-1EP_5x5mm_P0.5mm_EP3.5x3.5mm +Package_CSP:LFCSP-32-1EP_5x5mm_P0.5mm_EP3.5x3.5mm_ThermalVias +Package_CSP:LFCSP-32-1EP_5x5mm_P0.5mm_EP3.6x3.6mm +Package_CSP:LFCSP-32-1EP_5x5mm_P0.5mm_EP3.6x3.6mm_ThermalVias +Package_CSP:LFCSP-40-1EP_6x6mm_P0.5mm_EP3.9x3.9mm +Package_CSP:LFCSP-40-1EP_6x6mm_P0.5mm_EP3.9x3.9mm_ThermalVias +Package_CSP:LFCSP-40-1EP_6x6mm_P0.5mm_EP4.65x4.65mm +Package_CSP:LFCSP-40-1EP_6x6mm_P0.5mm_EP4.65x4.65mm_ThermalVias +Package_CSP:LFCSP-40-1EP_6x6mm_P0.5mm_EP4.6x4.6mm +Package_CSP:LFCSP-40-1EP_6x6mm_P0.5mm_EP4.6x4.6mm_ThermalVias +Package_CSP:LFCSP-48-1EP_7x7mm_P0.5mm_EP4.1x4.1mm +Package_CSP:LFCSP-48-1EP_7x7mm_P0.5mm_EP4.1x4.1mm_ThermalVias +Package_CSP:LFCSP-56-1EP_8x8mm_P0.5mm_EP6.6x6.6mm +Package_CSP:LFCSP-56-1EP_8x8mm_P0.5mm_EP6.6x6.6mm_ThermalVias +Package_CSP:LFCSP-6-1EP_2x2mm_P0.65mm_EP1x1.6mm +Package_CSP:LFCSP-64-1EP_9x9mm_P0.5mm_EP5.21x5.21mm +Package_CSP:LFCSP-64-1EP_9x9mm_P0.5mm_EP5.21x5.21mm_ThermalVias +Package_CSP:LFCSP-72-1EP_10x10mm_P0.5mm_EP5.3x5.3mm +Package_CSP:LFCSP-72-1EP_10x10mm_P0.5mm_EP5.3x5.3mm_ThermalVias +Package_CSP:LFCSP-72-1EP_10x10mm_P0.5mm_EP6.15x6.15mm +Package_CSP:LFCSP-8-1EP_3x2mm_P0.5mm_EP1.6x1.65mm +Package_CSP:LFCSP-8-1EP_3x3mm_P0.5mm_EP1.45x1.74mm +Package_CSP:LFCSP-8-1EP_3x3mm_P0.5mm_EP1.6x2.34mm +Package_CSP:LFCSP-8-1EP_3x3mm_P0.5mm_EP1.6x2.34mm_ThermalVias +Package_CSP:LFCSP-8_2x2mm_P0.5mm +Package_CSP:LFCSP-VQ-24-1EP_4x4mm_P0.5mm_EP2.642x2.642mm +Package_CSP:LFCSP-VQ-48-1EP_7x7mm_P0.5mm +Package_CSP:LFCSP-WD-10-1EP_3x3mm_P0.5mm_EP1.64x2.38mm +Package_CSP:LFCSP-WD-10-1EP_3x3mm_P0.5mm_EP1.64x2.38mm_ThermalVias +Package_CSP:LFCSP-WD-8-1EP_3x3mm_P0.65mm_EP1.6x2.44mm +Package_CSP:LFCSP-WD-8-1EP_3x3mm_P0.65mm_EP1.6x2.44mm_ThermalVias +Package_CSP:Macronix_WLCSP-12_2.02x2.09mm_Layout4x4_P0.5mm +Package_CSP:Maxim_WLCSP-35_2.998x2.168mm_Layout7x5_P0.4mm +Package_CSP:Nexperia_WLCSP-15_2.37x1.17mm_Layout6x3_P0.4mmP0.8mm +Package_CSP:OnSemi_ODCSP36_BGA-36_6.13x6.13mm_Layout6x6_P1.0mm +Package_CSP:OnSemi_ODCSP36_BGA-36_6.13x6.13mm_Layout6x6_P1.0mm_ManualAssembly +Package_CSP:OnSemi_ODCSP8_BGA-8_3.16x3.16mm_Layout3x3_P1.26mm +Package_CSP:OnSemi_ODCSP8_BGA-8_3.16x3.16mm_Layout3x3_P1.26mm_ManualAssembly +Package_CSP:pSemi_CSP-16_1.64x2.04mm_P0.4mm +Package_CSP:pSemi_CSP-16_1.64x2.04mm_P0.4mm_Pad0.18mm +Package_CSP:ST_WLCSP-100_4.437x4.456mm_Layout10x10_P0.4mm +Package_CSP:ST_WLCSP-100_4.4x4.38mm_Layout10x10_P0.4mm_Offcenter +Package_CSP:ST_WLCSP-100_Die422 +Package_CSP:ST_WLCSP-100_Die446 +Package_CSP:ST_WLCSP-100_Die452 +Package_CSP:ST_WLCSP-100_Die461 +Package_CSP:ST_WLCSP-101_3.86x3.79mm_Layout11x19_P0.35mm_Stagger +Package_CSP:ST_WLCSP-104_Die437 +Package_CSP:ST_WLCSP-115_3.73x4.15mm_Layout11x21_P0.35mm_Stagger +Package_CSP:ST_WLCSP-115_4.63x4.15mm_Layout21x11_P0.4mm_Stagger +Package_CSP:ST_WLCSP-12_1.7x1.42mm_Layout4x6_P0.35mm_Stagger +Package_CSP:ST_WLCSP-132_4.57x4.37mm_Layout12x11_P0.35mm +Package_CSP:ST_WLCSP-143_Die419 +Package_CSP:ST_WLCSP-143_Die449 +Package_CSP:ST_WLCSP-144_Die470 +Package_CSP:ST_WLCSP-150_5.38x5.47mm_Layout13x23_P0.4mm_Stagger +Package_CSP:ST_WLCSP-156_4.96x4.64mm_Layout13x12_P0.35mm +Package_CSP:ST_WLCSP-168_Die434 +Package_CSP:ST_WLCSP-180_Die451 +Package_CSP:ST_WLCSP-18_1.86x2.14mm_Layout7x5_P0.4mm_Stagger +Package_CSP:ST_WLCSP-208_5.38x5.47mm_Layout26x16_P0.35mm_Stagger +Package_CSP:ST_WLCSP-208_5.8x5.6mm_Layout26x16_P0.35mm_Stagger +Package_CSP:ST_WLCSP-20_1.94x2.4mm_Layout4x5_P0.4mm +Package_CSP:ST_WLCSP-25_2.33x2.24mm_Layout5x5_P0.4mm +Package_CSP:ST_WLCSP-25_2.3x2.48mm_Layout5x5_P0.4mm +Package_CSP:ST_WLCSP-25_Die425 +Package_CSP:ST_WLCSP-25_Die444 +Package_CSP:ST_WLCSP-25_Die457 +Package_CSP:ST_WLCSP-27_2.34x2.55mm_Layout9x6_P0.4mm_Stagger +Package_CSP:ST_WLCSP-27_2.55x2.34mm_P0.40mm_Stagger +Package_CSP:ST_WLCSP-36_2.58x3.07mm_Layout6x6_P0.4mm +Package_CSP:ST_WLCSP-36_Die417 +Package_CSP:ST_WLCSP-36_Die440 +Package_CSP:ST_WLCSP-36_Die445 +Package_CSP:ST_WLCSP-36_Die458 +Package_CSP:ST_WLCSP-41_2.98x2.76mm_Layout13x7_P0.4mm_Stagger +Package_CSP:ST_WLCSP-42_2.82x2.93mm_P0.40mm_Stagger +Package_CSP:ST_WLCSP-42_2.93x2.82mm_Layout12x7_P0.4mm_Stagger +Package_CSP:ST_WLCSP-49_3.15x3.13mm_Layout7x7_P0.4mm +Package_CSP:ST_WLCSP-49_3.3x3.38mm_Layout7x7_P0.4mm_Offcenter +Package_CSP:ST_WLCSP-49_Die423 +Package_CSP:ST_WLCSP-49_Die431 +Package_CSP:ST_WLCSP-49_Die433 +Package_CSP:ST_WLCSP-49_Die435 +Package_CSP:ST_WLCSP-49_Die438 +Package_CSP:ST_WLCSP-49_Die439 +Package_CSP:ST_WLCSP-49_Die447 +Package_CSP:ST_WLCSP-49_Die448 +Package_CSP:ST_WLCSP-52_3.09x3.15mm_Layout13x8_P0.4mm_Stagger +Package_CSP:ST_WLCSP-56_3.38x3.38mm_Layout14x8_P0.4mm_Stagger +Package_CSP:ST_WLCSP-63_Die427 +Package_CSP:ST_WLCSP-64_3.56x3.52mm_Layout8x8_P0.4mm +Package_CSP:ST_WLCSP-64_Die414 +Package_CSP:ST_WLCSP-64_Die427 +Package_CSP:ST_WLCSP-64_Die435 +Package_CSP:ST_WLCSP-64_Die436 +Package_CSP:ST_WLCSP-64_Die441 +Package_CSP:ST_WLCSP-64_Die442 +Package_CSP:ST_WLCSP-64_Die462 +Package_CSP:ST_WLCSP-66_Die411 +Package_CSP:ST_WLCSP-66_Die432 +Package_CSP:ST_WLCSP-72_3.38x3.38mm_Layout16x9_P0.35mm_Stagger +Package_CSP:ST_WLCSP-72_Die415 +Package_CSP:ST_WLCSP-80_3.5x3.27mm_Layout10x16_P0.35mm_Stagger_Offcenter +Package_CSP:ST_WLCSP-81_4.02x4.27mm_Layout9x9_P0.4mm +Package_CSP:ST_WLCSP-81_4.36x4.07mm_Layout9x9_P0.4mm +Package_CSP:ST_WLCSP-81_Die415 +Package_CSP:ST_WLCSP-81_Die421 +Package_CSP:ST_WLCSP-81_Die463 +Package_CSP:ST_WLCSP-90_4.2x3.95mm_Layout18x10_P0.4mm_Stagger +Package_CSP:ST_WLCSP-90_Die413 +Package_CSP:ST_WLCSP-99_4.42x3.77mm_Layout11x9_P0.35mm +Package_CSP:WLCSP-12_1.403x1.555mm_Layout6x4_P0.4mm_Stagger +Package_CSP:WLCSP-12_1.56x1.56mm_P0.4mm +Package_CSP:WLCSP-16_1.409x1.409mm_Layout4x4_P0.35mm +Package_CSP:WLCSP-16_2.225x2.17mm_Layout4x4_P0.5mm +Package_CSP:WLCSP-16_4x4_B2.17x2.32mm_P0.5mm +Package_CSP:WLCSP-20_1.934x2.434mm_Layout4x5_P0.4mm +Package_CSP:WLCSP-20_1.994x1.609mm_Layout5x4_P0.4mm +Package_CSP:WLCSP-20_1.994x1.94mm_Layout4x5_P0.4mm +Package_CSP:WLCSP-36_2.374x2.459mm_Layout6x6_P0.35mm +Package_CSP:WLCSP-36_2.82x2.67mm_Layout6x6_P0.4mm +Package_CSP:WLCSP-4_0.64x0.64mm_Layout2x2_P0.35mm +Package_CSP:WLCSP-4_0.89x0.89mm_Layout2x2_P0.5mm +Package_CSP:WLCSP-56_3.170x3.444mm_Layout7x8_P0.4mm +Package_CSP:WLCSP-6_1.4x1.0mm_P0.4mm +Package_CSP:WLCSP-81_4.41x3.76mm_P0.4mm +Package_CSP:WLCSP-8_1.551x2.284mm_Layout2x4_P0.5mm +Package_CSP:WLCSP-8_1.58x1.63x0.35mm_Layout3x5_P0.35x0.4mm_Ball0.25mm_Pad0.25mm_NSMD +Package_CSP:WLCSP-9_1.21x1.22mm_Layout3x3_P0.4mm +Package_DFN_QFN:AMS_QFN-4-1EP_2x2mm_P0.95mm_EP0.7x1.6mm +Package_DFN_QFN:Analog_QFN-28-36-2EP_5x6mm_P0.5mm +Package_DFN_QFN:AO_AOZ666xDI_DFN-8-1EP_3x3mm_P0.65mm_EP1.25x2.7mm +Package_DFN_QFN:AO_DFN-8-1EP_5.55x5.2mm_P1.27mm_EP4.12x4.6mm +Package_DFN_QFN:ArtInChip_QFN-100-1EP_12x12mm_P0.4mm_EP7.4x7.4mm +Package_DFN_QFN:ArtInChip_QFN-100-1EP_12x12mm_P0.4mm_EP7.4x7.4mm_ThermalVias +Package_DFN_QFN:ArtInChip_QFN-68-1EP_7x7mm_P0.35mm_EP5.49x5.49mm +Package_DFN_QFN:ArtInChip_QFN-68-1EP_7x7mm_P0.35mm_EP5.49x5.49mm_ThermalVias +Package_DFN_QFN:ArtInChip_QFN-88-1EP_10x10mm_P0.4mm_EP6.74x6.74mm +Package_DFN_QFN:ArtInChip_QFN-88-1EP_10x10mm_P0.4mm_EP6.74x6.74mm_ThermalVias +Package_DFN_QFN:Cypress_QFN-56-1EP_8x8mm_P0.5mm_EP6.22x6.22mm_ThermalVias +Package_DFN_QFN:DFN-10-1EP_2.6x2.6mm_P0.5mm_EP1.3x2.2mm +Package_DFN_QFN:DFN-10-1EP_2.6x2.6mm_P0.5mm_EP1.3x2.2mm_ThermalVias +Package_DFN_QFN:DFN-10-1EP_2x3mm_P0.5mm_EP0.64x2.4mm +Package_DFN_QFN:DFN-10-1EP_3x3mm_P0.5mm_EP1.55x2.48mm +Package_DFN_QFN:DFN-10-1EP_3x3mm_P0.5mm_EP1.58x2.35mm +Package_DFN_QFN:DFN-10-1EP_3x3mm_P0.5mm_EP1.58x2.35mm_ThermalVias +Package_DFN_QFN:DFN-10-1EP_3x3mm_P0.5mm_EP1.65x2.38mm +Package_DFN_QFN:DFN-10-1EP_3x3mm_P0.5mm_EP1.65x2.38mm_ThermalVias +Package_DFN_QFN:DFN-10-1EP_3x3mm_P0.5mm_EP1.75x2.7mm +Package_DFN_QFN:DFN-10-1EP_3x3mm_P0.5mm_EP1.7x2.5mm +Package_DFN_QFN:DFN-10_2x2mm_P0.4mm +Package_DFN_QFN:DFN-12-1EP_2x3mm_P0.45mm_EP0.64x2.4mm +Package_DFN_QFN:DFN-12-1EP_3x3mm_P0.45mm_EP1.65x2.38mm +Package_DFN_QFN:DFN-12-1EP_3x3mm_P0.45mm_EP1.65x2.38mm_ThermalVias +Package_DFN_QFN:DFN-12-1EP_3x3mm_P0.5mm_EP1.6x2.5mm +Package_DFN_QFN:DFN-12-1EP_3x3mm_P0.5mm_EP1.6x2.5mm_ThermalVias +Package_DFN_QFN:DFN-12-1EP_3x3mm_P0.5mm_EP2.05x2.86mm +Package_DFN_QFN:DFN-12-1EP_3x4mm_P0.5mm_EP1.7x3.3mm +Package_DFN_QFN:DFN-12-1EP_4x4mm_P0.5mm_EP2.66x3.38mm +Package_DFN_QFN:DFN-12-1EP_4x4mm_P0.65mm_EP2.64x3.54mm +Package_DFN_QFN:DFN-14-1EP_3x3mm_P0.4mm_EP1.78x2.35mm +Package_DFN_QFN:DFN-14-1EP_3x4.5mm_P0.65mm_EP1.65x4.25mm +Package_DFN_QFN:DFN-14-1EP_3x4.5mm_P0.65mm_EP1.65x4.25mm_ThermalVias +Package_DFN_QFN:DFN-14-1EP_3x4mm_P0.5mm_EP1.7x3.3mm +Package_DFN_QFN:DFN-14_1.35x3.5mm_P0.5mm +Package_DFN_QFN:DFN-16-1EP_3x4mm_P0.45mm_EP1.7x3.3mm +Package_DFN_QFN:DFN-16-1EP_3x5mm_P0.5mm_EP1.66x4.4mm +Package_DFN_QFN:DFN-16-1EP_4x5mm_P0.5mm_EP2.44x4.34mm +Package_DFN_QFN:DFN-16-1EP_5x5mm_P0.5mm_EP3.46x4mm +Package_DFN_QFN:DFN-18-1EP_3x5mm_P0.5mm_EP1.66x4.4mm +Package_DFN_QFN:DFN-18-1EP_4x5mm_P0.5mm_EP2.44x4.34mm +Package_DFN_QFN:DFN-20-1EP_5x6mm_P0.5mm_EP3.24x4.24mm +Package_DFN_QFN:DFN-22-1EP_5x6mm_P0.5mm_EP3.14x4.3mm +Package_DFN_QFN:DFN-24-1EP_4x7mm_P0.5mm_EP2.64x6.44mm +Package_DFN_QFN:DFN-32-1EP_4x7mm_P0.4mm_EP2.64x6.44mm +Package_DFN_QFN:DFN-44-1EP_5x8.9mm_P0.4mm_EP3.7x8.4mm +Package_DFN_QFN:DFN-4_5x7mm_P5.08mm +Package_DFN_QFN:DFN-6-1EP_1.2x1.2mm_P0.4mm_EP0.3x0.94mm_PullBack +Package_DFN_QFN:DFN-6-1EP_2x1.6mm_P0.5mm_EP1.15x1.3mm +Package_DFN_QFN:DFN-6-1EP_2x1.8mm_P0.5mm_EP1.2x1.6mm +Package_DFN_QFN:DFN-6-1EP_2x2mm_P0.5mm_EP0.61x1.42mm +Package_DFN_QFN:DFN-6-1EP_2x2mm_P0.5mm_EP0.6x1.37mm +Package_DFN_QFN:DFN-6-1EP_2x2mm_P0.5mm_EP0.7x1.6mm +Package_DFN_QFN:DFN-6-1EP_2x2mm_P0.65mm_EP1.01x1.7mm +Package_DFN_QFN:DFN-6-1EP_2x2mm_P0.65mm_EP1x1.6mm +Package_DFN_QFN:DFN-6-1EP_3x2mm_P0.5mm_EP1.65x1.35mm +Package_DFN_QFN:DFN-6-1EP_3x3mm_P0.95mm_EP1.7x2.6mm +Package_DFN_QFN:DFN-6-1EP_3x3mm_P1mm_EP1.5x2.4mm +Package_DFN_QFN:DFN-6_1.3x1.2mm_P0.4mm +Package_DFN_QFN:DFN-6_1.6x1.3mm_P0.4mm +Package_DFN_QFN:DFN-8-1EP_1.5x1.5mm_P0.4mm_EP0.7x1.2mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.45mm_EP0.64x1.37mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.5mm_EP0.6x1.2mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.5mm_EP0.86x1.55mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.5mm_EP0.8x1.6mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.5mm_EP0.9x1.3mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.5mm_EP0.9x1.5mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.5mm_EP0.9x1.7mm +Package_DFN_QFN:DFN-8-1EP_2x2mm_P0.5mm_EP1.05x1.75mm +Package_DFN_QFN:DFN-8-1EP_2x3mm_P0.5mm_EP0.61x2.2mm +Package_DFN_QFN:DFN-8-1EP_3x2mm_P0.45mm_EP1.66x1.36mm +Package_DFN_QFN:DFN-8-1EP_3x2mm_P0.5mm_EP1.36x1.46mm +Package_DFN_QFN:DFN-8-1EP_3x2mm_P0.5mm_EP1.3x1.5mm +Package_DFN_QFN:DFN-8-1EP_3x2mm_P0.5mm_EP1.75x1.45mm +Package_DFN_QFN:DFN-8-1EP_3x2mm_P0.5mm_EP1.7x1.4mm +Package_DFN_QFN:DFN-8-1EP_3x2mm_P0.5mm_EP1.7x1.6mm +Package_DFN_QFN:DFN-8-1EP_3x3mm_P0.5mm_EP1.65x2.38mm +Package_DFN_QFN:DFN-8-1EP_3x3mm_P0.5mm_EP1.65x2.38mm_ThermalVias +Package_DFN_QFN:DFN-8-1EP_3x3mm_P0.5mm_EP1.66x2.38mm +Package_DFN_QFN:DFN-8-1EP_3x3mm_P0.5mm_EP1.7x2.4mm +Package_DFN_QFN:DFN-8-1EP_3x3mm_P0.5mm_EP1.7x2.4mm_ThermalVias +Package_DFN_QFN:DFN-8-1EP_3x3mm_P0.65mm_EP1.55x2.4mm +Package_DFN_QFN:DFN-8-1EP_3x3mm_P0.65mm_EP1.5x2.25mm +Package_DFN_QFN:DFN-8-1EP_3x3mm_P0.65mm_EP1.7x2.05mm +Package_DFN_QFN:DFN-8-1EP_4x4mm_P0.8mm_EP2.39x2.21mm +Package_DFN_QFN:DFN-8-1EP_4x4mm_P0.8mm_EP2.3x3.24mm +Package_DFN_QFN:DFN-8-1EP_4x4mm_P0.8mm_EP2.5x3.6mm +Package_DFN_QFN:DFN-8-1EP_6x5mm_P1.27mm_EP2x2mm +Package_DFN_QFN:DFN-8-1EP_6x5mm_P1.27mm_EP4x4mm +Package_DFN_QFN:DFN-8_2x2mm_P0.5mm +Package_DFN_QFN:DFN-S-8-1EP_6x5mm_P1.27mm +Package_DFN_QFN:DHVQFN-14-1EP_2.5x3mm_P0.5mm_EP1x1.5mm +Package_DFN_QFN:DHVQFN-16-1EP_2.5x3.5mm_P0.5mm_EP1x2mm +Package_DFN_QFN:DHVQFN-20-1EP_2.5x4.5mm_P0.5mm_EP1x3mm +Package_DFN_QFN:Diodes_DFN1006-3 +Package_DFN_QFN:Diodes_UDFN-10_1.0x2.5mm_P0.5mm +Package_DFN_QFN:Diodes_UDFN2020-6_Type-F +Package_DFN_QFN:Diodes_UDFN3810-9_TYPE_B +Package_DFN_QFN:Diodes_ZL32_TQFN-32-1EP_3x6mm_P0.4mm_EP1.25x3.5mm +Package_DFN_QFN:EPC_QFN-13-3EP_3.5x5mm_P0.5mm +Package_DFN_QFN:HVQFN-16-1EP_3x3mm_P0.5mm_EP1.5x1.5mm +Package_DFN_QFN:HVQFN-24-1EP_4x4mm_P0.5mm_EP2.1x2.1mm +Package_DFN_QFN:HVQFN-24-1EP_4x4mm_P0.5mm_EP2.5x2.5mm +Package_DFN_QFN:HVQFN-24-1EP_4x4mm_P0.5mm_EP2.5x2.5mm_ThermalVias +Package_DFN_QFN:HVQFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm +Package_DFN_QFN:HVQFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:HVQFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm +Package_DFN_QFN:HVQFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm_ThermalVias +Package_DFN_QFN:HVQFN-40-1EP_6x6mm_P0.5mm_EP4.1x4.1mm +Package_DFN_QFN:HVQFN-40-1EP_6x6mm_P0.5mm_EP4.1x4.1mm_ThermalVias +Package_DFN_QFN:HXQFN-16-1EP_3x3mm_P0.5mm_EP1.85x1.85mm +Package_DFN_QFN:HXQFN-16-1EP_3x3mm_P0.5mm_EP1.85x1.85mm_ThermalVias +Package_DFN_QFN:Infineon_MLPQ-16-14-1EP_4x4mm_P0.5mm +Package_DFN_QFN:Infineon_MLPQ-40-32-1EP_7x7mm_P0.5mm +Package_DFN_QFN:Infineon_MLPQ-48-1EP_7x7mm_P0.5mm_EP5.15x5.15mm +Package_DFN_QFN:Infineon_MLPQ-48-1EP_7x7mm_P0.5mm_EP5.55x5.55mm +Package_DFN_QFN:Infineon_PQFN-22-15-4EP_6x5mm_P0.65mm +Package_DFN_QFN:Infineon_PQFN-44-31-5EP_7x7mm_P0.5mm +Package_DFN_QFN:Linear_DE14MA +Package_DFN_QFN:Linear_UGK52_QFN-46-52 +Package_DFN_QFN:LQFN-10-1EP_2x2mm_P0.5mm_EP0.7x0.7mm +Package_DFN_QFN:LQFN-12-1EP_2x2mm_P0.5mm_EP0.7x0.7mm +Package_DFN_QFN:LQFN-16-1EP_3x3mm_P0.5mm_EP1.7x1.7mm +Package_DFN_QFN:Maxim_FC2QFN-14_2.5x2.5mm_P0.5mm +Package_DFN_QFN:Maxim_TDFN-12-1EP_3x3mm_P0.5mm_EP1.7x2.5mm +Package_DFN_QFN:Maxim_TDFN-12-1EP_3x3mm_P0.5mm_EP1.7x2.5mm_ThermalVias +Package_DFN_QFN:Maxim_TDFN-6-1EP_3x3mm_P0.95mm_EP1.5x2.3mm +Package_DFN_QFN:Micrel_MLF-8-1EP_2x2mm_P0.5mm_EP0.6x1.2mm +Package_DFN_QFN:Micrel_MLF-8-1EP_2x2mm_P0.5mm_EP0.6x1.2mm_ThermalVias +Package_DFN_QFN:Micrel_MLF-8-1EP_2x2mm_P0.5mm_EP0.8x1.3mm_ThermalVias +Package_DFN_QFN:Microchip_8E-16 +Package_DFN_QFN:Microchip_DRQFN-44-1EP_5x5mm_P0.7mm_EP2.65x2.65mm +Package_DFN_QFN:Microchip_DRQFN-44-1EP_5x5mm_P0.7mm_EP2.65x2.65mm_ThermalVias +Package_DFN_QFN:Microchip_DRQFN-64-1EP_7x7mm_P0.65mm_EP4.1x4.1mm +Package_DFN_QFN:Microchip_DRQFN-64-1EP_7x7mm_P0.65mm_EP4.1x4.1mm_ThermalVias +Package_DFN_QFN:Microsemi_QFN-40-32-2EP_6x8mm_P0.5mm +Package_DFN_QFN:Mini-Circuits_DL805 +Package_DFN_QFN:Mini-Circuits_FG873-4_3x3mm +Package_DFN_QFN:MLF-20-1EP_4x4mm_P0.5mm_EP2.6x2.6mm +Package_DFN_QFN:MLF-20-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:MLF-6-1EP_1.6x1.6mm_P0.5mm_EP0.5x1.26mm +Package_DFN_QFN:MLF-8-1EP_3x3mm_P0.65mm_EP1.55x2.3mm +Package_DFN_QFN:MLF-8-1EP_3x3mm_P0.65mm_EP1.55x2.3mm_ThermalVias +Package_DFN_QFN:MLPQ-16-1EP_4x4mm_P0.65mm_EP2.8x2.8mm +Package_DFN_QFN:MPS_QFN-12_2x2mm_P0.4mm +Package_DFN_QFN:Nordic_AQFN-73-1EP_7x7mm_P0.5mm +Package_DFN_QFN:Nordic_AQFN-94-1EP_7x7mm_P0.4mm +Package_DFN_QFN:NXP_LQFN-48-1EP_7x7mm_P0.5mm_EP3.5x3.5mm_16xMask0.45x0.45 +Package_DFN_QFN:NXP_LQFN-48-1EP_7x7mm_P0.5mm_EP3.5x3.5mm_16xMask0.45x0.45_ThermalVias +Package_DFN_QFN:OnSemi_DFN-14-1EP_4x4mm_P0.5mm_EP2.7x3.4mm +Package_DFN_QFN:OnSemi_DFN-8_2x2mm_P0.5mm +Package_DFN_QFN:OnSemi_SIP-38-6EP-9x7mm_P0.65mm_EP1.2x1.2mm +Package_DFN_QFN:OnSemi_UDFN-16-1EP_1.35x3.3mm_P0.4mm_EP0.4x2.8mm +Package_DFN_QFN:OnSemi_UDFN-8_1.2x1.8mm_P0.4mm +Package_DFN_QFN:OnSemi_VCT-28_3.5x3.5mm_P0.4mm +Package_DFN_QFN:OnSemi_XDFN-10_1.35x2.2mm_P0.4mm +Package_DFN_QFN:OnSemi_XDFN4-1EP_1.0x1.0mm_EP0.52x0.52mm +Package_DFN_QFN:Panasonic_HQFN-16-1EP_4x4mm_P0.65mm_EP2.9x2.9mm +Package_DFN_QFN:Panasonic_HSON-8_8x8mm_P2.00mm +Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic +Package_DFN_QFN:QFN-12-1EP_3x3mm_P0.51mm_EP1.45x1.45mm +Package_DFN_QFN:QFN-12-1EP_3x3mm_P0.5mm_EP1.65x1.65mm +Package_DFN_QFN:QFN-12-1EP_3x3mm_P0.5mm_EP1.65x1.65mm_ThermalVias +Package_DFN_QFN:QFN-12-1EP_3x3mm_P0.5mm_EP1.6x1.6mm +Package_DFN_QFN:QFN-12-1EP_3x3mm_P0.5mm_EP1.6x1.6mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_1.8x2.6mm_P0.4mm_EP0.7x1.5mm +Package_DFN_QFN:QFN-16-1EP_1.8x2.6mm_P0.4mm_EP0.7x1.5mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.45x1.45mm +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.45x1.45mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.675x1.675mm +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.7x1.7mm +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.7x1.7mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.9x1.9mm +Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.9x1.9mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.5mm_EP2.45x2.45mm +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.5mm_EP2.45x2.45mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.15x2.15mm +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.15x2.15mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.5x2.5mm +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.5x2.5mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.7x2.7mm +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.7x2.7mm_PullBack +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.7x2.7mm_PullBack_ThermalVias +Package_DFN_QFN:QFN-16-1EP_4x4mm_P0.65mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:QFN-16-1EP_5x5mm_P0.8mm_EP2.7x2.7mm +Package_DFN_QFN:QFN-16-1EP_5x5mm_P0.8mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_3.5x3.5mm_P0.5mm_EP2x2mm +Package_DFN_QFN:QFN-20-1EP_3.5x3.5mm_P0.5mm_EP2x2mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_3x3mm_P0.45mm_EP1.6x1.6mm +Package_DFN_QFN:QFN-20-1EP_3x3mm_P0.45mm_EP1.6x1.6mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_3x3mm_P0.4mm_EP1.65x1.65mm +Package_DFN_QFN:QFN-20-1EP_3x3mm_P0.4mm_EP1.65x1.65mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_3x4mm_P0.5mm_EP1.65x2.65mm +Package_DFN_QFN:QFN-20-1EP_3x4mm_P0.5mm_EP1.65x2.65mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_4x4mm_P0.5mm_EP2.5x2.5mm +Package_DFN_QFN:QFN-20-1EP_4x4mm_P0.5mm_EP2.5x2.5mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_4x4mm_P0.5mm_EP2.6x2.6mm +Package_DFN_QFN:QFN-20-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_4x4mm_P0.5mm_EP2.7x2.7mm +Package_DFN_QFN:QFN-20-1EP_4x4mm_P0.5mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_4x5mm_P0.5mm_EP2.65x3.65mm +Package_DFN_QFN:QFN-20-1EP_4x5mm_P0.5mm_EP2.65x3.65mm_ThermalVias +Package_DFN_QFN:QFN-20-1EP_5x5mm_P0.65mm_EP3.35x3.35mm +Package_DFN_QFN:QFN-20-1EP_5x5mm_P0.65mm_EP3.35x3.35mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_3x3mm_P0.4mm_EP1.75x1.6mm +Package_DFN_QFN:QFN-24-1EP_3x3mm_P0.4mm_EP1.75x1.6mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_3x4mm_P0.4mm_EP1.65x2.65mm +Package_DFN_QFN:QFN-24-1EP_3x4mm_P0.4mm_EP1.65x2.65mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.15x2.15mm +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.15x2.15mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.5x2.5mm +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.65x2.65mm +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.65x2.65mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.75x2.75mm +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.75x2.75mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.6mm +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.6mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.7mm +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.8x2.8mm +Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.8x2.8mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_4x5mm_P0.5mm_EP2.65x3.65mm +Package_DFN_QFN:QFN-24-1EP_4x5mm_P0.5mm_EP2.65x3.65mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_5x5mm_P0.65mm_EP3.25x3.25mm +Package_DFN_QFN:QFN-24-1EP_5x5mm_P0.65mm_EP3.25x3.25mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_5x5mm_P0.65mm_EP3.2x3.2mm +Package_DFN_QFN:QFN-24-1EP_5x5mm_P0.65mm_EP3.2x3.2mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_5x5mm_P0.65mm_EP3.4x3.4mm +Package_DFN_QFN:QFN-24-1EP_5x5mm_P0.65mm_EP3.4x3.4mm_ThermalVias +Package_DFN_QFN:QFN-24-1EP_5x5mm_P0.65mm_EP3.6x3.6mm +Package_DFN_QFN:QFN-24-1EP_5x5mm_P0.65mm_EP3.6x3.6mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_3x6mm_P0.5mm_EP1.7x4.75mm +Package_DFN_QFN:QFN-28-1EP_3x6mm_P0.5mm_EP1.7x4.75mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.45mm_EP2.4x2.4mm +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.45mm_EP2.4x2.4mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.45mm_EP2.6x2.6mm +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.4mm_EP2.3x2.3mm +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.4mm_EP2.3x2.3mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.4mm_EP2.4x2.4mm +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.4mm_EP2.4x2.4mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.4mm_EP2.6x2.6mm +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.4mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_4x4mm_P0.4mm_EP2.7x2.7mm +Package_DFN_QFN:QFN-28-1EP_4x5mm_P0.5mm_EP2.65x3.65mm +Package_DFN_QFN:QFN-28-1EP_4x5mm_P0.5mm_EP2.65x3.65mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP2.7x2.7mm +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.1x3.1mm +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.1x3.1mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.25x3.25mm +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.25x3.25mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.35x3.35mm +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.35x3.35mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.75x3.75mm +Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.75x3.75mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_5x6mm_P0.5mm_EP3.65x4.65mm +Package_DFN_QFN:QFN-28-1EP_5x6mm_P0.5mm_EP3.65x4.65mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_6x6mm_P0.65mm_EP4.25x4.25mm +Package_DFN_QFN:QFN-28-1EP_6x6mm_P0.65mm_EP4.25x4.25mm_ThermalVias +Package_DFN_QFN:QFN-28-1EP_6x6mm_P0.65mm_EP4.8x4.8mm +Package_DFN_QFN:QFN-28-1EP_6x6mm_P0.65mm_EP4.8x4.8mm_ThermalVias +Package_DFN_QFN:QFN-28_4x4mm_P0.5mm +Package_DFN_QFN:QFN-32-1EP_4x4mm_P0.4mm_EP2.65x2.65mm +Package_DFN_QFN:QFN-32-1EP_4x4mm_P0.4mm_EP2.65x2.65mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_4x4mm_P0.4mm_EP2.9x2.9mm +Package_DFN_QFN:QFN-32-1EP_4x4mm_P0.4mm_EP2.9x2.9mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.3x3.3mm +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.3x3.3mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.45x3.45mm +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.45x3.45mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.65x3.65mm +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.65x3.65mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.6x3.6mm +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.6x3.6mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.7x3.7mm +Package_DFN_QFN:QFN-32-1EP_5x5mm_P0.5mm_EP3.7x3.7mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_7x7mm_P0.65mm_EP4.65x4.65mm +Package_DFN_QFN:QFN-32-1EP_7x7mm_P0.65mm_EP4.65x4.65mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_7x7mm_P0.65mm_EP4.7x4.7mm +Package_DFN_QFN:QFN-32-1EP_7x7mm_P0.65mm_EP4.7x4.7mm_ThermalVias +Package_DFN_QFN:QFN-32-1EP_7x7mm_P0.65mm_EP5.4x5.4mm +Package_DFN_QFN:QFN-32-1EP_7x7mm_P0.65mm_EP5.4x5.4mm_ThermalVias +Package_DFN_QFN:QFN-36-1EP_5x6mm_P0.5mm_EP3.6x4.1mm +Package_DFN_QFN:QFN-36-1EP_5x6mm_P0.5mm_EP3.6x4.1mm_ThermalVias +Package_DFN_QFN:QFN-36-1EP_5x6mm_P0.5mm_EP3.6x4.6mm +Package_DFN_QFN:QFN-36-1EP_5x6mm_P0.5mm_EP3.6x4.6mm_ThermalVias +Package_DFN_QFN:QFN-36-1EP_6x6mm_P0.5mm_EP3.7x3.7mm +Package_DFN_QFN:QFN-36-1EP_6x6mm_P0.5mm_EP3.7x3.7mm_ThermalVias +Package_DFN_QFN:QFN-36-1EP_6x6mm_P0.5mm_EP4.1x4.1mm +Package_DFN_QFN:QFN-36-1EP_6x6mm_P0.5mm_EP4.1x4.1mm_ThermalVias +Package_DFN_QFN:QFN-38-1EP_4x6mm_P0.4mm_EP2.65x4.65mm +Package_DFN_QFN:QFN-38-1EP_4x6mm_P0.4mm_EP2.65x4.65mm_ThermalVias +Package_DFN_QFN:QFN-38-1EP_5x7mm_P0.5mm_EP3.15x5.15mm +Package_DFN_QFN:QFN-38-1EP_5x7mm_P0.5mm_EP3.15x5.15mm_ThermalVias +Package_DFN_QFN:QFN-40-1EP_5x5mm_P0.4mm_EP3.6x3.6mm +Package_DFN_QFN:QFN-40-1EP_5x5mm_P0.4mm_EP3.6x3.6mm_ThermalVias +Package_DFN_QFN:QFN-40-1EP_5x5mm_P0.4mm_EP3.8x3.8mm +Package_DFN_QFN:QFN-40-1EP_5x5mm_P0.4mm_EP3.8x3.8mm_ThermalVias +Package_DFN_QFN:QFN-40-1EP_6x6mm_P0.5mm_EP4.6x4.6mm +Package_DFN_QFN:QFN-40-1EP_6x6mm_P0.5mm_EP4.6x4.6mm_ThermalVias +Package_DFN_QFN:QFN-42-1EP_5x6mm_P0.4mm_EP3.7x4.7mm +Package_DFN_QFN:QFN-42-1EP_5x6mm_P0.4mm_EP3.7x4.7mm_ThermalVias +Package_DFN_QFN:QFN-44-1EP_7x7mm_P0.5mm_EP5.15x5.15mm +Package_DFN_QFN:QFN-44-1EP_7x7mm_P0.5mm_EP5.15x5.15mm_ThermalVias +Package_DFN_QFN:QFN-44-1EP_7x7mm_P0.5mm_EP5.2x5.2mm +Package_DFN_QFN:QFN-44-1EP_7x7mm_P0.5mm_EP5.2x5.2mm_ThermalVias +Package_DFN_QFN:QFN-44-1EP_8x8mm_P0.65mm_EP6.45x6.45mm +Package_DFN_QFN:QFN-44-1EP_8x8mm_P0.65mm_EP6.45x6.45mm_ThermalVias +Package_DFN_QFN:QFN-44-1EP_9x9mm_P0.65mm_EP7.5x7.5mm +Package_DFN_QFN:QFN-44-1EP_9x9mm_P0.65mm_EP7.5x7.5mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_5x5mm_P0.35mm_EP3.7x3.7mm +Package_DFN_QFN:QFN-48-1EP_5x5mm_P0.35mm_EP3.7x3.7mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.2x4.2mm +Package_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.2x4.2mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.3x4.3mm +Package_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.3x4.3mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.66x4.66mm +Package_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.66x4.66mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.6x4.6mm +Package_DFN_QFN:QFN-48-1EP_6x6mm_P0.4mm_EP4.6x4.6mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP3.5x3.5mm +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP3.5x3.5mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.15x5.15mm +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.15x5.15mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.1x5.1mm +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.1x5.1mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.3x5.3mm +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.3x5.3mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.45x5.45mm +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.45x5.45mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.7x5.7mm +Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.7x5.7mm_ThermalVias +Package_DFN_QFN:QFN-48-1EP_8x8mm_P0.5mm_EP6.2x6.2mm +Package_DFN_QFN:QFN-48-1EP_8x8mm_P0.5mm_EP6.2x6.2mm_ThermalVias +Package_DFN_QFN:QFN-52-1EP_7x8mm_P0.5mm_EP5.41x6.45mm +Package_DFN_QFN:QFN-52-1EP_7x8mm_P0.5mm_EP5.41x6.45mm_ThermalVias +Package_DFN_QFN:QFN-56-1EP_7x7mm_P0.4mm_EP3.2x3.2mm +Package_DFN_QFN:QFN-56-1EP_7x7mm_P0.4mm_EP3.2x3.2mm_ThermalVias +Package_DFN_QFN:QFN-56-1EP_7x7mm_P0.4mm_EP4x4mm +Package_DFN_QFN:QFN-56-1EP_7x7mm_P0.4mm_EP4x4mm_ThermalVias +Package_DFN_QFN:QFN-56-1EP_7x7mm_P0.4mm_EP5.6x5.6mm +Package_DFN_QFN:QFN-56-1EP_7x7mm_P0.4mm_EP5.6x5.6mm_ThermalVias +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP4.3x4.3mm +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP4.3x4.3mm_ThermalVias +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP4.5x5.2mm +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP4.5x5.2mm_ThermalVias +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP4.5x5.2mm_ThermalVias_TopTented +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP5.6x5.6mm +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP5.6x5.6mm_ThermalVias +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP5.9x5.9mm +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP5.9x5.9mm_ThermalVias +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP6.1x6.1mm +Package_DFN_QFN:QFN-56-1EP_8x8mm_P0.5mm_EP6.1x6.1mm_ThermalVias +Package_DFN_QFN:QFN-60-1EP_7x7mm_P0.4mm_EP3.4x3.4mm +Package_DFN_QFN:QFN-60-1EP_7x7mm_P0.4mm_EP3.4x3.4mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_8x8mm_P0.4mm_EP6.5x6.5mm +Package_DFN_QFN:QFN-64-1EP_8x8mm_P0.4mm_EP6.5x6.5mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP3.4x3.4mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP3.4x3.4mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP3.8x3.8mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP3.8x3.8mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP4.1x4.1mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP4.1x4.1mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP4.35x4.35mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP4.35x4.35mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP4.7x4.7mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP4.7x4.7mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP5.2x5.2mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP5.2x5.2mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP5.45x5.45mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP5.45x5.45mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP5.4x5.4mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP5.4x5.4mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP5.7x5.7mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP5.7x5.7mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP6x6mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP6x6mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.15x7.15mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.15x7.15mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.25x7.25mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.35x7.35mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.3x7.3mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.3x7.3mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.5x7.5mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.5x7.5mm_ThermalVias +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.65x7.65mm +Package_DFN_QFN:QFN-64-1EP_9x9mm_P0.5mm_EP7.65x7.65mm_ThermalVias +Package_DFN_QFN:QFN-68-1EP_8x8mm_P0.4mm_EP5.2x5.2mm +Package_DFN_QFN:QFN-68-1EP_8x8mm_P0.4mm_EP5.2x5.2mm_ThermalVias +Package_DFN_QFN:QFN-68-1EP_8x8mm_P0.4mm_EP6.4x6.4mm +Package_DFN_QFN:QFN-68-1EP_8x8mm_P0.4mm_EP6.4x6.4mm_ThermalVias +Package_DFN_QFN:QFN-72-1EP_10x10mm_P0.5mm_EP6x6mm +Package_DFN_QFN:QFN-72-1EP_10x10mm_P0.5mm_EP6x6mm_ThermalVias +Package_DFN_QFN:QFN-76-1EP_9x9mm_P0.4mm_EP3.8x3.8mm +Package_DFN_QFN:QFN-76-1EP_9x9mm_P0.4mm_EP3.8x3.8mm_ThermalVias +Package_DFN_QFN:QFN-76-1EP_9x9mm_P0.4mm_EP5.81x6.31mm +Package_DFN_QFN:QFN-76-1EP_9x9mm_P0.4mm_EP5.81x6.31mm_ThermalVias +Package_DFN_QFN:QFN-80-1EP_10x10mm_P0.4mm_EP3.4x3.4mm +Package_DFN_QFN:QFN-80-1EP_10x10mm_P0.4mm_EP3.4x3.4mm_ThermalVias +Package_DFN_QFN:Qorvo_DFN-8-1EP_2x2mm_P0.5mm +Package_DFN_QFN:Qorvo_TQFN66-48-1EP_6x6mm_P0.4mm_EP4.2x4.2mm +Package_DFN_QFN:Qorvo_TQFN66-48-1EP_6x6mm_P0.4mm_EP4.2x4.2mm_ThermalVias +Package_DFN_QFN:ROHM_DFN0604-3 +Package_DFN_QFN:SiliconLabs_QFN-20-1EP_3x3mm_P0.5mm_EP1.8x1.8mm +Package_DFN_QFN:SiliconLabs_QFN-20-1EP_3x3mm_P0.5mm_EP1.8x1.8mm_ThermalVias +Package_DFN_QFN:ST_UFDFPN-12-1EP_3x3mm_P0.5mm_EP1.4x2.55mm +Package_DFN_QFN:ST_UFQFPN-20_3x3mm_P0.5mm +Package_DFN_QFN:ST_UQFN-6L_1.5x1.7mm_P0.5mm +Package_DFN_QFN:TDFN-10-1EP_2x3mm_P0.5mm_EP0.9x2mm +Package_DFN_QFN:TDFN-10-1EP_2x3mm_P0.5mm_EP0.9x2mm_ThermalVias +Package_DFN_QFN:TDFN-12-1EP_3x3mm_P0.4mm_EP1.7x2.45mm +Package_DFN_QFN:TDFN-12-1EP_3x3mm_P0.4mm_EP1.7x2.45mm_ThermalVias +Package_DFN_QFN:TDFN-12_2x3mm_P0.5mm +Package_DFN_QFN:TDFN-14-1EP_3x3mm_P0.4mm_EP1.78x2.35mm +Package_DFN_QFN:TDFN-14-1EP_3x3mm_P0.4mm_EP1.78x2.35mm_ThermalVias +Package_DFN_QFN:TDFN-6-1EP_2.5x2.5mm_P0.65mm_EP1.3x2mm +Package_DFN_QFN:TDFN-6-1EP_2.5x2.5mm_P0.65mm_EP1.3x2mm_ThermalVias +Package_DFN_QFN:TDFN-8-1EP_2x2mm_P0.5mm_EP0.8x1.2mm +Package_DFN_QFN:TDFN-8-1EP_3x2mm_P0.5mm_EP1.3x1.4mm +Package_DFN_QFN:TDFN-8-1EP_3x2mm_P0.5mm_EP1.4x1.4mm +Package_DFN_QFN:TDFN-8-1EP_3x2mm_P0.5mm_EP1.80x1.65mm +Package_DFN_QFN:TDFN-8-1EP_3x2mm_P0.5mm_EP1.80x1.65mm_ThermalVias +Package_DFN_QFN:TDFN-8_1.4x1.6mm_P0.4mm +Package_DFN_QFN:Texas_B3QFN-14-1EP_5x5.5mm_P0.65mm +Package_DFN_QFN:Texas_B3QFN-14-1EP_5x5.5mm_P0.65mm_ThermalVia +Package_DFN_QFN:Texas_DRB0008A +Package_DFN_QFN:Texas_MOF0009A +Package_DFN_QFN:Texas_PicoStar_DFN-3_0.69x0.60mm +Package_DFN_QFN:Texas_QFN-41_10x16mm +Package_DFN_QFN:Texas_R-PUQFN-N10 +Package_DFN_QFN:Texas_R-PUQFN-N12 +Package_DFN_QFN:Texas_REF0038A_WQFN-38-2EP_6x4mm_P0.4 +Package_DFN_QFN:Texas_RGC0064B_VQFN-64-1EP_9x9mm_P0.5mm_EP4.25x4.25mm +Package_DFN_QFN:Texas_RGC0064B_VQFN-64-1EP_9x9mm_P0.5mm_EP4.25x4.25mm_ThermalVias +Package_DFN_QFN:Texas_RGE0024C_VQFN-24-1EP_4x4mm_P0.5mm_EP2.1x2.1mm +Package_DFN_QFN:Texas_RGE0024C_VQFN-24-1EP_4x4mm_P0.5mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:Texas_RGE0024H_VQFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.7mm +Package_DFN_QFN:Texas_RGE0024H_VQFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:Texas_RGP0020D_VQFN-20-1EP_4x4mm_P0.5mm_EP2.7x2.7mm +Package_DFN_QFN:Texas_RGP0020D_VQFN-20-1EP_4x4mm_P0.5mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:Texas_RGP0020H_VQFN-20-1EP_4x4mm_P0.5mm_EP2.4x2.4mm +Package_DFN_QFN:Texas_RGP0020H_VQFN-20-1EP_4x4mm_P0.5mm_EP2.4x2.4mm_ThermalVias +Package_DFN_QFN:Texas_RGV0016A_VQFN-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm +Package_DFN_QFN:Texas_RGV0016A_VQFN-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:Texas_RGW0020A_VQFN-20-1EP_5x5mm_P0.65mm_EP3.15x3.15mm +Package_DFN_QFN:Texas_RGW0020A_VQFN-20-1EP_5x5mm_P0.65mm_EP3.15x3.15mm_ThermalVias +Package_DFN_QFN:Texas_RGY_R-PVQFN-N16_EP2.05x2.55mm +Package_DFN_QFN:Texas_RGY_R-PVQFN-N16_EP2.05x2.55mm_ThermalVias +Package_DFN_QFN:Texas_RGY_R-PVQFN-N20_EP2.05x3.05mm +Package_DFN_QFN:Texas_RGY_R-PVQFN-N20_EP2.05x3.05mm_ThermalVias +Package_DFN_QFN:Texas_RGY_R-PVQFN-N24_EP2.05x3.1mm +Package_DFN_QFN:Texas_RGY_R-PVQFN-N24_EP2.05x3.1mm_ThermalVias +Package_DFN_QFN:Texas_RGZ0048A_VQFN-48-1EP_7x7mm_P0.5mm_EP5.15x5.15mm +Package_DFN_QFN:Texas_RGZ0048A_VQFN-48-1EP_7x7mm_P0.5mm_EP5.15x5.15mm_ThermalVias +Package_DFN_QFN:Texas_RHA0040B_VQFN-40-1EP_6x6mm_P0.5mm_EP4.15x4.15mm +Package_DFN_QFN:Texas_RHA0040B_VQFN-40-1EP_6x6mm_P0.5mm_EP4.15x4.15mm_ThermalVias +Package_DFN_QFN:Texas_RHA0040D_VQFN-40-1EP_6x6mm_P0.5mm_EP2.9x2.9mm +Package_DFN_QFN:Texas_RHA0040D_VQFN-40-1EP_6x6mm_P0.5mm_EP2.9x2.9mm_ThermalVias +Package_DFN_QFN:Texas_RHA0040E_VQFN-40-1EP_6x6mm_P0.5mm_EP3.52x2.62mm +Package_DFN_QFN:Texas_RHA0040E_VQFN-40-1EP_6x6mm_P0.5mm_EP3.52x2.62mm_ThermalVias +Package_DFN_QFN:Texas_RHA_VQFN-40-1EP_6x6mm_P0.5mm_EP4.6x4.6mm +Package_DFN_QFN:Texas_RHA_VQFN-40-1EP_6x6mm_P0.5mm_EP4.6x4.6mm_ThermalVias +Package_DFN_QFN:Texas_RHB0032E_VQFN-32-1EP_5x5mm_P0.5mm_EP3.45x3.45mm +Package_DFN_QFN:Texas_RHB0032E_VQFN-32-1EP_5x5mm_P0.5mm_EP3.45x3.45mm_ThermalVias +Package_DFN_QFN:Texas_RHB0032M_VQFN-32-1EP_5x5mm_P0.5mm_EP2.1x2.1mm +Package_DFN_QFN:Texas_RHB0032M_VQFN-32-1EP_5x5mm_P0.5mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:Texas_RHH0036C_VQFN-36-1EP_6x6mm_P0.5mm_EP4.4x4.4mm +Package_DFN_QFN:Texas_RHH0036C_VQFN-36-1EP_6x6mm_P0.5mm_EP4.4x4.4mm_ThermalVias +Package_DFN_QFN:Texas_RJE0020A_VQFN-20-1EP_3x3mm_P0.45mm_EP0.675x0.76mm +Package_DFN_QFN:Texas_RJE0020A_VQFN-20-1EP_3x3mm_P0.45mm_EP0.675x0.76mm_ThermalVias +Package_DFN_QFN:Texas_RMG0012A_WQFN-12_1.8x1.8mm_P0.4mm +Package_DFN_QFN:Texas_RMQ0024A_WQFN-24-1EP_3x3mm_P0.4mm_EP1.9x1.9mm +Package_DFN_QFN:Texas_RMQ0024A_WQFN-24-1EP_3x3mm_P0.4mm_EP1.9x1.9mm_ThermalVias +Package_DFN_QFN:Texas_RNN0018A +Package_DFN_QFN:Texas_RNP0030B_WQFN-30-1EP_4x6mm_P0.5mm_EP1.8x4.5mm +Package_DFN_QFN:Texas_RNP0030B_WQFN-30-1EP_4x6mm_P0.5mm_EP1.8x4.5mm_ThermalVias +Package_DFN_QFN:Texas_RPU0010A_VQFN-HR-10_2x2mm_P0.5mm +Package_DFN_QFN:Texas_RSA_VQFN-16-1EP_4x4mm_P0.65mm_EP2.7x2.7mm +Package_DFN_QFN:Texas_RSA_VQFN-16-1EP_4x4mm_P0.65mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:Texas_RSN_WQFN-32-1EP_4x4mm_P0.4mm_EP2.8x2.8mm +Package_DFN_QFN:Texas_RSN_WQFN-32-1EP_4x4mm_P0.4mm_EP2.8x2.8mm_ThermalVias +Package_DFN_QFN:Texas_RSW0010A_UQFN-10_1.4x1.8mm_P0.4mm +Package_DFN_QFN:Texas_RTE0016D_WQFN-16-1EP_3x3mm_P0.5mm_EP0.8x0.8mm +Package_DFN_QFN:Texas_RTE0016D_WQFN-16-1EP_3x3mm_P0.5mm_EP0.8x0.8mm_ThermalVias +Package_DFN_QFN:Texas_RTE_WQFN-16-1EP_3x3mm_P0.5mm_EP1.2x0.8mm +Package_DFN_QFN:Texas_RTE_WQFN-16-1EP_3x3mm_P0.5mm_EP1.2x0.8mm_ThermalVias +Package_DFN_QFN:Texas_RTW_WQFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.7mm +Package_DFN_QFN:Texas_RTW_WQFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:Texas_RTY_WQFN-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm +Package_DFN_QFN:Texas_RTY_WQFN-16-1EP_4x4mm_P0.65mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:Texas_RUM0016A_WQFN-16-1EP_4x4mm_P0.65mm_EP2.6x2.6mm +Package_DFN_QFN:Texas_RUM0016A_WQFN-16-1EP_4x4mm_P0.65mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:Texas_RUN0010A_WQFN-10_2x2mm_P0.5mm +Package_DFN_QFN:Texas_RVA_VQFN-16-1EP_3.5x3.5mm_P0.5mm_EP2.14x2.14mm +Package_DFN_QFN:Texas_RVA_VQFN-16-1EP_3.5x3.5mm_P0.5mm_EP2.14x2.14mm_ThermalVias +Package_DFN_QFN:Texas_RVE0028A_VQFN-28-1EP_3.5x4.5mm_P0.4mm_EP2.1x3.1mm +Package_DFN_QFN:Texas_RVE0028A_VQFN-28-1EP_3.5x4.5mm_P0.4mm_EP2.1x3.1mm_ThermalVias +Package_DFN_QFN:Texas_RWH0032A +Package_DFN_QFN:Texas_RWH0032A_ThermalVias +Package_DFN_QFN:Texas_RWU0007A_VQFN-7_2x2mm_P0.5mm +Package_DFN_QFN:Texas_S-PDSO-N10_EP1.2x2mm +Package_DFN_QFN:Texas_S-PDSO-N10_EP1.2x2mm_ThermalVias +Package_DFN_QFN:Texas_S-PVQFN-N14 +Package_DFN_QFN:Texas_S-PVQFN-N14_ThermalVias +Package_DFN_QFN:Texas_S-PWQFN-N100_EP5.5x5.5mm +Package_DFN_QFN:Texas_S-PWQFN-N100_EP5.5x5.5mm_ThermalVias +Package_DFN_QFN:Texas_S-PWQFN-N20 +Package_DFN_QFN:Texas_S-PX2QFN-14 +Package_DFN_QFN:Texas_UQFN-10_1.5x2mm_P0.5mm +Package_DFN_QFN:Texas_VQFN-HR-12_2x2.5mm_P0.5mm +Package_DFN_QFN:Texas_VQFN-HR-12_2x2.5mm_P0.5mm_ThermalVias +Package_DFN_QFN:Texas_VQFN-HR-20_3x2.5mm_P0.5mm_RQQ0011A +Package_DFN_QFN:Texas_VQFN-RHL-20 +Package_DFN_QFN:Texas_VQFN-RHL-20_ThermalVias +Package_DFN_QFN:Texas_VQFN-RNR0011A-11 +Package_DFN_QFN:Texas_WQFN-MR-100_3x3-DapStencil +Package_DFN_QFN:Texas_WQFN-MR-100_ThermalVias_3x3-DapStencil +Package_DFN_QFN:Texas_X2QFN-12_1.6x1.6mm_P0.4mm +Package_DFN_QFN:Texas_X2QFN-RUE-12_1.4x2mm_P0.4mm +Package_DFN_QFN:TQFN-16-1EP_3x3mm_P0.5mm_EP1.23x1.23mm +Package_DFN_QFN:TQFN-16-1EP_3x3mm_P0.5mm_EP1.23x1.23mm_ThermalVias +Package_DFN_QFN:TQFN-16-1EP_3x3mm_P0.5mm_EP1.6x1.6mm +Package_DFN_QFN:TQFN-16-1EP_5x5mm_P0.8mm_EP2.29x2.29mm +Package_DFN_QFN:TQFN-16-1EP_5x5mm_P0.8mm_EP2.29x2.29mm_ThermalVias +Package_DFN_QFN:TQFN-16-1EP_5x5mm_P0.8mm_EP3.1x3.1mm +Package_DFN_QFN:TQFN-16-1EP_5x5mm_P0.8mm_EP3.1x3.1mm_ThermalVias +Package_DFN_QFN:TQFN-20-1EP_4x4mm_P0.5mm_EP2.1x2.1mm +Package_DFN_QFN:TQFN-20-1EP_4x4mm_P0.5mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:TQFN-20-1EP_4x4mm_P0.5mm_EP2.7x2.7mm +Package_DFN_QFN:TQFN-20-1EP_4x4mm_P0.5mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:TQFN-20-1EP_4x4mm_P0.5mm_EP2.9x2.9mm +Package_DFN_QFN:TQFN-20-1EP_4x4mm_P0.5mm_EP2.9x2.9mm_ThermalVias +Package_DFN_QFN:TQFN-20-1EP_5x5mm_P0.65mm_EP3.1x3.1mm +Package_DFN_QFN:TQFN-20-1EP_5x5mm_P0.65mm_EP3.1x3.1mm_ThermalVias +Package_DFN_QFN:TQFN-20-1EP_5x5mm_P0.65mm_EP3.25x3.25mm +Package_DFN_QFN:TQFN-20-1EP_5x5mm_P0.65mm_EP3.25x3.25mm_ThermalVias +Package_DFN_QFN:TQFN-24-1EP_4x4mm_P0.5mm_EP2.1x2.1mm +Package_DFN_QFN:TQFN-24-1EP_4x4mm_P0.5mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:TQFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm +Package_DFN_QFN:TQFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:TQFN-24-1EP_4x4mm_P0.5mm_EP2.8x2.8mm_PullBack +Package_DFN_QFN:TQFN-24-1EP_4x4mm_P0.5mm_EP2.8x2.8mm_PullBack_ThermalVias +Package_DFN_QFN:TQFN-28-1EP_5x5mm_P0.5mm_EP2.7x2.7mm +Package_DFN_QFN:TQFN-28-1EP_5x5mm_P0.5mm_EP2.7x2.7mm_ThermalVias +Package_DFN_QFN:TQFN-28-1EP_5x5mm_P0.5mm_EP3.25x3.25mm +Package_DFN_QFN:TQFN-28-1EP_5x5mm_P0.5mm_EP3.25x3.25mm_ThermalVias +Package_DFN_QFN:TQFN-32-1EP_5x5mm_P0.5mm_EP2.1x2.1mm +Package_DFN_QFN:TQFN-32-1EP_5x5mm_P0.5mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:TQFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm +Package_DFN_QFN:TQFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm_ThermalVias +Package_DFN_QFN:TQFN-32-1EP_5x5mm_P0.5mm_EP3.4x3.4mm +Package_DFN_QFN:TQFN-32-1EP_5x5mm_P0.5mm_EP3.4x3.4mm_ThermalVias +Package_DFN_QFN:TQFN-40-1EP_5x5mm_P0.4mm_EP3.5x3.5mm +Package_DFN_QFN:TQFN-40-1EP_5x5mm_P0.4mm_EP3.5x3.5mm_ThermalVias +Package_DFN_QFN:TQFN-44-1EP_7x7mm_P0.5mm_EP4.7x4.7mm +Package_DFN_QFN:TQFN-44-1EP_7x7mm_P0.5mm_EP4.7x4.7mm_ThermalVias +Package_DFN_QFN:TQFN-48-1EP_7x7mm_P0.5mm_EP5.1x5.1mm +Package_DFN_QFN:TQFN-48-1EP_7x7mm_P0.5mm_EP5.1x5.1mm_ThermalVias +Package_DFN_QFN:UDC-QFN-20-4EP_3x4mm_P0.5mm_EP0.41x0.25mm +Package_DFN_QFN:UDFN-10_1.35x2.6mm_P0.5mm +Package_DFN_QFN:UDFN-4-1EP_1x1mm_P0.65mm_EP0.48x0.48mm +Package_DFN_QFN:UDFN-9_1.0x3.8mm_P0.5mm +Package_DFN_QFN:UFQFPN-32-1EP_5x5mm_P0.5mm_EP3.5x3.5mm +Package_DFN_QFN:UFQFPN-32-1EP_5x5mm_P0.5mm_EP3.5x3.5mm_ThermalVias +Package_DFN_QFN:UQFN-10_1.3x1.8mm_P0.4mm +Package_DFN_QFN:UQFN-10_1.4x1.8mm_P0.4mm +Package_DFN_QFN:UQFN-10_1.6x2.1mm_P0.5mm +Package_DFN_QFN:UQFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm +Package_DFN_QFN:UQFN-16-1EP_4x4mm_P0.65mm_EP2.6x2.6mm +Package_DFN_QFN:UQFN-16-1EP_4x4mm_P0.65mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:UQFN-16-1EP_4x4mm_P0.65mm_EP2.7x2.7mm +Package_DFN_QFN:UQFN-16_1.8x2.6mm_P0.4mm +Package_DFN_QFN:UQFN-20-1EP_3x3mm_P0.4mm_EP1.7x1.7mm +Package_DFN_QFN:UQFN-20-1EP_3x3mm_P0.4mm_EP1.7x1.7mm_ThermalVias +Package_DFN_QFN:UQFN-20-1EP_3x3mm_P0.4mm_EP1.85x1.85mm +Package_DFN_QFN:UQFN-20-1EP_3x3mm_P0.4mm_EP1.85x1.85mm_ThermalVias +Package_DFN_QFN:UQFN-20-1EP_4x4mm_P0.5mm_EP2.8x2.8mm +Package_DFN_QFN:UQFN-20-1EP_4x4mm_P0.5mm_EP2.8x2.8mm_ThermalVias +Package_DFN_QFN:UQFN-20_3x3mm_P0.4mm +Package_DFN_QFN:UQFN-28-1EP_4x4mm_P0.4mm_EP2.35x2.35mm +Package_DFN_QFN:UQFN-28-1EP_4x4mm_P0.4mm_EP2.35x2.35mm_ThermalVias +Package_DFN_QFN:UQFN-40-1EP_5x5mm_P0.4mm_EP3.8x3.8mm +Package_DFN_QFN:UQFN-40-1EP_5x5mm_P0.4mm_EP3.8x3.8mm_ThermalVias +Package_DFN_QFN:UQFN-48-1EP_6x6mm_P0.4mm_EP4.45x4.45mm +Package_DFN_QFN:UQFN-48-1EP_6x6mm_P0.4mm_EP4.45x4.45mm_ThermalVias +Package_DFN_QFN:UQFN-48-1EP_6x6mm_P0.4mm_EP4.62x4.62mm +Package_DFN_QFN:UQFN-48-1EP_6x6mm_P0.4mm_EP4.62x4.62mm_ThermalVias +Package_DFN_QFN:VDFN-8-1EP_2x2mm_P0.5mm_EP0.9x1.7mm +Package_DFN_QFN:Vishay_PowerPAK_MLP44-24L +Package_DFN_QFN:Vishay_PowerPAK_MLP44-24L_ThermalVias +Package_DFN_QFN:VQFN-100-1EP_12x12mm_P0.4mm_EP8x8mm +Package_DFN_QFN:VQFN-100-1EP_12x12mm_P0.4mm_EP8x8mm_ThermalVias +Package_DFN_QFN:VQFN-12-1EP_4x4mm_P0.8mm_EP2.1x2.1mm +Package_DFN_QFN:VQFN-12-1EP_4x4mm_P0.8mm_EP2.1x2.1mm_ThermalVias +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.1x1.1mm +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.1x1.1mm_ThermalVias +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.45x1.45mm +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.45x1.45mm_ThermalVias +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm_ThermalVias +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.6x1.6mm +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.6x1.6mm_ThermalVias +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.8x1.8mm +Package_DFN_QFN:VQFN-16-1EP_3x3mm_P0.5mm_EP1.8x1.8mm_ThermalVias +Package_DFN_QFN:VQFN-20-1EP_3x3mm_P0.45mm_EP1.55x1.55mm +Package_DFN_QFN:VQFN-20-1EP_3x3mm_P0.45mm_EP1.55x1.55mm_ThermalVias +Package_DFN_QFN:VQFN-20-1EP_3x3mm_P0.4mm_EP1.7x1.7mm +Package_DFN_QFN:VQFN-20-1EP_3x3mm_P0.4mm_EP1.7x1.7mm_ThermalVias +Package_DFN_QFN:VQFN-24-1EP_4x4mm_P0.5mm_EP2.45x2.45mm +Package_DFN_QFN:VQFN-24-1EP_4x4mm_P0.5mm_EP2.45x2.45mm_ThermalVias +Package_DFN_QFN:VQFN-24-1EP_4x4mm_P0.5mm_EP2.5x2.5mm +Package_DFN_QFN:VQFN-24-1EP_4x4mm_P0.5mm_EP2.5x2.5mm_ThermalVias +Package_DFN_QFN:VQFN-28-1EP_4x4mm_P0.45mm_EP2.4x2.4mm +Package_DFN_QFN:VQFN-28-1EP_4x4mm_P0.45mm_EP2.4x2.4mm_ThermalVias +Package_DFN_QFN:VQFN-28-1EP_4x5mm_P0.5mm_EP2.55x3.55mm +Package_DFN_QFN:VQFN-28-1EP_4x5mm_P0.5mm_EP2.55x3.55mm_ThermalVias +Package_DFN_QFN:VQFN-28-1EP_5x5mm_P0.5mm_EP3.25x3.25mm +Package_DFN_QFN:VQFN-28-1EP_5x5mm_P0.5mm_EP3.25x3.25mm_ThermalVias +Package_DFN_QFN:VQFN-32-1EP_4x4mm_P0.4mm_EP2.8x2.8mm +Package_DFN_QFN:VQFN-32-1EP_4x4mm_P0.4mm_EP2.8x2.8mm_ThermalVias +Package_DFN_QFN:VQFN-32-1EP_5x5mm_P0.5mm_EP3.15x3.15mm +Package_DFN_QFN:VQFN-32-1EP_5x5mm_P0.5mm_EP3.15x3.15mm_ThermalVias +Package_DFN_QFN:VQFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm +Package_DFN_QFN:VQFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm_ThermalVias +Package_DFN_QFN:VQFN-32-1EP_5x5mm_P0.5mm_EP3.5x3.5mm +Package_DFN_QFN:VQFN-32-1EP_5x5mm_P0.5mm_EP3.5x3.5mm_ThermalVias +Package_DFN_QFN:VQFN-40-1EP_5x5mm_P0.4mm_EP3.5x3.5mm +Package_DFN_QFN:VQFN-40-1EP_5x5mm_P0.4mm_EP3.5x3.5mm_ThermalVias +Package_DFN_QFN:VQFN-40-1EP_5x5mm_P0.4mm_EP3.6x3.6mm +Package_DFN_QFN:VQFN-40-1EP_5x5mm_P0.4mm_EP3.6x3.6mm_ThermalVias +Package_DFN_QFN:VQFN-46-1EP_5x6mm_P0.4mm_EP2.8x3.8mm +Package_DFN_QFN:VQFN-46-1EP_5x6mm_P0.4mm_EP2.8x3.8mm_ThermalVias +Package_DFN_QFN:VQFN-48-1EP_6x6mm_P0.4mm_EP4.1x4.1mm +Package_DFN_QFN:VQFN-48-1EP_6x6mm_P0.4mm_EP4.1x4.1mm_ThermalVias +Package_DFN_QFN:VQFN-48-1EP_7x7mm_P0.5mm_EP4.1x4.1mm +Package_DFN_QFN:VQFN-48-1EP_7x7mm_P0.5mm_EP4.1x4.1mm_ThermalVias +Package_DFN_QFN:VQFN-48-1EP_7x7mm_P0.5mm_EP5.15x5.15mm +Package_DFN_QFN:VQFN-48-1EP_7x7mm_P0.5mm_EP5.15x5.15mm_ThermalVias +Package_DFN_QFN:VQFN-64-1EP_9x9mm_P0.5mm_EP5.4x5.4mm +Package_DFN_QFN:VQFN-64-1EP_9x9mm_P0.5mm_EP5.4x5.4mm_ThermalVias +Package_DFN_QFN:VQFN-64-1EP_9x9mm_P0.5mm_EP7.15x7.15mm +Package_DFN_QFN:VQFN-64-1EP_9x9mm_P0.5mm_EP7.15x7.15mm_ThermalVias +Package_DFN_QFN:W-PDFN-8-1EP_6x5mm_P1.27mm_EP3x3mm +Package_DFN_QFN:WCH_QFN-16-1EP_3x3mm_P0.5mm_EP1.8x1.8mm +Package_DFN_QFN:WDFN-10-1EP_3x3mm_P0.5mm_EP1.8x2.5mm +Package_DFN_QFN:WDFN-10-1EP_3x3mm_P0.5mm_EP1.8x2.5mm_ThermalVias +Package_DFN_QFN:WDFN-12-1EP_3x3mm_P0.45mm_EP1.7x2.5mm +Package_DFN_QFN:WDFN-6-2EP_4.0x2.6mm_P0.65mm +Package_DFN_QFN:WDFN-8-1EP_2x2.2mm_P0.5mm_EP0.80x0.54 +Package_DFN_QFN:WDFN-8-1EP_2x2mm_P0.5mm_EP0.8x1.2mm +Package_DFN_QFN:WDFN-8-1EP_3x2mm_P0.5mm_EP1.3x1.4mm +Package_DFN_QFN:WDFN-8-1EP_4x3mm_P0.65mm_EP2.4x1.8mm +Package_DFN_QFN:WDFN-8-1EP_4x3mm_P0.65mm_EP2.4x1.8mm_ThermalVias +Package_DFN_QFN:WDFN-8-1EP_6x5mm_P1.27mm_EP3.4x4mm +Package_DFN_QFN:WDFN-8-1EP_8x6mm_P1.27mm_EP6x4.8mm +Package_DFN_QFN:WDFN-8-1EP_8x6mm_P1.27mm_EP6x4.8mm_ThermalVias +Package_DFN_QFN:WDFN-8_2x2mm_P0.5mm +Package_DFN_QFN:WFDFPN-8-1EP_3x2mm_P0.5mm_EP1.25x1.35mm +Package_DFN_QFN:WQFN-14-1EP_2.5x2.5mm_P0.5mm_EP1.45x1.45mm +Package_DFN_QFN:WQFN-14-1EP_2.5x2.5mm_P0.5mm_EP1.45x1.45mm_ThermalVias +Package_DFN_QFN:WQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm +Package_DFN_QFN:WQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm_ThermalVias +Package_DFN_QFN:WQFN-16-1EP_3x3mm_P0.5mm_EP1.6x1.6mm +Package_DFN_QFN:WQFN-16-1EP_3x3mm_P0.5mm_EP1.6x1.6mm_ThermalVias +Package_DFN_QFN:WQFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm +Package_DFN_QFN:WQFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm_ThermalVias +Package_DFN_QFN:WQFN-16-1EP_4x4mm_P0.5mm_EP2.6x2.6mm +Package_DFN_QFN:WQFN-16-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:WQFN-20-1EP_2.5x4.5mm_P0.5mm_EP1x2.9mm +Package_DFN_QFN:WQFN-20-1EP_3x3mm_P0.4mm_EP1.7x1.7mm +Package_DFN_QFN:WQFN-20-1EP_3x3mm_P0.4mm_EP1.7x1.7mm_ThermalVias +Package_DFN_QFN:WQFN-24-1EP_4x4mm_P0.5mm_EP2.45x2.45mm +Package_DFN_QFN:WQFN-24-1EP_4x4mm_P0.5mm_EP2.45x2.45mm_ThermalVias +Package_DFN_QFN:WQFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm +Package_DFN_QFN:WQFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias +Package_DFN_QFN:WQFN-32-1EP_5x5mm_P0.5mm_EP3.1x3.1mm +Package_DFN_QFN:WQFN-42-1EP_3.5x9mm_P0.5mm_EP2.05x7.55mm +Package_DFN_QFN:WQFN-42-1EP_3.5x9mm_P0.5mm_EP2.05x7.55mm_ThermalVias +Package_DIP:CERDIP-14_W7.62mm_SideBrazed +Package_DIP:CERDIP-14_W7.62mm_SideBrazed_LongPads +Package_DIP:CERDIP-14_W7.62mm_SideBrazed_LongPads_Socket +Package_DIP:CERDIP-14_W7.62mm_SideBrazed_Socket +Package_DIP:CERDIP-16_W7.62mm_SideBrazed +Package_DIP:CERDIP-16_W7.62mm_SideBrazed_LongPads +Package_DIP:CERDIP-16_W7.62mm_SideBrazed_LongPads_Socket +Package_DIP:CERDIP-16_W7.62mm_SideBrazed_Socket +Package_DIP:CERDIP-18_W7.62mm_SideBrazed +Package_DIP:CERDIP-18_W7.62mm_SideBrazed_LongPads +Package_DIP:CERDIP-18_W7.62mm_SideBrazed_LongPads_Socket +Package_DIP:CERDIP-18_W7.62mm_SideBrazed_Socket +Package_DIP:CERDIP-20_W7.62mm_SideBrazed +Package_DIP:CERDIP-20_W7.62mm_SideBrazed_LongPads +Package_DIP:CERDIP-20_W7.62mm_SideBrazed_LongPads_Socket +Package_DIP:CERDIP-20_W7.62mm_SideBrazed_Socket +Package_DIP:CERDIP-22_W7.62mm_SideBrazed +Package_DIP:CERDIP-22_W7.62mm_SideBrazed_LongPads +Package_DIP:CERDIP-22_W7.62mm_SideBrazed_LongPads_Socket +Package_DIP:CERDIP-22_W7.62mm_SideBrazed_Socket +Package_DIP:CERDIP-24_W7.62mm_SideBrazed +Package_DIP:CERDIP-24_W7.62mm_SideBrazed_LongPads +Package_DIP:CERDIP-24_W7.62mm_SideBrazed_LongPads_Socket +Package_DIP:CERDIP-24_W7.62mm_SideBrazed_Socket +Package_DIP:CERDIP-28_W7.62mm_SideBrazed +Package_DIP:CERDIP-28_W7.62mm_SideBrazed_LongPads +Package_DIP:CERDIP-28_W7.62mm_SideBrazed_LongPads_Socket +Package_DIP:CERDIP-28_W7.62mm_SideBrazed_Socket +Package_DIP:CERDIP-8_W7.62mm_SideBrazed +Package_DIP:CERDIP-8_W7.62mm_SideBrazed_LongPads +Package_DIP:CERDIP-8_W7.62mm_SideBrazed_LongPads_Socket +Package_DIP:CERDIP-8_W7.62mm_SideBrazed_Socket +Package_DIP:DIP-10_W10.16mm +Package_DIP:DIP-10_W10.16mm_LongPads +Package_DIP:DIP-10_W7.62mm +Package_DIP:DIP-10_W7.62mm_LongPads +Package_DIP:DIP-10_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-10_W7.62mm_Socket +Package_DIP:DIP-10_W7.62mm_Socket_LongPads +Package_DIP:DIP-10_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-12_W10.16mm +Package_DIP:DIP-12_W10.16mm_LongPads +Package_DIP:DIP-12_W7.62mm +Package_DIP:DIP-12_W7.62mm_LongPads +Package_DIP:DIP-12_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-12_W7.62mm_Socket +Package_DIP:DIP-12_W7.62mm_Socket_LongPads +Package_DIP:DIP-12_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-14_W10.16mm +Package_DIP:DIP-14_W10.16mm_LongPads +Package_DIP:DIP-14_W7.62mm +Package_DIP:DIP-14_W7.62mm_LongPads +Package_DIP:DIP-14_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-14_W7.62mm_Socket +Package_DIP:DIP-14_W7.62mm_Socket_LongPads +Package_DIP:DIP-14_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-16_W10.16mm +Package_DIP:DIP-16_W10.16mm_LongPads +Package_DIP:DIP-16_W7.62mm +Package_DIP:DIP-16_W7.62mm_LongPads +Package_DIP:DIP-16_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-16_W7.62mm_Socket +Package_DIP:DIP-16_W7.62mm_Socket_LongPads +Package_DIP:DIP-16_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-18_W7.62mm +Package_DIP:DIP-18_W7.62mm_LongPads +Package_DIP:DIP-18_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-18_W7.62mm_Socket +Package_DIP:DIP-18_W7.62mm_Socket_LongPads +Package_DIP:DIP-18_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-20_W7.62mm +Package_DIP:DIP-20_W7.62mm_LongPads +Package_DIP:DIP-20_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-20_W7.62mm_Socket +Package_DIP:DIP-20_W7.62mm_Socket_LongPads +Package_DIP:DIP-20_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-22_W10.16mm +Package_DIP:DIP-22_W10.16mm_LongPads +Package_DIP:DIP-22_W10.16mm_SMDSocket_SmallPads +Package_DIP:DIP-22_W10.16mm_Socket +Package_DIP:DIP-22_W10.16mm_Socket_LongPads +Package_DIP:DIP-22_W11.43mm_SMDSocket_LongPads +Package_DIP:DIP-22_W7.62mm +Package_DIP:DIP-22_W7.62mm_LongPads +Package_DIP:DIP-22_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-22_W7.62mm_Socket +Package_DIP:DIP-22_W7.62mm_Socket_LongPads +Package_DIP:DIP-22_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-24_18.0mmx34.29mm_W15.24mm +Package_DIP:DIP-24_18.0mmx34.29mm_W15.24mm_LongPads +Package_DIP:DIP-24_18.0mmx34.29mm_W15.24mm_SMDSocket_SmallPads +Package_DIP:DIP-24_18.0mmx34.29mm_W15.24mm_Socket +Package_DIP:DIP-24_18.0mmx34.29mm_W15.24mm_Socket_LongPads +Package_DIP:DIP-24_W10.16mm +Package_DIP:DIP-24_W10.16mm_LongPads +Package_DIP:DIP-24_W10.16mm_SMDSocket_SmallPads +Package_DIP:DIP-24_W10.16mm_Socket +Package_DIP:DIP-24_W10.16mm_Socket_LongPads +Package_DIP:DIP-24_W11.43mm_SMDSocket_LongPads +Package_DIP:DIP-24_W15.24mm +Package_DIP:DIP-24_W15.24mm_LongPads +Package_DIP:DIP-24_W15.24mm_SMDSocket_SmallPads +Package_DIP:DIP-24_W15.24mm_Socket +Package_DIP:DIP-24_W15.24mm_Socket_LongPads +Package_DIP:DIP-24_W16.51mm_SMDSocket_LongPads +Package_DIP:DIP-24_W7.62mm +Package_DIP:DIP-24_W7.62mm_LongPads +Package_DIP:DIP-24_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-24_W7.62mm_Socket +Package_DIP:DIP-24_W7.62mm_Socket_LongPads +Package_DIP:DIP-24_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-28_W15.24mm +Package_DIP:DIP-28_W15.24mm_LongPads +Package_DIP:DIP-28_W15.24mm_SMDSocket_SmallPads +Package_DIP:DIP-28_W15.24mm_Socket +Package_DIP:DIP-28_W15.24mm_Socket_LongPads +Package_DIP:DIP-28_W16.51mm_SMDSocket_LongPads +Package_DIP:DIP-28_W7.62mm +Package_DIP:DIP-28_W7.62mm_LongPads +Package_DIP:DIP-28_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-28_W7.62mm_Socket +Package_DIP:DIP-28_W7.62mm_Socket_LongPads +Package_DIP:DIP-28_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-32_W15.24mm +Package_DIP:DIP-32_W15.24mm_LongPads +Package_DIP:DIP-32_W15.24mm_SMDSocket_SmallPads +Package_DIP:DIP-32_W15.24mm_Socket +Package_DIP:DIP-32_W15.24mm_Socket_LongPads +Package_DIP:DIP-32_W16.51mm_SMDSocket_LongPads +Package_DIP:DIP-32_W7.62mm +Package_DIP:DIP-40_W15.24mm +Package_DIP:DIP-40_W15.24mm_LongPads +Package_DIP:DIP-40_W15.24mm_SMDSocket_SmallPads +Package_DIP:DIP-40_W15.24mm_Socket +Package_DIP:DIP-40_W15.24mm_Socket_LongPads +Package_DIP:DIP-40_W16.51mm_SMDSocket_LongPads +Package_DIP:DIP-40_W25.4mm +Package_DIP:DIP-40_W25.4mm_LongPads +Package_DIP:DIP-40_W25.4mm_SMDSocket_SmallPads +Package_DIP:DIP-40_W25.4mm_Socket +Package_DIP:DIP-40_W25.4mm_Socket_LongPads +Package_DIP:DIP-40_W26.67mm_SMDSocket_LongPads +Package_DIP:DIP-42_W15.24mm +Package_DIP:DIP-42_W15.24mm_LongPads +Package_DIP:DIP-42_W15.24mm_SMDSocket_SmallPads +Package_DIP:DIP-42_W15.24mm_Socket +Package_DIP:DIP-42_W15.24mm_Socket_LongPads +Package_DIP:DIP-42_W16.51mm_SMDSocket_LongPads +Package_DIP:DIP-48_W15.24mm +Package_DIP:DIP-48_W15.24mm_LongPads +Package_DIP:DIP-48_W15.24mm_SMDSocket_SmallPads +Package_DIP:DIP-48_W15.24mm_Socket +Package_DIP:DIP-48_W15.24mm_Socket_LongPads +Package_DIP:DIP-48_W16.51mm_SMDSocket_LongPads +Package_DIP:DIP-4_W10.16mm +Package_DIP:DIP-4_W10.16mm_LongPads +Package_DIP:DIP-4_W7.62mm +Package_DIP:DIP-4_W7.62mm_LongPads +Package_DIP:DIP-4_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-4_W7.62mm_Socket +Package_DIP:DIP-4_W7.62mm_Socket_LongPads +Package_DIP:DIP-4_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-5-6_W10.16mm +Package_DIP:DIP-5-6_W10.16mm_LongPads +Package_DIP:DIP-5-6_W7.62mm +Package_DIP:DIP-5-6_W7.62mm_LongPads +Package_DIP:DIP-5-6_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-5-6_W7.62mm_Socket +Package_DIP:DIP-5-6_W7.62mm_Socket_LongPads +Package_DIP:DIP-5-6_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-64_W15.24mm +Package_DIP:DIP-64_W15.24mm_LongPads +Package_DIP:DIP-64_W15.24mm_SMDSocket_SmallPads +Package_DIP:DIP-64_W15.24mm_Socket +Package_DIP:DIP-64_W15.24mm_Socket_LongPads +Package_DIP:DIP-64_W16.51mm_SMDSocket_LongPads +Package_DIP:DIP-64_W22.86mm +Package_DIP:DIP-64_W22.86mm_LongPads +Package_DIP:DIP-64_W22.86mm_SMDSocket_SmallPads +Package_DIP:DIP-64_W22.86mm_Socket +Package_DIP:DIP-64_W22.86mm_Socket_LongPads +Package_DIP:DIP-64_W24.13mm_SMDSocket_LongPads +Package_DIP:DIP-64_W25.4mm +Package_DIP:DIP-64_W25.4mm_LongPads +Package_DIP:DIP-64_W25.4mm_SMDSocket_SmallPads +Package_DIP:DIP-64_W25.4mm_Socket +Package_DIP:DIP-64_W25.4mm_Socket_LongPads +Package_DIP:DIP-64_W26.67mm_SMDSocket_LongPads +Package_DIP:DIP-6_W10.16mm +Package_DIP:DIP-6_W10.16mm_LongPads +Package_DIP:DIP-6_W7.62mm +Package_DIP:DIP-6_W7.62mm_LongPads +Package_DIP:DIP-6_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-6_W7.62mm_Socket +Package_DIP:DIP-6_W7.62mm_Socket_LongPads +Package_DIP:DIP-6_W8.89mm_SMDSocket_LongPads +Package_DIP:DIP-8-16_W7.62mm +Package_DIP:DIP-8-16_W7.62mm_Socket +Package_DIP:DIP-8-16_W7.62mm_Socket_LongPads +Package_DIP:DIP-8-N6_W7.62mm +Package_DIP:DIP-8-N7_W7.62mm +Package_DIP:DIP-8_W10.16mm +Package_DIP:DIP-8_W10.16mm_LongPads +Package_DIP:DIP-8_W7.62mm +Package_DIP:DIP-8_W7.62mm_LongPads +Package_DIP:DIP-8_W7.62mm_SMDSocket_SmallPads +Package_DIP:DIP-8_W7.62mm_Socket +Package_DIP:DIP-8_W7.62mm_Socket_LongPads +Package_DIP:DIP-8_W8.89mm_SMDSocket_LongPads +Package_DIP:Fairchild_LSOP-8 +Package_DIP:IXYS_Flatpak-8_6.3x9.7mm_P2.54mm +Package_DIP:IXYS_SMD-8_6.3x9.7mm_P2.54mm +Package_DIP:PowerIntegrations_eDIP-12B +Package_DIP:PowerIntegrations_PDIP-8B +Package_DIP:PowerIntegrations_PDIP-8C +Package_DIP:PowerIntegrations_SDIP-10C +Package_DIP:PowerIntegrations_SMD-8 +Package_DIP:PowerIntegrations_SMD-8B +Package_DIP:PowerIntegrations_SMD-8C +Package_DIP:SMDIP-10_W11.48mm +Package_DIP:SMDIP-10_W7.62mm +Package_DIP:SMDIP-10_W9.53mm +Package_DIP:SMDIP-10_W9.53mm_Clearance8mm +Package_DIP:SMDIP-12_W11.48mm +Package_DIP:SMDIP-12_W7.62mm +Package_DIP:SMDIP-12_W9.53mm +Package_DIP:SMDIP-12_W9.53mm_Clearance8mm +Package_DIP:SMDIP-14_W11.48mm +Package_DIP:SMDIP-14_W7.62mm +Package_DIP:SMDIP-14_W9.53mm +Package_DIP:SMDIP-14_W9.53mm_Clearance8mm +Package_DIP:SMDIP-16_W11.48mm +Package_DIP:SMDIP-16_W7.62mm +Package_DIP:SMDIP-16_W9.53mm +Package_DIP:SMDIP-16_W9.53mm_Clearance8mm +Package_DIP:SMDIP-18_W11.48mm +Package_DIP:SMDIP-18_W7.62mm +Package_DIP:SMDIP-18_W9.53mm +Package_DIP:SMDIP-18_W9.53mm_Clearance8mm +Package_DIP:SMDIP-20_W11.48mm +Package_DIP:SMDIP-20_W7.62mm +Package_DIP:SMDIP-20_W9.53mm +Package_DIP:SMDIP-20_W9.53mm_Clearance8mm +Package_DIP:SMDIP-22_W11.48mm +Package_DIP:SMDIP-22_W7.62mm +Package_DIP:SMDIP-22_W9.53mm +Package_DIP:SMDIP-22_W9.53mm_Clearance8mm +Package_DIP:SMDIP-24_W11.48mm +Package_DIP:SMDIP-24_W15.24mm +Package_DIP:SMDIP-24_W7.62mm +Package_DIP:SMDIP-24_W9.53mm +Package_DIP:SMDIP-28_W15.24mm +Package_DIP:SMDIP-32_W11.48mm +Package_DIP:SMDIP-32_W15.24mm +Package_DIP:SMDIP-32_W7.62mm +Package_DIP:SMDIP-32_W9.53mm +Package_DIP:SMDIP-40_W15.24mm +Package_DIP:SMDIP-40_W25.24mm +Package_DIP:SMDIP-42_W15.24mm +Package_DIP:SMDIP-48_W15.24mm +Package_DIP:SMDIP-4_W11.48mm +Package_DIP:SMDIP-4_W7.62mm +Package_DIP:SMDIP-4_W9.53mm +Package_DIP:SMDIP-4_W9.53mm_Clearance8mm +Package_DIP:SMDIP-64_W15.24mm +Package_DIP:SMDIP-6_W11.48mm +Package_DIP:SMDIP-6_W7.62mm +Package_DIP:SMDIP-6_W9.53mm +Package_DIP:SMDIP-6_W9.53mm_Clearance8mm +Package_DIP:SMDIP-8_W11.48mm +Package_DIP:SMDIP-8_W7.62mm +Package_DIP:SMDIP-8_W9.53mm +Package_DIP:SMDIP-8_W9.53mm_Clearance8mm +Package_DIP:Toshiba_11-7A9 +Package_DIP:Vishay_HVM-DIP-3_W7.62mm +Package_DirectFET:DirectFET_L4 +Package_DirectFET:DirectFET_L6 +Package_DirectFET:DirectFET_L8 +Package_DirectFET:DirectFET_LA +Package_DirectFET:DirectFET_M2 +Package_DirectFET:DirectFET_M4 +Package_DirectFET:DirectFET_MA +Package_DirectFET:DirectFET_MB +Package_DirectFET:DirectFET_MC +Package_DirectFET:DirectFET_MD +Package_DirectFET:DirectFET_ME +Package_DirectFET:DirectFET_MF +Package_DirectFET:DirectFET_MN +Package_DirectFET:DirectFET_MP +Package_DirectFET:DirectFET_MQ +Package_DirectFET:DirectFET_MT +Package_DirectFET:DirectFET_MU +Package_DirectFET:DirectFET_MX +Package_DirectFET:DirectFET_MZ +Package_DirectFET:DirectFET_S1 +Package_DirectFET:DirectFET_S2 +Package_DirectFET:DirectFET_S3C +Package_DirectFET:DirectFET_SA +Package_DirectFET:DirectFET_SB +Package_DirectFET:DirectFET_SC +Package_DirectFET:DirectFET_SH +Package_DirectFET:DirectFET_SJ +Package_DirectFET:DirectFET_SQ +Package_DirectFET:DirectFET_ST +Package_LCC:Analog_LCC-8_5x5mm_P1.27mm +Package_LCC:PLCC-20 +Package_LCC:PLCC-20_SMD-Socket +Package_LCC:PLCC-20_THT-Socket +Package_LCC:PLCC-28 +Package_LCC:PLCC-28_SMD-Socket +Package_LCC:PLCC-28_THT-Socket +Package_LCC:PLCC-32_11.4x14.0mm_P1.27mm +Package_LCC:PLCC-32_THT-Socket +Package_LCC:PLCC-44 +Package_LCC:PLCC-44_16.6x16.6mm_P1.27mm +Package_LCC:PLCC-44_SMD-Socket +Package_LCC:PLCC-44_THT-Socket +Package_LCC:PLCC-52 +Package_LCC:PLCC-52_SMD-Socket +Package_LCC:PLCC-52_THT-Socket +Package_LCC:PLCC-68 +Package_LCC:PLCC-68_24.2x24.2mm_P1.27mm +Package_LCC:PLCC-68_SMD-Socket +Package_LCC:PLCC-68_THT-Socket +Package_LCC:PLCC-84 +Package_LCC:PLCC-84_29.3x29.3mm_P1.27mm +Package_LCC:PLCC-84_SMD-Socket +Package_LCC:PLCC-84_THT-Socket +Package_LGA:AMS_LGA-10-1EP_2.7x4mm_P0.6mm +Package_LGA:AMS_LGA-20_4.7x4.5mm_P0.65mm +Package_LGA:AMS_OLGA-8_2x3.1mm_P0.8mm +Package_LGA:Bosch_LGA-14_3x2.5mm_P0.5mm +Package_LGA:Bosch_LGA-8_2.5x2.5mm_P0.65mm_ClockwisePinNumbering +Package_LGA:Bosch_LGA-8_2x2.5mm_P0.65mm_ClockwisePinNumbering +Package_LGA:Bosch_LGA-8_3x3mm_P0.8mm_ClockwisePinNumbering +Package_LGA:Infineon_PG-TSNP-6-10_0.7x1.1mm_0.7x1.1mm_P0.4mm +Package_LGA:Kionix_LGA-12_2x2mm_P0.5mm_LayoutBorder2x4y +Package_LGA:LGA-12_2x2mm_P0.5mm +Package_LGA:LGA-14_2x2mm_P0.35mm_LayoutBorder3x4y +Package_LGA:LGA-14_3x2.5mm_P0.5mm_LayoutBorder3x4y +Package_LGA:LGA-14_3x5mm_P0.8mm_LayoutBorder1x6y +Package_LGA:LGA-16_3x3mm_P0.5mm +Package_LGA:LGA-16_3x3mm_P0.5mm_LayoutBorder3x5y +Package_LGA:LGA-16_4x4mm_P0.65mm_LayoutBorder4x4y +Package_LGA:LGA-24L_3x3.5mm_P0.43mm +Package_LGA:LGA-28_5.2x3.8mm_P0.5mm +Package_LGA:LGA-8_3x5mm_P1.25mm +Package_LGA:LGA-8_8x6.2mm_P1.27mm +Package_LGA:LGA-8_8x6mm_P1.27mm +Package_LGA:Linear_LGA-133_15.0x15.0mm_Layout12x12_P1.27mm +Package_LGA:MPS_LGA-18-10EP_12x12mm_P3.3mm +Package_LGA:Nordic_nRF9160-SIxx_LGA-102-59EP_16.0x10.5mm_P0.5mm +Package_LGA:NXP_LGA-8_3x5mm_P1.25mm_H1.1mm +Package_LGA:NXP_LGA-8_3x5mm_P1.25mm_H1.2mm +Package_LGA:Rohm_MLGA010V020A_LGA-10_2x2mm_P0.45mm_LayoutBorder2x3y +Package_LGA:ST_CCLGA-7L_2.8x2.8mm_P1.15mm_H1.95mm +Package_LGA:ST_HLGA-10_2.5x2.5mm_P0.6mm_LayoutBorder3x2y +Package_LGA:ST_HLGA-10_2x2mm_P0.5mm_LayoutBorder3x2y +Package_LGA:Texas_SIL0008D_MicroSiP-8-1EP_2.8x3mm_P0.65mm_EP1.1x1.9mm +Package_LGA:Texas_SIL0008D_MicroSiP-8-1EP_2.8x3mm_P0.65mm_EP1.1x1.9mm_ThermalVias +Package_LGA:Texas_SIL0010A_MicroSiP-10-1EP_3.8x3mm_P0.6mm_EP0.7x2.9mm +Package_LGA:Texas_SIL0010A_MicroSiP-10-1EP_3.8x3mm_P0.6mm_EP0.7x2.9mm_ThermalVias +Package_LGA:VLGA-4_2x2.5mm_P1.65mm +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP4x4mm +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP4x4mm_ThermalVias +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP5x5mm +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP5x5mm_ThermalVias +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP6.61x5.615mm +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP6.61x5.615mm_ThermalVias +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP7.2x6.35mm +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP7.2x6.35mm_ThermalVias +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP8.93x8.7mm +Package_QFP:EQFP-144-1EP_20x20mm_P0.5mm_EP8.93x8.7mm_ThermalVias +Package_QFP:HTQFP-64-1EP_10x10mm_P0.5mm_EP8x8mm +Package_QFP:HTQFP-64-1EP_10x10mm_P0.5mm_EP8x8mm_Mask4.4x4.4mm_ThermalVias +Package_QFP:LQFP-100_14x14mm_P0.5mm +Package_QFP:LQFP-128_14x14mm_P0.4mm +Package_QFP:LQFP-128_14x20mm_P0.5mm +Package_QFP:LQFP-144-1EP_20x20mm_P0.5mm_EP6.5x6.5mm +Package_QFP:LQFP-144-1EP_20x20mm_P0.5mm_EP6.5x6.5mm_ThermalVias +Package_QFP:LQFP-144_20x20mm_P0.5mm +Package_QFP:LQFP-160_24x24mm_P0.5mm +Package_QFP:LQFP-176_20x20mm_P0.4mm +Package_QFP:LQFP-176_24x24mm_P0.5mm +Package_QFP:LQFP-208_28x28mm_P0.5mm +Package_QFP:LQFP-216_24x24mm_P0.4mm +Package_QFP:LQFP-32_5x5mm_P0.5mm +Package_QFP:LQFP-32_7x7mm_P0.8mm +Package_QFP:LQFP-36_7x7mm_P0.65mm +Package_QFP:LQFP-44_10x10mm_P0.8mm +Package_QFP:LQFP-48-1EP_7x7mm_P0.5mm_EP3.6x3.6mm +Package_QFP:LQFP-48-1EP_7x7mm_P0.5mm_EP3.6x3.6mm_ThermalVias +Package_QFP:LQFP-48_7x7mm_P0.5mm +Package_QFP:LQFP-52-1EP_10x10mm_P0.65mm_EP4.8x4.8mm +Package_QFP:LQFP-52-1EP_10x10mm_P0.65mm_EP4.8x4.8mm_ThermalVias +Package_QFP:LQFP-52_10x10mm_P0.65mm +Package_QFP:LQFP-52_14x14mm_P1mm +Package_QFP:LQFP-64-1EP_10x10mm_P0.5mm_EP5x5mm +Package_QFP:LQFP-64-1EP_10x10mm_P0.5mm_EP5x5mm_ThermalVias +Package_QFP:LQFP-64-1EP_10x10mm_P0.5mm_EP6.5x6.5mm +Package_QFP:LQFP-64-1EP_10x10mm_P0.5mm_EP6.5x6.5mm_ThermalVias +Package_QFP:LQFP-64_10x10mm_P0.5mm +Package_QFP:LQFP-64_14x14mm_P0.8mm +Package_QFP:LQFP-64_7x7mm_P0.4mm +Package_QFP:LQFP-80_10x10mm_P0.4mm +Package_QFP:LQFP-80_12x12mm_P0.5mm +Package_QFP:LQFP-80_14x14mm_P0.65mm +Package_QFP:Microchip_PQFP-44_10x10mm_P0.8mm +Package_QFP:MQFP-44_10x10mm_P0.8mm +Package_QFP:PQFP-100_14x20mm_P0.65mm +Package_QFP:PQFP-112_20x20mm_P0.65mm +Package_QFP:PQFP-132_24x24mm_P0.635mm +Package_QFP:PQFP-132_24x24mm_P0.635mm_i386 +Package_QFP:PQFP-144_28x28mm_P0.65mm +Package_QFP:PQFP-160_28x28mm_P0.65mm +Package_QFP:PQFP-168_28x28mm_P0.65mm +Package_QFP:PQFP-208_28x28mm_P0.5mm +Package_QFP:PQFP-240_32.1x32.1mm_P0.5mm +Package_QFP:PQFP-256_28x28mm_P0.4mm +Package_QFP:PQFP-32_5x5mm_P0.5mm +Package_QFP:PQFP-44_10x10mm_P0.8mm +Package_QFP:PQFP-64_14x14mm_P0.8mm +Package_QFP:PQFP-80_14x20mm_P0.8mm +Package_QFP:Texas_PHP0048E_HTQFP-48-1EP_7x7mm_P0.5mm_EP6.5x6.5mm_Mask3.62x3.62mm +Package_QFP:Texas_PHP0048E_HTQFP-48-1EP_7x7mm_P0.5mm_EP6.5x6.5mm_Mask3.62x3.62mm_ThermalVias +Package_QFP:TQFP-100-1EP_14x14mm_P0.5mm_EP5x5mm +Package_QFP:TQFP-100-1EP_14x14mm_P0.5mm_EP5x5mm_ThermalVias +Package_QFP:TQFP-100_12x12mm_P0.4mm +Package_QFP:TQFP-100_14x14mm_P0.5mm +Package_QFP:TQFP-120_14x14mm_P0.4mm +Package_QFP:TQFP-128_14x14mm_P0.4mm +Package_QFP:TQFP-144_16x16mm_P0.4mm +Package_QFP:TQFP-144_20x20mm_P0.5mm +Package_QFP:TQFP-176_24x24mm_P0.5mm +Package_QFP:TQFP-32_7x7mm_P0.8mm +Package_QFP:TQFP-44-1EP_10x10mm_P0.8mm_EP4.5x4.5mm +Package_QFP:TQFP-44_10x10mm_P0.8mm +Package_QFP:TQFP-48-1EP_7x7mm_P0.5mm_EP3.5x3.5mm +Package_QFP:TQFP-48-1EP_7x7mm_P0.5mm_EP4.11x4.11mm +Package_QFP:TQFP-48-1EP_7x7mm_P0.5mm_EP5x5mm +Package_QFP:TQFP-48-1EP_7x7mm_P0.5mm_EP5x5mm_ThermalVias +Package_QFP:TQFP-48_7x7mm_P0.5mm +Package_QFP:TQFP-52-1EP_10x10mm_P0.65mm_EP6.5x6.5mm +Package_QFP:TQFP-52-1EP_10x10mm_P0.65mm_EP6.5x6.5mm_ThermalVias +Package_QFP:TQFP-64-1EP_10x10mm_P0.5mm_EP5.305x5.305mm +Package_QFP:TQFP-64-1EP_10x10mm_P0.5mm_EP5.305x5.305mm_ThermalVias +Package_QFP:TQFP-64-1EP_10x10mm_P0.5mm_EP8x8mm +Package_QFP:TQFP-64_10x10mm_P0.5mm +Package_QFP:TQFP-64_14x14mm_P0.8mm +Package_QFP:TQFP-64_7x7mm_P0.4mm +Package_QFP:TQFP-80-1EP_14x14mm_P0.65mm_EP9.5x9.5mm +Package_QFP:TQFP-80_12x12mm_P0.5mm +Package_QFP:TQFP-80_14x14mm_P0.65mm +Package_QFP:VQFP-100_14x14mm_P0.5mm +Package_QFP:VQFP-128_14x14mm_P0.4mm +Package_QFP:VQFP-176_20x20mm_P0.4mm +Package_QFP:VQFP-80_14x14mm_P0.65mm +Package_SIP:PowerIntegrations_eSIP-7C +Package_SIP:PowerIntegrations_eSIP-7F +Package_SIP:Sanyo_STK4xx-15_59.2x8.0mm_P2.54mm +Package_SIP:Sanyo_STK4xx-15_78.0x8.0mm_P2.54mm +Package_SIP:SIP-8_19x3mm_P2.54mm +Package_SIP:SIP-9_21.54x3mm_P2.54mm +Package_SIP:SIP-9_22.3x3mm_P2.54mm +Package_SIP:SIP3_11.6x8.5mm +Package_SIP:SIP4_Sharp-SSR_P7.62mm_Angled +Package_SIP:SIP4_Sharp-SSR_P7.62mm_Angled_NoHole +Package_SIP:SIP4_Sharp-SSR_P7.62mm_Straight +Package_SIP:SIP9_Housing +Package_SIP:SIP9_Housing_BigPads +Package_SIP:SLA704XM +Package_SIP:STK672-040-E +Package_SIP:STK672-080-E +Package_SO:Analog_MSOP-12-16-1EP_3x4.039mm_P0.5mm_EP1.651x2.845mm +Package_SO:Analog_MSOP-12-16-1EP_3x4.039mm_P0.5mm_EP1.651x2.845mm_ThermalVias +Package_SO:Analog_MSOP-12-16_3x4.039mm_P0.5mm +Package_SO:Diodes_PSOP-8 +Package_SO:Diodes_SO-8EP +Package_SO:ETSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3x4.2mm +Package_SO:HSOP-20-1EP_11.0x15.9mm_P1.27mm_SlugDown +Package_SO:HSOP-20-1EP_11.0x15.9mm_P1.27mm_SlugDown_ThermalVias +Package_SO:HSOP-20-1EP_11.0x15.9mm_P1.27mm_SlugUp +Package_SO:HSOP-32-1EP_7.5x11mm_P0.65mm_EP4.7x4.7mm +Package_SO:HSOP-36-1EP_11.0x15.9mm_P0.65mm_SlugDown +Package_SO:HSOP-36-1EP_11.0x15.9mm_P0.65mm_SlugDown_ThermalVias +Package_SO:HSOP-36-1EP_11.0x15.9mm_P0.65mm_SlugUp +Package_SO:HSOP-54-1EP_7.5x17.9mm_P0.65mm_EP4.6x4.6mm +Package_SO:HSOP-8-1EP_3.9x4.9mm_P1.27mm_EP2.3x2.3mm +Package_SO:HSOP-8-1EP_3.9x4.9mm_P1.27mm_EP2.3x2.3mm_ThermalVias +Package_SO:HSOP-8-1EP_3.9x4.9mm_P1.27mm_EP2.41x3.1mm +Package_SO:HSOP-8-1EP_3.9x4.9mm_P1.27mm_EP2.41x3.1mm_ThermalVias +Package_SO:HTSOP-8-1EP_3.9x4.9mm_P1.27mm_EP2.4x3.2mm +Package_SO:HTSOP-8-1EP_3.9x4.9mm_P1.27mm_EP2.4x3.2mm_ThermalVias +Package_SO:HTSSOP-14-1EP_4.4x5mm_P0.65mm_EP3.4x5mm_Mask3x3.1mm +Package_SO:HTSSOP-14-1EP_4.4x5mm_P0.65mm_EP3.4x5mm_Mask3x3.1mm_ThermalVias +Package_SO:HTSSOP-16-1EP_4.4x5mm_P0.65mm_EP3.4x5mm +Package_SO:HTSSOP-16-1EP_4.4x5mm_P0.65mm_EP3.4x5mm_Mask2.46x2.31mm +Package_SO:HTSSOP-16-1EP_4.4x5mm_P0.65mm_EP3.4x5mm_Mask2.46x2.31mm_ThermalVias +Package_SO:HTSSOP-16-1EP_4.4x5mm_P0.65mm_EP3.4x5mm_Mask3x3mm_ThermalVias +Package_SO:HTSSOP-16-1EP_4.4x5mm_P0.65mm_EP3x3mm +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP2.74x3.86mm +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP2.85x4mm +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3.4x6.5mm +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3.4x6.5mm_Mask2.4x3.7mm +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3.4x6.5mm_Mask2.75x3.43mm +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3.4x6.5mm_Mask2.75x3.43mm_ThermalVias +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3.4x6.5mm_Mask2.75x3.43mm_ThermalVias_HandSolder +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3.4x6.5mm_Mask2.96x2.96mm +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3.4x6.5mm_Mask2.96x2.96mm_ThermalVias +Package_SO:HTSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP3.4x6.5mm_ThermalVias +Package_SO:HTSSOP-24-1EP_4.4x7.8mm_P0.65mm_EP3.2x5mm +Package_SO:HTSSOP-24-1EP_4.4x7.8mm_P0.65mm_EP3.4x7.8mm_Mask2.4x2.98mm +Package_SO:HTSSOP-24-1EP_4.4x7.8mm_P0.65mm_EP3.4x7.8mm_Mask2.4x2.98mm_ThermalVias +Package_SO:HTSSOP-24-1EP_4.4x7.8mm_P0.65mm_EP3.4x7.8mm_Mask2.4x4.68mm +Package_SO:HTSSOP-24-1EP_4.4x7.8mm_P0.65mm_EP3.4x7.8mm_Mask2.4x4.68mm_ThermalVias +Package_SO:HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.75x6.2mm +Package_SO:HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.75x6.2mm_ThermalVias +Package_SO:HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.85x5.4mm +Package_SO:HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.85x5.4mm_ThermalVias +Package_SO:HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.4x9.5mm +Package_SO:HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.4x9.5mm_Mask2.4x6.17mm +Package_SO:HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.4x9.5mm_Mask2.4x6.17mm_ThermalVias +Package_SO:HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.4x9.5mm_ThermalVias +Package_SO:HTSSOP-32-1EP_6.1x11mm_P0.65mm_EP5.2x11mm_Mask4.11x4.36mm +Package_SO:HTSSOP-32-1EP_6.1x11mm_P0.65mm_EP5.2x11mm_Mask4.11x4.36mm_ThermalVias +Package_SO:HTSSOP-38-1EP_4.4x9.7mm_P0.5mm_EP1.5x3.3mm +Package_SO:HTSSOP-38-1EP_4.4x9.7mm_P0.5mm_EP1.5x3.3mm_ThermalVias +Package_SO:HTSSOP-38-1EP_4.4x9.7mm_P0.5mm_EP2.74x4.75mm +Package_SO:HTSSOP-38-1EP_4.4x9.7mm_P0.5mm_EP2.74x4.75mm_ThermalVias +Package_SO:HTSSOP-38-1EP_6.1x12.5mm_P0.65mm_EP5.2x12.5mm_Mask3.39x6.35mm +Package_SO:HTSSOP-38-1EP_6.1x12.5mm_P0.65mm_EP5.2x12.5mm_Mask3.39x6.35mm_ThermalVias +Package_SO:HTSSOP-44-1EP_6.1x14mm_P0.635mm_EP5.2x14mm_Mask4.31x8.26mm +Package_SO:HTSSOP-44-1EP_6.1x14mm_P0.635mm_EP5.2x14mm_Mask4.31x8.26mm_ThermalVias +Package_SO:HTSSOP-44_6.1x14mm_P0.635mm_TopEP4.14x7.01mm +Package_SO:HTSSOP-56-1EP_6.1x14mm_P0.5mm_EP3.61x6.35mm +Package_SO:HVSSOP-10-1EP_3x3mm_P0.5mm_EP1.57x1.88mm +Package_SO:HVSSOP-10-1EP_3x3mm_P0.5mm_EP1.57x1.88mm_ThermalVias +Package_SO:HVSSOP-8-1EP_3x3mm_P0.65mm_EP1.57x1.89mm +Package_SO:HVSSOP-8-1EP_3x3mm_P0.65mm_EP1.57x1.89mm_ThermalVias +Package_SO:Infineon_PG-DSO-12-11 +Package_SO:Infineon_PG-DSO-12-11_ThermalVias +Package_SO:Infineon_PG-DSO-12-9 +Package_SO:Infineon_PG-DSO-12-9_ThermalVias +Package_SO:Infineon_PG-DSO-20-30 +Package_SO:Infineon_PG-DSO-20-30_ThermalVias +Package_SO:Infineon_PG-DSO-20-32 +Package_SO:Infineon_PG-DSO-20-85 +Package_SO:Infineon_PG-DSO-20-85_ThermalVias +Package_SO:Infineon_PG-DSO-20-87 +Package_SO:Infineon_PG-DSO-20-U03_7.5x12.8mm +Package_SO:Infineon_PG-DSO-8-24_4x5mm +Package_SO:Infineon_PG-DSO-8-27_3.9x4.9mm_EP2.65x3mm +Package_SO:Infineon_PG-DSO-8-27_3.9x4.9mm_EP2.65x3mm_ThermalVias +Package_SO:Infineon_PG-DSO-8-43 +Package_SO:Infineon_PG-DSO-8-59_7.5x6.3mm +Package_SO:Infineon_PG-TSDSO-14-22 +Package_SO:Infineon_SOIC-20W_7.6x12.8mm_P1.27mm +Package_SO:Linear_HTSSOP-31-38-1EP_4.4x9.7mm_P0.5mm_EP2.74x4.75mm +Package_SO:Linear_HTSSOP-31-38-1EP_4.4x9.7mm_P0.5mm_EP2.74x4.75mm_ThermalVias +Package_SO:MFSOP6-4_4.4x3.6mm_P1.27mm +Package_SO:MFSOP6-5_4.4x3.6mm_P1.27mm +Package_SO:MSOP-10-1EP_3x3mm_P0.5mm_EP1.68x1.88mm +Package_SO:MSOP-10-1EP_3x3mm_P0.5mm_EP1.68x1.88mm_ThermalVias +Package_SO:MSOP-10-1EP_3x3mm_P0.5mm_EP1.73x1.98mm +Package_SO:MSOP-10-1EP_3x3mm_P0.5mm_EP1.73x1.98mm_ThermalVias +Package_SO:MSOP-10-1EP_3x3mm_P0.5mm_EP2.2x3.1mm_Mask1.83x1.89mm +Package_SO:MSOP-10-1EP_3x3mm_P0.5mm_EP2.2x3.1mm_Mask1.83x1.89mm_ThermalVias +Package_SO:MSOP-10_3x3mm_P0.5mm +Package_SO:MSOP-12-1EP_3x4.039mm_P0.65mm_EP1.651x2.845mm +Package_SO:MSOP-12-1EP_3x4.039mm_P0.65mm_EP1.651x2.845mm_ThermalVias +Package_SO:MSOP-12_3x4.039mm_P0.65mm +Package_SO:MSOP-16-1EP_3x4.039mm_P0.5mm_EP1.651x2.845mm +Package_SO:MSOP-16-1EP_3x4.039mm_P0.5mm_EP1.651x2.845mm_ThermalVias +Package_SO:MSOP-16_3x4.039mm_P0.5mm +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP1.5x1.8mm +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP1.5x1.8mm_ThermalVias +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP1.68x1.88mm +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP1.68x1.88mm_ThermalVias +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP1.73x1.85mm +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP1.73x1.85mm_ThermalVias +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP1.95x2.15mm +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP1.95x2.15mm_ThermalVias +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP2.5x3mm_Mask1.73x2.36mm +Package_SO:MSOP-8-1EP_3x3mm_P0.65mm_EP2.5x3mm_Mask1.73x2.36mm_ThermalVias +Package_SO:MSOP-8_3x3mm_P0.65mm +Package_SO:NXP_HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.2x3.4mm +Package_SO:NXP_HTSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.2x3.4mm_ThermalVias +Package_SO:OnSemi_Micro8 +Package_SO:ONSemi_SO-8FL_488AA +Package_SO:PowerIntegrations_eSOP-12B +Package_SO:PowerIntegrations_SO-8 +Package_SO:PowerIntegrations_SO-8B +Package_SO:PowerIntegrations_SO-8C +Package_SO:PowerPAK_SO-8L_Single +Package_SO:PowerPAK_SO-8_Dual +Package_SO:PowerPAK_SO-8_Single +Package_SO:PowerSSO-16-1EP_3.9x4.9mm_P0.5mm_EP2.5x3.61mm +Package_SO:PowerSSO-16-1EP_3.9x4.9mm_P0.5mm_EP2.5x3.61mm_ThermalVias +Package_SO:PSOP-44_16.9x27.17mm_P1.27mm +Package_SO:QSOP-16_3.9x4.9mm_P0.635mm +Package_SO:QSOP-20_3.9x8.7mm_P0.635mm +Package_SO:QSOP-24_3.9x8.7mm_P0.635mm +Package_SO:QSOP-28_3.9x9.9mm_P0.635mm +Package_SO:Renesas_SOP-32_11.4x20.75mm_P1.27mm +Package_SO:SO-14_3.9x8.65mm_P1.27mm +Package_SO:SO-14_5.3x10.2mm_P1.27mm +Package_SO:SO-16_3.9x9.9mm_P1.27mm +Package_SO:SO-16_5.3x10.2mm_P1.27mm +Package_SO:SO-20-1EP_7.52x12.825mm_P1.27mm_EP6.045x12.09mm_Mask3.56x4.47mm +Package_SO:SO-20-1EP_7.52x12.825mm_P1.27mm_EP6.045x12.09mm_Mask3.56x4.47mm_ThermalVias +Package_SO:SO-20_12.8x7.5mm_P1.27mm +Package_SO:SO-20_5.3x12.6mm_P1.27mm +Package_SO:SO-24_5.3x15mm_P1.27mm +Package_SO:SO-4_4.4x2.3mm_P1.27mm +Package_SO:SO-4_4.4x3.6mm_P2.54mm +Package_SO:SO-4_4.4x3.9mm_P2.54mm +Package_SO:SO-4_4.4x4.3mm_P2.54mm +Package_SO:SO-4_7.6x3.6mm_P2.54mm +Package_SO:SO-5-6_4.55x3.7mm_P1.27mm +Package_SO:SO-5_4.4x3.6mm_P1.27mm +Package_SO:SO-6L_10x3.84mm_P1.27mm +Package_SO:SO-6_4.4x3.6mm_P1.27mm +Package_SO:SO-8_3.9x4.9mm_P1.27mm +Package_SO:SOIC-10_3.9x4.9mm_P1mm +Package_SO:SOIC-14-16_3.9x9.9mm_P1.27mm +Package_SO:SOIC-14W_7.5x9mm_P1.27mm +Package_SO:SOIC-14_3.9x8.7mm_P1.27mm +Package_SO:SOIC-16W-12_7.5x10.3mm_P1.27mm +Package_SO:SOIC-16W_5.3x10.2mm_P1.27mm +Package_SO:SOIC-16W_7.5x10.3mm_P1.27mm +Package_SO:SOIC-16W_7.5x12.8mm_P1.27mm +Package_SO:SOIC-16_3.9x9.9mm_P1.27mm +Package_SO:SOIC-16_4.55x10.3mm_P1.27mm +Package_SO:SOIC-18W_7.5x11.6mm_P1.27mm +Package_SO:SOIC-20W_7.5x12.8mm_P1.27mm +Package_SO:SOIC-20W_7.5x15.4mm_P1.27mm +Package_SO:SOIC-24W_7.5x15.4mm_P1.27mm +Package_SO:SOIC-28W_7.5x17.9mm_P1.27mm +Package_SO:SOIC-28W_7.5x18.7mm_P1.27mm +Package_SO:SOIC-32_7.518x20.777mm_P1.27mm +Package_SO:SOIC-4_4.55x2.6mm_P1.27mm +Package_SO:SOIC-4_4.55x3.7mm_P2.54mm +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.29x3mm +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.29x3mm_ThermalVias +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.41x3.3mm +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.41x3.3mm_ThermalVias +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.41x3.81mm +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.41x3.81mm_ThermalVias +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.514x3.2mm +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.514x3.2mm_ThermalVias +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.62x3.51mm +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.62x3.51mm_ThermalVias +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.95x4.9mm_Mask2.71x3.4mm +Package_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.95x4.9mm_Mask2.71x3.4mm_ThermalVias +Package_SO:SOIC-8-N7_3.9x4.9mm_P1.27mm +Package_SO:SOIC-8_3.9x4.9mm_P1.27mm +Package_SO:SOIC-8_5.3x5.3mm_P1.27mm +Package_SO:SOIC-8_5.3x6.2mm_P1.27mm +Package_SO:SOIC-8_7.5x5.85mm_P1.27mm +Package_SO:SOJ-24_7.62x15.875mm_P1.27mm +Package_SO:SOJ-28_10.16x18.415mm_P1.27mm +Package_SO:SOJ-28_7.62x18.415mm_P1.27mm +Package_SO:SOJ-32_10.16x20.955mm_P1.27mm +Package_SO:SOJ-32_7.62x20.955mm_P1.27mm +Package_SO:SOJ-36_10.16x23.495mm_P1.27mm +Package_SO:SOJ-44_10.16x28.575mm_P1.27mm +Package_SO:SOP-16_3.9x9.9mm_P1.27mm +Package_SO:SOP-16_4.4x10.4mm_P1.27mm +Package_SO:SOP-16_4.55x10.3mm_P1.27mm +Package_SO:SOP-18_7.495x11.515mm_P1.27mm +Package_SO:SOP-18_7x12.5mm_P1.27mm +Package_SO:SOP-20_7.5x12.8mm_P1.27mm +Package_SO:SOP-24_7.5x15.4mm_P1.27mm +Package_SO:SOP-28_8.4x18.16mm_P1.27mm +Package_SO:SOP-32_11.305x20.495mm_P1.27mm +Package_SO:SOP-44_12.6x28.5mm_P1.27mm +Package_SO:SOP-44_13.3x28.2mm_P1.27mm +Package_SO:SOP-4_3.8x4.1mm_P2.54mm +Package_SO:SOP-4_4.4x2.6mm_P1.27mm +Package_SO:SOP-4_7.5x4.1mm_P2.54mm +Package_SO:SOP-8-1EP_4.57x4.57mm_P1.27mm_EP4.57x4.45mm +Package_SO:SOP-8-1EP_4.57x4.57mm_P1.27mm_EP4.57x4.45mm_ThermalVias +Package_SO:SOP-8_3.76x4.96mm_P1.27mm +Package_SO:SOP-8_3.9x4.9mm_P1.27mm +Package_SO:SOP-8_6.605x9.655mm_P2.54mm +Package_SO:SOP-8_6.62x9.15mm_P2.54mm +Package_SO:SSO-4_6.7x5.1mm_P2.54mm_Clearance8mm +Package_SO:SSO-6_6.8x4.6mm_P1.27mm_Clearance7mm +Package_SO:SSO-6_6.8x4.6mm_P1.27mm_Clearance8mm +Package_SO:SSO-7-8_6.4x9.78mm_P2.54mm +Package_SO:SSO-8-7_6.4x9.7mm_P2.54mm +Package_SO:SSO-8_13.6x6.3mm_P1.27mm_Clearance14.2mm +Package_SO:SSO-8_6.7x9.8mm_P2.54mm_Clearance8mm +Package_SO:SSO-8_6.8x5.9mm_P1.27mm_Clearance7mm +Package_SO:SSO-8_6.8x5.9mm_P1.27mm_Clearance8mm +Package_SO:SSO-8_9.6x6.3mm_P1.27mm_Clearance10.5mm +Package_SO:SSOP-10-1EP_3.9x4.9mm_P1mm_EP2.1x3.3mm +Package_SO:SSOP-10-1EP_3.9x4.9mm_P1mm_EP2.1x3.3mm_ThermalVias +Package_SO:SSOP-10_3.9x4.9mm_P1.00mm +Package_SO:SSOP-14_5.3x6.2mm_P0.65mm +Package_SO:SSOP-16_3.9x4.9mm_P0.635mm +Package_SO:SSOP-16_4.4x5.2mm_P0.65mm +Package_SO:SSOP-16_5.3x6.2mm_P0.65mm +Package_SO:SSOP-18_4.4x6.5mm_P0.65mm +Package_SO:SSOP-20_3.9x8.7mm_P0.635mm +Package_SO:SSOP-20_4.4x6.5mm_P0.65mm +Package_SO:SSOP-20_5.3x7.2mm_P0.65mm +Package_SO:SSOP-24_3.9x8.7mm_P0.635mm +Package_SO:SSOP-24_5.3x8.2mm_P0.65mm +Package_SO:SSOP-28_3.9x9.9mm_P0.635mm +Package_SO:SSOP-28_5.3x10.2mm_P0.65mm +Package_SO:SSOP-44_5.3x12.8mm_P0.5mm +Package_SO:SSOP-48_5.3x12.8mm_P0.5mm +Package_SO:SSOP-48_7.5x15.9mm_P0.635mm +Package_SO:SSOP-4_4.4x2.6mm_P1.27mm +Package_SO:SSOP-56_7.5x18.5mm_P0.635mm +Package_SO:SSOP-8_2.95x2.8mm_P0.65mm +Package_SO:SSOP-8_3.95x5.21x3.27mm_P1.27mm +Package_SO:SSOP-8_3.9x5.05mm_P1.27mm +Package_SO:SSOP-8_5.25x5.24mm_P1.27mm +Package_SO:STC_SOP-16_3.9x9.9mm_P1.27mm +Package_SO:ST_MultiPowerSO-30 +Package_SO:ST_PowerSSO-24_SlugDown +Package_SO:ST_PowerSSO-24_SlugDown_ThermalVias +Package_SO:ST_PowerSSO-24_SlugUp +Package_SO:ST_PowerSSO-36_SlugDown +Package_SO:ST_PowerSSO-36_SlugDown_ThermalVias +Package_SO:ST_PowerSSO-36_SlugUp +Package_SO:Texas_DAD0032A_HTSSOP-32_6.1x11mm_P0.65mm_TopEP3.71x3.81mm +Package_SO:Texas_DGN0008B_VSSOP-8-1EP_3x3mm_P0.65mm_EP2x3mm_Mask1.88x1.98mm +Package_SO:Texas_DGN0008B_VSSOP-8-1EP_3x3mm_P0.65mm_EP2x3mm_Mask1.88x1.98mm_ThermalVias +Package_SO:Texas_DGN0008D_VSSOP-8-1EP_3x3mm_P0.65mm_EP2x2.94mm_Mask1.57x1.89mm +Package_SO:Texas_DGN0008D_VSSOP-8-1EP_3x3mm_P0.65mm_EP2x2.94mm_Mask1.57x1.89mm_ThermalVias +Package_SO:Texas_DGN0008G_VSSOP-8-1EP_3x3mm_P0.65mm_EP2x2.94mm_Mask1.846x2.15mm +Package_SO:Texas_DGN0008G_VSSOP-8-1EP_3x3mm_P0.65mm_EP2x2.94mm_Mask1.846x2.15mm_ThermalVias +Package_SO:Texas_DKD0036A_HSSOP-36_11x15.9mm_P0.65mm_TopEP5.85x12.65mm +Package_SO:Texas_DYY0016A_TSOT-23-16_4.2x2.0mm_P0.5mm +Package_SO:Texas_HSOP-8-1EP_3.9x4.9mm_P1.27mm +Package_SO:Texas_HSOP-8-1EP_3.9x4.9mm_P1.27mm_ThermalVias +Package_SO:Texas_HTSOP-8-1EP_3.9x4.9mm_P1.27mm_EP2.95x4.9mm_Mask2.4x3.1mm_ThermalVias +Package_SO:Texas_PW0020A_TSSOP-20_4.4x6.5mm_P0.65mm +Package_SO:Texas_PWP0020A +Package_SO:Texas_PWP0028V_TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.4x9.7mm_Mask2.94x5.62mm +Package_SO:Texas_PWP0028V_TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.4x9.7mm_Mask2.94x5.62mm_ThermalVias +Package_SO:Texas_R-PDSO-G8_EP2.95x4.9mm_Mask2.4x3.1mm +Package_SO:Texas_R-PDSO-G8_EP2.95x4.9mm_Mask2.4x3.1mm_ThermalVias +Package_SO:Texas_S-PDSO-G8_3x3mm_P0.65mm +Package_SO:TI_SO-PowerPAD-8 +Package_SO:TI_SO-PowerPAD-8_ThermalVias +Package_SO:TSOP-5_1.65x3.05mm_P0.95mm +Package_SO:TSOP-6_1.65x3.05mm_P0.95mm +Package_SO:TSOP-I-24_12.4x6mm_P0.5mm +Package_SO:TSOP-I-24_14.4x6mm_P0.5mm +Package_SO:TSOP-I-24_16.4x6mm_P0.5mm +Package_SO:TSOP-I-24_18.4x6mm_P0.5mm +Package_SO:TSOP-I-28_11.8x8mm_P0.55mm +Package_SO:TSOP-I-32_11.8x8mm_P0.5mm +Package_SO:TSOP-I-32_12.4x8mm_P0.5mm +Package_SO:TSOP-I-32_14.4x8mm_P0.5mm +Package_SO:TSOP-I-32_16.4x8mm_P0.5mm +Package_SO:TSOP-I-32_18.4x8mm_P0.5mm +Package_SO:TSOP-I-32_18.4x8mm_P0.5mm_Reverse +Package_SO:TSOP-I-40_12.4x10mm_P0.5mm +Package_SO:TSOP-I-40_14.4x10mm_P0.5mm +Package_SO:TSOP-I-40_16.4x10mm_P0.5mm +Package_SO:TSOP-I-40_18.4x10mm_P0.5mm +Package_SO:TSOP-I-48_12.4x12mm_P0.5mm +Package_SO:TSOP-I-48_14.4x12mm_P0.5mm +Package_SO:TSOP-I-48_16.4x12mm_P0.5mm +Package_SO:TSOP-I-48_18.4x12mm_P0.5mm +Package_SO:TSOP-I-56_14.4x14mm_P0.5mm +Package_SO:TSOP-I-56_16.4x14mm_P0.5mm +Package_SO:TSOP-I-56_18.4x14mm_P0.5mm +Package_SO:TSOP-II-32_21.0x10.2mm_P1.27mm +Package_SO:TSOP-II-40-44_10.16x18.37mm_P0.8mm +Package_SO:TSOP-II-44_10.16x18.41mm_P0.8mm +Package_SO:TSOP-II-54_22.2x10.16mm_P0.8mm +Package_SO:TSSOP-100_6.1x20.8mm_P0.4mm +Package_SO:TSSOP-10_3x3mm_P0.5mm +Package_SO:TSSOP-14-1EP_4.4x5mm_P0.65mm +Package_SO:TSSOP-14_4.4x3.6mm_P0.4mm +Package_SO:TSSOP-14_4.4x5mm_P0.65mm +Package_SO:TSSOP-16-1EP_4.4x5mm_P0.65mm +Package_SO:TSSOP-16-1EP_4.4x5mm_P0.65mm_EP3x3mm +Package_SO:TSSOP-16-1EP_4.4x5mm_P0.65mm_EP3x3mm_ThermalVias +Package_SO:TSSOP-16_4.4x3.6mm_P0.4mm +Package_SO:TSSOP-16_4.4x5mm_P0.65mm +Package_SO:TSSOP-20-1EP_4.4x6.5mm_P0.65mm_EP2.15x3.35mm +Package_SO:TSSOP-20_4.4x5mm_P0.4mm +Package_SO:TSSOP-20_4.4x5mm_P0.5mm +Package_SO:TSSOP-20_4.4x6.5mm_P0.65mm +Package_SO:TSSOP-24-1EP_4.4x7.8mm_P0.65mm_EP3.2x5mm +Package_SO:TSSOP-24_4.4x5mm_P0.4mm +Package_SO:TSSOP-24_4.4x6.5mm_P0.5mm +Package_SO:TSSOP-24_4.4x7.8mm_P0.65mm +Package_SO:TSSOP-24_6.1x7.8mm_P0.65mm +Package_SO:TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.74x4.75mm +Package_SO:TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.74x4.75mm_ThermalVias +Package_SO:TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP2.85x6.7mm +Package_SO:TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.05x7.56mm +Package_SO:TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.05x7.56mm_ThermalVias +Package_SO:TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.4x9.7mm_Mask3.1x4.05mm +Package_SO:TSSOP-28-1EP_4.4x9.7mm_P0.65mm_EP3.4x9.7mm_Mask3.1x4.05mm_ThermalVias +Package_SO:TSSOP-28_4.4x7.8mm_P0.5mm +Package_SO:TSSOP-28_4.4x9.7mm_P0.65mm +Package_SO:TSSOP-28_6.1x7.8mm_P0.5mm +Package_SO:TSSOP-28_6.1x9.7mm_P0.65mm +Package_SO:TSSOP-28_8x9.7mm_P0.65mm +Package_SO:TSSOP-30_4.4x7.8mm_P0.5mm +Package_SO:TSSOP-30_6.1x9.7mm_P0.65mm +Package_SO:TSSOP-32_4.4x6.5mm_P0.4mm +Package_SO:TSSOP-32_6.1x11mm_P0.65mm +Package_SO:TSSOP-32_8x11mm_P0.65mm +Package_SO:TSSOP-36_4.4x7.8mm_P0.4mm +Package_SO:TSSOP-36_4.4x9.7mm_P0.5mm +Package_SO:TSSOP-36_6.1x12.5mm_P0.65mm +Package_SO:TSSOP-36_6.1x7.8mm_P0.4mm +Package_SO:TSSOP-36_6.1x9.7mm_P0.5mm +Package_SO:TSSOP-36_8x12.5mm_P0.65mm +Package_SO:TSSOP-36_8x9.7mm_P0.5mm +Package_SO:TSSOP-38_4.4x9.7mm_P0.5mm +Package_SO:TSSOP-38_6.1x12.5mm_P0.65mm +Package_SO:TSSOP-40_6.1x11mm_P0.5mm +Package_SO:TSSOP-40_6.1x14mm_P0.65mm +Package_SO:TSSOP-40_8x11mm_P0.5mm +Package_SO:TSSOP-40_8x14mm_P0.65mm +Package_SO:TSSOP-44_4.4x11.2mm_P0.5mm +Package_SO:TSSOP-44_4.4x11mm_P0.5mm +Package_SO:TSSOP-44_6.1x11mm_P0.5mm +Package_SO:TSSOP-48_4.4x9.7mm_P0.4mm +Package_SO:TSSOP-48_6.1x12.5mm_P0.5mm +Package_SO:TSSOP-48_6.1x9.7mm_P0.4mm +Package_SO:TSSOP-48_8x12.5mm_P0.5mm +Package_SO:TSSOP-48_8x9.7mm_P0.4mm +Package_SO:TSSOP-4_4.4x5mm_P4mm +Package_SO:TSSOP-50_4.4x12.5mm_P0.5mm +Package_SO:TSSOP-52_6.1x11mm_P0.4mm +Package_SO:TSSOP-52_8x11mm_P0.4mm +Package_SO:TSSOP-56_4.4x11.3mm_P0.4mm +Package_SO:TSSOP-56_6.1x12.5mm_P0.4mm +Package_SO:TSSOP-56_6.1x14mm_P0.5mm +Package_SO:TSSOP-56_8x12.5mm_P0.4mm +Package_SO:TSSOP-56_8x14mm_P0.5mm +Package_SO:TSSOP-60_8x12.5mm_P0.4mm +Package_SO:TSSOP-64_6.1x14mm_P0.4mm +Package_SO:TSSOP-64_6.1x17mm_P0.5mm +Package_SO:TSSOP-64_8x14mm_P0.4mm +Package_SO:TSSOP-68_8x14mm_P0.4mm +Package_SO:TSSOP-80_6.1x17mm_P0.4mm +Package_SO:TSSOP-8_3x3mm_P0.65mm +Package_SO:TSSOP-8_4.4x3mm_P0.65mm +Package_SO:Vishay_PowerPAK_1212-8_Dual +Package_SO:Vishay_PowerPAK_1212-8_Single +Package_SO:VSO-40_7.6x15.4mm_P0.762mm +Package_SO:VSO-56_11.1x21.5mm_P0.75mm +Package_SO:VSSOP-10_3x3mm_P0.5mm +Package_SO:VSSOP-8_2.3x2mm_P0.5mm +Package_SO:VSSOP-8_3x3mm_P0.65mm +Package_SO:Zetex_SM8 +Package_SON:Diodes_PowerDI3333-8 +Package_SON:Diodes_PowerDI3333-8_UXC_3.3x3.3mm_P0.65mm +Package_SON:EPSON_CE-USON-10_USON-10_3.2x2.5mm_P0.7mm +Package_SON:Fairchild_DualPower33-6_3x3mm +Package_SON:Fairchild_MicroPak-6_1.0x1.45mm_P0.5mm +Package_SON:Fairchild_MicroPak2-6_1.0x1.0mm_P0.35mm +Package_SON:HUSON-3-1EP_2x2mm_P1.3mm_EP1.1x1.6mm +Package_SON:HVSON-8-1EP_3x3mm_P0.65mm_EP1.6x2.4mm +Package_SON:HVSON-8-1EP_4x4mm_P0.8mm_EP2.2x3.1mm +Package_SON:Infineon_PG-LSON-8-1 +Package_SON:Infineon_PG-TDSON-8_6.15x5.15mm +Package_SON:Infineon_PG-TISON-8-2 +Package_SON:Infineon_PG-TISON-8-3 +Package_SON:Infineon_PG-TISON-8-4 +Package_SON:Infineon_PG-TISON-8-5 +Package_SON:MicroCrystal_C7_SON-8_1.5x3.2mm_P0.9mm +Package_SON:Nexperia_HUSON-12_USON-12-1EP_1.35x2.5mm_P0.4mm_EP0.4x2mm +Package_SON:Nexperia_HUSON-16_USON-16-1EP_1.35x3.3mm_P0.4mm_EP0.4x2.8mm +Package_SON:Nexperia_HUSON-8_USON-8-1EP_1.35x1.7mm_P0.4mm_EP0.4x1.2mm +Package_SON:NXP_XSON-16 +Package_SON:ROHM_VML0806 +Package_SON:RTC_SMD_MicroCrystal_C3_2.5x3.7mm +Package_SON:SON-8-1EP_3x2mm_P0.5mm_EP1.4x1.6mm +Package_SON:ST_PowerFLAT-6L_5x6mm_P1.27mm +Package_SON:ST_PowerFLAT_HV-5_8x8mm +Package_SON:ST_PowerFLAT_HV-8_5x6mm +Package_SON:Texas_DPY0002A_0.6x1mm_P0.65mm +Package_SON:Texas_DQK +Package_SON:Texas_DQX002A +Package_SON:Texas_DRC0010J +Package_SON:Texas_DRC0010J_ThermalVias +Package_SON:Texas_DRX_WSON-10_2.5x2.5mm_P0.5mm +Package_SON:Texas_DSC0010J +Package_SON:Texas_DSC0010J_ThermalVias +Package_SON:Texas_PWSON-N6 +Package_SON:Texas_R-PUSON-N14 +Package_SON:Texas_R-PUSON-N8_USON-8-1EP_1.6x2.1mm_P0.5mm_EP0.4x1.7mm +Package_SON:Texas_R-PWSON-N12_EP0.4x2mm +Package_SON:Texas_S-PDSO-N12 +Package_SON:Texas_S-PVSON-N10 +Package_SON:Texas_S-PVSON-N10_ThermalVias +Package_SON:Texas_S-PVSON-N8 +Package_SON:Texas_S-PVSON-N8_ThermalVias +Package_SON:Texas_S-PWSON-N10 +Package_SON:Texas_S-PWSON-N10_ThermalVias +Package_SON:Texas_S-PWSON-N8_EP1.2x2mm +Package_SON:Texas_S-PWSON-N8_EP1.2x2mm_ThermalVias +Package_SON:Texas_USON-6_1x1.45mm_P0.5mm_SMD +Package_SON:Texas_VSON-HR-8_1.5x2mm_P0.5mm +Package_SON:Texas_X2SON-4_1x1mm_P0.65mm +Package_SON:Texas_X2SON-5_0.8x0.8mm_P0.48mm +Package_SON:Texas_X2SON-5_0.8x0.8mm_P0.48mm_RoutingVia +Package_SON:USON-10_2.5x1.0mm_P0.5mm +Package_SON:USON-20_2x4mm_P0.4mm +Package_SON:VSON-10-1EP_3x3mm_P0.5mm_EP1.2x2mm +Package_SON:VSON-10-1EP_3x3mm_P0.5mm_EP1.2x2mm_ThermalVias +Package_SON:VSON-10-1EP_3x3mm_P0.5mm_EP1.65x2.4mm +Package_SON:VSON-10-1EP_3x3mm_P0.5mm_EP1.65x2.4mm_ThermalVias +Package_SON:VSON-14-1EP_3x4.45mm_P0.65mm_EP1.6x4.2mm +Package_SON:VSON-14-1EP_3x4.45mm_P0.65mm_EP1.6x4.2mm_ThermalVias +Package_SON:VSON-8-1EP_3x3mm_P0.65mm_EP1.65x2.4mm +Package_SON:VSON-8-1EP_3x3mm_P0.65mm_EP1.65x2.4mm_ThermalVias +Package_SON:VSON-8-1EP_3x3mm_P0.65mm_EP1.6x2.4mm +Package_SON:VSON-8_1.5x2mm_P0.5mm +Package_SON:VSON-8_3.3x3.3mm_P0.65mm_NexFET +Package_SON:VSONP-8-1EP_5x6_P1.27mm +Package_SON:Winbond_USON-8-1EP_3x2mm_P0.5mm_EP0.2x1.6mm +Package_SON:Winbond_USON-8-2EP_3x4mm_P0.8mm_EP0.2x0.8mm +Package_SON:WSON-10-1EP_2.5x2.5mm_P0.5mm_EP1.2x2mm +Package_SON:WSON-10-1EP_2.5x2.5mm_P0.5mm_EP1.2x2mm_ThermalVias +Package_SON:WSON-10-1EP_2x3mm_P0.5mm_EP0.84x2.4mm +Package_SON:WSON-10-1EP_2x3mm_P0.5mm_EP0.84x2.4mm_ThermalVias +Package_SON:WSON-10-1EP_4x3mm_P0.5mm_EP2.2x2mm +Package_SON:WSON-10-1EP_4x4mm_P0.8mm_EP2.6x3mm +Package_SON:WSON-10-1EP_4x4mm_P0.8mm_EP2.6x3mm_ThermalVias +Package_SON:WSON-12-1EP_3x2mm_P0.5mm_EP1x2.65 +Package_SON:WSON-12-1EP_3x2mm_P0.5mm_EP1x2.65_ThermalVias +Package_SON:WSON-12-1EP_3x3mm_P0.5mm_EP1.5x2.5mm +Package_SON:WSON-12-1EP_3x3mm_P0.5mm_EP1.5x2.5mm_ThermalVias +Package_SON:WSON-12-1EP_4x4mm_P0.5mm_EP2.6x3mm +Package_SON:WSON-12-1EP_4x4mm_P0.5mm_EP2.6x3mm_ThermalVias +Package_SON:WSON-14-1EP_4.0x4.0mm_P0.5mm_EP2.6x2.6mm +Package_SON:WSON-16_3.3x1.35_P0.4mm +Package_SON:WSON-6-1EP_2x2mm_P0.65mm_EP1x1.6mm +Package_SON:WSON-6-1EP_2x2mm_P0.65mm_EP1x1.6mm_ThermalVias +Package_SON:WSON-6-1EP_3x3mm_P0.95mm +Package_SON:WSON-6_1.5x1.5mm_P0.5mm +Package_SON:WSON-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm +Package_SON:WSON-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm_ThermalVias +Package_SON:WSON-8-1EP_3x2.5mm_P0.5mm_EP1.2x1.5mm_PullBack +Package_SON:WSON-8-1EP_3x2.5mm_P0.5mm_EP1.2x1.5mm_PullBack_ThermalVias +Package_SON:WSON-8-1EP_3x3mm_P0.5mm_EP1.2x2mm +Package_SON:WSON-8-1EP_3x3mm_P0.5mm_EP1.2x2mm_ThermalVias +Package_SON:WSON-8-1EP_3x3mm_P0.5mm_EP1.45x2.4mm +Package_SON:WSON-8-1EP_3x3mm_P0.5mm_EP1.45x2.4mm_ThermalVias +Package_SON:WSON-8-1EP_3x3mm_P0.5mm_EP1.6x2.0mm +Package_SON:WSON-8-1EP_4x4mm_P0.8mm_EP1.98x3mm +Package_SON:WSON-8-1EP_4x4mm_P0.8mm_EP1.98x3mm_ThermalVias +Package_SON:WSON-8-1EP_4x4mm_P0.8mm_EP2.2x3mm +Package_SON:WSON-8-1EP_4x4mm_P0.8mm_EP2.2x3mm_ThermalVias +Package_SON:WSON-8-1EP_4x4mm_P0.8mm_EP2.6x3mm +Package_SON:WSON-8-1EP_4x4mm_P0.8mm_EP2.6x3mm_ThermalVias +Package_SON:WSON-8-1EP_6x5mm_P1.27mm_EP3.4x4.3mm +Package_SON:WSON-8-1EP_6x5mm_P1.27mm_EP3.4x4mm +Package_SON:WSON-8-1EP_8x6mm_P1.27mm_EP3.4x4.3mm +Package_SON:X2SON-8_1.4x1mm_P0.35mm +Package_SO_J-Lead:TSOC-6_3.76x3.94mm_P1.27mm +Package_TO_SOT_SMD:Analog_KS-4 +Package_TO_SOT_SMD:ATPAK-2 +Package_TO_SOT_SMD:Diodes_SOT-553 +Package_TO_SOT_SMD:HVSOF5 +Package_TO_SOT_SMD:HVSOF6 +Package_TO_SOT_SMD:Infineon_PG-HDSOP-10-1 +Package_TO_SOT_SMD:Infineon_PG-HSOF-8-1 +Package_TO_SOT_SMD:Infineon_PG-HSOF-8-1_ThermalVias +Package_TO_SOT_SMD:Infineon_PG-HSOF-8-2 +Package_TO_SOT_SMD:Infineon_PG-HSOF-8-2_ThermalVias +Package_TO_SOT_SMD:Infineon_PG-HSOF-8-2_ThermalVias2 +Package_TO_SOT_SMD:Infineon_PG-HSOF-8-3 +Package_TO_SOT_SMD:Infineon_PG-HSOF-8-3_ThermalVias +Package_TO_SOT_SMD:Infineon_PG-TO-220-7Lead_TabPin8 +Package_TO_SOT_SMD:Infineon_PG-TSFP-3-1 +Package_TO_SOT_SMD:LFPAK33 +Package_TO_SOT_SMD:LFPAK56 +Package_TO_SOT_SMD:LFPAK88 +Package_TO_SOT_SMD:Nexperia_CFP15_SOT-1289 +Package_TO_SOT_SMD:OnSemi_ECH8 +Package_TO_SOT_SMD:PowerMacro_M234_NoHole +Package_TO_SOT_SMD:PowerMacro_M234_WithHole +Package_TO_SOT_SMD:PQFN_8x8 +Package_TO_SOT_SMD:Rohm_HRP7 +Package_TO_SOT_SMD:ROHM_SOT-457_ClockwisePinNumbering +Package_TO_SOT_SMD:SC-59 +Package_TO_SOT_SMD:SC-59_Handsoldering +Package_TO_SOT_SMD:SC-70-8 +Package_TO_SOT_SMD:SC-70-8_Handsoldering +Package_TO_SOT_SMD:SC-74-6_1.55x2.9mm_P0.95mm +Package_TO_SOT_SMD:SC-74A-5_1.55x2.9mm_P0.95mm +Package_TO_SOT_SMD:SC-82AA +Package_TO_SOT_SMD:SC-82AA_Handsoldering +Package_TO_SOT_SMD:SC-82AB +Package_TO_SOT_SMD:SC-82AB_Handsoldering +Package_TO_SOT_SMD:SOT-1123 +Package_TO_SOT_SMD:SOT-1333-1 +Package_TO_SOT_SMD:SOT-1334-1 +Package_TO_SOT_SMD:SOT-143 +Package_TO_SOT_SMD:SOT-143R +Package_TO_SOT_SMD:SOT-143R_Handsoldering +Package_TO_SOT_SMD:SOT-143_Handsoldering +Package_TO_SOT_SMD:SOT-223-3_TabPin2 +Package_TO_SOT_SMD:SOT-223-5 +Package_TO_SOT_SMD:SOT-223-6 +Package_TO_SOT_SMD:SOT-223-6_TabPin3 +Package_TO_SOT_SMD:SOT-223-8 +Package_TO_SOT_SMD:SOT-223 +Package_TO_SOT_SMD:SOT-23-3 +Package_TO_SOT_SMD:SOT-23-5 +Package_TO_SOT_SMD:SOT-23-5_HandSoldering +Package_TO_SOT_SMD:SOT-23-6 +Package_TO_SOT_SMD:SOT-23-6_Handsoldering +Package_TO_SOT_SMD:SOT-23-8 +Package_TO_SOT_SMD:SOT-23-8_Handsoldering +Package_TO_SOT_SMD:SOT-23 +Package_TO_SOT_SMD:SOT-23W +Package_TO_SOT_SMD:SOT-23W_Handsoldering +Package_TO_SOT_SMD:SOT-23_Handsoldering +Package_TO_SOT_SMD:SOT-323_SC-70 +Package_TO_SOT_SMD:SOT-323_SC-70_Handsoldering +Package_TO_SOT_SMD:SOT-343_SC-70-4 +Package_TO_SOT_SMD:SOT-343_SC-70-4_Handsoldering +Package_TO_SOT_SMD:SOT-353_SC-70-5 +Package_TO_SOT_SMD:SOT-353_SC-70-5_Handsoldering +Package_TO_SOT_SMD:SOT-363_SC-70-6 +Package_TO_SOT_SMD:SOT-363_SC-70-6_Handsoldering +Package_TO_SOT_SMD:SOT-383F +Package_TO_SOT_SMD:SOT-383FL +Package_TO_SOT_SMD:SOT-416 +Package_TO_SOT_SMD:SOT-523 +Package_TO_SOT_SMD:SOT-543 +Package_TO_SOT_SMD:SOT-553 +Package_TO_SOT_SMD:SOT-563 +Package_TO_SOT_SMD:SOT-583-8 +Package_TO_SOT_SMD:SOT-665 +Package_TO_SOT_SMD:SOT-666 +Package_TO_SOT_SMD:SOT-723 +Package_TO_SOT_SMD:SOT-883 +Package_TO_SOT_SMD:SOT-886 +Package_TO_SOT_SMD:SOT-89-3 +Package_TO_SOT_SMD:SOT-89-3_Handsoldering +Package_TO_SOT_SMD:SOT-89-5 +Package_TO_SOT_SMD:SOT-89-5_Handsoldering +Package_TO_SOT_SMD:SOT-963 +Package_TO_SOT_SMD:SuperSOT-3 +Package_TO_SOT_SMD:SuperSOT-6 +Package_TO_SOT_SMD:SuperSOT-8 +Package_TO_SOT_SMD:TDSON-8-1 +Package_TO_SOT_SMD:Texas_DRT-3 +Package_TO_SOT_SMD:Texas_NDQ +Package_TO_SOT_SMD:Texas_NDW-7_TabPin4 +Package_TO_SOT_SMD:Texas_NDW-7_TabPin8 +Package_TO_SOT_SMD:Texas_NDY0011A +Package_TO_SOT_SMD:Texas_R-PDSO-G5_DCK-5 +Package_TO_SOT_SMD:Texas_R-PDSO-G6 +Package_TO_SOT_SMD:Texas_R-PDSO-N5_DRL-5 +Package_TO_SOT_SMD:Texas_R-PDSO-N6_DRL-6 +Package_TO_SOT_SMD:TO-252-2 +Package_TO_SOT_SMD:TO-252-2_TabPin1 +Package_TO_SOT_SMD:TO-252-3_TabPin2 +Package_TO_SOT_SMD:TO-252-3_TabPin4 +Package_TO_SOT_SMD:TO-252-4 +Package_TO_SOT_SMD:TO-252-5_TabPin3 +Package_TO_SOT_SMD:TO-252-5_TabPin6 +Package_TO_SOT_SMD:TO-263-2 +Package_TO_SOT_SMD:TO-263-2_TabPin1 +Package_TO_SOT_SMD:TO-263-3_TabPin2 +Package_TO_SOT_SMD:TO-263-3_TabPin4 +Package_TO_SOT_SMD:TO-263-4 +Package_TO_SOT_SMD:TO-263-5_TabPin3 +Package_TO_SOT_SMD:TO-263-5_TabPin6 +Package_TO_SOT_SMD:TO-263-6 +Package_TO_SOT_SMD:TO-263-7_TabPin4 +Package_TO_SOT_SMD:TO-263-7_TabPin8 +Package_TO_SOT_SMD:TO-263-9_TabPin10 +Package_TO_SOT_SMD:TO-263-9_TabPin5 +Package_TO_SOT_SMD:TO-268-2 +Package_TO_SOT_SMD:TO-269AA +Package_TO_SOT_SMD:TO-277A +Package_TO_SOT_SMD:TO-277B +Package_TO_SOT_SMD:TO-50-3_LongPad-NoHole_Housing +Package_TO_SOT_SMD:TO-50-3_LongPad-WithHole_Housing +Package_TO_SOT_SMD:TO-50-3_ShortPad-NoHole_Housing +Package_TO_SOT_SMD:TO-50-3_ShortPad-WithHole_Housing +Package_TO_SOT_SMD:TO-50-4_LongPad-NoHole_Housing +Package_TO_SOT_SMD:TO-50-4_LongPad-WithHole_Housing +Package_TO_SOT_SMD:TO-50-4_ShortPad-NoHole_Housing +Package_TO_SOT_SMD:TO-50-4_ShortPad-WithHole_Housing +Package_TO_SOT_SMD:TSOT-23-5 +Package_TO_SOT_SMD:TSOT-23-5_HandSoldering +Package_TO_SOT_SMD:TSOT-23-6 +Package_TO_SOT_SMD:TSOT-23-6_HandSoldering +Package_TO_SOT_SMD:TSOT-23-8 +Package_TO_SOT_SMD:TSOT-23-8_HandSoldering +Package_TO_SOT_SMD:TSOT-23 +Package_TO_SOT_SMD:TSOT-23_HandSoldering +Package_TO_SOT_SMD:Vishay_PowerPAK_SC70-6L_Dual +Package_TO_SOT_SMD:Vishay_PowerPAK_SC70-6L_Single +Package_TO_SOT_SMD:VSOF5 +Package_TO_SOT_THT:Analog_TO-46-4_ThermalShield +Package_TO_SOT_THT:Fairchild_TO-220F-6L +Package_TO_SOT_THT:Heraeus_TO-92-2 +Package_TO_SOT_THT:NEC_Molded_7x4x9mm +Package_TO_SOT_THT:PowerIntegrations_TO-220-7C +Package_TO_SOT_THT:SIPAK-1EP_Horizontal_TabDown +Package_TO_SOT_THT:SIPAK_Vertical +Package_TO_SOT_THT:SOD-70_P2.54mm +Package_TO_SOT_THT:SOD-70_P5.08mm +Package_TO_SOT_THT:SOT-227 +Package_TO_SOT_THT:TO-100-10 +Package_TO_SOT_THT:TO-100-10_Window +Package_TO_SOT_THT:TO-11-2 +Package_TO_SOT_THT:TO-11-2_Window +Package_TO_SOT_THT:TO-11-3 +Package_TO_SOT_THT:TO-11-3_Window +Package_TO_SOT_THT:TO-12-4 +Package_TO_SOT_THT:TO-12-4_Window +Package_TO_SOT_THT:TO-126-2_Horizontal_TabDown +Package_TO_SOT_THT:TO-126-2_Horizontal_TabUp +Package_TO_SOT_THT:TO-126-2_Vertical +Package_TO_SOT_THT:TO-126-3_Horizontal_TabDown +Package_TO_SOT_THT:TO-126-3_Horizontal_TabUp +Package_TO_SOT_THT:TO-126-3_Vertical +Package_TO_SOT_THT:TO-17-4 +Package_TO_SOT_THT:TO-17-4_Window +Package_TO_SOT_THT:TO-18-2 +Package_TO_SOT_THT:TO-18-2_Lens +Package_TO_SOT_THT:TO-18-2_Window +Package_TO_SOT_THT:TO-18-3 +Package_TO_SOT_THT:TO-18-3_Lens +Package_TO_SOT_THT:TO-18-3_Window +Package_TO_SOT_THT:TO-18-4 +Package_TO_SOT_THT:TO-18-4_Lens +Package_TO_SOT_THT:TO-18-4_Window +Package_TO_SOT_THT:TO-218-2_Horizontal_TabDown +Package_TO_SOT_THT:TO-218-2_Horizontal_TabUp +Package_TO_SOT_THT:TO-218-2_Vertical +Package_TO_SOT_THT:TO-218-3_Horizontal_TabDown +Package_TO_SOT_THT:TO-218-3_Horizontal_TabUp +Package_TO_SOT_THT:TO-218-3_Vertical +Package_TO_SOT_THT:TO-220-11_P3.4x2.54mm_StaggerEven_Lead5.84mm_TabDown +Package_TO_SOT_THT:TO-220-11_P3.4x2.54mm_StaggerOdd_Lead5.84mm_TabDown +Package_TO_SOT_THT:TO-220-11_P3.4x5.08mm_StaggerEven_Lead4.58mm_Vertical +Package_TO_SOT_THT:TO-220-11_P3.4x5.08mm_StaggerOdd_Lead4.58mm_Vertical +Package_TO_SOT_THT:TO-220-11_P3.4x5.08mm_StaggerOdd_Lead8.45mm_TabDown +Package_TO_SOT_THT:TO-220-15_P2.54x2.54mm_StaggerEven_Lead5.84mm_TabDown +Package_TO_SOT_THT:TO-220-15_P2.54x2.54mm_StaggerOdd_Lead5.84mm_TabDown +Package_TO_SOT_THT:TO-220-15_P2.54x5.08mm_StaggerEven_Lead4.58mm_Vertical +Package_TO_SOT_THT:TO-220-15_P2.54x5.08mm_StaggerOdd_Lead4.58mm_Vertical +Package_TO_SOT_THT:TO-220-2_Horizontal_TabDown +Package_TO_SOT_THT:TO-220-2_Horizontal_TabUp +Package_TO_SOT_THT:TO-220-2_Vertical +Package_TO_SOT_THT:TO-220-3_Horizontal_TabDown +Package_TO_SOT_THT:TO-220-3_Horizontal_TabUp +Package_TO_SOT_THT:TO-220-3_Vertical +Package_TO_SOT_THT:TO-220-4_Horizontal_TabDown +Package_TO_SOT_THT:TO-220-4_Horizontal_TabUp +Package_TO_SOT_THT:TO-220-4_P5.08x3.7mm_StaggerEven_Lead3.8mm_Vertical +Package_TO_SOT_THT:TO-220-4_P5.08x3.7mm_StaggerOdd_Lead3.8mm_Vertical +Package_TO_SOT_THT:TO-220-4_P5.08x3.8mm_StaggerEven_Lead5.85mm_TabDown +Package_TO_SOT_THT:TO-220-4_P5.08x3.8mm_StaggerOdd_Lead5.85mm_TabDown +Package_TO_SOT_THT:TO-220-4_Vertical +Package_TO_SOT_THT:TO-220-5_Horizontal_TabDown +Package_TO_SOT_THT:TO-220-5_Horizontal_TabUp +Package_TO_SOT_THT:TO-220-5_P3.4x3.7mm_StaggerEven_Lead3.8mm_Vertical +Package_TO_SOT_THT:TO-220-5_P3.4x3.7mm_StaggerOdd_Lead3.8mm_Vertical +Package_TO_SOT_THT:TO-220-5_P3.4x3.8mm_StaggerEven_Lead7.13mm_TabDown +Package_TO_SOT_THT:TO-220-5_P3.4x3.8mm_StaggerOdd_Lead7.13mm_TabDown +Package_TO_SOT_THT:TO-220-5_Vertical +Package_TO_SOT_THT:TO-220-7_P2.54x3.7mm_StaggerEven_Lead3.8mm_Vertical +Package_TO_SOT_THT:TO-220-7_P2.54x3.7mm_StaggerOdd_Lead3.8mm_Vertical +Package_TO_SOT_THT:TO-220-7_P2.54x3.8mm_StaggerEven_Lead5.85mm_TabDown +Package_TO_SOT_THT:TO-220-7_P2.54x3.8mm_StaggerOdd_Lead5.85mm_TabDown +Package_TO_SOT_THT:TO-220-7_P2.54x5.08mm_StaggerOdd_Lead3.08mm_Vertical +Package_TO_SOT_THT:TO-220-7_P2.54x5.1mm_StaggerOdd_Lead8.025mm_TabDown +Package_TO_SOT_THT:TO-220-8_Vertical +Package_TO_SOT_THT:TO-220-9_P1.94x3.7mm_StaggerEven_Lead3.8mm_Vertical +Package_TO_SOT_THT:TO-220-9_P1.94x3.7mm_StaggerOdd_Lead3.8mm_Vertical +Package_TO_SOT_THT:TO-220-9_P1.94x3.8mm_StaggerEven_Lead5.85mm_TabDown +Package_TO_SOT_THT:TO-220-9_P1.94x3.8mm_StaggerOdd_Lead5.85mm_TabDown +Package_TO_SOT_THT:TO-220F-11_P3.4x5.08mm_StaggerEven_Lead5.08mm_Vertical +Package_TO_SOT_THT:TO-220F-11_P3.4x5.08mm_StaggerOdd_Lead5.08mm_Vertical +Package_TO_SOT_THT:TO-220F-15_P2.54x5.08mm_StaggerEven_Lead5.08mm_Vertical +Package_TO_SOT_THT:TO-220F-15_P2.54x5.08mm_StaggerOdd_Lead5.08mm_Vertical +Package_TO_SOT_THT:TO-220F-2_Horizontal_TabDown +Package_TO_SOT_THT:TO-220F-2_Horizontal_TabUp +Package_TO_SOT_THT:TO-220F-2_Vertical +Package_TO_SOT_THT:TO-220F-3_Horizontal_TabDown +Package_TO_SOT_THT:TO-220F-3_Horizontal_TabUp +Package_TO_SOT_THT:TO-220F-3_Vertical +Package_TO_SOT_THT:TO-220F-4_Horizontal_TabDown +Package_TO_SOT_THT:TO-220F-4_Horizontal_TabUp +Package_TO_SOT_THT:TO-220F-4_P5.08x2.05mm_StaggerEven_Lead1.85mm_Vertical +Package_TO_SOT_THT:TO-220F-4_P5.08x2.05mm_StaggerOdd_Lead1.85mm_Vertical +Package_TO_SOT_THT:TO-220F-4_P5.08x3.7mm_StaggerEven_Lead3.5mm_Vertical +Package_TO_SOT_THT:TO-220F-4_P5.08x3.7mm_StaggerOdd_Lead3.5mm_Vertical +Package_TO_SOT_THT:TO-220F-4_Vertical +Package_TO_SOT_THT:TO-220F-5_Horizontal_TabDown +Package_TO_SOT_THT:TO-220F-5_Horizontal_TabUp +Package_TO_SOT_THT:TO-220F-5_P3.4x2.06mm_StaggerEven_Lead1.86mm_Vertical +Package_TO_SOT_THT:TO-220F-5_P3.4x2.06mm_StaggerOdd_Lead1.86mm_Vertical +Package_TO_SOT_THT:TO-220F-5_P3.4x3.7mm_StaggerEven_Lead3.5mm_Vertical +Package_TO_SOT_THT:TO-220F-5_P3.4x3.7mm_StaggerOdd_Lead3.5mm_Vertical +Package_TO_SOT_THT:TO-220F-5_Vertical +Package_TO_SOT_THT:TO-220F-7_P2.54x3.7mm_StaggerEven_Lead3.5mm_Vertical +Package_TO_SOT_THT:TO-220F-7_P2.54x3.7mm_StaggerOdd_Lead3.5mm_Vertical +Package_TO_SOT_THT:TO-220F-9_P1.8x3.7mm_StaggerEven_Lead3.5mm_Vertical +Package_TO_SOT_THT:TO-220F-9_P1.8x3.7mm_StaggerOdd_Lead3.5mm_Vertical +Package_TO_SOT_THT:TO-247-2_Horizontal_TabDown +Package_TO_SOT_THT:TO-247-2_Horizontal_TabUp +Package_TO_SOT_THT:TO-247-2_Vertical +Package_TO_SOT_THT:TO-247-3_Horizontal_TabDown +Package_TO_SOT_THT:TO-247-3_Horizontal_TabUp +Package_TO_SOT_THT:TO-247-3_Vertical +Package_TO_SOT_THT:TO-247-4_Horizontal_TabDown +Package_TO_SOT_THT:TO-247-4_Horizontal_TabUp +Package_TO_SOT_THT:TO-247-4_Vertical +Package_TO_SOT_THT:TO-247-5_Horizontal_TabDown +Package_TO_SOT_THT:TO-247-5_Horizontal_TabUp +Package_TO_SOT_THT:TO-247-5_Vertical +Package_TO_SOT_THT:TO-251-2-1EP_Horizontal_TabDown +Package_TO_SOT_THT:TO-251-2_Vertical +Package_TO_SOT_THT:TO-251-3-1EP_Horizontal_TabDown +Package_TO_SOT_THT:TO-251-3_Vertical +Package_TO_SOT_THT:TO-262-3-1EP_Horizontal_TabDown +Package_TO_SOT_THT:TO-262-3_Vertical +Package_TO_SOT_THT:TO-262-5-1EP_Horizontal_TabDown +Package_TO_SOT_THT:TO-262-5_Vertical +Package_TO_SOT_THT:TO-264-2_Horizontal_TabDown +Package_TO_SOT_THT:TO-264-2_Horizontal_TabUp +Package_TO_SOT_THT:TO-264-2_Vertical +Package_TO_SOT_THT:TO-264-3_Horizontal_TabDown +Package_TO_SOT_THT:TO-264-3_Horizontal_TabUp +Package_TO_SOT_THT:TO-264-3_Vertical +Package_TO_SOT_THT:TO-264-5_Horizontal_TabDown +Package_TO_SOT_THT:TO-264-5_Horizontal_TabUp +Package_TO_SOT_THT:TO-264-5_Vertical +Package_TO_SOT_THT:TO-3 +Package_TO_SOT_THT:TO-33-4 +Package_TO_SOT_THT:TO-33-4_Window +Package_TO_SOT_THT:TO-38-2 +Package_TO_SOT_THT:TO-38-2_Window +Package_TO_SOT_THT:TO-38-3 +Package_TO_SOT_THT:TO-38-3_Window +Package_TO_SOT_THT:TO-39-10 +Package_TO_SOT_THT:TO-39-10_Window +Package_TO_SOT_THT:TO-39-2 +Package_TO_SOT_THT:TO-39-2_Window +Package_TO_SOT_THT:TO-39-3 +Package_TO_SOT_THT:TO-39-3_Window +Package_TO_SOT_THT:TO-39-4 +Package_TO_SOT_THT:TO-39-4_Window +Package_TO_SOT_THT:TO-39-6 +Package_TO_SOT_THT:TO-39-6_Window +Package_TO_SOT_THT:TO-39-8 +Package_TO_SOT_THT:TO-39-8_Window +Package_TO_SOT_THT:TO-3P-3_Horizontal_TabDown +Package_TO_SOT_THT:TO-3P-3_Horizontal_TabUp +Package_TO_SOT_THT:TO-3P-3_Vertical +Package_TO_SOT_THT:TO-3PB-3_Horizontal_TabDown +Package_TO_SOT_THT:TO-3PB-3_Horizontal_TabUp +Package_TO_SOT_THT:TO-3PB-3_Vertical +Package_TO_SOT_THT:TO-46-2 +Package_TO_SOT_THT:TO-46-2_Pin2Center +Package_TO_SOT_THT:TO-46-2_Pin2Center_Window +Package_TO_SOT_THT:TO-46-2_Window +Package_TO_SOT_THT:TO-46-3 +Package_TO_SOT_THT:TO-46-3_Pin2Center +Package_TO_SOT_THT:TO-46-3_Pin2Center_Window +Package_TO_SOT_THT:TO-46-3_Window +Package_TO_SOT_THT:TO-46-4 +Package_TO_SOT_THT:TO-46-4_Window +Package_TO_SOT_THT:TO-5-10 +Package_TO_SOT_THT:TO-5-10_Window +Package_TO_SOT_THT:TO-5-2 +Package_TO_SOT_THT:TO-5-2_Window +Package_TO_SOT_THT:TO-5-3 +Package_TO_SOT_THT:TO-5-3_Window +Package_TO_SOT_THT:TO-5-4 +Package_TO_SOT_THT:TO-5-4_Window +Package_TO_SOT_THT:TO-5-6 +Package_TO_SOT_THT:TO-5-6_Window +Package_TO_SOT_THT:TO-5-8 +Package_TO_SOT_THT:TO-5-8_PD5.08 +Package_TO_SOT_THT:TO-5-8_PD5.08_Window +Package_TO_SOT_THT:TO-5-8_Window +Package_TO_SOT_THT:TO-52-2 +Package_TO_SOT_THT:TO-52-2_Window +Package_TO_SOT_THT:TO-52-3 +Package_TO_SOT_THT:TO-52-3_Window +Package_TO_SOT_THT:TO-72-4 +Package_TO_SOT_THT:TO-72-4_Window +Package_TO_SOT_THT:TO-75-6 +Package_TO_SOT_THT:TO-75-6_Window +Package_TO_SOT_THT:TO-78-10 +Package_TO_SOT_THT:TO-78-10_Window +Package_TO_SOT_THT:TO-78-6 +Package_TO_SOT_THT:TO-78-6_Window +Package_TO_SOT_THT:TO-78-8 +Package_TO_SOT_THT:TO-78-8_Window +Package_TO_SOT_THT:TO-8-2 +Package_TO_SOT_THT:TO-8-2_Window +Package_TO_SOT_THT:TO-8-3 +Package_TO_SOT_THT:TO-8-3_Window +Package_TO_SOT_THT:TO-92-2 +Package_TO_SOT_THT:TO-92-2_Horizontal1 +Package_TO_SOT_THT:TO-92-2_Horizontal2 +Package_TO_SOT_THT:TO-92-2_W4.0mm_Horizontal_FlatSideDown +Package_TO_SOT_THT:TO-92-2_W4.0mm_Horizontal_FlatSideUp +Package_TO_SOT_THT:TO-92-2_Wide +Package_TO_SOT_THT:TO-92 +Package_TO_SOT_THT:TO-92Flat +Package_TO_SOT_THT:TO-92L +Package_TO_SOT_THT:TO-92L_HandSolder +Package_TO_SOT_THT:TO-92L_Inline +Package_TO_SOT_THT:TO-92L_Inline_Wide +Package_TO_SOT_THT:TO-92L_Wide +Package_TO_SOT_THT:TO-92Mini-2 +Package_TO_SOT_THT:TO-92S-2 +Package_TO_SOT_THT:TO-92S +Package_TO_SOT_THT:TO-92S_Wide +Package_TO_SOT_THT:TO-92_HandSolder +Package_TO_SOT_THT:TO-92_Horizontal1 +Package_TO_SOT_THT:TO-92_Horizontal2 +Package_TO_SOT_THT:TO-92_Inline +Package_TO_SOT_THT:TO-92_Inline_Horizontal1 +Package_TO_SOT_THT:TO-92_Inline_Horizontal2 +Package_TO_SOT_THT:TO-92_Inline_W4.0mm_Horizontal_FlatSideDown +Package_TO_SOT_THT:TO-92_Inline_W4.0mm_Horizontal_FlatSideUp +Package_TO_SOT_THT:TO-92_Inline_Wide +Package_TO_SOT_THT:TO-92_W4.0mm_StaggerEven_Horizontal_FlatSideDown +Package_TO_SOT_THT:TO-92_W4.0mm_StaggerEven_Horizontal_FlatSideUp +Package_TO_SOT_THT:TO-92_Wide +Package_TO_SOT_THT:TO-99-6 +Package_TO_SOT_THT:TO-99-6_Window +Package_TO_SOT_THT:TO-99-8 +Package_TO_SOT_THT:TO-99-8_Window +Potentiometer_SMD:Potentiometer_ACP_CA14-VSMD_Vertical +Potentiometer_SMD:Potentiometer_ACP_CA14-VSMD_Vertical_Hole +Potentiometer_SMD:Potentiometer_ACP_CA6-VSMD_Vertical +Potentiometer_SMD:Potentiometer_ACP_CA6-VSMD_Vertical_Hole +Potentiometer_SMD:Potentiometer_ACP_CA9-VSMD_Vertical +Potentiometer_SMD:Potentiometer_ACP_CA9-VSMD_Vertical_Hole +Potentiometer_SMD:Potentiometer_Bourns_3214G_Horizontal +Potentiometer_SMD:Potentiometer_Bourns_3214J_Horizontal +Potentiometer_SMD:Potentiometer_Bourns_3214W_Vertical +Potentiometer_SMD:Potentiometer_Bourns_3214X_Vertical +Potentiometer_SMD:Potentiometer_Bourns_3224G_Horizontal +Potentiometer_SMD:Potentiometer_Bourns_3224J_Horizontal +Potentiometer_SMD:Potentiometer_Bourns_3224W_Vertical +Potentiometer_SMD:Potentiometer_Bourns_3224X_Vertical +Potentiometer_SMD:Potentiometer_Bourns_3269P_Horizontal +Potentiometer_SMD:Potentiometer_Bourns_3269W_Vertical +Potentiometer_SMD:Potentiometer_Bourns_3269X_Horizontal +Potentiometer_SMD:Potentiometer_Bourns_3314G_Vertical +Potentiometer_SMD:Potentiometer_Bourns_3314J_Vertical +Potentiometer_SMD:Potentiometer_Bourns_3314R-1_Vertical_Hole +Potentiometer_SMD:Potentiometer_Bourns_3314R-GM5_Vertical +Potentiometer_SMD:Potentiometer_Bourns_3314S_Horizontal +Potentiometer_SMD:Potentiometer_Bourns_PRS11S_Vertical +Potentiometer_SMD:Potentiometer_Bourns_TC33X_Vertical +Potentiometer_SMD:Potentiometer_Vishay_TS53YJ_Vertical +Potentiometer_SMD:Potentiometer_Vishay_TS53YL_Vertical +Potentiometer_THT:Potentiometer_ACP_CA14-H2,5_Horizontal +Potentiometer_THT:Potentiometer_ACP_CA14-H4_Horizontal +Potentiometer_THT:Potentiometer_ACP_CA14-H5_Horizontal +Potentiometer_THT:Potentiometer_ACP_CA14V-15_Vertical +Potentiometer_THT:Potentiometer_ACP_CA14V-15_Vertical_Hole +Potentiometer_THT:Potentiometer_ACP_CA6-H2,5_Horizontal +Potentiometer_THT:Potentiometer_ACP_CA9-H2,5_Horizontal +Potentiometer_THT:Potentiometer_ACP_CA9-H3,8_Horizontal +Potentiometer_THT:Potentiometer_ACP_CA9-H5_Horizontal +Potentiometer_THT:Potentiometer_ACP_CA9-V10_Vertical +Potentiometer_THT:Potentiometer_ACP_CA9-V10_Vertical_Hole +Potentiometer_THT:Potentiometer_Alpha_RD901F-40-00D_Single_Vertical +Potentiometer_THT:Potentiometer_Alpha_RD901F-40-00D_Single_Vertical_CircularHoles +Potentiometer_THT:Potentiometer_Alpha_RD902F-40-00D_Dual_Vertical +Potentiometer_THT:Potentiometer_Alpha_RD902F-40-00D_Dual_Vertical_CircularHoles +Potentiometer_THT:Potentiometer_Alps_RK097_Dual_Horizontal +Potentiometer_THT:Potentiometer_Alps_RK097_Dual_Horizontal_Switch +Potentiometer_THT:Potentiometer_Alps_RK097_Single_Horizontal +Potentiometer_THT:Potentiometer_Alps_RK097_Single_Horizontal_Switch +Potentiometer_THT:Potentiometer_Alps_RK09K_Single_Horizontal +Potentiometer_THT:Potentiometer_Alps_RK09K_Single_Vertical +Potentiometer_THT:Potentiometer_Alps_RK09L_Double_Horizontal +Potentiometer_THT:Potentiometer_Alps_RK09L_Double_Vertical +Potentiometer_THT:Potentiometer_Alps_RK09L_Single_Horizontal +Potentiometer_THT:Potentiometer_Alps_RK09L_Single_Vertical +Potentiometer_THT:Potentiometer_Alps_RK09Y11_Single_Horizontal +Potentiometer_THT:Potentiometer_Alps_RK163_Dual_Horizontal +Potentiometer_THT:Potentiometer_Alps_RK163_Single_Horizontal +Potentiometer_THT:Potentiometer_Bourns_20P_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3005_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3006P_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3006W_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3006Y_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3009P_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3009Y_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3266P_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3266W_Vertical +Potentiometer_THT:Potentiometer_Bourns_3266X_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3266Y_Vertical +Potentiometer_THT:Potentiometer_Bourns_3266Z_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3296P_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3296W_Vertical +Potentiometer_THT:Potentiometer_Bourns_3296X_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3296Y_Vertical +Potentiometer_THT:Potentiometer_Bourns_3296Z_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3299P_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3299W_Vertical +Potentiometer_THT:Potentiometer_Bourns_3299X_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3299Y_Vertical +Potentiometer_THT:Potentiometer_Bourns_3299Z_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3339H_Vertical +Potentiometer_THT:Potentiometer_Bourns_3339P_Vertical +Potentiometer_THT:Potentiometer_Bourns_3339P_Vertical_HandSoldering +Potentiometer_THT:Potentiometer_Bourns_3339S_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3339W_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3386C_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3386F_Vertical +Potentiometer_THT:Potentiometer_Bourns_3386P_Vertical +Potentiometer_THT:Potentiometer_Bourns_3386W_Horizontal +Potentiometer_THT:Potentiometer_Bourns_3386X_Horizontal +Potentiometer_THT:Potentiometer_Bourns_PTA1543_Single_Slide +Potentiometer_THT:Potentiometer_Bourns_PTA2043_Single_Slide +Potentiometer_THT:Potentiometer_Bourns_PTA3043_Single_Slide +Potentiometer_THT:Potentiometer_Bourns_PTA4543_Single_Slide +Potentiometer_THT:Potentiometer_Bourns_PTA6043_Single_Slide +Potentiometer_THT:Potentiometer_Bourns_PTV09A-1_Single_Vertical +Potentiometer_THT:Potentiometer_Bourns_PTV09A-2_Single_Horizontal +Potentiometer_THT:Potentiometer_Bourns_PTV112-4_Dual_Vertical +Potentiometer_THT:Potentiometer_Omeg_PC16BU_Horizontal +Potentiometer_THT:Potentiometer_Omeg_PC16BU_Vertical +Potentiometer_THT:Potentiometer_Piher_PC-16_Dual_Horizontal +Potentiometer_THT:Potentiometer_Piher_PC-16_Single_Horizontal +Potentiometer_THT:Potentiometer_Piher_PC-16_Single_Vertical +Potentiometer_THT:Potentiometer_Piher_PC-16_Triple_Horizontal +Potentiometer_THT:Potentiometer_Piher_PT-10-H01_Horizontal +Potentiometer_THT:Potentiometer_Piher_PT-10-H05_Horizontal +Potentiometer_THT:Potentiometer_Piher_PT-10-V05_Vertical +Potentiometer_THT:Potentiometer_Piher_PT-10-V10_Vertical +Potentiometer_THT:Potentiometer_Piher_PT-10-V10_Vertical_Hole +Potentiometer_THT:Potentiometer_Piher_PT-15-H01_Horizontal +Potentiometer_THT:Potentiometer_Piher_PT-15-H05_Horizontal +Potentiometer_THT:Potentiometer_Piher_PT-15-H06_Horizontal +Potentiometer_THT:Potentiometer_Piher_PT-15-H25_Horizontal +Potentiometer_THT:Potentiometer_Piher_PT-15-V02_Vertical +Potentiometer_THT:Potentiometer_Piher_PT-15-V02_Vertical_Hole +Potentiometer_THT:Potentiometer_Piher_PT-15-V15_Vertical +Potentiometer_THT:Potentiometer_Piher_PT-15-V15_Vertical_Hole +Potentiometer_THT:Potentiometer_Piher_PT-6-H_Horizontal +Potentiometer_THT:Potentiometer_Piher_PT-6-V_Vertical +Potentiometer_THT:Potentiometer_Piher_PT-6-V_Vertical_Hole +Potentiometer_THT:Potentiometer_Piher_T-16H_Double_Horizontal +Potentiometer_THT:Potentiometer_Piher_T-16H_Single_Horizontal +Potentiometer_THT:Potentiometer_Piher_T-16L_Single_Vertical_Hole +Potentiometer_THT:Potentiometer_Runtron_RM-063_Horizontal +Potentiometer_THT:Potentiometer_Runtron_RM-065_Vertical +Potentiometer_THT:Potentiometer_TT_P0915N +Potentiometer_THT:Potentiometer_Vishay_148-149_Dual_Horizontal +Potentiometer_THT:Potentiometer_Vishay_148-149_Single_Horizontal +Potentiometer_THT:Potentiometer_Vishay_148-149_Single_Vertical +Potentiometer_THT:Potentiometer_Vishay_148E-149E_Dual_Horizontal +Potentiometer_THT:Potentiometer_Vishay_148E-149E_Single_Horizontal +Potentiometer_THT:Potentiometer_Vishay_248BH-249BH_Single_Horizontal +Potentiometer_THT:Potentiometer_Vishay_248GJ-249GJ_Single_Horizontal +Potentiometer_THT:Potentiometer_Vishay_248GJ-249GJ_Single_Vertical +Potentiometer_THT:Potentiometer_Vishay_43_Horizontal +Potentiometer_THT:Potentiometer_Vishay_T7-YA_Single_Vertical +Potentiometer_THT:Potentiometer_Vishay_T73XW_Horizontal +Potentiometer_THT:Potentiometer_Vishay_T73XX_Horizontal +Potentiometer_THT:Potentiometer_Vishay_T73YP_Vertical +Potentiometer_THT:Potentiometer_Vishay_T93XA_Horizontal +Potentiometer_THT:Potentiometer_Vishay_T93YA_Vertical +Relay_SMD:Relay_2P2T_10x6mm_TE_IMxxG +Relay_SMD:Relay_DPDT_AXICOM_IMSeries_JLeg +Relay_SMD:Relay_DPDT_FRT5_SMD +Relay_SMD:Relay_DPDT_Kemet_EE2_NU +Relay_SMD:Relay_DPDT_Kemet_EE2_NUH +Relay_SMD:Relay_DPDT_Kemet_EE2_NUH_DoubleCoil +Relay_SMD:Relay_DPDT_Kemet_EE2_NUX_DoubleCoil +Relay_SMD:Relay_DPDT_Kemet_EE2_NUX_NKX +Relay_SMD:Relay_DPDT_Kemet_EE2_NU_DoubleCoil +Relay_SMD:Relay_DPDT_Omron_G6H-2F +Relay_SMD:Relay_DPDT_Omron_G6K-2F-Y +Relay_SMD:Relay_DPDT_Omron_G6K-2F +Relay_SMD:Relay_DPDT_Omron_G6K-2G-Y +Relay_SMD:Relay_DPDT_Omron_G6K-2G +Relay_SMD:Relay_DPDT_Omron_G6S-2F +Relay_SMD:Relay_DPDT_Omron_G6S-2G +Relay_SMD:Relay_DPDT_Omron_G6SK-2F +Relay_SMD:Relay_DPDT_Omron_G6SK-2G +Relay_SMD:Relay_Fujitsu_FTR-B3S +Relay_SMD:Relay_SPDT_AXICOM_HF3Series_50ohms_Pitch1.27mm +Relay_SMD:Relay_SPDT_AXICOM_HF3Series_75ohms_Pitch1.27mm +Relay_THT:Relay_1-Form-A_Schrack-RYII_RM5mm +Relay_THT:Relay_1-Form-B_Schrack-RYII_RM5mm +Relay_THT:Relay_1-Form-C_Schrack-RYII_RM3.2mm +Relay_THT:Relay_3PST_COTO_3650 +Relay_THT:Relay_3PST_COTO_3660 +Relay_THT:Relay_DPDT_AXICOM_IMSeries_Pitch3.2mm +Relay_THT:Relay_DPDT_AXICOM_IMSeries_Pitch5.08mm +Relay_THT:Relay_DPDT_Finder_30.22 +Relay_THT:Relay_DPDT_Finder_40.52 +Relay_THT:Relay_DPDT_Finder_40.62 +Relay_THT:Relay_DPDT_FRT5 +Relay_THT:Relay_DPDT_Fujitsu_FTR-F1C +Relay_THT:Relay_DPDT_Hongfa_HF115F-2Z-x4 +Relay_THT:Relay_DPDT_Kemet_EC2_NJ +Relay_THT:Relay_DPDT_Kemet_EC2_NJ_DoubleCoil +Relay_THT:Relay_DPDT_Kemet_EC2_NU +Relay_THT:Relay_DPDT_Kemet_EC2_NU_DoubleCoil +Relay_THT:Relay_DPDT_Omron_G2RL-2 +Relay_THT:Relay_DPDT_Omron_G5V-2 +Relay_THT:Relay_DPDT_Omron_G6A +Relay_THT:Relay_DPDT_Omron_G6AK +Relay_THT:Relay_DPDT_Omron_G6H-2 +Relay_THT:Relay_DPDT_Omron_G6K-2P-Y +Relay_THT:Relay_DPDT_Omron_G6K-2P +Relay_THT:Relay_DPDT_Omron_G6S-2 +Relay_THT:Relay_DPDT_Omron_G6SK-2 +Relay_THT:Relay_DPDT_Panasonic_JW2 +Relay_THT:Relay_DPDT_Schrack-RT2-FormC-Dual-Coil_RM5mm +Relay_THT:Relay_DPDT_Schrack-RT2-FormC_RM5mm +Relay_THT:Relay_DPST_COTO_3602 +Relay_THT:Relay_DPST_Fujitsu_FTR-F1A +Relay_THT:Relay_DPST_Omron_G2RL-2A +Relay_THT:Relay_DPST_Schrack-RT2-FormA_RM5mm +Relay_THT:Relay_NCR_HHG1D-1 +Relay_THT:Relay_Socket_3PDT_Omron_PLE11-0 +Relay_THT:Relay_Socket_4PDT_Omron_PY14-02 +Relay_THT:Relay_Socket_DPDT_Finder_96.12 +Relay_THT:Relay_Socket_DPDT_Omron_PLE08-0 +Relay_THT:Relay_SPDT_Finder_32.21-x000 +Relay_THT:Relay_SPDT_Finder_34.51_Horizontal +Relay_THT:Relay_SPDT_Finder_34.51_Vertical +Relay_THT:Relay_SPDT_Finder_36.11 +Relay_THT:Relay_SPDT_Finder_40.11 +Relay_THT:Relay_SPDT_Finder_40.31 +Relay_THT:Relay_SPDT_Finder_40.41 +Relay_THT:Relay_SPDT_Finder_40.51 +Relay_THT:Relay_SPDT_Finder_40.61 +Relay_THT:Relay_SPDT_Fujitsu_FTR-LYCA005x_FormC_Vertical +Relay_THT:Relay_SPDT_HJR-4102 +Relay_THT:Relay_SPDT_Hongfa_HF3F-L-xx-1ZL1T +Relay_THT:Relay_SPDT_Hongfa_HF3F-L-xx-1ZL2T-R +Relay_THT:Relay_SPDT_Hongfa_HF3F-L-xx-1ZL2T +Relay_THT:Relay_SPDT_Hongfa_JQC-3FF_0XX-1Z +Relay_THT:Relay_SPDT_HsinDa_Y14 +Relay_THT:Relay_SPDT_Omron-G5LE-1 +Relay_THT:Relay_SPDT_Omron-G5Q-1 +Relay_THT:Relay_SPDT_Omron_G2RL-1-E +Relay_THT:Relay_SPDT_Omron_G2RL-1 +Relay_THT:Relay_SPDT_Omron_G5V-1 +Relay_THT:Relay_SPDT_Omron_G6E +Relay_THT:Relay_SPDT_Omron_G6EK +Relay_THT:Relay_SPDT_Panasonic_DR-L +Relay_THT:Relay_SPDT_Panasonic_DR-L2 +Relay_THT:Relay_SPDT_Panasonic_DR +Relay_THT:Relay_SPDT_Panasonic_JW1_FormC +Relay_THT:Relay_SPDT_PotterBrumfield_T9AP5D52_12V30A +Relay_THT:Relay_SPDT_RAYEX-L90 +Relay_THT:Relay_SPDT_RAYEX-L90S +Relay_THT:Relay_SPDT_SANYOU_SRD_Series_Form_C +Relay_THT:Relay_SPDT_Schrack-RP-II-1-16A-FormC_RM5mm +Relay_THT:Relay_SPDT_Schrack-RP-II-1-FormC_RM3.5mm +Relay_THT:Relay_SPDT_Schrack-RP-II-1-FormC_RM5mm +Relay_THT:Relay_SPDT_Schrack-RT1-16A-FormC_RM5mm +Relay_THT:Relay_SPDT_Schrack-RT1-FormC_RM3.5mm +Relay_THT:Relay_SPDT_Schrack-RT1-FormC_RM5mm +Relay_THT:Relay_SPDT_StandexMeder_SIL_Form1C +Relay_THT:Relay_SPST-NO_Fujitsu_FTR-LYAA005x_FormA_Vertical +Relay_THT:Relay_SPST_Finder_32.21-x300 +Relay_THT:Relay_SPST_Hongfa_HF3F-L-xx-1HL1T +Relay_THT:Relay_SPST_Hongfa_HF3F-L-xx-1HL2T-R +Relay_THT:Relay_SPST_Hongfa_HF3F-L-xx-1HL2T +Relay_THT:Relay_SPST_Hongfa_JQC-3FF_0XX-1H +Relay_THT:Relay_SPST_Omron-G5Q-1A +Relay_THT:Relay_SPST_Omron_G2RL-1A-E +Relay_THT:Relay_SPST_Omron_G2RL-1A +Relay_THT:Relay_SPST_Omron_G5NB +Relay_THT:Relay_SPST_Omron_G5PZ +Relay_THT:Relay_SPST_Panasonic_ADW11 +Relay_THT:Relay_SPST_Panasonic_ALFG_FormA +Relay_THT:Relay_SPST_Panasonic_ALFG_FormA_CircularHoles +Relay_THT:Relay_SPST_Panasonic_JW1_FormA +Relay_THT:Relay_SPST_PotterBrumfield_T9AP1D52_12V30A +Relay_THT:Relay_SPST_RAYEX-L90A +Relay_THT:Relay_SPST_RAYEX-L90AS +Relay_THT:Relay_SPST_RAYEX-L90B +Relay_THT:Relay_SPST_RAYEX-L90BS +Relay_THT:Relay_SPST_SANYOU_SRD_Series_Form_A +Relay_THT:Relay_SPST_SANYOU_SRD_Series_Form_B +Relay_THT:Relay_SPST_Schrack-RP-II-1-16A-FormA_RM5mm +Relay_THT:Relay_SPST_Schrack-RP-II-1-FormA_RM3.5mm +Relay_THT:Relay_SPST_Schrack-RP-II-1-FormA_RM5mm +Relay_THT:Relay_SPST_Schrack-RP3SL-1coil_RM5mm +Relay_THT:Relay_SPST_Schrack-RP3SL_RM5mm +Relay_THT:Relay_SPST_Schrack-RT1-16A-FormA_RM5mm +Relay_THT:Relay_SPST_Schrack-RT1-FormA_RM3.5mm +Relay_THT:Relay_SPST_Schrack-RT1-FormA_RM5mm +Relay_THT:Relay_SPST_StandexMeder_MS_Form1AB +Relay_THT:Relay_SPST_StandexMeder_SIL_Form1A +Relay_THT:Relay_SPST_StandexMeder_SIL_Form1B +Relay_THT:Relay_SPST_TE_PCH-1xxx2M +Relay_THT:Relay_SPST_TE_PCN-1xxD3MHZ +Relay_THT:Relay_SPST_Zettler-AZSR131 +Relay_THT:Relay_StandexMeder_DIP_HighProfile +Relay_THT:Relay_StandexMeder_DIP_LowProfile +Relay_THT:Relay_StandexMeder_UMS +Relay_THT:Relay_Tyco_V23072_Sealed +Resistor_SMD:R_01005_0402Metric +Resistor_SMD:R_01005_0402Metric_Pad0.57x0.30mm_HandSolder +Resistor_SMD:R_0201_0603Metric +Resistor_SMD:R_0201_0603Metric_Pad0.64x0.40mm_HandSolder +Resistor_SMD:R_0402_1005Metric +Resistor_SMD:R_0402_1005Metric_Pad0.72x0.64mm_HandSolder +Resistor_SMD:R_0603_1608Metric +Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder +Resistor_SMD:R_0612_1632Metric +Resistor_SMD:R_0612_1632Metric_Pad1.18x3.40mm_HandSolder +Resistor_SMD:R_0805_2012Metric +Resistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolder +Resistor_SMD:R_0815_2038Metric +Resistor_SMD:R_0815_2038Metric_Pad1.20x4.05mm_HandSolder +Resistor_SMD:R_1020_2550Metric +Resistor_SMD:R_1020_2550Metric_Pad1.33x5.20mm_HandSolder +Resistor_SMD:R_1206_3216Metric +Resistor_SMD:R_1206_3216Metric_Pad1.30x1.75mm_HandSolder +Resistor_SMD:R_1210_3225Metric +Resistor_SMD:R_1210_3225Metric_Pad1.30x2.65mm_HandSolder +Resistor_SMD:R_1218_3246Metric +Resistor_SMD:R_1218_3246Metric_Pad1.22x4.75mm_HandSolder +Resistor_SMD:R_1812_4532Metric +Resistor_SMD:R_1812_4532Metric_Pad1.30x3.40mm_HandSolder +Resistor_SMD:R_2010_5025Metric +Resistor_SMD:R_2010_5025Metric_Pad1.40x2.65mm_HandSolder +Resistor_SMD:R_2512_6332Metric +Resistor_SMD:R_2512_6332Metric_Pad1.40x3.35mm_HandSolder +Resistor_SMD:R_2816_7142Metric +Resistor_SMD:R_2816_7142Metric_Pad3.20x4.45mm_HandSolder +Resistor_SMD:R_4020_10251Metric +Resistor_SMD:R_4020_10251Metric_Pad1.65x5.30mm_HandSolder +Resistor_SMD:R_Array_Concave_2x0603 +Resistor_SMD:R_Array_Concave_4x0402 +Resistor_SMD:R_Array_Concave_4x0603 +Resistor_SMD:R_Array_Convex_2x0402 +Resistor_SMD:R_Array_Convex_2x0603 +Resistor_SMD:R_Array_Convex_2x0606 +Resistor_SMD:R_Array_Convex_2x1206 +Resistor_SMD:R_Array_Convex_4x0402 +Resistor_SMD:R_Array_Convex_4x0603 +Resistor_SMD:R_Array_Convex_4x0612 +Resistor_SMD:R_Array_Convex_4x1206 +Resistor_SMD:R_Array_Convex_5x0603 +Resistor_SMD:R_Array_Convex_5x1206 +Resistor_SMD:R_Array_Convex_8x0602 +Resistor_SMD:R_Cat16-2 +Resistor_SMD:R_Cat16-4 +Resistor_SMD:R_Cat16-8 +Resistor_SMD:R_MELF_MMB-0207 +Resistor_SMD:R_MicroMELF_MMU-0102 +Resistor_SMD:R_MiniMELF_MMA-0204 +Resistor_SMD:R_Shunt_Isabellenhuette_BVR4026 +Resistor_SMD:R_Shunt_Ohmite_LVK12 +Resistor_SMD:R_Shunt_Ohmite_LVK20 +Resistor_SMD:R_Shunt_Ohmite_LVK24 +Resistor_SMD:R_Shunt_Ohmite_LVK25 +Resistor_SMD:R_Shunt_Vishay_WSK2512_6332Metric_T1.19mm +Resistor_SMD:R_Shunt_Vishay_WSK2512_6332Metric_T2.21mm +Resistor_SMD:R_Shunt_Vishay_WSK2512_6332Metric_T2.66mm +Resistor_SMD:R_Shunt_Vishay_WSKW0612 +Resistor_SMD:R_Shunt_Vishay_WSR2_WSR3 +Resistor_SMD:R_Shunt_Vishay_WSR2_WSR3_KelvinConnection +Resistor_THT:R_Array_SIP10 +Resistor_THT:R_Array_SIP11 +Resistor_THT:R_Array_SIP12 +Resistor_THT:R_Array_SIP13 +Resistor_THT:R_Array_SIP14 +Resistor_THT:R_Array_SIP4 +Resistor_THT:R_Array_SIP5 +Resistor_THT:R_Array_SIP6 +Resistor_THT:R_Array_SIP7 +Resistor_THT:R_Array_SIP8 +Resistor_THT:R_Array_SIP9 +Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P1.90mm_Vertical +Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P2.54mm_Vertical +Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P5.08mm_Horizontal +Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P5.08mm_Vertical +Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal +Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal +Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P15.24mm_Horizontal +Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P2.54mm_Vertical +Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P5.08mm_Vertical +Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal +Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P12.70mm_Horizontal +Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P15.24mm_Horizontal +Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P2.54mm_Vertical +Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P20.32mm_Horizontal +Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P25.40mm_Horizontal +Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P5.08mm_Vertical +Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P12.70mm_Horizontal +Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P15.24mm_Horizontal +Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P20.32mm_Horizontal +Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P25.40mm_Horizontal +Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P5.08mm_Vertical +Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P7.62mm_Vertical +Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P15.24mm_Horizontal +Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P20.32mm_Horizontal +Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P25.40mm_Horizontal +Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P5.08mm_Vertical +Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P7.62mm_Vertical +Resistor_THT:R_Axial_DIN0516_L15.5mm_D5.0mm_P20.32mm_Horizontal +Resistor_THT:R_Axial_DIN0516_L15.5mm_D5.0mm_P25.40mm_Horizontal +Resistor_THT:R_Axial_DIN0516_L15.5mm_D5.0mm_P30.48mm_Horizontal +Resistor_THT:R_Axial_DIN0516_L15.5mm_D5.0mm_P5.08mm_Vertical +Resistor_THT:R_Axial_DIN0516_L15.5mm_D5.0mm_P7.62mm_Vertical +Resistor_THT:R_Axial_DIN0614_L14.3mm_D5.7mm_P15.24mm_Horizontal +Resistor_THT:R_Axial_DIN0614_L14.3mm_D5.7mm_P20.32mm_Horizontal +Resistor_THT:R_Axial_DIN0614_L14.3mm_D5.7mm_P25.40mm_Horizontal +Resistor_THT:R_Axial_DIN0614_L14.3mm_D5.7mm_P5.08mm_Vertical +Resistor_THT:R_Axial_DIN0614_L14.3mm_D5.7mm_P7.62mm_Vertical +Resistor_THT:R_Axial_DIN0617_L17.0mm_D6.0mm_P20.32mm_Horizontal +Resistor_THT:R_Axial_DIN0617_L17.0mm_D6.0mm_P25.40mm_Horizontal +Resistor_THT:R_Axial_DIN0617_L17.0mm_D6.0mm_P30.48mm_Horizontal +Resistor_THT:R_Axial_DIN0617_L17.0mm_D6.0mm_P5.08mm_Vertical +Resistor_THT:R_Axial_DIN0617_L17.0mm_D6.0mm_P7.62mm_Vertical +Resistor_THT:R_Axial_DIN0918_L18.0mm_D9.0mm_P22.86mm_Horizontal +Resistor_THT:R_Axial_DIN0918_L18.0mm_D9.0mm_P25.40mm_Horizontal +Resistor_THT:R_Axial_DIN0918_L18.0mm_D9.0mm_P30.48mm_Horizontal +Resistor_THT:R_Axial_DIN0918_L18.0mm_D9.0mm_P7.62mm_Vertical +Resistor_THT:R_Axial_DIN0922_L20.0mm_D9.0mm_P25.40mm_Horizontal +Resistor_THT:R_Axial_DIN0922_L20.0mm_D9.0mm_P30.48mm_Horizontal +Resistor_THT:R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical +Resistor_THT:R_Axial_Power_L20.0mm_W6.4mm_P22.40mm +Resistor_THT:R_Axial_Power_L20.0mm_W6.4mm_P25.40mm +Resistor_THT:R_Axial_Power_L20.0mm_W6.4mm_P30.48mm +Resistor_THT:R_Axial_Power_L20.0mm_W6.4mm_P5.08mm_Vertical +Resistor_THT:R_Axial_Power_L20.0mm_W6.4mm_P7.62mm_Vertical +Resistor_THT:R_Axial_Power_L25.0mm_W6.4mm_P27.94mm +Resistor_THT:R_Axial_Power_L25.0mm_W6.4mm_P30.48mm +Resistor_THT:R_Axial_Power_L25.0mm_W9.0mm_P10.16mm_Vertical +Resistor_THT:R_Axial_Power_L25.0mm_W9.0mm_P27.94mm +Resistor_THT:R_Axial_Power_L25.0mm_W9.0mm_P30.48mm +Resistor_THT:R_Axial_Power_L25.0mm_W9.0mm_P7.62mm_Vertical +Resistor_THT:R_Axial_Power_L38.0mm_W6.4mm_P40.64mm +Resistor_THT:R_Axial_Power_L38.0mm_W6.4mm_P45.72mm +Resistor_THT:R_Axial_Power_L38.0mm_W9.0mm_P40.64mm +Resistor_THT:R_Axial_Power_L38.0mm_W9.0mm_P45.72mm +Resistor_THT:R_Axial_Power_L48.0mm_W12.5mm_P10.16mm_Vertical +Resistor_THT:R_Axial_Power_L48.0mm_W12.5mm_P55.88mm +Resistor_THT:R_Axial_Power_L48.0mm_W12.5mm_P60.96mm +Resistor_THT:R_Axial_Power_L48.0mm_W12.5mm_P7.62mm_Vertical +Resistor_THT:R_Axial_Power_L50.0mm_W9.0mm_P55.88mm +Resistor_THT:R_Axial_Power_L50.0mm_W9.0mm_P60.96mm +Resistor_THT:R_Axial_Power_L60.0mm_W14.0mm_P10.16mm_Vertical +Resistor_THT:R_Axial_Power_L60.0mm_W14.0mm_P66.04mm +Resistor_THT:R_Axial_Power_L60.0mm_W14.0mm_P71.12mm +Resistor_THT:R_Axial_Power_L75.0mm_W9.0mm_P81.28mm +Resistor_THT:R_Axial_Power_L75.0mm_W9.0mm_P86.36mm +Resistor_THT:R_Axial_Shunt_L22.2mm_W8.0mm_PS14.30mm_P25.40mm +Resistor_THT:R_Axial_Shunt_L22.2mm_W9.5mm_PS14.30mm_P25.40mm +Resistor_THT:R_Axial_Shunt_L35.3mm_W9.5mm_PS25.40mm_P38.10mm +Resistor_THT:R_Axial_Shunt_L47.6mm_W12.7mm_PS34.93mm_P50.80mm +Resistor_THT:R_Axial_Shunt_L47.6mm_W9.5mm_PS34.93mm_P50.80mm +Resistor_THT:R_Bare_Metal_Element_L12.4mm_W4.8mm_P11.40mm +Resistor_THT:R_Bare_Metal_Element_L16.3mm_W4.8mm_P15.30mm +Resistor_THT:R_Bare_Metal_Element_L21.3mm_W4.8mm_P20.30mm +Resistor_THT:R_Box_L13.0mm_W4.0mm_P9.00mm +Resistor_THT:R_Box_L14.0mm_W5.0mm_P9.00mm +Resistor_THT:R_Box_L26.0mm_W5.0mm_P20.00mm +Resistor_THT:R_Box_L8.4mm_W2.5mm_P5.08mm +Resistor_THT:R_Radial_Power_L11.0mm_W7.0mm_P5.00mm +Resistor_THT:R_Radial_Power_L12.0mm_W8.0mm_P5.00mm +Resistor_THT:R_Radial_Power_L13.0mm_W9.0mm_P5.00mm +Resistor_THT:R_Radial_Power_L16.1mm_W9.0mm_P7.37mm +Resistor_THT:R_Radial_Power_L7.0mm_W8.0mm_Px2.40mm_Py2.30mm +Resistor_THT:R_Radial_Power_L9.0mm_W10.0mm_Px2.70mm_Py2.30mm +RF:Skyworks_SKY13575_639LF +RF:Skyworks_SKY65404-31 +RF_Antenna:Abracon_APAES868R8060C16-T +RF_Antenna:Abracon_PRO-OB-440 +RF_Antenna:Abracon_PRO-OB-471 +RF_Antenna:Antenova_SR4G013_GPS +RF_Antenna:Astrocast_AST50127-00 +RF_Antenna:AVX_M620720 +RF_Antenna:Coilcraft_MA5532-AE_RFID +RF_Antenna:Johanson_2450AT18x100 +RF_Antenna:Johanson_2450AT43F0100 +RF_Antenna:Molex_47948-0001_2.4Ghz +RF_Antenna:NiceRF_SW868-TH13_868Mhz +RF_Antenna:Pulse_W3000 +RF_Antenna:Pulse_W3011 +RF_Antenna:Texas_SWRA117D_2.4GHz_Left +RF_Antenna:Texas_SWRA117D_2.4GHz_Right +RF_Antenna:Texas_SWRA416_868MHz_915MHz +RF_Converter:Anaren_0805_2012Metric-6 +RF_Converter:Balun_Johanson_0896BM15A0001 +RF_Converter:Balun_Johanson_0900FM15K0039 +RF_Converter:Balun_Johanson_0900PC15J0013 +RF_Converter:Balun_Johanson_1.6x0.8mm +RF_Converter:Balun_Johanson_5400BL15B050E +RF_Converter:RF_Attenuator_Susumu_PAT1220 +RF_GPS:Linx_RXM-GPS +RF_GPS:OriginGPS_ORG1510 +RF_GPS:Quectel_L70-R +RF_GPS:Quectel_L76 +RF_GPS:Quectel_L80-R +RF_GPS:Quectel_L96 +RF_GPS:Sierra_XA11X0 +RF_GPS:Sierra_XM11X0 +RF_GPS:SIM28ML +RF_GPS:ublox_LEA +RF_GPS:ublox_MAX +RF_GPS:ublox_NEO +RF_GPS:ublox_SAM-M8Q +RF_GPS:ublox_SAM-M8Q_HandSolder +RF_GPS:ublox_ZED +RF_GPS:ublox_ZOE_M8 +RF_GSM:Quectel_BC66 +RF_GSM:Quectel_BC95 +RF_GSM:Quectel_BG95 +RF_GSM:Quectel_BG96 +RF_GSM:Quectel_M95 +RF_GSM:SIMCom_SIM800C +RF_GSM:SIMCom_SIM900 +RF_GSM:Telit_SE150A4 +RF_GSM:Telit_xL865 +RF_GSM:ublox_LENA-R8_LGA-100 +RF_GSM:ublox_SARA_LGA-96 +RF_Mini-Circuits:Mini-Circuits_BK377 +RF_Mini-Circuits:Mini-Circuits_BK377_LandPatternPL-005 +RF_Mini-Circuits:Mini-Circuits_CD541_H2.08mm +RF_Mini-Circuits:Mini-Circuits_CD542_H2.84mm +RF_Mini-Circuits:Mini-Circuits_CD542_LandPatternPL-052 +RF_Mini-Circuits:Mini-Circuits_CD542_LandPatternPL-094 +RF_Mini-Circuits:Mini-Circuits_CD636_H4.11mm +RF_Mini-Circuits:Mini-Circuits_CD636_LandPatternPL-035 +RF_Mini-Circuits:Mini-Circuits_CD637_H5.23mm +RF_Mini-Circuits:Mini-Circuits_CK605 +RF_Mini-Circuits:Mini-Circuits_CK605_LandPatternPL-012 +RF_Mini-Circuits:Mini-Circuits_DB1627 +RF_Mini-Circuits:Mini-Circuits_GP1212 +RF_Mini-Circuits:Mini-Circuits_GP1212_LandPatternPL-176 +RF_Mini-Circuits:Mini-Circuits_GP731 +RF_Mini-Circuits:Mini-Circuits_GP731_LandPatternPL-176 +RF_Mini-Circuits:Mini-Circuits_HF1139 +RF_Mini-Circuits:Mini-Circuits_HF1139_LandPatternPL-230 +RF_Mini-Circuits:Mini-Circuits_HQ1157 +RF_Mini-Circuits:Mini-Circuits_HZ1198 +RF_Mini-Circuits:Mini-Circuits_HZ1198_LandPatternPL-247 +RF_Mini-Circuits:Mini-Circuits_MMM168 +RF_Mini-Circuits:Mini-Circuits_MMM168_LandPatternPL-225 +RF_Mini-Circuits:Mini-Circuits_QQQ130_ClockwisePinNumbering +RF_Mini-Circuits:Mini-Circuits_QQQ130_LandPattern_PL-236_ClockwisePinNumbering +RF_Mini-Circuits:Mini-Circuits_TT1224_ClockwisePinNumbering +RF_Mini-Circuits:Mini-Circuits_TT1224_LandPatternPL-258_ClockwisePinNumbering +RF_Mini-Circuits:Mini-Circuits_TTT167 +RF_Mini-Circuits:Mini-Circuits_TTT167_LandPatternPL-079 +RF_Mini-Circuits:Mini-Circuits_YY161 +RF_Mini-Circuits:Mini-Circuits_YY161_LandPatternPL-049 +RF_Module:Ai-Thinker-Ra-01-LoRa +RF_Module:Astrocast_AST50147-00 +RF_Module:Atmel_ATSAMR21G18-MR210UA_NoRFPads +RF_Module:BLE112-A +RF_Module:BM78SPPS5xC2 +RF_Module:CMWX1ZZABZ +RF_Module:CYBLE-21Pin-10x10mm +RF_Module:DecaWave_DWM1001 +RF_Module:Digi_XBee_SMT +RF_Module:DWM1000 +RF_Module:E18-MS1-PCB +RF_Module:E73-2G4M04S +RF_Module:ESP-01 +RF_Module:ESP-07 +RF_Module:ESP-12E +RF_Module:ESP-WROOM-02 +RF_Module:ESP32-C3-DevKitM-1 +RF_Module:ESP32-C3-WROOM-02 +RF_Module:ESP32-C3-WROOM-02U +RF_Module:ESP32-C6-MINI-1 +RF_Module:ESP32-S2-MINI-1 +RF_Module:ESP32-S2-MINI-1U +RF_Module:ESP32-S2-WROVER +RF_Module:ESP32-S3-WROOM-1 +RF_Module:ESP32-S3-WROOM-1U +RF_Module:ESP32-S3-WROOM-2 +RF_Module:ESP32-WROOM-32 +RF_Module:ESP32-WROOM-32D +RF_Module:ESP32-WROOM-32E +RF_Module:ESP32-WROOM-32U +RF_Module:ESP32-WROOM-32UE +RF_Module:Garmin_M8-35_9.8x14.0mm_Layout6x6_P1.5mm +RF_Module:Heltec_HT-CT62 +RF_Module:HOPERF_RFM69HW +RF_Module:HOPERF_RFM9XW_SMD +RF_Module:HOPERF_RFM9XW_THT +RF_Module:IQRF_TRx2DA_KON-SIM-01 +RF_Module:IQRF_TRx2D_KON-SIM-01 +RF_Module:Jadak_Thingmagic_M6e-Nano +RF_Module:Laird_BL652 +RF_Module:MCU_Seeed_ESP32C3 +RF_Module:Microchip_BM83 +RF_Module:Microchip_RN4871 +RF_Module:MOD-nRF8001 +RF_Module:Modtronix_inAir9 +RF_Module:MonoWireless_TWE-L-WX +RF_Module:NINA-B111 +RF_Module:nRF24L01_Breakout +RF_Module:Particle_P1 +RF_Module:RAK3172 +RF_Module:RAK4200 +RF_Module:RAK811 +RF_Module:Raytac_MDBT42Q +RF_Module:Raytac_MDBT50Q +RF_Module:RFDigital_RFD77101 +RF_Module:RN2483 +RF_Module:RN42 +RF_Module:RN42N +RF_Module:ST-SiP-LGA-86-11x7.3mm +RF_Module:ST_SPBTLE +RF_Module:Taiyo-Yuden_EYSGJNZWY +RF_Module:TD1205 +RF_Module:TD1208 +RF_Module:WEMOS_C3_mini +RF_Module:WEMOS_D1_mini_light +RF_Module:ZETA-433-SO_SMD +RF_Module:ZETA-433-SO_THT +RF_Shielding:Laird_Technologies_97-2002_25.40x25.40mm +RF_Shielding:Laird_Technologies_97-2003_12.70x13.37mm +RF_Shielding:Laird_Technologies_BMI-S-101_13.66x12.70mm +RF_Shielding:Laird_Technologies_BMI-S-102_16.50x16.50mm +RF_Shielding:Laird_Technologies_BMI-S-103_26.21x26.21mm +RF_Shielding:Laird_Technologies_BMI-S-104_32.00x32.00mm +RF_Shielding:Laird_Technologies_BMI-S-105_38.10x25.40mm +RF_Shielding:Laird_Technologies_BMI-S-106_36.83x33.68mm +RF_Shielding:Laird_Technologies_BMI-S-107_44.37x44.37mm +RF_Shielding:Laird_Technologies_BMI-S-201-F_13.66x12.70mm +RF_Shielding:Laird_Technologies_BMI-S-202-F_16.50x16.50mm +RF_Shielding:Laird_Technologies_BMI-S-203-F_26.21x26.21mm +RF_Shielding:Laird_Technologies_BMI-S-204-F_32.00x32.00mm +RF_Shielding:Laird_Technologies_BMI-S-205-F_38.10x25.40mm +RF_Shielding:Laird_Technologies_BMI-S-206-F_36.83x33.68mm +RF_Shielding:Laird_Technologies_BMI-S-207-F_44.37x44.37mm +RF_Shielding:Laird_Technologies_BMI-S-208-F_39.60x39.60mm +RF_Shielding:Laird_Technologies_BMI-S-209-F_29.36x18.50mm +RF_Shielding:Laird_Technologies_BMI-S-210-F_44.00x30.50mm +RF_Shielding:Laird_Technologies_BMI-S-230-F_50.8x38.1mm +RF_Shielding:Wuerth_36103205_20x20mm +RF_Shielding:Wuerth_36103255_25x25mm +RF_Shielding:Wuerth_36103305_30x30mm +RF_Shielding:Wuerth_36103505_50x50mm +RF_Shielding:Wuerth_36103605_60x60mm +RF_Shielding:Wuerth_36503205_20x20mm +RF_Shielding:Wuerth_36503255_25x25mm +RF_Shielding:Wuerth_36503305_30x30mm +RF_Shielding:Wuerth_36503505_50x50mm +RF_Shielding:Wuerth_36503605_60x60mm +RF_WiFi:USR-C322 +Rotary_Encoder:RotaryEncoder_Alps_EC11E-Switch_Vertical_H20mm +Rotary_Encoder:RotaryEncoder_Alps_EC11E-Switch_Vertical_H20mm_CircularMountingHoles +Rotary_Encoder:RotaryEncoder_Alps_EC11E-Switch_Vertical_H20mm_MountingHoles +Rotary_Encoder:RotaryEncoder_Alps_EC11E_Vertical_H20mm +Rotary_Encoder:RotaryEncoder_Alps_EC11E_Vertical_H20mm_CircularMountingHoles +Rotary_Encoder:RotaryEncoder_Alps_EC12E-Switch_Vertical_H20mm +Rotary_Encoder:RotaryEncoder_Alps_EC12E-Switch_Vertical_H20mm_CircularMountingHoles +Rotary_Encoder:RotaryEncoder_Alps_EC12E_Vertical_H20mm +Rotary_Encoder:RotaryEncoder_Alps_EC12E_Vertical_H20mm_CircularMountingHoles +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEC09-2xxxF-Nxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEC09-2xxxF-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEC12R-2x17F-Nxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEC12R-2x17F-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEL12D-2x16F-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEL12D-2x18F-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEL12D-2x21F-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEL12D-2x25S-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEL12D-2x26F-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Horizontal_PEL12D-2x31F-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Vertical_PEC12R-3x17F-Nxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Vertical_PEC12R-3x17F-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Vertical_PEL12D-4x25S-Sxxxx +Rotary_Encoder:RotaryEncoder_Bourns_Vertical_PEL12D-4xxxF-Sxxxx +Sensor:Aosong_DHT11_5.5x12.0_P2.54mm +Sensor:ASAIR_AM2302_P2.54mm_Lead2.75mm_TabDown +Sensor:ASAIR_AM2302_P2.54mm_Vertical +Sensor:Avago_APDS-9960 +Sensor:LuminOX_LOX-O2 +Sensor:MQ-6 +Sensor:Rohm_RPR-0521RS +Sensor:Senseair_S8_Down +Sensor:Senseair_S8_Up +Sensor:Sensirion_SCD4x-1EP_10.1x10.1mm_P1.25mm_EP4.8x4.8mm +Sensor:Sensortech_MiCS_5x7mm_P1.25mm +Sensor:SHT1x +Sensor:SPEC_110-xxx_SMD-10Pin_20x20mm_P4.0mm +Sensor:TGS-5141 +Sensor:Winson_GM-402B_5x5mm_P1.27mm +Sensor_Audio:CUI_CMC-4013-SMT +Sensor_Audio:Infineon_PG-LLGA-5-1 +Sensor_Audio:Infineon_PG-LLGA-5-2 +Sensor_Audio:InvenSense_ICS-43434-6_3.5x2.65mm +Sensor_Audio:Knowles_LGA-5_3.5x2.65mm +Sensor_Audio:Knowles_LGA-6_4.72x3.76mm +Sensor_Audio:Knowles_SPH0645LM4H-6_3.5x2.65mm +Sensor_Audio:POM-2244P-C3310-2-R +Sensor_Audio:ST_HLGA-6_3.76x4.72mm_P1.65mm +Sensor_Current:AKM_CQ_7 +Sensor_Current:AKM_CQ_7S +Sensor_Current:AKM_CQ_VSOP-24_5.6x7.9mm_P0.65mm +Sensor_Current:AKM_CZ_SSOP-10_6.5x8.1mm_P0.95mm +Sensor_Current:Allegro_CB_PFF +Sensor_Current:Allegro_CB_PSF +Sensor_Current:Allegro_CB_PSS +Sensor_Current:Allegro_PSOF-7_4.8x6.4mm_P1.60mm +Sensor_Current:Allegro_QFN-12-10-1EP_3x3mm_P0.5mm +Sensor_Current:Allegro_QSOP-24_3.9x8.7mm_P0.635mm +Sensor_Current:Allegro_SIP-3 +Sensor_Current:Allegro_SIP-4 +Sensor_Current:Diodes_SIP-3_4.1x1.5mm_P1.27mm +Sensor_Current:Diodes_SIP-3_4.1x1.5mm_P2.65mm +Sensor_Current:Honeywell_CSLW +Sensor_Current:LEM_CKSR +Sensor_Current:LEM_HO40-NP +Sensor_Current:LEM_HO8-NP +Sensor_Current:LEM_HO8-NSM +Sensor_Current:LEM_HTFS +Sensor_Current:LEM_HX02-P +Sensor_Current:LEM_HX03-P-SP2 +Sensor_Current:LEM_HX04-P +Sensor_Current:LEM_HX05-NP +Sensor_Current:LEM_HX05-P-SP2 +Sensor_Current:LEM_HX06-P +Sensor_Current:LEM_HX10-NP +Sensor_Current:LEM_HX10-P-SP2 +Sensor_Current:LEM_HX15-NP +Sensor_Current:LEM_HX15-P-SP2 +Sensor_Current:LEM_HX20-P-SP2 +Sensor_Current:LEM_HX25-P-SP2 +Sensor_Current:LEM_HX50-P-SP2 +Sensor_Current:LEM_LA25-NP +Sensor_Current:LEM_LA25-P +Sensor_Current:LEM_LTSR-NP +Sensor_Distance:AMS_OLGA12 +Sensor_Distance:ST_VL53L1x +Sensor_Humidity:Sensirion_DFN-4-1EP_2x2mm_P1mm_EP0.7x1.6mm +Sensor_Humidity:Sensirion_DFN-4_1.5x1.5mm_P0.8mm_SHT4x_NoCentralPad +Sensor_Humidity:Sensirion_DFN-8-1EP_2.5x2.5mm_P0.5mm_EP1.1x1.7mm +Sensor_Motion:Analog_LGA-16_3.25x3mm_P0.5mm_LayoutBorder3x5y +Sensor_Motion:InvenSense_QFN-24_3x3mm_P0.4mm +Sensor_Motion:InvenSense_QFN-24_3x3mm_P0.4mm_NoMask +Sensor_Motion:InvenSense_QFN-24_4x4mm_P0.5mm +Sensor_Motion:InvenSense_QFN-24_4x4mm_P0.5mm_NoMask +Sensor_Pressure:CFSensor_XGZP6859D_7x7mm +Sensor_Pressure:CFSensor_XGZP6897x +Sensor_Pressure:CFSensor_XGZP6899x +Sensor_Pressure:Freescale_98ARH99066A +Sensor_Pressure:Freescale_98ARH99089A +Sensor_Pressure:Honeywell_40PCxxxG1A +Sensor_Pressure:TE_MS5525DSO-DBxxxyS +Sensor_Pressure:TE_MS5837-xxBA +Sensor_Voltage:LEM_LV25-P +Socket:3M_Textool_240-1288-00-0602J_2x20_P2.54mm +Socket:DIP_Socket-14_W4.3_W5.08_W7.62_W10.16_W10.9_3M_214-3339-00-0602J +Socket:DIP_Socket-16_W4.3_W5.08_W7.62_W10.16_W10.9_3M_216-3340-00-0602J +Socket:DIP_Socket-18_W4.3_W5.08_W7.62_W10.16_W10.9_3M_218-3341-00-0602J +Socket:DIP_Socket-20_W4.3_W5.08_W7.62_W10.16_W10.9_3M_220-3342-00-0602J +Socket:DIP_Socket-22_W6.9_W7.62_W10.16_W12.7_W13.5_3M_222-3343-00-0602J +Socket:DIP_Socket-24_W11.9_W12.7_W15.24_W17.78_W18.5_3M_224-1275-00-0602J +Socket:DIP_Socket-24_W4.3_W5.08_W7.62_W10.16_W10.9_3M_224-5248-00-0602J +Socket:DIP_Socket-28_W11.9_W12.7_W15.24_W17.78_W18.5_3M_228-1277-00-0602J +Socket:DIP_Socket-28_W6.9_W7.62_W10.16_W12.7_W13.5_3M_228-4817-00-0602J +Socket:DIP_Socket-32_W11.9_W12.7_W15.24_W17.78_W18.5_3M_232-1285-00-0602J +Socket:DIP_Socket-40_W11.9_W12.7_W15.24_W17.78_W18.5_3M_240-1280-00-0602J +Socket:DIP_Socket-40_W22.1_W22.86_W25.4_W27.94_W28.7_3M_240-3639-00-0602J +Socket:DIP_Socket-42_W11.9_W12.7_W15.24_W17.78_W18.5_3M_242-1281-00-0602J +Socket:Wells_648-0482211SA01 +Symbol:CE-Logo_11.2x8mm_SilkScreen +Symbol:CE-Logo_16.8x12mm_SilkScreen +Symbol:CE-Logo_28x20mm_SilkScreen +Symbol:CE-Logo_42x30mm_SilkScreen +Symbol:CE-Logo_56.1x40mm_SilkScreen +Symbol:CE-Logo_8.5x6mm_SilkScreen +Symbol:EasterEgg_EWG1308-2013_ClassA +Symbol:ESD-Logo_13.2x12mm_SilkScreen +Symbol:ESD-Logo_22x20mm_SilkScreen +Symbol:ESD-Logo_33x30mm_SilkScreen +Symbol:ESD-Logo_44.1x40mm_SilkScreen +Symbol:ESD-Logo_6.6x6mm_SilkScreen +Symbol:ESD-Logo_8.9x8mm_SilkScreen +Symbol:FCC-Logo_14.6x12mm_SilkScreen +Symbol:FCC-Logo_24.2x20mm_SilkScreen +Symbol:FCC-Logo_36.3x30mm_SilkScreen +Symbol:FCC-Logo_48.3x40mm_SilkScreen +Symbol:FCC-Logo_7.3x6mm_SilkScreen +Symbol:FCC-Logo_9.6x8mm_SilkScreen +Symbol:KiCad-Logo2_12mm_Copper +Symbol:KiCad-Logo2_12mm_SilkScreen +Symbol:KiCad-Logo2_20mm_Copper +Symbol:KiCad-Logo2_20mm_SilkScreen +Symbol:KiCad-Logo2_30mm_Copper +Symbol:KiCad-Logo2_30mm_SilkScreen +Symbol:KiCad-Logo2_40mm_Copper +Symbol:KiCad-Logo2_40mm_SilkScreen +Symbol:KiCad-Logo2_5mm_Copper +Symbol:KiCad-Logo2_5mm_SilkScreen +Symbol:KiCad-Logo2_6mm_Copper +Symbol:KiCad-Logo2_6mm_SilkScreen +Symbol:KiCad-Logo2_8mm_Copper +Symbol:KiCad-Logo2_8mm_SilkScreen +Symbol:KiCad-Logo_12mm_Copper +Symbol:KiCad-Logo_12mm_SilkScreen +Symbol:KiCad-Logo_20mm_Copper +Symbol:KiCad-Logo_20mm_SilkScreen +Symbol:KiCad-Logo_30mm_Copper +Symbol:KiCad-Logo_30mm_SilkScreen +Symbol:KiCad-Logo_40mm_Copper +Symbol:KiCad-Logo_40mm_SilkScreen +Symbol:KiCad-Logo_5mm_Copper +Symbol:KiCad-Logo_5mm_SilkScreen +Symbol:KiCad-Logo_6mm_Copper +Symbol:KiCad-Logo_6mm_SilkScreen +Symbol:KiCad-Logo_8mm_Copper +Symbol:KiCad-Logo_8mm_SilkScreen +Symbol:OSHW-Logo2_14.6x12mm_Copper +Symbol:OSHW-Logo2_14.6x12mm_SilkScreen +Symbol:OSHW-Logo2_24.3x20mm_Copper +Symbol:OSHW-Logo2_24.3x20mm_SilkScreen +Symbol:OSHW-Logo2_36.5x30mm_Copper +Symbol:OSHW-Logo2_36.5x30mm_SilkScreen +Symbol:OSHW-Logo2_48.7x40mm_Copper +Symbol:OSHW-Logo2_48.7x40mm_SilkScreen +Symbol:OSHW-Logo2_7.3x6mm_Copper +Symbol:OSHW-Logo2_7.3x6mm_SilkScreen +Symbol:OSHW-Logo2_9.8x8mm_Copper +Symbol:OSHW-Logo2_9.8x8mm_SilkScreen +Symbol:OSHW-Logo_11.4x12mm_Copper +Symbol:OSHW-Logo_11.4x12mm_SilkScreen +Symbol:OSHW-Logo_19x20mm_Copper +Symbol:OSHW-Logo_19x20mm_SilkScreen +Symbol:OSHW-Logo_28.5x30mm_Copper +Symbol:OSHW-Logo_28.5x30mm_SilkScreen +Symbol:OSHW-Logo_38.1x40mm_Copper +Symbol:OSHW-Logo_38.1x40mm_SilkScreen +Symbol:OSHW-Logo_5.7x6mm_Copper +Symbol:OSHW-Logo_5.7x6mm_SilkScreen +Symbol:OSHW-Logo_7.5x8mm_Copper +Symbol:OSHW-Logo_7.5x8mm_SilkScreen +Symbol:OSHW-Symbol_13.4x12mm_Copper +Symbol:OSHW-Symbol_13.4x12mm_SilkScreen +Symbol:OSHW-Symbol_22.3x20mm_Copper +Symbol:OSHW-Symbol_22.3x20mm_SilkScreen +Symbol:OSHW-Symbol_33.5x30mm_Copper +Symbol:OSHW-Symbol_33.5x30mm_SilkScreen +Symbol:OSHW-Symbol_44.5x40mm_Copper +Symbol:OSHW-Symbol_44.5x40mm_SilkScreen +Symbol:OSHW-Symbol_6.7x6mm_Copper +Symbol:OSHW-Symbol_6.7x6mm_SilkScreen +Symbol:OSHW-Symbol_8.9x8mm_Copper +Symbol:OSHW-Symbol_8.9x8mm_SilkScreen +Symbol:Polarity_Center_Negative_12mm_SilkScreen +Symbol:Polarity_Center_Negative_20mm_SilkScreen +Symbol:Polarity_Center_Negative_30mm_SilkScreen +Symbol:Polarity_Center_Negative_40mm_SilkScreen +Symbol:Polarity_Center_Negative_6mm_SilkScreen +Symbol:Polarity_Center_Negative_8mm_SilkScreen +Symbol:Polarity_Center_Positive_12mm_SilkScreen +Symbol:Polarity_Center_Positive_20mm_SilkScreen +Symbol:Polarity_Center_Positive_30mm_SilkScreen +Symbol:Polarity_Center_Positive_40mm_SilkScreen +Symbol:Polarity_Center_Positive_6mm_SilkScreen +Symbol:Polarity_Center_Positive_8mm_SilkScreen +Symbol:RoHS-Logo_12mm_SilkScreen +Symbol:RoHS-Logo_20mm_SilkScreen +Symbol:RoHS-Logo_30mm_SilkScreen +Symbol:RoHS-Logo_40mm_SilkScreen +Symbol:RoHS-Logo_6mm_SilkScreen +Symbol:RoHS-Logo_8mm_SilkScreen +Symbol:Smolhaj_Scale_0.1 +Symbol:Symbol_Attention_Triangle_17x15mm_Copper +Symbol:Symbol_Attention_Triangle_8x7mm_Copper +Symbol:Symbol_Barrel_Polarity +Symbol:Symbol_CC-Attribution_CopperTop_Big +Symbol:Symbol_CC-Attribution_CopperTop_Small +Symbol:Symbol_CC-Noncommercial_CopperTop_Big +Symbol:Symbol_CC-Noncommercial_CopperTop_Small +Symbol:Symbol_CC-PublicDomain_CopperTop_Big +Symbol:Symbol_CC-PublicDomain_CopperTop_Small +Symbol:Symbol_CC-PublicDomain_SilkScreenTop_Big +Symbol:Symbol_CC-ShareAlike_CopperTop_Big +Symbol:Symbol_CC-ShareAlike_CopperTop_Small +Symbol:Symbol_CreativeCommonsPublicDomain_CopperTop_Small +Symbol:Symbol_CreativeCommonsPublicDomain_SilkScreenTop_Small +Symbol:Symbol_CreativeCommons_CopperTop_Type1_Big +Symbol:Symbol_CreativeCommons_CopperTop_Type2_Big +Symbol:Symbol_CreativeCommons_CopperTop_Type2_Small +Symbol:Symbol_CreativeCommons_SilkScreenTop_Type2_Big +Symbol:Symbol_Danger_18x16mm_Copper +Symbol:Symbol_Danger_8x8mm_Copper +Symbol:Symbol_ESD-Logo-Text_CopperTop +Symbol:Symbol_ESD-Logo_CopperTop +Symbol:Symbol_GNU-GPL_CopperTop_Big +Symbol:Symbol_GNU-GPL_CopperTop_Small +Symbol:Symbol_GNU-Logo_CopperTop +Symbol:Symbol_GNU-Logo_SilkscreenTop +Symbol:Symbol_HighVoltage_NoTriangle_2x5mm_Copper +Symbol:Symbol_HighVoltage_NoTriangle_6x15mm_Copper +Symbol:Symbol_HighVoltage_Triangle_17x15mm_Copper +Symbol:Symbol_HighVoltage_Triangle_6x6mm_Copper +Symbol:Symbol_HighVoltage_Triangle_8x7mm_Copper +Symbol:UKCA-Logo_12x12mm_SilkScreen +Symbol:UKCA-Logo_20x20mm_SilkScreen +Symbol:UKCA-Logo_30x30mm_SilkScreen +Symbol:UKCA-Logo_40x40mm_SilkScreen +Symbol:UKCA-Logo_6x6mm_SilkScreen +Symbol:UKCA-Logo_8x8mm_SilkScreen +Symbol:WEEE-Logo_14x20mm_SilkScreen +Symbol:WEEE-Logo_21x30mm_SilkScreen +Symbol:WEEE-Logo_28.1x40mm_SilkScreen +Symbol:WEEE-Logo_4.2x6mm_SilkScreen +Symbol:WEEE-Logo_5.6x8mm_SilkScreen +Symbol:WEEE-Logo_8.4x12mm_SilkScreen +TerminalBlock:TerminalBlock_Altech_AK300-2_P5.00mm +TerminalBlock:TerminalBlock_Altech_AK300-3_P5.00mm +TerminalBlock:TerminalBlock_Altech_AK300-4_P5.00mm +TerminalBlock:TerminalBlock_bornier-2_P5.08mm +TerminalBlock:TerminalBlock_bornier-3_P5.08mm +TerminalBlock:TerminalBlock_bornier-4_P5.08mm +TerminalBlock:TerminalBlock_bornier-5_P5.08mm +TerminalBlock:TerminalBlock_bornier-6_P5.08mm +TerminalBlock:TerminalBlock_bornier-8_P5.08mm +TerminalBlock:TerminalBlock_Degson_DG246-3.81-03P +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-02P_1x02_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-03P_1x03_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-04P_1x04_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-05P_1x05_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-06P_1x06_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-07P_1x07_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-08P_1x08_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-09P_1x09_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-10P_1x10_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-11P_1x11_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-12P_1x12_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-13P_1x13_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-14P_1x14_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-15P_1x15_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-16P_1x16_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-17P_1x17_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-18P_1x18_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-19P_1x19_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-20P_1x20_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-21P_1x21_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-22P_1x22_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-23P_1x23_P5.00mm +TerminalBlock:TerminalBlock_MaiXu_MX126-5.0-24P_1x24_P5.00mm +TerminalBlock:TerminalBlock_Wuerth_691311400102_P7.62mm +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-10P_1x10_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-11P_1x11_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-12P_1x12_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-13P_1x13_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-14P_1x14_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-15P_1x15_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-16P_1x16_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-17P_1x17_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-18P_1x18_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-19P_1x19_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-20P_1x20_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-21P_1x21_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-22P_1x22_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-23P_1x23_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-2P_1x02_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-4P_1x04_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-5P_1x05_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-6P_1x06_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-7P_1x07_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-8P_1x08_P2.54mm_Horizontal +TerminalBlock:TerminalBlock_Xinya_XY308-2.54-9P_1x09_P2.54mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x02_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x02_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x03_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x03_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x04_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x04_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x05_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x05_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x06_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x06_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x07_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x07_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x08_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x08_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x09_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x09_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x10_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x10_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x11_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x11_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x12_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x12_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x13_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x13_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x14_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x14_P3.50mm_Vertical +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x15_P3.50mm_Horizontal +TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x15_P3.50mm_Vertical +TerminalBlock_Altech:Altech_AK100_1x02_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x03_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x04_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x05_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x06_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x07_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x08_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x09_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x10_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x11_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x12_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x13_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x14_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x15_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x16_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x17_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x18_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x19_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x20_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x21_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x22_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x23_P5.00mm +TerminalBlock_Altech:Altech_AK100_1x24_P5.00mm +TerminalBlock_Altech:Altech_AK300_1x02_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x03_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x04_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x05_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x06_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x07_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x08_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x09_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x10_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x11_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x12_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x13_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x14_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x15_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x16_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x17_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x18_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x19_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x20_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x21_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x22_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x23_P5.00mm_45-Degree +TerminalBlock_Altech:Altech_AK300_1x24_P5.00mm_45-Degree +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-02_1x02_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-03_1x03_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-04_1x04_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-05_1x05_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-06_1x06_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-07_1x07_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-08_1x08_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-09_1x09_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-10_1x10_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-11_1x11_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-12_1x12_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-13_1x13_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-14_1x14_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-15_1x15_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-16_1x16_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-17_1x17_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-18_1x18_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-19_1x19_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-20_1x20_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-21_1x21_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-22_1x22_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-23_1x23_P5.08mm_Horizontal +TerminalBlock_CUI:TerminalBlock_CUI_TB007-508-24_1x24_P5.08mm_Horizontal +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-02_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-03_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-04_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-05_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-06_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-07_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-08_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-09_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-10_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-11_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-12_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-13_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-14_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-15_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-16_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-17_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-18_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-19_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-20_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-21_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-22_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-23_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-24_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-25_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-26_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-27_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-28_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-29_P10.00mm +TerminalBlock_Dinkle:TerminalBlock_Dinkle_DT-55-B01X-30_P10.00mm +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_360271_1x01_Horizontal_ScrewM3.0_Boxed +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_360272_1x01_Horizontal_ScrewM2.6 +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_360273_1x01_Horizontal_ScrewM2.6_WireProtection +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_360291_1x01_Horizontal_ScrewM3.0_Boxed +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_360322_1x01_Horizontal_ScrewM3.0_WireProtection +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_360381_1x01_Horizontal_ScrewM3.0 +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_360410_1x01_Horizontal_ScrewM3.0 +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_360425_1x01_Horizontal_ScrewM4.0_Boxed +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type011_RT05502HBWC_1x02_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type011_RT05503HBWC_1x03_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type011_RT05504HBWC_1x04_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type011_RT05505HBWC_1x05_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type011_RT05506HBWC_1x06_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type055_RT01502HDWU_1x02_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type055_RT01503HDWU_1x03_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type055_RT01504HDWU_1x04_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type059_RT06302HBWC_1x02_P3.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type059_RT06303HBWC_1x03_P3.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type059_RT06304HBWC_1x04_P3.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type059_RT06305HBWC_1x05_P3.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type059_RT06306HBWC_1x06_P3.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type067_RT01902HDWC_1x02_P10.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type067_RT01903HDWC_1x03_P10.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type067_RT01904HDWC_1x04_P10.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type067_RT01905HDWC_1x05_P10.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type073_RT02602HBLU_1x02_P5.08mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type073_RT02603HBLU_1x03_P5.08mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type086_RT03402HBLC_1x02_P3.81mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type086_RT03403HBLC_1x03_P3.81mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type086_RT03404HBLC_1x04_P3.81mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type086_RT03405HBLC_1x05_P3.81mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type086_RT03406HBLC_1x06_P3.81mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type094_RT03502HBLU_1x02_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type094_RT03503HBLU_1x03_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type094_RT03504HBLU_1x04_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type094_RT03505HBLU_1x05_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type094_RT03506HBLU_1x06_P5.00mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type101_RT01602HBWC_1x02_P5.08mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type101_RT01603HBWC_1x03_P5.08mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type101_RT01604HBWC_1x04_P5.08mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type101_RT01605HBWC_1x05_P5.08mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type101_RT01606HBWC_1x06_P5.08mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type171_RT13702HBWC_1x02_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type171_RT13703HBWC_1x03_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type171_RT13704HBWC_1x04_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type171_RT13705HBWC_1x05_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type171_RT13706HBWC_1x06_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type175_RT02702HBLC_1x02_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type175_RT02703HBLC_1x03_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type175_RT02704HBLC_1x04_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type175_RT02705HBLC_1x05_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type175_RT02706HBLC_1x06_P7.50mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type205_RT04502UBLC_1x02_P5.00mm_45Degree +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type205_RT04503UBLC_1x03_P5.00mm_45Degree +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type205_RT04504UBLC_1x04_P5.00mm_45Degree +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type205_RT04505UBLC_1x05_P5.00mm_45Degree +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type205_RT04506UBLC_1x06_P5.00mm_45Degree +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type701_RT11L02HGLU_1x02_P6.35mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type701_RT11L03HGLU_1x03_P6.35mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type703_RT10N02HGLU_1x02_P9.52mm_Horizontal +TerminalBlock_MetzConnect:TerminalBlock_MetzConnect_Type703_RT10N03HGLU_1x03_P9.52mm_Horizontal +TerminalBlock_Philmore:TerminalBlock_Philmore_TB132_1x02_P5.00mm_Horizontal +TerminalBlock_Philmore:TerminalBlock_Philmore_TB133_1x03_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-10-5.08_1x10_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-10_1x10_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-11-5.08_1x11_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-11_1x11_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-12-5.08_1x12_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-12_1x12_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-13-5.08_1x13_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-13_1x13_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-14-5.08_1x14_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-14_1x14_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-15-5.08_1x15_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-15_1x15_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-16-5.08_1x16_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-16_1x16_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2_1x02_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-3-5.08_1x03_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-3_1x03_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-4-5.08_1x04_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-4_1x04_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-5-5.08_1x05_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-5_1x05_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-6-5.08_1x06_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-6_1x06_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-7-5.08_1x07_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-7_1x07_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-8-5.08_1x08_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-8_1x08_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-9-5.08_1x09_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-9_1x09_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-10-5.08_1x10_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-11-5.08_1x11_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-12-5.08_1x12_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-13-5.08_1x13_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-14-5.08_1x14_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-15-5.08_1x15_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-16-5.08_1x16_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-2-5.08_1x02_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-3-5.08_1x03_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-4-5.08_1x04_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-5-5.08_1x05_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-6-5.08_1x06_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-7-5.08_1x07_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-8-5.08_1x08_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-3-9-5.08_1x09_P5.08mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-10-2.54_1x10_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-11-2.54_1x11_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-12-2.54_1x12_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-2-2.54_1x02_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-3-2.54_1x03_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-4-2.54_1x04_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-5-2.54_1x05_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-6-2.54_1x06_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-7-2.54_1x07_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-8-2.54_1x08_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-9-2.54_1x09_P2.54mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-10-3.5-H_1x10_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-10-5.0-H_1x10_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-11-3.5-H_1x11_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-11-5.0-H_1x11_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-12-3.5-H_1x12_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-12-5.0-H_1x12_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-13-3.5-H_1x13_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-13-5.0-H_1x13_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-14-3.5-H_1x14_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-14-5.0-H_1x14_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-15-3.5-H_1x15_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-15-5.0-H_1x15_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-16-3.5-H_1x16_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-16-5.0-H_1x16_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-2-3.5-H_1x02_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-2-5.0-H_1x02_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-3-3.5-H_1x03_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-3-5.0-H_1x03_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-4-3.5-H_1x04_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-4-5.0-H_1x04_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-5-3.5-H_1x05_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-5-5.0-H_1x05_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-6-3.5-H_1x06_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-6-5.0-H_1x06_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-7-3.5-H_1x07_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-7-5.0-H_1x07_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-8-3.5-H_1x08_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-8-5.0-H_1x08_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-9-3.5-H_1x09_P3.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-9-5.0-H_1x09_P5.00mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-2-2,5-V-SMD_1x02-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-2-2.5-H-THR_1x02_P2.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-2-2.5-V-THR_1x02_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-2-HV-2.5-SMD_1x02-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-3-2,5-V-SMD_1x03-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-3-2.5-H-THR_1x03_P2.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-3-2.5-V-THR_1x03_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-3-HV-2.5-SMD_1x03-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-4-2,5-V-SMD_1x04-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-4-2.5-H-THR_1x04_P2.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-4-2.5-V-THR_1x04_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-4-HV-2.5-SMD_1x04-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-5-2,5-V-SMD_1x05-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-5-2.5-H-THR_1x05_P2.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-5-2.5-V-THR_1x05_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-5-HV-2.5-SMD_1x05-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-6-2,5-V-SMD_1x06-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-6-2.5-H-THR_1x06_P2.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-6-2.5-V-THR_1x06_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-6-HV-2.5-SMD_1x06-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-7-2,5-V-SMD_1x07-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-7-2.5-H-THR_1x07_P2.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-7-2.5-V-THR_1x07_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-7-HV-2.5-SMD_1x07-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-8-2,5-V-SMD_1x08-1MP_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-8-2.5-H-THR_1x08_P2.50mm_Horizontal +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-8-2.5-V-THR_1x08_P2.50mm_Vertical +TerminalBlock_Phoenix:TerminalBlock_Phoenix_PTSM-0,5-8-HV-2.5-SMD_1x08-1MP_P2.50mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00001_1x02_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00002_1x03_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00003_1x04_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00004_1x05_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00005_1x06_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00006_1x07_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00007_1x08_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00008_1x09_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00009_1x10_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00010_1x11_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00011_1x12_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00012_1x02_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00013_1x03_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00014_1x04_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00015_1x05_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00016_1x06_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00017_1x07_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00018_1x08_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00019_1x09_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00020_1x10_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00021_1x11_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00022_1x12_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00023_1x02_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00024_1x03_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00025_1x04_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00026_1x05_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00027_1x06_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00028_1x07_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00029_1x08_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00030_1x09_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00031_1x10_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00032_1x11_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00033_1x12_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00045_1x02_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00046_1x03_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00047_1x04_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00048_1x05_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00049_1x06_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00050_1x07_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00051_1x08_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00052_1x09_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00053_1x10_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00054_1x11_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00055_1x12_P5.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00056_1x02_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00057_1x03_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00058_1x04_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00059_1x05_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00060_1x06_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00061_1x07_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00062_1x08_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00063_1x09_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00064_1x10_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00065_1x11_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00066_1x12_P5.00mm_45Degree +TerminalBlock_RND:TerminalBlock_RND_205-00067_1x02_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00068_1x03_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00069_1x04_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00070_1x05_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00071_1x06_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00072_1x07_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00073_1x08_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00074_1x09_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00075_1x10_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00076_1x11_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00077_1x12_P7.50mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00078_1x02_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00079_1x03_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00080_1x04_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00081_1x05_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00082_1x06_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00083_1x07_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00084_1x08_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00085_1x09_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00086_1x10_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00087_1x11_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00088_1x12_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00232_1x02_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00233_1x03_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00234_1x04_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00235_1x05_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00236_1x06_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00237_1x07_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00238_1x08_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00239_1x09_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00240_1x10_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00241_1x02_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00241_1x11_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00242_1x03_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00242_1x12_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00243_1x04_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00244_1x05_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00245_1x06_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00246_1x07_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00247_1x08_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00248_1x09_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00249_1x10_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00250_1x11_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00251_1x12_P10.16mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00276_1x02_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00277_1x03_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00278_1x04_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00279_1x05_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00280_1x06_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00281_1x07_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00282_1x08_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00283_1x09_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00284_1x10_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00285_1x11_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00286_1x12_P5.00mm_Vertical +TerminalBlock_RND:TerminalBlock_RND_205-00287_1x02_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00288_1x03_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00289_1x04_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00290_1x05_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00291_1x06_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00292_1x07_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00293_1x08_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00294_1x09_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00295_1x10_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00296_1x11_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00297_1x12_P5.08mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00298_1x02_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00299_1x03_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00300_1x04_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00301_1x05_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00302_1x06_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00303_1x07_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00304_1x08_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00305_1x09_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00306_1x10_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00307_1x11_P10.00mm_Horizontal +TerminalBlock_RND:TerminalBlock_RND_205-00308_1x12_P10.00mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_1-282834-0_1x10_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_1-282834-1_1x11_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_1-282834-2_1x12_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_282834-2_1x02_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_282834-3_1x03_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_282834-4_1x04_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_282834-5_1x05_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_282834-6_1x06_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_282834-7_1x07_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_282834-8_1x08_P2.54mm_Horizontal +TerminalBlock_TE-Connectivity:TerminalBlock_TE_282834-9_1x09_P2.54mm_Horizontal +TerminalBlock_WAGO:TerminalBlock_WAGO_233-502_2x02_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-503_2x03_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-504_2x04_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-505_2x05_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-506_2x06_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-507_2x07_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-508_2x08_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-509_2x09_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-510_2x10_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_233-512_2x12_P2.54mm +TerminalBlock_WAGO:TerminalBlock_WAGO_236-101_1x01_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-102_1x02_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-103_1x03_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-104_1x04_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-105_1x05_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-106_1x06_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-107_1x07_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-108_1x08_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-109_1x09_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-110_1x10_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-111_1x11_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-112_1x12_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-113_1x13_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-114_1x14_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-115_1x15_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-116_1x16_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-124_1x24_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-136_1x36_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-148_1x48_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-201_1x01_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-202_1x02_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-203_1x03_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-204_1x04_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-205_1x05_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-206_1x06_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-207_1x07_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-208_1x08_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-209_1x09_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-210_1x10_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-211_1x11_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-212_1x12_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-213_1x13_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-214_1x14_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-215_1x15_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-216_1x16_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-224_1x24_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-301_1x01_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-302_1x02_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-303_1x03_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-304_1x04_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-305_1x05_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-306_1x06_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-307_1x07_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-308_1x08_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-309_1x09_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-310_1x10_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-311_1x11_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-312_1x12_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-313_1x13_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-314_1x14_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-315_1x15_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-316_1x16_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-324_1x24_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-401_1x01_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-402_1x02_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-403_1x03_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-404_1x04_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-405_1x05_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-406_1x06_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-407_1x07_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-408_1x08_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-409_1x09_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-410_1x10_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-411_1x11_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-412_1x12_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-413_1x13_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-414_1x14_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-415_1x15_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-416_1x16_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-424_1x24_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-436_1x36_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-448_1x48_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-501_1x01_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-502_1x02_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-503_1x03_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-504_1x04_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-505_1x05_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-506_1x06_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-507_1x07_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-508_1x08_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-509_1x09_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-510_1x10_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-511_1x11_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-512_1x12_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-513_1x13_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-514_1x14_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-515_1x15_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-516_1x16_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-524_1x24_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-601_1x01_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-602_1x02_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-603_1x03_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-604_1x04_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-605_1x05_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-606_1x06_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-607_1x07_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-608_1x08_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-609_1x09_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-610_1x10_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-611_1x11_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-612_1x12_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-613_1x13_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-614_1x14_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-615_1x15_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-616_1x16_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_236-624_1x24_P10.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-101_1x01_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-102_1x02_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-103_1x03_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-104_1x04_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-105_1x05_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-106_1x06_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-107_1x07_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-108_1x08_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-109_1x09_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-110_1x10_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-111_1x11_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-112_1x12_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-113_1x13_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-114_1x14_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-115_1x15_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-116_1x16_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-124_1x24_P5.00mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-301_1x01_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-302_1x02_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-303_1x03_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-304_1x04_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-305_1x05_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-306_1x06_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-307_1x07_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-308_1x08_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-309_1x09_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-310_1x10_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-311_1x11_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-312_1x12_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-316_1x16_P7.50mm_45Degree +TerminalBlock_WAGO:TerminalBlock_WAGO_804-324_1x24_P7.50mm_45Degree +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRBU_74650073_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRBU_74650074_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRBU_74650094_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRBU_74650173_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRBU_74650174_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRBU_74650194_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRBU_74650195_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRBU_74655095_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRSH_74651173_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRSH_74651174_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRSH_74651175_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRSH_74651194_THR +TerminalBlock_Wuerth:Wuerth_REDCUBE-THR_WP-THRSH_74651195_THR +TestPoint:TestPoint_2Pads_Pitch2.54mm_Drill0.8mm +TestPoint:TestPoint_2Pads_Pitch5.08mm_Drill1.3mm +TestPoint:TestPoint_Bridge_Pitch2.0mm_Drill0.7mm +TestPoint:TestPoint_Bridge_Pitch2.54mm_Drill0.7mm +TestPoint:TestPoint_Bridge_Pitch2.54mm_Drill1.0mm +TestPoint:TestPoint_Bridge_Pitch2.54mm_Drill1.3mm +TestPoint:TestPoint_Bridge_Pitch3.81mm_Drill1.3mm +TestPoint:TestPoint_Bridge_Pitch5.08mm_Drill0.7mm +TestPoint:TestPoint_Bridge_Pitch5.08mm_Drill1.3mm +TestPoint:TestPoint_Bridge_Pitch6.35mm_Drill1.3mm +TestPoint:TestPoint_Bridge_Pitch7.62mm_Drill1.3mm +TestPoint:TestPoint_Keystone_5000-5004_Miniature +TestPoint:TestPoint_Keystone_5005-5009_Compact +TestPoint:TestPoint_Keystone_5010-5014_Multipurpose +TestPoint:TestPoint_Keystone_5015_Micro_Mini +TestPoint:TestPoint_Keystone_5019_Miniature +TestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_Beaded +TestPoint:TestPoint_Loop_D2.50mm_Drill1.0mm +TestPoint:TestPoint_Loop_D2.50mm_Drill1.0mm_LowProfile +TestPoint:TestPoint_Loop_D2.50mm_Drill1.85mm +TestPoint:TestPoint_Loop_D2.54mm_Drill1.5mm_Beaded +TestPoint:TestPoint_Loop_D2.60mm_Drill0.9mm_Beaded +TestPoint:TestPoint_Loop_D2.60mm_Drill1.4mm_Beaded +TestPoint:TestPoint_Loop_D2.60mm_Drill1.6mm_Beaded +TestPoint:TestPoint_Loop_D3.50mm_Drill0.9mm_Beaded +TestPoint:TestPoint_Loop_D3.50mm_Drill1.4mm_Beaded +TestPoint:TestPoint_Loop_D3.80mm_Drill2.0mm +TestPoint:TestPoint_Loop_D3.80mm_Drill2.5mm +TestPoint:TestPoint_Loop_D3.80mm_Drill2.8mm +TestPoint:TestPoint_Pad_1.0x1.0mm +TestPoint:TestPoint_Pad_1.5x1.5mm +TestPoint:TestPoint_Pad_2.0x2.0mm +TestPoint:TestPoint_Pad_2.5x2.5mm +TestPoint:TestPoint_Pad_3.0x3.0mm +TestPoint:TestPoint_Pad_4.0x4.0mm +TestPoint:TestPoint_Pad_D1.0mm +TestPoint:TestPoint_Pad_D1.5mm +TestPoint:TestPoint_Pad_D2.0mm +TestPoint:TestPoint_Pad_D2.5mm +TestPoint:TestPoint_Pad_D3.0mm +TestPoint:TestPoint_Pad_D4.0mm +TestPoint:TestPoint_Plated_Hole_D2.0mm +TestPoint:TestPoint_Plated_Hole_D3.0mm +TestPoint:TestPoint_Plated_Hole_D4.0mm +TestPoint:TestPoint_Plated_Hole_D5.0mm +TestPoint:TestPoint_THTPad_1.0x1.0mm_Drill0.5mm +TestPoint:TestPoint_THTPad_1.5x1.5mm_Drill0.7mm +TestPoint:TestPoint_THTPad_2.0x2.0mm_Drill1.0mm +TestPoint:TestPoint_THTPad_2.5x2.5mm_Drill1.2mm +TestPoint:TestPoint_THTPad_3.0x3.0mm_Drill1.5mm +TestPoint:TestPoint_THTPad_4.0x4.0mm_Drill2.0mm +TestPoint:TestPoint_THTPad_D1.0mm_Drill0.5mm +TestPoint:TestPoint_THTPad_D1.5mm_Drill0.7mm +TestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mm +TestPoint:TestPoint_THTPad_D2.5mm_Drill1.2mm +TestPoint:TestPoint_THTPad_D3.0mm_Drill1.5mm +TestPoint:TestPoint_THTPad_D4.0mm_Drill2.0mm +Transformer_SMD:Pulse_P0926NL +Transformer_SMD:Pulse_PA1323NL +Transformer_SMD:Pulse_PA2001NL +Transformer_SMD:Pulse_PA2002NL-PA2008NL-PA2009NL +Transformer_SMD:Pulse_PA2004NL +Transformer_SMD:Pulse_PA2005NL +Transformer_SMD:Pulse_PA2006NL +Transformer_SMD:Pulse_PA2007NL +Transformer_SMD:Pulse_PA2777NL +Transformer_SMD:Pulse_PA3493NL +Transformer_SMD:Transformer_Coilcraft_CST1 +Transformer_SMD:Transformer_Coilcraft_CST2 +Transformer_SMD:Transformer_Coilcraft_CST2010 +Transformer_SMD:Transformer_CurrentSense_8.4x7.2mm +Transformer_SMD:Transformer_ED8_4-Lead_10.5x8mm_P5mm +Transformer_SMD:Transformer_Ethernet_Bel_S558-5999-T7-F +Transformer_SMD:Transformer_Ethernet_Bourns_PT61017PEL +Transformer_SMD:Transformer_Ethernet_Bourns_PT61020EL +Transformer_SMD:Transformer_Ethernet_Halo_N2_SO-16_7.11x12.7mm +Transformer_SMD:Transformer_Ethernet_Halo_N5_SO-16_7.11x12.7mm +Transformer_SMD:Transformer_Ethernet_Halo_N6_SO-16_7.11x14.73mm +Transformer_SMD:Transformer_Ethernet_HALO_TG111-MSC13 +Transformer_SMD:Transformer_Ethernet_Wuerth_749013011A +Transformer_SMD:Transformer_Ethernet_YDS_30F-51NL_SO-24_7.1x15.1mm +Transformer_SMD:Transformer_MACOM_SM-22 +Transformer_SMD:Transformer_MiniCircuits_AT224-1A +Transformer_SMD:Transformer_Murata_78250JC +Transformer_SMD:Transformer_NF_ETAL_P2781 +Transformer_SMD:Transformer_NF_ETAL_P2781_HandSoldering +Transformer_SMD:Transformer_NF_ETAL_P3000 +Transformer_SMD:Transformer_NF_ETAL_P3000_HandSoldering +Transformer_SMD:Transformer_NF_ETAL_P3181 +Transformer_SMD:Transformer_NF_ETAL_P3181_HandSoldering +Transformer_SMD:Transformer_NF_ETAL_P3188 +Transformer_SMD:Transformer_NF_ETAL_P3188_HandSoldering +Transformer_SMD:Transformer_NF_ETAL_P3191 +Transformer_SMD:Transformer_NF_ETAL_P3191_HandSoldering +Transformer_SMD:Transformer_Pulse_H1100NL +Transformer_SMD:Transformer_Wuerth_750315371 +Transformer_SMD:Transformer_Wurth_WE-AGDT-EP7 +Transformer_THT:Autotransformer_Toroid_1Tap_Horizontal_D10.5mm_Amidon-T37 +Transformer_THT:Autotransformer_Toroid_1Tap_Horizontal_D12.5mm_Amidon-T44 +Transformer_THT:Autotransformer_Toroid_1Tap_Horizontal_D14.0mm_Amidon-T50 +Transformer_THT:Autotransformer_Toroid_1Tap_Horizontal_D9.0mm_Amidon-T30 +Transformer_THT:Autotransformer_ZS1052-AC +Transformer_THT:Transformer_37x44 +Transformer_THT:Transformer_Breve_TEZ-22x24 +Transformer_THT:Transformer_Breve_TEZ-28x33 +Transformer_THT:Transformer_Breve_TEZ-35x42 +Transformer_THT:Transformer_Breve_TEZ-38x45 +Transformer_THT:Transformer_Breve_TEZ-44x52 +Transformer_THT:Transformer_Breve_TEZ-47x57 +Transformer_THT:Transformer_CHK_EI30-2VA_1xSec +Transformer_THT:Transformer_CHK_EI30-2VA_2xSec +Transformer_THT:Transformer_CHK_EI30-2VA_Neutral +Transformer_THT:Transformer_CHK_EI38-3VA_1xSec +Transformer_THT:Transformer_CHK_EI38-3VA_2xSec +Transformer_THT:Transformer_CHK_EI38-3VA_Neutral +Transformer_THT:Transformer_CHK_EI42-5VA_1xSec +Transformer_THT:Transformer_CHK_EI42-5VA_2xSec +Transformer_THT:Transformer_CHK_EI42-5VA_Neutral +Transformer_THT:Transformer_CHK_EI48-10VA_1xSec +Transformer_THT:Transformer_CHK_EI48-10VA_2xSec +Transformer_THT:Transformer_CHK_EI48-10VA_Neutral +Transformer_THT:Transformer_CHK_EI48-8VA_1xSec +Transformer_THT:Transformer_CHK_EI48-8VA_2xSec +Transformer_THT:Transformer_CHK_EI48-8VA_Neutral +Transformer_THT:Transformer_CHK_EI54-12VA_1xSec +Transformer_THT:Transformer_CHK_EI54-12VA_2xSec +Transformer_THT:Transformer_CHK_EI54-12VA_Neutral +Transformer_THT:Transformer_CHK_EI54-16VA_1xSec +Transformer_THT:Transformer_CHK_EI54-16VA_2xSec +Transformer_THT:Transformer_CHK_EI54-16VA_Neutral +Transformer_THT:Transformer_CHK_UI30-4VA_Flat +Transformer_THT:Transformer_CHK_UI39-10VA_Flat +Transformer_THT:Transformer_Coilcraft_Q4434-B_Rhombus-T1311 +Transformer_THT:Transformer_EPCOS_B66359A1013T_Horizontal +Transformer_THT:Transformer_EPCOS_B66359J1014T_Vertical +Transformer_THT:Transformer_Microphone_Lundahl_LL1538 +Transformer_THT:Transformer_Microphone_Lundahl_LL1587 +Transformer_THT:Transformer_Myrra_74040_Horizontal +Transformer_THT:Transformer_Myrra_EF20_7408x +Transformer_THT:Transformer_Myrra_EI30-5_44000_Horizontal +Transformer_THT:Transformer_NF_ETAL_1-1_P1200 +Transformer_THT:Transformer_NF_ETAL_P1165 +Transformer_THT:Transformer_NF_ETAL_P3324 +Transformer_THT:Transformer_NF_ETAL_P3356 +Transformer_THT:Transformer_Toroid_Horizontal_D10.5mm_Amidon-T37 +Transformer_THT:Transformer_Toroid_Horizontal_D12.5mm_Amidon-T44 +Transformer_THT:Transformer_Toroid_Horizontal_D14.0mm_Amidon-T50 +Transformer_THT:Transformer_Toroid_Horizontal_D18.0mm +Transformer_THT:Transformer_Toroid_Horizontal_D9.0mm_Amidon-T30 +Transformer_THT:Transformer_Toroid_Tapped_Horizontal_D10.5mm_Amidon-T37 +Transformer_THT:Transformer_Toroid_Tapped_Horizontal_D12.5mm_Amidon-T44 +Transformer_THT:Transformer_Toroid_Tapped_Horizontal_D14.0mm_Amidon-T50 +Transformer_THT:Transformer_Toroid_Tapped_Horizontal_D9.0mm_Amidon-T30 +Transformer_THT:Transformer_Triad_VPP16-310 +Transformer_THT:Transformer_Wuerth_750343373 +Transformer_THT:Transformer_Wuerth_760871131 +Transformer_THT:Transformer_Zeming_ZMCT103C +Transformer_THT:Transformer_Zeming_ZMPT101K +Transistor_Power:GaN_Systems_GaNPX-3_5x6.6mm_Drain2.93x0.6mm +Transistor_Power:GaN_Systems_GaNPX-3_5x6.6mm_Drain3.76x0.6mm +Transistor_Power:GaN_Systems_GaNPX-4_7x8.4mm +Transistor_Power_Module:Infineon_AG-ECONO2 +Transistor_Power_Module:Infineon_AG-ECONO3 +Transistor_Power_Module:Infineon_AG-ECONO3B +Transistor_Power_Module:Infineon_EasyPACK-1B +Transistor_Power_Module:Infineon_EasyPACK-1B_PressFIT +Transistor_Power_Module:Infineon_EasyPIM-1B +Transistor_Power_Module:Infineon_EasyPIM-2B +Transistor_Power_Module:Littelfuse_Package_H_XBN2MM +Transistor_Power_Module:Littelfuse_Package_H_XN2MM +Transistor_Power_Module:Littelfuse_Package_W_XBN2MM +Transistor_Power_Module:Littelfuse_Package_W_XN2MM +Transistor_Power_Module:ST_ACEPACK-2-CIB +Transistor_Power_Module:ST_ACEPACK-2-CIB_PressFIT +Transistor_Power_Module:ST_SDIP-25L +Valve:Valve_ECC-83-1 +Valve:Valve_ECC-83-2 +Valve:Valve_EURO +Valve:Valve_Glimm +Valve:Valve_Mini_G +Valve:Valve_Mini_P +Valve:Valve_Mini_Pentode_Linear +Valve:Valve_Noval_G +Valve:Valve_Noval_P +Valve:Valve_Octal +Varistor:RV_Disc_D12mm_W3.9mm_P7.5mm +Varistor:RV_Disc_D12mm_W4.2mm_P7.5mm +Varistor:RV_Disc_D12mm_W4.3mm_P7.5mm +Varistor:RV_Disc_D12mm_W4.4mm_P7.5mm +Varistor:RV_Disc_D12mm_W4.5mm_P7.5mm +Varistor:RV_Disc_D12mm_W4.6mm_P7.5mm +Varistor:RV_Disc_D12mm_W4.7mm_P7.5mm +Varistor:RV_Disc_D12mm_W4.8mm_P7.5mm +Varistor:RV_Disc_D12mm_W4mm_P7.5mm +Varistor:RV_Disc_D12mm_W5.1mm_P7.5mm +Varistor:RV_Disc_D12mm_W5.4mm_P7.5mm +Varistor:RV_Disc_D12mm_W5.8mm_P7.5mm +Varistor:RV_Disc_D12mm_W5mm_P7.5mm +Varistor:RV_Disc_D12mm_W6.1mm_P7.5mm +Varistor:RV_Disc_D12mm_W6.2mm_P7.5mm +Varistor:RV_Disc_D12mm_W6.3mm_P7.5mm +Varistor:RV_Disc_D12mm_W6.7mm_P7.5mm +Varistor:RV_Disc_D12mm_W7.1mm_P7.5mm +Varistor:RV_Disc_D12mm_W7.5mm_P7.5mm +Varistor:RV_Disc_D12mm_W7.9mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W11mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W3.9mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4.2mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4.3mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4.4mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4.5mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4.6mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4.7mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4.8mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4.9mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W4mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W5.2mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W5.4mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W5.9mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W5mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W6.1mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W6.3mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W6.4mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W6.8mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W7.2mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W7.5mm_P7.5mm +Varistor:RV_Disc_D15.5mm_W8mm_P7.5mm +Varistor:RV_Disc_D16.5mm_W6.7mm_P7.5mm +Varistor:RV_Disc_D21.5mm_W11.4mm_P10mm +Varistor:RV_Disc_D21.5mm_W4.3mm_P10mm +Varistor:RV_Disc_D21.5mm_W4.4mm_P10mm +Varistor:RV_Disc_D21.5mm_W4.5mm_P10mm +Varistor:RV_Disc_D21.5mm_W4.6mm_P10mm +Varistor:RV_Disc_D21.5mm_W4.7mm_P10mm +Varistor:RV_Disc_D21.5mm_W4.8mm_P10mm +Varistor:RV_Disc_D21.5mm_W4.9mm_P10mm +Varistor:RV_Disc_D21.5mm_W5.1mm_P10mm +Varistor:RV_Disc_D21.5mm_W5.3mm_P10mm +Varistor:RV_Disc_D21.5mm_W5.4mm_P10mm +Varistor:RV_Disc_D21.5mm_W5.6mm_P10mm +Varistor:RV_Disc_D21.5mm_W5.8mm_P10mm +Varistor:RV_Disc_D21.5mm_W5mm_P10mm +Varistor:RV_Disc_D21.5mm_W6.1mm_P7.5mm +Varistor:RV_Disc_D21.5mm_W6.3mm_P10mm +Varistor:RV_Disc_D21.5mm_W6.5mm_P10mm +Varistor:RV_Disc_D21.5mm_W6.7mm_P10mm +Varistor:RV_Disc_D21.5mm_W6.8mm_P10mm +Varistor:RV_Disc_D21.5mm_W7.1mm_P10mm +Varistor:RV_Disc_D21.5mm_W7.5mm_P10mm +Varistor:RV_Disc_D21.5mm_W7.9mm_P10mm +Varistor:RV_Disc_D21.5mm_W8.4mm_P10mm +Varistor:RV_Disc_D7mm_W3.4mm_P5mm +Varistor:RV_Disc_D7mm_W3.5mm_P5mm +Varistor:RV_Disc_D7mm_W3.6mm_P5mm +Varistor:RV_Disc_D7mm_W3.7mm_P5mm +Varistor:RV_Disc_D7mm_W3.8mm_P5mm +Varistor:RV_Disc_D7mm_W3.9mm_P5mm +Varistor:RV_Disc_D7mm_W4.2mm_P5mm +Varistor:RV_Disc_D7mm_W4.3mm_P5mm +Varistor:RV_Disc_D7mm_W4.5mm_P5mm +Varistor:RV_Disc_D7mm_W4.8mm_P5mm +Varistor:RV_Disc_D7mm_W4.9mm_P5mm +Varistor:RV_Disc_D7mm_W4mm_P5mm +Varistor:RV_Disc_D7mm_W5.1mm_P5mm +Varistor:RV_Disc_D7mm_W5.4mm_P5mm +Varistor:RV_Disc_D7mm_W5.5mm_P5mm +Varistor:RV_Disc_D7mm_W5.7mm_P5mm +Varistor:RV_Disc_D9mm_W3.3mm_P5mm +Varistor:RV_Disc_D9mm_W3.4mm_P5mm +Varistor:RV_Disc_D9mm_W3.5mm_P5mm +Varistor:RV_Disc_D9mm_W3.6mm_P5mm +Varistor:RV_Disc_D9mm_W3.7mm_P5mm +Varistor:RV_Disc_D9mm_W3.8mm_P5mm +Varistor:RV_Disc_D9mm_W3.9mm_P5mm +Varistor:RV_Disc_D9mm_W4.1mm_P5mm +Varistor:RV_Disc_D9mm_W4.2mm_P5mm +Varistor:RV_Disc_D9mm_W4.4mm_P5mm +Varistor:RV_Disc_D9mm_W4.5mm_P5mm +Varistor:RV_Disc_D9mm_W4.8mm_P5mm +Varistor:RV_Disc_D9mm_W4mm_P5mm +Varistor:RV_Disc_D9mm_W5.2mm_P5mm +Varistor:RV_Disc_D9mm_W5.4mm_P5mm +Varistor:RV_Disc_D9mm_W5.5mm_P5mm +Varistor:RV_Disc_D9mm_W5.7mm_P5mm +Varistor:RV_Disc_D9mm_W6.1mm_P5mm +Varistor:RV_Rect_V25S440P_L26.5mm_W8.2mm_P12.7mm +Varistor:Varistor_Panasonic_VF diff --git a/public/kicad/symbols.txt b/public/kicad/symbols.txt new file mode 100644 index 00000000..1cee1692 --- /dev/null +++ b/public/kicad/symbols.txt @@ -0,0 +1,21807 @@ +# This file contains all the KiCad symbols available in the official library +# Generated by symbols.sh +# on Sun Feb 16 21:42:01 CET 2025 +4xxx:14528 +4xxx:14529 +4xxx:14538 +4xxx:4001 +4xxx:4002 +4xxx:4009 +4xxx:4010 +4xxx:40106 +4xxx:4011 +4xxx:4012 +4xxx:4013 +4xxx:4016 +4xxx:4017 +4xxx:4020 +4xxx:4021 +4xxx:4022 +4xxx:4023 +4xxx:4025 +4xxx:4027 +4xxx:4028 +4xxx:4029 +4xxx:4040 +4xxx:4046 +4xxx:4047 +4xxx:4049 +4xxx:4050 +4xxx:4051 +4xxx:4052 +4xxx:4053 +4xxx:4056 +4xxx:4060 +4xxx:4066 +4xxx:4069 +4xxx:4070 +4xxx:4071 +4xxx:4072 +4xxx:4073 +4xxx:4075 +4xxx:4077 +4xxx:4081 +4xxx:4098 +4xxx:4504 +4xxx:4510 +4xxx:4518 +4xxx:4520 +4xxx:4528 +4xxx:4538 +4xxx:4543 +4xxx:CD4033B +4xxx:HEF4093B +4xxx:HEF4094B +4xxx_IEEE:4001 +4xxx_IEEE:4002 +4xxx_IEEE:4006 +4xxx_IEEE:4008 +4xxx_IEEE:4009 +4xxx_IEEE:4010 +4xxx_IEEE:40104 +4xxx_IEEE:40106 +4xxx_IEEE:4011 +4xxx_IEEE:40110 +4xxx_IEEE:4012 +4xxx_IEEE:4013 +4xxx_IEEE:4014 +4xxx_IEEE:4015 +4xxx_IEEE:4016 +4xxx_IEEE:40160 +4xxx_IEEE:40161 +4xxx_IEEE:40162 +4xxx_IEEE:40163 +4xxx_IEEE:4017 +4xxx_IEEE:40174 +4xxx_IEEE:40175 +4xxx_IEEE:4018 +4xxx_IEEE:4019 +4xxx_IEEE:40192 +4xxx_IEEE:40193 +4xxx_IEEE:40194 +4xxx_IEEE:4020 +4xxx_IEEE:4021 +4xxx_IEEE:4022 +4xxx_IEEE:4023 +4xxx_IEEE:4024 +4xxx_IEEE:40240 +4xxx_IEEE:40244 +4xxx_IEEE:40245 +4xxx_IEEE:4025 +4xxx_IEEE:4027 +4xxx_IEEE:4028 +4xxx_IEEE:4029 +4xxx_IEEE:4030 +4xxx_IEEE:40373 +4xxx_IEEE:40374 +4xxx_IEEE:4040 +4xxx_IEEE:4041 +4xxx_IEEE:4042 +4xxx_IEEE:4043 +4xxx_IEEE:4044 +4xxx_IEEE:4046 +4xxx_IEEE:4048 +4xxx_IEEE:4049 +4xxx_IEEE:4050 +4xxx_IEEE:4051 +4xxx_IEEE:4052 +4xxx_IEEE:4053 +4xxx_IEEE:4060 +4xxx_IEEE:4066 +4xxx_IEEE:4068 +4xxx_IEEE:4069 +4xxx_IEEE:4070 +4xxx_IEEE:4071 +4xxx_IEEE:4072 +4xxx_IEEE:4073 +4xxx_IEEE:4075 +4xxx_IEEE:4077 +4xxx_IEEE:4078 +4xxx_IEEE:4081 +4xxx_IEEE:4082 +4xxx_IEEE:4093 +4xxx_IEEE:4095 +4xxx_IEEE:4096 +4xxx_IEEE:4099 +4xxx_IEEE:4104 +4xxx_IEEE:4160 +4xxx_IEEE:4161 +4xxx_IEEE:4162 +4xxx_IEEE:4163 +4xxx_IEEE:4174 +4xxx_IEEE:4175 +4xxx_IEEE:4502 +4xxx_IEEE:4504 +4xxx_IEEE:4507 +4xxx_IEEE:4508 +4xxx_IEEE:4510 +4xxx_IEEE:4511 +4xxx_IEEE:4512 +4xxx_IEEE:4514 +4xxx_IEEE:4515 +4xxx_IEEE:4518 +4xxx_IEEE:4520 +4xxx_IEEE:4528 +4xxx_IEEE:4529 +4xxx_IEEE:4530 +4xxx_IEEE:4538 +4xxx_IEEE:4539 +4xxx_IEEE:4543 +4xxx_IEEE:4555 +4xxx_IEEE:4556 +4xxx_IEEE:4584 +4xxx_IEEE:4585 +74xGxx:74AHC1G00 +74xGxx:74AHC1G02 +74xGxx:74AHC1G04 +74xGxx:74AHC1G08 +74xGxx:74AHC1G125 +74xGxx:74AHC1G126 +74xGxx:74AHC1G14 +74xGxx:74AHC1G32 +74xGxx:74AHC1G4210 +74xGxx:74AHC1G86 +74xGxx:74AHC1GU04 +74xGxx:74AHC2G00 +74xGxx:74AHCT1G00 +74xGxx:74AHCT1G02 +74xGxx:74AHCT1G04 +74xGxx:74AHCT1G08 +74xGxx:74AHCT1G125 +74xGxx:74AHCT1G126 +74xGxx:74AHCT1G14 +74xGxx:74AHCT1G32 +74xGxx:74AHCT1G86 +74xGxx:74AHCT1GU04 +74xGxx:74AHCT2G00 +74xGxx:74AUC1G00 +74xGxx:74AUC1G02 +74xGxx:74AUC1G04 +74xGxx:74AUC1G06 +74xGxx:74AUC1G07 +74xGxx:74AUC1G08 +74xGxx:74AUC1G125 +74xGxx:74AUC1G126 +74xGxx:74AUC1G14 +74xGxx:74AUC1G17 +74xGxx:74AUC1G18 +74xGxx:74AUC1G19 +74xGxx:74AUC1G240 +74xGxx:74AUC1G32 +74xGxx:74AUC1G66 +74xGxx:74AUC1G74 +74xGxx:74AUC1G79 +74xGxx:74AUC1G80 +74xGxx:74AUC1G86 +74xGxx:74AUC1GU04 +74xGxx:74AUC2G00 +74xGxx:74AUC2G02 +74xGxx:74AUC2G04 +74xGxx:74AUC2G06 +74xGxx:74AUC2G07 +74xGxx:74AUC2G08 +74xGxx:74AUC2G125 +74xGxx:74AUC2G126 +74xGxx:74AUC2G240 +74xGxx:74AUC2G241 +74xGxx:74AUC2G32 +74xGxx:74AUC2G34 +74xGxx:74AUC2G53 +74xGxx:74AUC2G66 +74xGxx:74AUC2G79 +74xGxx:74AUC2G80 +74xGxx:74AUC2G86 +74xGxx:74AUC2GU04 +74xGxx:74AUP1G00 +74xGxx:74AUP1G02 +74xGxx:74AUP1G04 +74xGxx:74AUP1G06 +74xGxx:74AUP1G07 +74xGxx:74AUP1G08 +74xGxx:74AUP1G125 +74xGxx:74AUP1G126 +74xGxx:74AUP1G14 +74xGxx:74AUP1G17 +74xGxx:74AUP1G240 +74xGxx:74AUP1G32 +74xGxx:74AUP1G34 +74xGxx:74AUP1G57 +74xGxx:74AUP1G58 +74xGxx:74AUP1G74 +74xGxx:74AUP1G79 +74xGxx:74AUP1G80 +74xGxx:74AUP1G97 +74xGxx:74AUP1G98 +74xGxx:74AUP1G99 +74xGxx:74AUP1GU04 +74xGxx:74CB3T1G125 +74xGxx:74CBT1G125 +74xGxx:74CBT1G384 +74xGxx:74CBTD1G125 +74xGxx:74CBTD1G384 +74xGxx:74CBTLV1G125 +74xGxx:74LVC1G00 +74xGxx:74LVC1G02 +74xGxx:74LVC1G04 +74xGxx:74LVC1G06 +74xGxx:74LVC1G07 +74xGxx:74LVC1G08 +74xGxx:74LVC1G0832 +74xGxx:74LVC1G10 +74xGxx:74LVC1G11 +74xGxx:74LVC1G123 +74xGxx:74LVC1G125 +74xGxx:74LVC1G126 +74xGxx:74LVC1G139 +74xGxx:74LVC1G14 +74xGxx:74LVC1G17 +74xGxx:74LVC1G175 +74xGxx:74LVC1G18 +74xGxx:74LVC1G19 +74xGxx:74LVC1G240 +74xGxx:74LVC1G27 +74xGxx:74LVC1G29 +74xGxx:74LVC1G3157 +74xGxx:74LVC1G32 +74xGxx:74LVC1G3208 +74xGxx:74LVC1G332 +74xGxx:74LVC1G34 +74xGxx:74LVC1G373 +74xGxx:74LVC1G374 +74xGxx:74LVC1G38 +74xGxx:74LVC1G386 +74xGxx:74LVC1G57 +74xGxx:74LVC1G58 +74xGxx:74LVC1G66 +74xGxx:74LVC1G79 +74xGxx:74LVC1G80 +74xGxx:74LVC1G86 +74xGxx:74LVC1G97 +74xGxx:74LVC1G98 +74xGxx:74LVC1G99 +74xGxx:74LVC1GU04 +74xGxx:74LVC1GU04DRL +74xGxx:74LVC2G00 +74xGxx:74LVC2G02 +74xGxx:74LVC2G04 +74xGxx:74LVC2G06 +74xGxx:74LVC2G07 +74xGxx:74LVC2G08 +74xGxx:74LVC2G125 +74xGxx:74LVC2G126 +74xGxx:74LVC2G14 +74xGxx:74LVC2G157 +74xGxx:74LVC2G17 +74xGxx:74LVC2G240 +74xGxx:74LVC2G241 +74xGxx:74LVC2G32 +74xGxx:74LVC2G34 +74xGxx:74LVC2G38 +74xGxx:74LVC2G53 +74xGxx:74LVC2G66 +74xGxx:74LVC2G74 +74xGxx:74LVC2G79 +74xGxx:74LVC2G80 +74xGxx:74LVC2G86 +74xGxx:74LVC2GU04 +74xGxx:74LVC3G04 +74xGxx:74LVC3G06 +74xGxx:74LVC3G07 +74xGxx:74LVC3G14 +74xGxx:74LVC3G17 +74xGxx:74LVC3G34 +74xGxx:74LVC3GU04 +74xGxx:Inverter_Schmitt_Dual +74xGxx:NC7SVU04P5X +74xGxx:NC7SZ125M5X +74xGxx:NC7SZ125P5X +74xGxx:SN74LVC1G00DBV +74xGxx:SN74LVC1G00DCK +74xGxx:SN74LVC1G00DRL +74xGxx:SN74LVC1G125DBV +74xGxx:SN74LVC1G125DCK +74xGxx:SN74LVC1G125DRL +74xGxx:SN74LVC1G14DBV +74xGxx:SN74LVC1G14DRL +74xGxx:SN74LVC2G14DBV +74xGxx:TC7PZ14FU +74xx:7400 +74xx:7402 +74xx:74469 +74xx:7454 +74xx:74AHC04 +74xx:74AHC240 +74xx:74AHC244 +74xx:74AHC273 +74xx:74AHC373 +74xx:74AHC374 +74xx:74AHC541 +74xx:74AHC595 +74xx:74AHCT04 +74xx:74AHCT123 +74xx:74AHCT125 +74xx:74AHCT240 +74xx:74AHCT244 +74xx:74AHCT273 +74xx:74AHCT373 +74xx:74AHCT374 +74xx:74AHCT541 +74xx:74AHCT595 +74xx:74ALVC164245 +74xx:74CB3Q16210DGG +74xx:74CB3Q16210DGV +74xx:74CB3Q16210DL +74xx:74CB3T16210DGG +74xx:74CB3T16210DGV +74xx:74CBT16210CDGG +74xx:74CBT16210CDGV +74xx:74CBT16210CDL +74xx:74CBT3861 +74xx:74CBTD16210DGG +74xx:74CBTD16210DGV +74xx:74CBTD16210DL +74xx:74CBTD3861 +74xx:74CBTLV16212 +74xx:74CBTLV3257 +74xx:74CBTLV3861 +74xx:74HC00 +74xx:74HC02 +74xx:74HC04 +74xx:74HC123 +74xx:74HC137 +74xx:74HC138 +74xx:74HC14 +74xx:74HC164 +74xx:74HC165 +74xx:74HC173 +74xx:74HC192 +74xx:74HC193 +74xx:74HC237 +74xx:74HC238 +74xx:74HC240 +74xx:74HC244 +74xx:74HC245 +74xx:74HC273 +74xx:74HC373 +74xx:74HC374 +74xx:74HC4024 +74xx:74HC4051 +74xx:74HC4060 +74xx:74HC590 +74xx:74HC590A +74xx:74HC594 +74xx:74HC595 +74xx:74HC596 +74xx:74HC688 +74xx:74HC7014 +74xx:74HC74 +74xx:74HC85 +74xx:74HC86 +74xx:74HCT00 +74xx:74HCT02 +74xx:74HCT04 +74xx:74HCT123 +74xx:74HCT137 +74xx:74HCT138 +74xx:74HCT164 +74xx:74HCT173 +74xx:74HCT193 +74xx:74HCT237 +74xx:74HCT238 +74xx:74HCT240 +74xx:74HCT244 +74xx:74HCT273 +74xx:74HCT373 +74xx:74HCT374 +74xx:74HCT4051 +74xx:74HCT541 +74xx:74HCT574 +74xx:74HCT595 +74xx:74HCT596 +74xx:74HCT688 +74xx:74HCT74 +74xx:74HCT85 +74xx:74LCX07 +74xx:74LS00 +74xx:74LS01 +74xx:74LS02 +74xx:74LS03 +74xx:74LS04 +74xx:74LS05 +74xx:74LS06 +74xx:74LS06N +74xx:74LS07 +74xx:74LS08 +74xx:74LS09 +74xx:74LS10 +74xx:74LS107 +74xx:74LS109 +74xx:74LS11 +74xx:74LS112 +74xx:74LS113 +74xx:74LS114 +74xx:74LS12 +74xx:74LS121 +74xx:74LS122 +74xx:74LS123 +74xx:74LS125 +74xx:74LS126 +74xx:74LS13 +74xx:74LS132 +74xx:74LS133 +74xx:74LS136 +74xx:74LS137 +74xx:74LS138 +74xx:74LS139 +74xx:74LS14 +74xx:74LS145 +74xx:74LS147 +74xx:74LS148 +74xx:74LS15 +74xx:74LS151 +74xx:74LS153 +74xx:74LS154 +74xx:74LS155 +74xx:74LS156 +74xx:74LS157 +74xx:74LS158 +74xx:74LS160 +74xx:74LS161 +74xx:74LS162 +74xx:74LS163 +74xx:74LS165 +74xx:74LS166 +74xx:74LS168 +74xx:74LS169 +74xx:74LS170 +74xx:74LS173 +74xx:74LS174 +74xx:74LS175 +74xx:74LS181 +74xx:74LS182 +74xx:74LS190 +74xx:74LS191 +74xx:74LS192 +74xx:74LS193 +74xx:74LS194 +74xx:74LS195 +74xx:74LS196 +74xx:74LS197 +74xx:74LS20 +74xx:74LS21 +74xx:74LS22 +74xx:74LS221 +74xx:74LS240 +74xx:74LS240_Split +74xx:74LS241 +74xx:74LS241_Split +74xx:74LS242 +74xx:74LS243 +74xx:74LS244 +74xx:74LS244_Split +74xx:74LS245 +74xx:74LS246 +74xx:74LS247 +74xx:74LS248 +74xx:74LS249 +74xx:74LS251 +74xx:74LS253 +74xx:74LS256 +74xx:74LS257 +74xx:74LS258 +74xx:74LS259 +74xx:74LS26 +74xx:74LS27 +74xx:74LS273 +74xx:74LS279 +74xx:74LS28 +74xx:74LS280 +74xx:74LS283 +74xx:74LS290 +74xx:74LS293 +74xx:74LS295 +74xx:74LS298 +74xx:74LS299 +74xx:74LS30 +74xx:74LS32 +74xx:74LS322 +74xx:74LS323 +74xx:74LS33 +74xx:74LS348 +74xx:74LS352 +74xx:74LS353 +74xx:74LS365 +74xx:74LS366 +74xx:74LS367 +74xx:74LS368 +74xx:74LS37 +74xx:74LS373 +74xx:74LS374 +74xx:74LS375 +74xx:74LS377 +74xx:74LS378 +74xx:74LS379 +74xx:74LS38 +74xx:74LS385 +74xx:74LS386 +74xx:74LS390 +74xx:74LS393 +74xx:74LS395 +74xx:74LS398 +74xx:74LS399 +74xx:74LS40 +74xx:74LS42 +74xx:74LS46 +74xx:74LS47 +74xx:74LS48 +74xx:74LS49 +74xx:74LS51 +74xx:74LS540 +74xx:74LS541 +74xx:74LS54N +74xx:74LS55 +74xx:74LS573 +74xx:74LS574 +74xx:74LS590 +74xx:74LS595 +74xx:74LS596 +74xx:74LS629 +74xx:74LS670 +74xx:74LS688 +74xx:74LS73 +74xx:74LS74 +74xx:74LS75 +74xx:74LS76 +74xx:74LS77 +74xx:74LS78 +74xx:74LS83 +74xx:74LS85 +74xx:74LS86 +74xx:74LS90 +74xx:74LS91 +74xx:74LS92 +74xx:74LS93 +74xx:74LS95 +74xx:74LV14 +74xx:74LV8154 +74xx:74LVC125 +74xx:74VHC9164FT +74xx:CD74AC238 +74xx:CD74HC4067M +74xx:CD74HC4067SM +74xx:MC74LCX16245DT +74xx:MM74C923 +74xx:SN74ALVC164245DGG +74xx:SN74ALVC164245DL +74xx:SN74AVC16827DGGR +74xx:SN74CB3Q3384ADBQ +74xx:SN74CB3Q3384APW +74xx:SN74LS07 +74xx:SN74LS07N +74xx:SN74LV4T125 +74xx_IEEE:7400 +74xx_IEEE:7401 +74xx_IEEE:7402 +74xx_IEEE:7403 +74xx_IEEE:7404 +74xx_IEEE:7405 +74xx_IEEE:7406 +74xx_IEEE:7407 +74xx_IEEE:7408 +74xx_IEEE:7409 +74xx_IEEE:7410 +74xx_IEEE:7411 +74xx_IEEE:7412 +74xx_IEEE:74125 +74xx_IEEE:74126 +74xx_IEEE:74128 +74xx_IEEE:7413 +74xx_IEEE:74132 +74xx_IEEE:74136 +74xx_IEEE:7414 +74xx_IEEE:74141 +74xx_IEEE:74145 +74xx_IEEE:74147 +74xx_IEEE:74148 +74xx_IEEE:74151 +74xx_IEEE:74153 +74xx_IEEE:74154 +74xx_IEEE:74155 +74xx_IEEE:74156 +74xx_IEEE:74157 +74xx_IEEE:74158 +74xx_IEEE:74159 +74xx_IEEE:7416 +74xx_IEEE:74164 +74xx_IEEE:74165 +74xx_IEEE:74166 +74xx_IEEE:7417 +74xx_IEEE:74173 +74xx_IEEE:74176 +74xx_IEEE:74196 +74xx_IEEE:7420 +74xx_IEEE:7421 +74xx_IEEE:7422 +74xx_IEEE:74246 +74xx_IEEE:74247 +74xx_IEEE:74248 +74xx_IEEE:74249 +74xx_IEEE:7425 +74xx_IEEE:74251 +74xx_IEEE:74253 +74xx_IEEE:7426 +74xx_IEEE:7427 +74xx_IEEE:74278 +74xx_IEEE:7428 +74xx_IEEE:74293 +74xx_IEEE:7430 +74xx_IEEE:7432 +74xx_IEEE:7433 +74xx_IEEE:7437 +74xx_IEEE:7438 +74xx_IEEE:7439 +74xx_IEEE:7440 +74xx_IEEE:7442 +74xx_IEEE:74425 +74xx_IEEE:74426 +74xx_IEEE:7443 +74xx_IEEE:7444 +74xx_IEEE:7445 +74xx_IEEE:7446 +74xx_IEEE:7447 +74xx_IEEE:7448 +74xx_IEEE:7451 +74xx_IEEE:7454 +74xx_IEEE:7483 +74xx_IEEE:7485 +74xx_IEEE:7486 +74xx_IEEE:7490 +74xx_IEEE:7491 +74xx_IEEE:7492 +74xx_IEEE:7493 +74xx_IEEE:7495 +74xx_IEEE:7496 +74xx_IEEE:74HC237 +74xx_IEEE:74HC238 +74xx_IEEE:74HC36 +74xx_IEEE:74HC804 +74xx_IEEE:74HC805 +74xx_IEEE:74HC808 +74xx_IEEE:74HC832 +74xx_IEEE:74LS133 +74xx_IEEE:74LS137 +74xx_IEEE:74LS138 +74xx_IEEE:74LS139 +74xx_IEEE:74LS15 +74xx_IEEE:74LS152 +74xx_IEEE:74LS160 +74xx_IEEE:74LS161 +74xx_IEEE:74LS162 +74xx_IEEE:74LS163 +74xx_IEEE:74LS168 +74xx_IEEE:74LS169 +74xx_IEEE:74LS170 +74xx_IEEE:74LS177 +74xx_IEEE:74LS18 +74xx_IEEE:74LS19 +74xx_IEEE:74LS190 +74xx_IEEE:74LS191 +74xx_IEEE:74LS192 +74xx_IEEE:74LS193 +74xx_IEEE:74LS194 +74xx_IEEE:74LS195 +74xx_IEEE:74LS197 +74xx_IEEE:74LS239 +74xx_IEEE:74LS24 +74xx_IEEE:74LS240 +74xx_IEEE:74LS241 +74xx_IEEE:74LS242 +74xx_IEEE:74LS243 +74xx_IEEE:74LS244 +74xx_IEEE:74LS245 +74xx_IEEE:74LS257 +74xx_IEEE:74LS258 +74xx_IEEE:74LS266 +74xx_IEEE:74LS280 +74xx_IEEE:74LS283 +74xx_IEEE:74LS290 +74xx_IEEE:74LS295 +74xx_IEEE:74LS298 +74xx_IEEE:74LS299 +74xx_IEEE:74LS323 +74xx_IEEE:74LS347 +74xx_IEEE:74LS348 +74xx_IEEE:74LS352 +74xx_IEEE:74LS353 +74xx_IEEE:74LS365 +74xx_IEEE:74LS366 +74xx_IEEE:74LS367 +74xx_IEEE:74LS368 +74xx_IEEE:74LS386 +74xx_IEEE:74LS390 +74xx_IEEE:74LS395 +74xx_IEEE:74LS396 +74xx_IEEE:74LS398 +74xx_IEEE:74LS399 +74xx_IEEE:74LS445 +74xx_IEEE:74LS447 +74xx_IEEE:74LS465 +74xx_IEEE:74LS466 +74xx_IEEE:74LS467 +74xx_IEEE:74LS468 +74xx_IEEE:74LS49 +74xx_IEEE:74LS540 +74xx_IEEE:74LS541 +74xx_IEEE:74LS55 +74xx_IEEE:74LS56 +74xx_IEEE:74LS57 +74xx_IEEE:74LS590 +74xx_IEEE:74LS591 +74xx_IEEE:74LS594 +74xx_IEEE:74LS595 +74xx_IEEE:74LS596 +74xx_IEEE:74LS597 +74xx_IEEE:74LS599 +74xx_IEEE:74LS620 +74xx_IEEE:74LS621 +74xx_IEEE:74LS622 +74xx_IEEE:74LS623 +74xx_IEEE:74LS638 +74xx_IEEE:74LS639 +74xx_IEEE:74LS640 +74xx_IEEE:74LS641 +74xx_IEEE:74LS642 +74xx_IEEE:74LS645 +74xx_IEEE:74LS668 +74xx_IEEE:74LS669 +74xx_IEEE:74LS670 +74xx_IEEE:74LS682 +74xx_IEEE:74LS683 +74xx_IEEE:74LS684 +74xx_IEEE:74LS685 +74xx_IEEE:74LS686 +74xx_IEEE:74LS687 +74xx_IEEE:74LS688 +74xx_IEEE:74LS689 +74xx_IEEE:74S140 +Amplifier_Audio:IR4301 +Amplifier_Audio:IR4302 +Amplifier_Audio:IR4311 +Amplifier_Audio:IR4312 +Amplifier_Audio:IR4321 +Amplifier_Audio:IR4322 +Amplifier_Audio:IRS2052M +Amplifier_Audio:IRS2092 +Amplifier_Audio:IRS2092S +Amplifier_Audio:IRS2093M +Amplifier_Audio:IRS20957S +Amplifier_Audio:IRS20965S +Amplifier_Audio:IRS2452AM +Amplifier_Audio:IS31AP4991-GRLS2 +Amplifier_Audio:IS31AP4991-SLS2 +Amplifier_Audio:LM1875 +Amplifier_Audio:LM1876 +Amplifier_Audio:LM1877 +Amplifier_Audio:LM2876 +Amplifier_Audio:LM380N +Amplifier_Audio:LM380N-8 +Amplifier_Audio:LM384 +Amplifier_Audio:LM386 +Amplifier_Audio:LM3886 +Amplifier_Audio:LM4752TS +Amplifier_Audio:LM4755TS +Amplifier_Audio:LM4766 +Amplifier_Audio:LM4810 +Amplifier_Audio:LM4811 +Amplifier_Audio:LM4950TA +Amplifier_Audio:LM4950TS +Amplifier_Audio:LM4990ITL +Amplifier_Audio:LM4990LD +Amplifier_Audio:LM4990MH +Amplifier_Audio:LM4990MM +Amplifier_Audio:LME49600 +Amplifier_Audio:MA12040 +Amplifier_Audio:MA12040P +Amplifier_Audio:MA12070 +Amplifier_Audio:MA12070P +Amplifier_Audio:MAX9701xTG +Amplifier_Audio:MAX9715xTE+ +Amplifier_Audio:MAX9744 +Amplifier_Audio:MAX9814 +Amplifier_Audio:MAX98306xDT +Amplifier_Audio:MAX98396EWB+ +Amplifier_Audio:MAX9850xTI +Amplifier_Audio:OPA1622 +Amplifier_Audio:PAM8301 +Amplifier_Audio:PAM8302AAD +Amplifier_Audio:PAM8302AAS +Amplifier_Audio:PAM8302AAY +Amplifier_Audio:PAM8403D +Amplifier_Audio:PAM8406D +Amplifier_Audio:SSM2017P +Amplifier_Audio:SSM2018 +Amplifier_Audio:SSM2120 +Amplifier_Audio:SSM2122 +Amplifier_Audio:SSM2165 +Amplifier_Audio:SSM2167 +Amplifier_Audio:SSM2211CP +Amplifier_Audio:SSM2211S +Amplifier_Audio:STK433_Sanyo +Amplifier_Audio:STK435_Sanyo +Amplifier_Audio:STK436_Sanyo +Amplifier_Audio:STK437_Sanyo +Amplifier_Audio:STK439_Sanyo +Amplifier_Audio:STK441_Sanyo +Amplifier_Audio:STK443_Sanyo +Amplifier_Audio:Si8241BB +Amplifier_Audio:Si8241CB +Amplifier_Audio:Si8244BB +Amplifier_Audio:Si8244CB +Amplifier_Audio:TAS5825MRHB +Amplifier_Audio:TDA1308 +Amplifier_Audio:TDA2003 +Amplifier_Audio:TDA2005 +Amplifier_Audio:TDA2030 +Amplifier_Audio:TDA2050 +Amplifier_Audio:TDA7052A +Amplifier_Audio:TDA7264 +Amplifier_Audio:TDA7265 +Amplifier_Audio:TDA7265B +Amplifier_Audio:TDA7266 +Amplifier_Audio:TDA7266D +Amplifier_Audio:TDA7266M +Amplifier_Audio:TDA7266P +Amplifier_Audio:TDA7269A +Amplifier_Audio:TDA7292 +Amplifier_Audio:TDA7293 +Amplifier_Audio:TDA7294 +Amplifier_Audio:TDA7295 +Amplifier_Audio:TDA7296 +Amplifier_Audio:TDA7297 +Amplifier_Audio:TDA7496 +Amplifier_Audio:TFA9879HN +Amplifier_Audio:THAT151xx08 +Amplifier_Audio:THAT2180 +Amplifier_Audio:THAT2181 +Amplifier_Audio:TPA3251 +Amplifier_Audio:TPA6110A2DGN +Amplifier_Audio:TPA6132A2RTE +Amplifier_Audio:TPA6203A1DGN +Amplifier_Audio:TPA6203A1DRB +Amplifier_Buffer:BUF602xD +Amplifier_Buffer:BUF602xDBV +Amplifier_Buffer:BUF634AxD +Amplifier_Buffer:BUF634AxDDA +Amplifier_Buffer:BUF634AxDRB +Amplifier_Buffer:BUF634U +Amplifier_Buffer:EL2001CN +Amplifier_Buffer:LH0002H +Amplifier_Buffer:LM6321H +Amplifier_Buffer:LM6321M +Amplifier_Buffer:LM6321N +Amplifier_Current:AD8202 +Amplifier_Current:AD8203 +Amplifier_Current:AD8205 +Amplifier_Current:AD8206 +Amplifier_Current:AD8208 +Amplifier_Current:AD8209 +Amplifier_Current:AD8210 +Amplifier_Current:AD8211 +Amplifier_Current:AD8212 +Amplifier_Current:AD8213 +Amplifier_Current:AD8215 +Amplifier_Current:AD8216 +Amplifier_Current:AD8217 +Amplifier_Current:AD8218xCP +Amplifier_Current:AD8218xRM +Amplifier_Current:AD8219 +Amplifier_Current:AD8417 +Amplifier_Current:AD8418 +Amplifier_Current:BQ500100DCK +Amplifier_Current:INA138 +Amplifier_Current:INA139 +Amplifier_Current:INA168 +Amplifier_Current:INA169 +Amplifier_Current:INA180A1 +Amplifier_Current:INA180A2 +Amplifier_Current:INA180A3 +Amplifier_Current:INA180A4 +Amplifier_Current:INA180B1 +Amplifier_Current:INA180B2 +Amplifier_Current:INA180B3 +Amplifier_Current:INA180B4 +Amplifier_Current:INA181 +Amplifier_Current:INA185 +Amplifier_Current:INA193 +Amplifier_Current:INA194 +Amplifier_Current:INA195 +Amplifier_Current:INA196 +Amplifier_Current:INA197 +Amplifier_Current:INA198 +Amplifier_Current:INA199xxDCK +Amplifier_Current:INA200D +Amplifier_Current:INA200DGK +Amplifier_Current:INA201D +Amplifier_Current:INA201DGK +Amplifier_Current:INA202D +Amplifier_Current:INA202DGK +Amplifier_Current:INA225 +Amplifier_Current:INA240A1D +Amplifier_Current:INA240A1PW +Amplifier_Current:INA240A2D +Amplifier_Current:INA240A2PW +Amplifier_Current:INA240A3D +Amplifier_Current:INA240A3PW +Amplifier_Current:INA240A4D +Amplifier_Current:INA240A4PW +Amplifier_Current:INA241A1xD +Amplifier_Current:INA241A1xDDF +Amplifier_Current:INA241A1xDGK +Amplifier_Current:INA241A2xD +Amplifier_Current:INA241A2xDDF +Amplifier_Current:INA241A2xDGK +Amplifier_Current:INA241A3xD +Amplifier_Current:INA241A3xDDF +Amplifier_Current:INA241A3xDGK +Amplifier_Current:INA241A4xD +Amplifier_Current:INA241A4xDDF +Amplifier_Current:INA241A4xDGK +Amplifier_Current:INA241A5xD +Amplifier_Current:INA241A5xDDF +Amplifier_Current:INA241A5xDGK +Amplifier_Current:INA241B1xD +Amplifier_Current:INA241B1xDDF +Amplifier_Current:INA241B1xDGK +Amplifier_Current:INA241B2xD +Amplifier_Current:INA241B2xDDF +Amplifier_Current:INA241B2xDGK +Amplifier_Current:INA241B3xD +Amplifier_Current:INA241B3xDDF +Amplifier_Current:INA241B3xDGK +Amplifier_Current:INA241B4xD +Amplifier_Current:INA241B4xDDF +Amplifier_Current:INA241B4xDGK +Amplifier_Current:INA241B5xD +Amplifier_Current:INA241B5xDDF +Amplifier_Current:INA241B5xDGK +Amplifier_Current:INA253 +Amplifier_Current:INA281A1 +Amplifier_Current:INA281A2 +Amplifier_Current:INA281A3 +Amplifier_Current:INA281A4 +Amplifier_Current:INA281A5 +Amplifier_Current:INA282 +Amplifier_Current:INA283 +Amplifier_Current:INA284 +Amplifier_Current:INA285 +Amplifier_Current:INA286 +Amplifier_Current:INA293A1 +Amplifier_Current:INA293A2 +Amplifier_Current:INA293A3 +Amplifier_Current:INA293A4 +Amplifier_Current:INA293A5 +Amplifier_Current:INA293B1 +Amplifier_Current:INA293B2 +Amplifier_Current:INA293B3 +Amplifier_Current:INA293B4 +Amplifier_Current:INA293B5 +Amplifier_Current:INA4180A1 +Amplifier_Current:INA4180A2 +Amplifier_Current:INA4180A3 +Amplifier_Current:INA4180A4 +Amplifier_Current:LMP8640 +Amplifier_Current:LT6106 +Amplifier_Current:LTC6102HVxDD +Amplifier_Current:LTC6102HVxMS8 +Amplifier_Current:LTC6102xDD +Amplifier_Current:LTC6102xDD-1 +Amplifier_Current:LTC6102xMS8 +Amplifier_Current:LTC6102xMS8-1 +Amplifier_Current:MAX4080F +Amplifier_Current:MAX4080S +Amplifier_Current:MAX4080T +Amplifier_Current:MAX4081F +Amplifier_Current:MAX4081S +Amplifier_Current:MAX4081T +Amplifier_Current:MAX471 +Amplifier_Current:MAX472 +Amplifier_Current:NCS210 +Amplifier_Current:NCS211 +Amplifier_Current:NCS213 +Amplifier_Current:NCS214 +Amplifier_Current:NCV210 +Amplifier_Current:NCV211 +Amplifier_Current:NCV213 +Amplifier_Current:NCV214 +Amplifier_Current:ZXCT1009F +Amplifier_Current:ZXCT1009T8 +Amplifier_Current:ZXCT1010 +Amplifier_Current:ZXCT1107 +Amplifier_Current:ZXCT1109 +Amplifier_Current:ZXCT1110 +Amplifier_Difference:AD628 +Amplifier_Difference:AD8207 +Amplifier_Difference:AD8276 +Amplifier_Difference:AD8475ACPZ +Amplifier_Difference:AD8475xRMZ +Amplifier_Difference:ADA4938-1 +Amplifier_Difference:ADA4940-1xCP +Amplifier_Difference:ADA4940-2 +Amplifier_Difference:AMC1100DWV +Amplifier_Difference:AMC1200BDWV +Amplifier_Difference:AMC1300BDWV +Amplifier_Difference:AMC1300DWV +Amplifier_Difference:INA105KP +Amplifier_Difference:INA105KU +Amplifier_Difference:LM733CH +Amplifier_Difference:LM733CN +Amplifier_Difference:LM733H +Amplifier_Difference:LTC1992-x-xMS8 +Amplifier_Difference:THS4521ID +Amplifier_Difference:THS4521IDGK +Amplifier_Difference:THS4551xRGT +Amplifier_Instrumentation:AD620 +Amplifier_Instrumentation:AD623 +Amplifier_Instrumentation:AD623AN +Amplifier_Instrumentation:AD623ANZ +Amplifier_Instrumentation:AD623AR +Amplifier_Instrumentation:AD623ARM +Amplifier_Instrumentation:AD623ARMZ +Amplifier_Instrumentation:AD623ARZ +Amplifier_Instrumentation:AD623BN +Amplifier_Instrumentation:AD623BNZ +Amplifier_Instrumentation:AD623BR +Amplifier_Instrumentation:AD623BRZ +Amplifier_Instrumentation:AD8230 +Amplifier_Instrumentation:AD8231 +Amplifier_Instrumentation:AD8236 +Amplifier_Instrumentation:AD8236ARMZ +Amplifier_Instrumentation:AD8421 +Amplifier_Instrumentation:AD8421ARMZ +Amplifier_Instrumentation:AD8421ARZ +Amplifier_Instrumentation:AD8421BRMZ +Amplifier_Instrumentation:AD8421BRZ +Amplifier_Instrumentation:AD8422 +Amplifier_Instrumentation:AD8422ARMZ +Amplifier_Instrumentation:AD8422ARZ +Amplifier_Instrumentation:AD8422BRMZ +Amplifier_Instrumentation:AD8422BRZ +Amplifier_Instrumentation:AD8429 +Amplifier_Instrumentation:AD8429ARZ +Amplifier_Instrumentation:AD8429BRZ +Amplifier_Instrumentation:INA128 +Amplifier_Instrumentation:INA129 +Amplifier_Instrumentation:INA326 +Amplifier_Instrumentation:INA327 +Amplifier_Instrumentation:INA333xxDGK +Amplifier_Instrumentation:INA333xxDRG +Amplifier_Instrumentation:INA849D +Amplifier_Instrumentation:INA849DGK +Amplifier_Instrumentation:LTC1100xN8 +Amplifier_Instrumentation:LTC1100xSW +Amplifier_Operational:AD797 +Amplifier_Operational:AD8001AN +Amplifier_Operational:AD8001AR +Amplifier_Operational:AD8015 +Amplifier_Operational:AD8021AR +Amplifier_Operational:AD8021ARM +Amplifier_Operational:AD817 +Amplifier_Operational:AD8603 +Amplifier_Operational:AD8606ARM +Amplifier_Operational:AD8606ARZ +Amplifier_Operational:AD8610xR +Amplifier_Operational:AD8610xRM +Amplifier_Operational:AD8620 +Amplifier_Operational:AD8620xRM +Amplifier_Operational:AD8655 +Amplifier_Operational:AD8656 +Amplifier_Operational:AD8676xR +Amplifier_Operational:ADA4075-2 +Amplifier_Operational:ADA4077-1xR +Amplifier_Operational:ADA4077-1xRM +Amplifier_Operational:ADA4084-4xCP +Amplifier_Operational:ADA4099-1xUJ +Amplifier_Operational:ADA4099-2xCP +Amplifier_Operational:ADA4099-2xR +Amplifier_Operational:ADA4099-2xRM +Amplifier_Operational:ADA4522-1 +Amplifier_Operational:ADA4522-2 +Amplifier_Operational:ADA4522-4 +Amplifier_Operational:ADA4530-1 +Amplifier_Operational:ADA4610-1xR +Amplifier_Operational:ADA4610-1xRJ +Amplifier_Operational:ADA4610-2xCP +Amplifier_Operational:ADA4610-2xR +Amplifier_Operational:ADA4610-2xRM +Amplifier_Operational:ADA4610-4xCP +Amplifier_Operational:ADA4610-4xR +Amplifier_Operational:ADA4622-2xCP +Amplifier_Operational:ADA4622-4xCP +Amplifier_Operational:ADA4625-1ARDZ +Amplifier_Operational:ADA4625-2ARDZ +Amplifier_Operational:ADA4807-1 +Amplifier_Operational:ADA4807-2ACP +Amplifier_Operational:ADA4807-2ARM +Amplifier_Operational:ADA4807-4ARUZ +Amplifier_Operational:ADA4817-1ACP +Amplifier_Operational:ADA4817-1ARD +Amplifier_Operational:ADA4817-2ACP +Amplifier_Operational:ADA4841-1YRJ +Amplifier_Operational:ADA4870ARRZ +Amplifier_Operational:ADA4898-1YRDZ +Amplifier_Operational:ADA4898-2 +Amplifier_Operational:AS13704 +Amplifier_Operational:CA3080 +Amplifier_Operational:CA3080A +Amplifier_Operational:CA3130 +Amplifier_Operational:CA3140 +Amplifier_Operational:HMC799LP3E +Amplifier_Operational:L272 +Amplifier_Operational:L272D +Amplifier_Operational:L272M +Amplifier_Operational:LF155 +Amplifier_Operational:LF156 +Amplifier_Operational:LF256 +Amplifier_Operational:LF257 +Amplifier_Operational:LF351D +Amplifier_Operational:LF351N +Amplifier_Operational:LF355 +Amplifier_Operational:LF356 +Amplifier_Operational:LF357 +Amplifier_Operational:LM101 +Amplifier_Operational:LM13600 +Amplifier_Operational:LM13700 +Amplifier_Operational:LM201 +Amplifier_Operational:LM2902 +Amplifier_Operational:LM2904 +Amplifier_Operational:LM301 +Amplifier_Operational:LM318H +Amplifier_Operational:LM318J +Amplifier_Operational:LM318M +Amplifier_Operational:LM318N +Amplifier_Operational:LM321 +Amplifier_Operational:LM324 +Amplifier_Operational:LM324A +Amplifier_Operational:LM358 +Amplifier_Operational:LM358_DFN +Amplifier_Operational:LM4250 +Amplifier_Operational:LM4562 +Amplifier_Operational:LM6142xIx +Amplifier_Operational:LM6144xIx +Amplifier_Operational:LM6171D +Amplifier_Operational:LM6171xxN +Amplifier_Operational:LM6172 +Amplifier_Operational:LM6361 +Amplifier_Operational:LM675 +Amplifier_Operational:LM7171xIM +Amplifier_Operational:LM7171xIN +Amplifier_Operational:LM7332 +Amplifier_Operational:LM741 +Amplifier_Operational:LM8261 +Amplifier_Operational:LMC6062 +Amplifier_Operational:LMC6082 +Amplifier_Operational:LMC6482 +Amplifier_Operational:LMC6484 +Amplifier_Operational:LMH6551MA +Amplifier_Operational:LMH6551MM +Amplifier_Operational:LMH6609MA +Amplifier_Operational:LMH6609MF +Amplifier_Operational:LMH6611 +Amplifier_Operational:LMH6702MA +Amplifier_Operational:LMH6702MF +Amplifier_Operational:LMH6733 +Amplifier_Operational:LMV321 +Amplifier_Operational:LMV324 +Amplifier_Operational:LMV358 +Amplifier_Operational:LMV601 +Amplifier_Operational:LOG114AxRGV +Amplifier_Operational:LPV811DBV +Amplifier_Operational:LPV812DGK +Amplifier_Operational:LT1012 +Amplifier_Operational:LT1363 +Amplifier_Operational:LT1492 +Amplifier_Operational:LT1493 +Amplifier_Operational:LT6015xS5 +Amplifier_Operational:LT6230xS6 +Amplifier_Operational:LT6234 +Amplifier_Operational:LT6237 +Amplifier_Operational:LTC1151CN8 +Amplifier_Operational:LTC1151CSW +Amplifier_Operational:LTC1152 +Amplifier_Operational:LTC6081xDD +Amplifier_Operational:LTC6081xMS8 +Amplifier_Operational:LTC6082xDHC +Amplifier_Operational:LTC6082xGN +Amplifier_Operational:LTC6228xDC +Amplifier_Operational:LTC6228xS6 +Amplifier_Operational:LTC6228xS8 +Amplifier_Operational:LTC6229xDD +Amplifier_Operational:LTC6229xMS8E +Amplifier_Operational:LTC6253xMS8 +Amplifier_Operational:LTC6268xS6-10 +Amplifier_Operational:LTC6268xS8-10 +Amplifier_Operational:LTC6269xDD +Amplifier_Operational:LTC6269xMS8E +Amplifier_Operational:LTC6362xDD +Amplifier_Operational:LTC6362xMS8 +Amplifier_Operational:MAX4238ASA +Amplifier_Operational:MAX4238AUT +Amplifier_Operational:MAX4239ASA +Amplifier_Operational:MAX4239AUT +Amplifier_Operational:MAX4395ESD +Amplifier_Operational:MAX4395EUD +Amplifier_Operational:MC33078 +Amplifier_Operational:MC33079 +Amplifier_Operational:MC33172 +Amplifier_Operational:MC33174 +Amplifier_Operational:MC33178 +Amplifier_Operational:MC33179 +Amplifier_Operational:MCP6001-OT +Amplifier_Operational:MCP6001R +Amplifier_Operational:MCP6001U +Amplifier_Operational:MCP6001x-LT +Amplifier_Operational:MCP6002-xMC +Amplifier_Operational:MCP6002-xMS +Amplifier_Operational:MCP6002-xP +Amplifier_Operational:MCP6002-xSN +Amplifier_Operational:MCP6004 +Amplifier_Operational:MCP601-xOT +Amplifier_Operational:MCP601-xP +Amplifier_Operational:MCP601-xSN +Amplifier_Operational:MCP601-xST +Amplifier_Operational:MCP601R +Amplifier_Operational:MCP602 +Amplifier_Operational:MCP6022 +Amplifier_Operational:MCP603-xCH +Amplifier_Operational:MCP603-xP +Amplifier_Operational:MCP603-xSN +Amplifier_Operational:MCP603-xST +Amplifier_Operational:MCP604 +Amplifier_Operational:MCP6401RT-xOT +Amplifier_Operational:MCP6401T-xLT +Amplifier_Operational:MCP6401T-xOT +Amplifier_Operational:MCP6401UT-xOT +Amplifier_Operational:MCP6L01Rx-xOT +Amplifier_Operational:MCP6L01Ux-xOT +Amplifier_Operational:MCP6L01x-xLT +Amplifier_Operational:MCP6L01x-xOT +Amplifier_Operational:MCP6L02x-xMS +Amplifier_Operational:MCP6L02x-xSN +Amplifier_Operational:MCP6L04-xST +Amplifier_Operational:MCP6L04x-xSL +Amplifier_Operational:MCP6L91RT-EOT +Amplifier_Operational:MCP6L91T-EOT +Amplifier_Operational:MCP6L92 +Amplifier_Operational:MCP6L94 +Amplifier_Operational:MCP6V67EMS +Amplifier_Operational:MCP6V67xMNY +Amplifier_Operational:NCS20071SN +Amplifier_Operational:NCS20071XV +Amplifier_Operational:NCS20072D +Amplifier_Operational:NCS20072DM +Amplifier_Operational:NCS20072DTB +Amplifier_Operational:NCS20074D +Amplifier_Operational:NCS20074DTB +Amplifier_Operational:NCS2325D +Amplifier_Operational:NCS2325DM +Amplifier_Operational:NCS325 +Amplifier_Operational:NCS4325 +Amplifier_Operational:NE5532 +Amplifier_Operational:NE5534 +Amplifier_Operational:NJM2043 +Amplifier_Operational:NJM2114 +Amplifier_Operational:NJM4556A +Amplifier_Operational:NJM4558 +Amplifier_Operational:NJM4559 +Amplifier_Operational:NJM4560 +Amplifier_Operational:NJM4580 +Amplifier_Operational:NJM5532 +Amplifier_Operational:OP07 +Amplifier_Operational:OP1177AR +Amplifier_Operational:OP1177ARM +Amplifier_Operational:OP179GRT +Amplifier_Operational:OP179GS +Amplifier_Operational:OP249 +Amplifier_Operational:OP249GS +Amplifier_Operational:OP275 +Amplifier_Operational:OP279 +Amplifier_Operational:OP77 +Amplifier_Operational:OPA121KM +Amplifier_Operational:OPA121KP +Amplifier_Operational:OPA121KU +Amplifier_Operational:OPA134 +Amplifier_Operational:OPA1602 +Amplifier_Operational:OPA1604 +Amplifier_Operational:OPA1612AxD +Amplifier_Operational:OPA1641 +Amplifier_Operational:OPA1655D +Amplifier_Operational:OPA1655DBV +Amplifier_Operational:OPA1656ID +Amplifier_Operational:OPA1678 +Amplifier_Operational:OPA1679 +Amplifier_Operational:OPA1692xD +Amplifier_Operational:OPA1692xDGK +Amplifier_Operational:OPA188xxD +Amplifier_Operational:OPA188xxDBV +Amplifier_Operational:OPA196xD +Amplifier_Operational:OPA196xDBV +Amplifier_Operational:OPA196xDGK +Amplifier_Operational:OPA197xD +Amplifier_Operational:OPA197xDBV +Amplifier_Operational:OPA197xDGK +Amplifier_Operational:OPA2134 +Amplifier_Operational:OPA2156xD +Amplifier_Operational:OPA2156xDGK +Amplifier_Operational:OPA2196xD +Amplifier_Operational:OPA2196xDGK +Amplifier_Operational:OPA2197xD +Amplifier_Operational:OPA2197xDGK +Amplifier_Operational:OPA2277 +Amplifier_Operational:OPA2325 +Amplifier_Operational:OPA2333xxD +Amplifier_Operational:OPA2333xxDGK +Amplifier_Operational:OPA2333xxDRB +Amplifier_Operational:OPA2340 +Amplifier_Operational:OPA2356xxD +Amplifier_Operational:OPA2356xxDGK +Amplifier_Operational:OPA2376xxD +Amplifier_Operational:OPA2376xxDGK +Amplifier_Operational:OPA2376xxYZD +Amplifier_Operational:OPA2604AP +Amplifier_Operational:OPA2691 +Amplifier_Operational:OPA2691-14 +Amplifier_Operational:OPA2695xD +Amplifier_Operational:OPA2695xRGT +Amplifier_Operational:OPA2890ID +Amplifier_Operational:OPA2890IDGS +Amplifier_Operational:OPA2994xD +Amplifier_Operational:OPA310SxDBV +Amplifier_Operational:OPA310SxDCK +Amplifier_Operational:OPA310xDBV +Amplifier_Operational:OPA310xDCK +Amplifier_Operational:OPA330xxD +Amplifier_Operational:OPA330xxDBV +Amplifier_Operational:OPA330xxDCK +Amplifier_Operational:OPA330xxYFF +Amplifier_Operational:OPA333xxD +Amplifier_Operational:OPA333xxDBV +Amplifier_Operational:OPA333xxDCK +Amplifier_Operational:OPA336Nx +Amplifier_Operational:OPA336Ux +Amplifier_Operational:OPA340NA +Amplifier_Operational:OPA340P +Amplifier_Operational:OPA340UA +Amplifier_Operational:OPA355NA +Amplifier_Operational:OPA356xxD +Amplifier_Operational:OPA356xxDBV +Amplifier_Operational:OPA365xxD +Amplifier_Operational:OPA365xxDBV +Amplifier_Operational:OPA376xxD +Amplifier_Operational:OPA376xxDBV +Amplifier_Operational:OPA376xxDCK +Amplifier_Operational:OPA4134 +Amplifier_Operational:OPA4196xD +Amplifier_Operational:OPA4196xPW +Amplifier_Operational:OPA4197xD +Amplifier_Operational:OPA4197xPW +Amplifier_Operational:OPA4340EA +Amplifier_Operational:OPA4340UA +Amplifier_Operational:OPA4376 +Amplifier_Operational:OPA551P +Amplifier_Operational:OPA551U +Amplifier_Operational:OPA552P +Amplifier_Operational:OPA552U +Amplifier_Operational:OPA569DWP +Amplifier_Operational:OPA690xD +Amplifier_Operational:OPA690xDBV +Amplifier_Operational:OPA810xD +Amplifier_Operational:OPA810xDBV +Amplifier_Operational:OPA810xDCK +Amplifier_Operational:OPA818xDRG +Amplifier_Operational:OPA842xD +Amplifier_Operational:OPA842xDBV +Amplifier_Operational:OPA843xD +Amplifier_Operational:OPA843xDBV +Amplifier_Operational:OPA846xD +Amplifier_Operational:OPA846xDBV +Amplifier_Operational:OPA847xD +Amplifier_Operational:OPA847xDBV +Amplifier_Operational:OPA855xDSG +Amplifier_Operational:OPA858xDSG +Amplifier_Operational:OPA859xDSG +Amplifier_Operational:OPA890xD +Amplifier_Operational:OPA890xDBV +Amplifier_Operational:RC4558 +Amplifier_Operational:RC4560 +Amplifier_Operational:RC4580 +Amplifier_Operational:SA5532 +Amplifier_Operational:SA5534 +Amplifier_Operational:THS3491xDDA +Amplifier_Operational:THS4631D +Amplifier_Operational:THS4631DDA +Amplifier_Operational:THS4631DGN +Amplifier_Operational:TL061 +Amplifier_Operational:TL062 +Amplifier_Operational:TL064 +Amplifier_Operational:TL071 +Amplifier_Operational:TL072 +Amplifier_Operational:TL074 +Amplifier_Operational:TL081 +Amplifier_Operational:TL082 +Amplifier_Operational:TL084 +Amplifier_Operational:TLC272 +Amplifier_Operational:TLC274 +Amplifier_Operational:TLC277 +Amplifier_Operational:TLC279 +Amplifier_Operational:TLC27M2xD +Amplifier_Operational:TLC27M2xPS +Amplifier_Operational:TLC27M2xPW +Amplifier_Operational:TLC27M7xD +Amplifier_Operational:TLC27M7xPS +Amplifier_Operational:TLE2141ACD +Amplifier_Operational:TLE2141ACP +Amplifier_Operational:TLE2141AID +Amplifier_Operational:TLE2141AIP +Amplifier_Operational:TLE2141CD +Amplifier_Operational:TLE2141CP +Amplifier_Operational:TLE2141ID +Amplifier_Operational:TLE2141IP +Amplifier_Operational:TLE2141MD +Amplifier_Operational:TLV172IDCK +Amplifier_Operational:TLV2371D +Amplifier_Operational:TLV2371DBV +Amplifier_Operational:TLV2371P +Amplifier_Operational:TLV2372 +Amplifier_Operational:TLV6001DCK +Amplifier_Operational:TLV9001IDCK +Amplifier_Operational:TLV9004xRUCR +Amplifier_Operational:TLV9061xDBV +Amplifier_Operational:TLV9061xDCK +Amplifier_Operational:TLV9061xDPW +Amplifier_Operational:TLV9062 +Amplifier_Operational:TLV9062xD +Amplifier_Operational:TLV9062xDSG +Amplifier_Operational:TLV9064 +Amplifier_Operational:TLV9064xRTE +Amplifier_Operational:TLV9301xDBV +Amplifier_Operational:TLV9301xDCK +Amplifier_Operational:TLV9302xD +Amplifier_Operational:TLV9302xDDF +Amplifier_Operational:TLV9302xDGK +Amplifier_Operational:TLV9302xPW +Amplifier_Operational:TLV9304xD +Amplifier_Operational:TLV9304xPW +Amplifier_Operational:TS881xCx +Amplifier_Operational:TS881xLx +Amplifier_Operational:TS912 +Amplifier_Operational:TSV524xIQ4T +Amplifier_Operational:TSV911IDT +Amplifier_Operational:TSV911RILT +Amplifier_Operational:TSV911xxLx +Amplifier_Operational:TSV912IDT +Amplifier_Operational:TSV912IQ2T +Amplifier_Operational:TSV912IST +Amplifier_Operational:TSV914 +Amplifier_Operational:TSV991AILT +Amplifier_Operational:TSV991AIQ1T +Amplifier_Operational:TSV994 +Amplifier_Video:AD813 +Amplifier_Video:MAX453 +Amplifier_Video:THS7374 +Analog:AD5593R +Analog:AD630ARZ +Analog:AD637xQ +Analog:AD637xRZ +Analog:AD654JN +Analog:AD654JR +Analog:LF398H +Analog:LF398_DIP8 +Analog:LF398_SOIC14 +Analog:LF398_SOIC8 +Analog:LM231N +Analog:LM331N +Analog:LTC1966 +Analog:LTC1967 +Analog:LTC1968 +Analog:MLX90314xDF +Analog:MLX90320xFR +Analog:MPY634KP +Analog:MPY634KU +Analog:PGA112 +Analog:PGA113 +Analog_ADC:AD40xxBCPZ +Analog_ADC:AD40xxBRMZ +Analog_ADC:AD574A +Analog_ADC:AD6644 +Analog_ADC:AD6645 +Analog_ADC:AD7171 +Analog_ADC:AD7298 +Analog_ADC:AD7321 +Analog_ADC:AD7322 +Analog_ADC:AD7323 +Analog_ADC:AD7324 +Analog_ADC:AD7327 +Analog_ADC:AD7328 +Analog_ADC:AD7329 +Analog_ADC:AD7606 +Analog_ADC:AD7606-4 +Analog_ADC:AD7606-6 +Analog_ADC:AD7616 +Analog_ADC:AD7682BCP +Analog_ADC:AD7689xCP +Analog_ADC:AD7699BCP +Analog_ADC:AD7722 +Analog_ADC:AD7745 +Analog_ADC:AD7746 +Analog_ADC:AD7794 +Analog_ADC:AD7795 +Analog_ADC:AD7819 +Analog_ADC:AD7949BCP +Analog_ADC:AD9280ARS +Analog_ADC:AD9283 +Analog_ADC:ADC0800 +Analog_ADC:ADC08060 +Analog_ADC:ADC081C021CIMM +Analog_ADC:ADC0832 +Analog_ADC:ADC101C021CIMK +Analog_ADC:ADC101C021CIMM +Analog_ADC:ADC1173 +Analog_ADC:ADC121C021CIMM +Analog_ADC:ADC1283 +Analog_ADC:ADC128D818 +Analog_ADC:ADS1013IDGS +Analog_ADC:ADS1014IDGS +Analog_ADC:ADS1015IDGS +Analog_ADC:ADS1018IDGS +Analog_ADC:ADS1110 +Analog_ADC:ADS1113IDGS +Analog_ADC:ADS1114IDGS +Analog_ADC:ADS1115IDGS +Analog_ADC:ADS1118IDGS +Analog_ADC:ADS1120-PW +Analog_ADC:ADS1120-RVA +Analog_ADC:ADS1220xPW +Analog_ADC:ADS1232IPW +Analog_ADC:ADS1234IPW +Analog_ADC:ADS1243 +Analog_ADC:ADS1251 +Analog_ADC:ADS127L01IPBS +Analog_ADC:ADS1298xPAG +Analog_ADC:ADS7029 +Analog_ADC:ADS7039 +Analog_ADC:ADS7040xDCU +Analog_ADC:ADS7041xDCU +Analog_ADC:ADS7042xDCU +Analog_ADC:ADS7043xDCU +Analog_ADC:ADS7044xDCU +Analog_ADC:ADS7049 +Analog_ADC:ADS7828 +Analog_ADC:ADS7866 +Analog_ADC:ADS7867 +Analog_ADC:ADS7868 +Analog_ADC:ADS8681RUM +Analog_ADC:ADS8684 +Analog_ADC:ADS8685RUM +Analog_ADC:ADS8688 +Analog_ADC:ADS8689RUM +Analog_ADC:AMC3336 +Analog_ADC:CA3300 +Analog_ADC:HX711 +Analog_ADC:ICL7106CPL +Analog_ADC:ICL7107CPL +Analog_ADC:INA234AxYBJ +Analog_ADC:LTC1406CGN +Analog_ADC:LTC1406IGN +Analog_ADC:LTC1594CS +Analog_ADC:LTC1594IS +Analog_ADC:LTC1598CG +Analog_ADC:LTC1598IG +Analog_ADC:LTC1742 +Analog_ADC:LTC1744 +Analog_ADC:LTC1746 +Analog_ADC:LTC1748 +Analog_ADC:LTC1864 +Analog_ADC:LTC1864L +Analog_ADC:LTC1865-MS +Analog_ADC:LTC1865-S8 +Analog_ADC:LTC1865L-MS +Analog_ADC:LTC1865L-S8 +Analog_ADC:LTC2282xUP +Analog_ADC:LTC2284xUP +Analog_ADC:LTC2290xUP +Analog_ADC:LTC2291xUP +Analog_ADC:LTC2292xUP +Analog_ADC:LTC2293xUP +Analog_ADC:LTC2294xUP +Analog_ADC:LTC2295xUP +Analog_ADC:LTC2296xUP +Analog_ADC:LTC2297xUP +Analog_ADC:LTC2298xUP +Analog_ADC:LTC2299xUP +Analog_ADC:LTC2309xF +Analog_ADC:LTC2309xUF +Analog_ADC:LTC2311-16 +Analog_ADC:LTC2325-16 +Analog_ADC:LTC2358-16 +Analog_ADC:LTC2358-18 +Analog_ADC:LTC2451xDDB +Analog_ADC:LTC2451xTS8 +Analog_ADC:LTC2508CDKD-32 +Analog_ADC:LTC2508IDKD-32 +Analog_ADC:MAX1112 +Analog_ADC:MAX11120xTI +Analog_ADC:MAX11121xTI +Analog_ADC:MAX11122xTI +Analog_ADC:MAX11123xTI +Analog_ADC:MAX11124xTI +Analog_ADC:MAX11125xTI +Analog_ADC:MAX11126xTI +Analog_ADC:MAX11127xTI +Analog_ADC:MAX11128xTI +Analog_ADC:MAX1113 +Analog_ADC:MAX11612 +Analog_ADC:MAX11613 +Analog_ADC:MAX11614 +Analog_ADC:MAX11615 +Analog_ADC:MAX11616 +Analog_ADC:MAX11617 +Analog_ADC:MAX1248 +Analog_ADC:MAX1249 +Analog_ADC:MAX1274 +Analog_ADC:MAX1275 +Analog_ADC:MCP3002 +Analog_ADC:MCP3004 +Analog_ADC:MCP3008 +Analog_ADC:MCP3201 +Analog_ADC:MCP3202 +Analog_ADC:MCP3204 +Analog_ADC:MCP3208 +Analog_ADC:MCP3221 +Analog_ADC:MCP3301 +Analog_ADC:MCP3421A0T-ECH +Analog_ADC:MCP3422Axx-xMS +Analog_ADC:MCP3422Axx-xSN +Analog_ADC:MCP3423x-xUN +Analog_ADC:MCP3424x-xSL +Analog_ADC:MCP3424x-xST +Analog_ADC:MCP3425Axx-xCH +Analog_ADC:MCP3426Axx-xMC +Analog_ADC:MCP3426Axx-xMS +Analog_ADC:MCP3426Axx-xSN +Analog_ADC:MCP3427x-xMF +Analog_ADC:MCP3427x-xUN +Analog_ADC:MCP3428x-xSL +Analog_ADC:MCP3428x-xST +Analog_ADC:MCP3550-50-EMS +Analog_ADC:MCP3550-60-ESN +Analog_ADC:MCP3551-EMS +Analog_ADC:MCP3553-ESN +Analog_DAC:AD390JD +Analog_DAC:AD390KD +Analog_DAC:AD558JN +Analog_DAC:AD558JP +Analog_DAC:AD558KN +Analog_DAC:AD558KP +Analog_DAC:AD5687BCPZ +Analog_DAC:AD5687BRUZ +Analog_DAC:AD5687RBCPZ +Analog_DAC:AD5687RBRUZ +Analog_DAC:AD5689BCPZ +Analog_DAC:AD5689BRUZ +Analog_DAC:AD5689RxCPZ +Analog_DAC:AD5689RxRUZ +Analog_DAC:AD5691RxRM +Analog_DAC:AD5692RxRM +Analog_DAC:AD5693RxRM +Analog_DAC:AD5697RBCPZ +Analog_DAC:AD5697RBRUZ +Analog_DAC:AD5781xRUZ +Analog_DAC:AD5791xRUZ +Analog_DAC:AD7224KN +Analog_DAC:AD7224KP +Analog_DAC:AD7224KR-1 +Analog_DAC:AD7224KR-18 +Analog_DAC:AD7224LN +Analog_DAC:AD7224LP +Analog_DAC:AD7224LR-1 +Analog_DAC:AD7224LR-18 +Analog_DAC:AD7225BRS +Analog_DAC:AD7225CRS +Analog_DAC:AD7225KN +Analog_DAC:AD7225KP +Analog_DAC:AD7225KR +Analog_DAC:AD7225LN +Analog_DAC:AD7225LP +Analog_DAC:AD7225LR +Analog_DAC:AD7226BRSZ +Analog_DAC:AD7226KN +Analog_DAC:AD7226KP +Analog_DAC:AD7226KR +Analog_DAC:AD7228ABN +Analog_DAC:AD7228ABP +Analog_DAC:AD7228ABR +Analog_DAC:AD7228ACN +Analog_DAC:AD7228ACP +Analog_DAC:AD7228ACR +Analog_DAC:AD7304 +Analog_DAC:AD7305 +Analog_DAC:AD7390 +Analog_DAC:AD7391 +Analog_DAC:AD7533JN +Analog_DAC:AD7533JP +Analog_DAC:AD7533KN +Analog_DAC:AD7533KP +Analog_DAC:AD7533KR +Analog_DAC:AD7533LN +Analog_DAC:AD775 +Analog_DAC:AD9106BCP +Analog_DAC:AD9142 +Analog_DAC:AD9744 +Analog_DAC:ADS7830 +Analog_DAC:CS434x-xZZ +Analog_DAC:DAC08 +Analog_DAC:DAC0808_DIP +Analog_DAC:DAC0808_SOIC +Analog_DAC:DAC081C081CIMK +Analog_DAC:DAC1006LCN +Analog_DAC:DAC1006LCWM +Analog_DAC:DAC1007LCN +Analog_DAC:DAC1008LCN +Analog_DAC:DAC101C081CIMK +Analog_DAC:DAC121C081CIMK +Analog_DAC:DAC1220E +Analog_DAC:DAC5311xDCK +Analog_DAC:DAC5578xPW +Analog_DAC:DAC5578xRGE +Analog_DAC:DAC60502 +Analog_DAC:DAC60504 +Analog_DAC:DAC6311xDCK +Analog_DAC:DAC6578xPW +Analog_DAC:DAC6578xRGE +Analog_DAC:DAC70502 +Analog_DAC:DAC70504 +Analog_DAC:DAC7311xDCK +Analog_DAC:DAC7513_DCN +Analog_DAC:DAC7565 +Analog_DAC:DAC7578xPW +Analog_DAC:DAC7578xRGE +Analog_DAC:DAC7750xRHA +Analog_DAC:DAC80502 +Analog_DAC:DAC80504 +Analog_DAC:DAC8165 +Analog_DAC:DAC8501E +Analog_DAC:DAC8531E +Analog_DAC:DAC8531IDRB +Analog_DAC:DAC8550IxDGK +Analog_DAC:DAC8551IxDGK +Analog_DAC:DAC8552 +Analog_DAC:DAC8560IxDGK +Analog_DAC:DAC8565 +Analog_DAC:DAC8571IDGK +Analog_DAC:DAC8750xRHA +Analog_DAC:LTC1257 +Analog_DAC:LTC1446 +Analog_DAC:LTC1446L +Analog_DAC:LTC1664CGN +Analog_DAC:LTC1664CN +Analog_DAC:LTC1664IGN +Analog_DAC:LTC1664IN +Analog_DAC:MAX5138 +Analog_DAC:MAX5139 +Analog_DAC:MAX5215 +Analog_DAC:MAX5217 +Analog_DAC:MAX5717xSD +Analog_DAC:MAX5719xSD +Analog_DAC:MAX5741 +Analog_DAC:MAX5813 +Analog_DAC:MAX5813WLP +Analog_DAC:MAX5814 +Analog_DAC:MAX5814WLP +Analog_DAC:MAX5815 +Analog_DAC:MAX5815WLP +Analog_DAC:MC1408_DIP +Analog_DAC:MC1408_SOIC +Analog_DAC:MCP4725xxx-xCH +Analog_DAC:MCP4728 +Analog_DAC:MCP4801 +Analog_DAC:MCP4801-EMC +Analog_DAC:MCP4802 +Analog_DAC:MCP4811 +Analog_DAC:MCP4811-EMC +Analog_DAC:MCP4812 +Analog_DAC:MCP4821 +Analog_DAC:MCP4821-EMC +Analog_DAC:MCP4822 +Analog_DAC:MCP4901 +Analog_DAC:MCP4901-EMC +Analog_DAC:MCP4902 +Analog_DAC:MCP4911 +Analog_DAC:MCP4911-EMC +Analog_DAC:MCP4912 +Analog_DAC:MCP4921 +Analog_DAC:MCP4921-EMC +Analog_DAC:MCP4921-EMS +Analog_DAC:MCP4921-EP +Analog_DAC:MCP4921-ESN +Analog_DAC:MCP4922 +Analog_DAC:MCP4922-EP +Analog_DAC:MCP4922-ESL +Analog_DAC:MCP4922-EST +Analog_DAC:THS5641AxDW +Analog_DAC:THS5641AxPW +Analog_DAC:TLV5627CD +Analog_DAC:TLV5627CPW +Analog_Switch:ADG1207BCPZ +Analog_Switch:ADG1408YRUZ +Analog_Switch:ADG1414BRU +Analog_Switch:ADG1607xCP +Analog_Switch:ADG417BN +Analog_Switch:ADG417BR +Analog_Switch:ADG419BN +Analog_Switch:ADG419BR +Analog_Switch:ADG419BRM +Analog_Switch:ADG633YCP +Analog_Switch:ADG633YRU +Analog_Switch:ADG658YCP +Analog_Switch:ADG707BRU +Analog_Switch:ADG715 +Analog_Switch:ADG728 +Analog_Switch:ADG729 +Analog_Switch:ADG733BRQ +Analog_Switch:ADG733BRU +Analog_Switch:ADG734 +Analog_Switch:ADG758CPZ +Analog_Switch:ADG824BCP +Analog_Switch:ADG884xCP +Analog_Switch:ADG884xRM +Analog_Switch:CBTL02043A +Analog_Switch:CBTL02043B +Analog_Switch:CD4051B +Analog_Switch:CD4052B +Analog_Switch:CD4053B +Analog_Switch:CD4066BE +Analog_Switch:CD4066BM +Analog_Switch:CD4066BNS +Analog_Switch:CD4066BPW +Analog_Switch:CD4097B +Analog_Switch:DG308AxJ +Analog_Switch:DG308AxY +Analog_Switch:DG309xJ +Analog_Switch:DG309xY +Analog_Switch:DG411xJ +Analog_Switch:DG411xUE +Analog_Switch:DG411xY +Analog_Switch:DG412xJ +Analog_Switch:DG412xUE +Analog_Switch:DG412xY +Analog_Switch:DG413xJ +Analog_Switch:DG413xUE +Analog_Switch:DG413xY +Analog_Switch:DG417LDJ +Analog_Switch:DG417LDJ_Maxim +Analog_Switch:DG417LDY +Analog_Switch:DG417LDY_Maxim +Analog_Switch:DG417LEUA +Analog_Switch:DG417LEUA_Maxim +Analog_Switch:DG417xJ +Analog_Switch:DG417xY +Analog_Switch:DG418LDJ +Analog_Switch:DG418LDJ_Maxim +Analog_Switch:DG418LDY +Analog_Switch:DG418LDY_Maxim +Analog_Switch:DG418LEUA +Analog_Switch:DG418LEUA_Maxim +Analog_Switch:DG418xJ +Analog_Switch:DG418xY +Analog_Switch:DG419LDJ +Analog_Switch:DG419LDJ_Maxim +Analog_Switch:DG419LDY +Analog_Switch:DG419LDY_Maxim +Analog_Switch:DG419LEUA +Analog_Switch:DG419LEUA_Maxim +Analog_Switch:DG419xJ +Analog_Switch:DG419xY +Analog_Switch:DG441xJ +Analog_Switch:DG441xY +Analog_Switch:DG442xJ +Analog_Switch:DG442xY +Analog_Switch:DG884DN +Analog_Switch:DG9421DV +Analog_Switch:DG9422DV +Analog_Switch:FSA3157L6X +Analog_Switch:FSA3157P6X +Analog_Switch:HEF4066BT +Analog_Switch:HI524 +Analog_Switch:MAX14662 +Analog_Switch:MAX14759 +Analog_Switch:MAX14761 +Analog_Switch:MAX14778 +Analog_Switch:MAX312CPE +Analog_Switch:MAX312CSE +Analog_Switch:MAX312CUE +Analog_Switch:MAX313CPE +Analog_Switch:MAX313CSE +Analog_Switch:MAX313CUE +Analog_Switch:MAX314CPE +Analog_Switch:MAX314CSE +Analog_Switch:MAX314CUE +Analog_Switch:MAX317xPA +Analog_Switch:MAX317xSA +Analog_Switch:MAX318xPA +Analog_Switch:MAX318xSA +Analog_Switch:MAX319xPA +Analog_Switch:MAX319xSA +Analog_Switch:MAX323CPA +Analog_Switch:MAX323CSA +Analog_Switch:MAX323CUA +Analog_Switch:MAX324CPA +Analog_Switch:MAX324CSA +Analog_Switch:MAX324CUA +Analog_Switch:MAX325CPA +Analog_Switch:MAX325CSA +Analog_Switch:MAX325CUA +Analog_Switch:MAX333 +Analog_Switch:MAX333A +Analog_Switch:MAX394 +Analog_Switch:MAX40200ANS +Analog_Switch:MAX40200AUK +Analog_Switch:NC7SB3157L6X +Analog_Switch:NC7SB3157P6X +Analog_Switch:NX3L4051HR +Analog_Switch:NX3L4051PW +Analog_Switch:SN74CBT3253 +Analog_Switch:TMUX1101DBV +Analog_Switch:TMUX1101DCK +Analog_Switch:TMUX1102DBV +Analog_Switch:TMUX1102DCK +Analog_Switch:TMUX1108PW +Analog_Switch:TMUX154EDGS +Analog_Switch:TMUX154ERSW +Analog_Switch:TS3A24159DGS +Analog_Switch:TS3A24159DRC +Analog_Switch:TS3A24159YZP +Analog_Switch:TS3A27518EPW +Analog_Switch:TS3A27518ERTW +Analog_Switch:TS3A5017RGY +Analog_Switch:TS3A5017RSV +Analog_Switch:TS3A5223RSW +Analog_Switch:TS3DS10224RUK +Analog_Switch:TS3L501ERUA +Analog_Switch:TS5A23159DGS +Analog_Switch:TS5A23159RSE +Analog_Switch:TS5A3159ADBVR +Analog_Switch:TS5A3159ADCK +Analog_Switch:TS5A3159AYZPR +Analog_Switch:TS5A3159DBV +Analog_Switch:TS5A3159DCK +Analog_Switch:TS5A3160DBV +Analog_Switch:TS5A3160DCK +Analog_Switch:TS5A3166DBVR +Analog_Switch:TS5A3166DCKR +Analog_Switch:TS5A63157DBV +Audio:AD1853 +Audio:AD1855 +Audio:AD1955 +Audio:ADAU1978xBCP +Audio:ADAU1979xBCP +Audio:AK5392VS +Audio:AK5393VS +Audio:AK5394AVS +Audio:AK5720VT +Audio:AK7742EQ +Audio:AS3310 +Audio:AS3320 +Audio:AS3320F +Audio:AS3330 +Audio:AS3330F +Audio:AS3340 +Audio:AS3345 +Audio:AS3345F +Audio:AS3360 +Audio:CS4245 +Audio:CS4265 +Audio:CS4270 +Audio:CS4272 +Audio:CS4334 +Audio:CS4344 +Audio:CS4345 +Audio:CS4348 +Audio:CS43L21 +Audio:CS5343 +Audio:CS5344 +Audio:CS5361 +Audio:CS8406 +Audio:CS8414 +Audio:CS8416-xNZ +Audio:CS8416-xSZ +Audio:CS8416-xZZ +Audio:CS8420 +Audio:DSD1794A +Audio:ISD25120P +Audio:ISD25120S +Audio:ISD2560E +Audio:ISD2560P +Audio:ISD2560S +Audio:ISD2575E +Audio:ISD2575P +Audio:ISD2575S +Audio:ISD2590E +Audio:ISD2590P +Audio:ISD2590S +Audio:MAX98357A +Audio:MAX98357B +Audio:MN3005 +Audio:MN3007 +Audio:MN3207 +Audio:MSGEQ7 +Audio:PCM1754DBQ +Audio:PCM1780 +Audio:PCM1792A +Audio:PCM1794A +Audio:PCM2902 +Audio:PCM3060 +Audio:PCM5100 +Audio:PCM5100A +Audio:PCM5101 +Audio:PCM5101A +Audio:PCM5102 +Audio:PCM5102A +Audio:PCM5121PW +Audio:PCM5122PW +Audio:PGA2310PA +Audio:PGA2310UA +Audio:PGA2500 +Audio:PGA4311 +Audio:PT2258 +Audio:PT2258-S +Audio:PT2399 +Audio:RD5106A +Audio:RD5107A +Audio:RE46C317 +Audio:RE46C318 +Audio:SAD1024 +Audio:SAD512 +Audio:SGTL5000XNAA3 +Audio:SGTL5000XNLA3 +Audio:SPN1001 +Audio:SRC4392xPFB +Audio:SSI2144 +Audio:SSI2164 +Audio:TDA1022 +Audio:THAT1580 +Audio:THAT1583 +Audio:THAT5171 +Audio:THAT5173 +Audio:THAT5263 +Audio:THAT6261 +Audio:THAT6262 +Audio:THAT6263 +Audio:TLV320AIC23BPW +Audio:TLV320AIC23BRHD +Audio:TLV320AIC23BxQE +Audio:TLV320AIC3100 +Audio:TPA5050 +Audio:UDA1334ATS +Audio:WM8731CLSEFL +Audio:WM8731CSEFL +Audio:WM8731SEDS +Audio:YM2149 +Battery_Management:ADP5063 +Battery_Management:ADP5090ACP +Battery_Management:ADP5091 +Battery_Management:ADP5092 +Battery_Management:AP9101CK +Battery_Management:AP9101CK6 +Battery_Management:APW7261 +Battery_Management:AS8506C +Battery_Management:BQ2003 +Battery_Management:BQ21040DBV +Battery_Management:BQ24004 +Battery_Management:BQ24005 +Battery_Management:BQ24006 +Battery_Management:BQ24012 +Battery_Management:BQ24013 +Battery_Management:BQ24072RGT +Battery_Management:BQ24073RGT +Battery_Management:BQ24074RGT +Battery_Management:BQ24075RGT +Battery_Management:BQ24079RGT +Battery_Management:BQ24090DGQ +Battery_Management:BQ24133RGY +Battery_Management:BQ24166RGE +Battery_Management:BQ24167RGE +Battery_Management:BQ24610 +Battery_Management:BQ24617 +Battery_Management:BQ24650 +Battery_Management:BQ2501x +Battery_Management:BQ25040 +Battery_Management:BQ25173DSG +Battery_Management:BQ25504 +Battery_Management:BQ25570 +Battery_Management:BQ25601 +Battery_Management:BQ25886RGE +Battery_Management:BQ25887 +Battery_Management:BQ25895RTW +Battery_Management:BQ27441-G1 +Battery_Management:BQ27441DRZR-G1A +Battery_Management:BQ27441DRZR-G1B +Battery_Management:BQ27441DRZT-G1A +Battery_Management:BQ27441DRZT-G1B +Battery_Management:BQ27750 +Battery_Management:BQ297xy +Battery_Management:BQ51050BRHL +Battery_Management:BQ51050BYFP +Battery_Management:BQ51051BRHL +Battery_Management:BQ51051BYFP +Battery_Management:BQ51052BYFP +Battery_Management:BQ76200PW +Battery_Management:BQ76920PW +Battery_Management:BQ76930DBT +Battery_Management:BQ76940DBT +Battery_Management:BQ78350DBT +Battery_Management:BQ78350DBT-R1 +Battery_Management:DS2745U +Battery_Management:DW01A +Battery_Management:LC709203FQH-01TWG +Battery_Management:LC709203FQH-02TWG +Battery_Management:LC709203FQH-03TWG +Battery_Management:LC709203FQH-04TWG +Battery_Management:LGS5500EP +Battery_Management:LP3947 +Battery_Management:LT3652EDD +Battery_Management:LT3652EMSE +Battery_Management:LT3652IDD +Battery_Management:LT3652IMSE +Battery_Management:LTC2942 +Battery_Management:LTC2942-1 +Battery_Management:LTC2959 +Battery_Management:LTC3553 +Battery_Management:LTC3555 +Battery_Management:LTC3555-1 +Battery_Management:LTC3555-3 +Battery_Management:LTC4001 +Battery_Management:LTC4001-1 +Battery_Management:LTC4002EDD-4.2 +Battery_Management:LTC4002EDD-8.4 +Battery_Management:LTC4002ES8-4.2 +Battery_Management:LTC4002ES8-8.4 +Battery_Management:LTC4007 +Battery_Management:LTC4011CFE +Battery_Management:LTC4054ES5-4.2 +Battery_Management:LTC4054XES5-4.2 +Battery_Management:LTC4055 +Battery_Management:LTC4055-1 +Battery_Management:LTC4060EDHC +Battery_Management:LTC4060EFE +Battery_Management:LTC4067EDE +Battery_Management:LTC4156 +Battery_Management:LTC6803-2 +Battery_Management:LTC6803-4 +Battery_Management:LTC6804-1 +Battery_Management:MAX1647 +Battery_Management:MAX1648 +Battery_Management:MAX17261xxTD +Battery_Management:MAX17261xxWL +Battery_Management:MAX17263xxTE +Battery_Management:MAX1811 +Battery_Management:MAX1873REEE +Battery_Management:MAX1873SEEE +Battery_Management:MAX1873TEEE +Battery_Management:MAX712CPE +Battery_Management:MAX712CSE +Battery_Management:MAX712EPE +Battery_Management:MAX712ESE +Battery_Management:MAX712MJE +Battery_Management:MAX713CPE +Battery_Management:MAX713CSE +Battery_Management:MAX713EPE +Battery_Management:MAX713ESE +Battery_Management:MAX713MJE +Battery_Management:MC34673 +Battery_Management:MCP73811T-420I-OT +Battery_Management:MCP73811T-435I-OT +Battery_Management:MCP73812T-420I-OT +Battery_Management:MCP73812T-435I-OT +Battery_Management:MCP73831-2-MC +Battery_Management:MCP73831-2-OT +Battery_Management:MCP73831-3-MC +Battery_Management:MCP73831-3-OT +Battery_Management:MCP73831-4-MC +Battery_Management:MCP73831-4-OT +Battery_Management:MCP73831-5-MC +Battery_Management:MCP73831-5-OT +Battery_Management:MCP73832-2-MC +Battery_Management:MCP73832-2-OT +Battery_Management:MCP73832-3-MC +Battery_Management:MCP73832-3-OT +Battery_Management:MCP73832-4-MC +Battery_Management:MCP73832-4-OT +Battery_Management:MCP73832-5-MC +Battery_Management:MCP73832-5-OT +Battery_Management:MCP73833-xxx-MF +Battery_Management:MCP73833-xxx-UN +Battery_Management:MCP73871 +Battery_Management:MCP73871-1AA +Battery_Management:MCP73871-1CA +Battery_Management:MCP73871-1CC +Battery_Management:MCP73871-2AA +Battery_Management:MCP73871-2CA +Battery_Management:MCP73871-2CC +Battery_Management:MCP73871-3CA +Battery_Management:MCP73871-3CC +Battery_Management:MCP73871-4CA +Battery_Management:MCP73871-4CC +Battery_Management:SLM6800 +Battery_Management:TP4056-42-ESOP8 +Battery_Management:TP4057 +Buffer:CDCV304 +Buffer:PI6C5946002ZH +Comparator:AD8561 +Comparator:ADCMP350 +Comparator:ADCMP354 +Comparator:ADCMP356 +Comparator:LM2901 +Comparator:LM2903 +Comparator:LM311 +Comparator:LM319 +Comparator:LM319H +Comparator:LM339 +Comparator:LM393 +Comparator:LM397 +Comparator:LMH7324 +Comparator:LMV331 +Comparator:LMV339 +Comparator:LMV393 +Comparator:LMV7219M5 +Comparator:LMV7219M7 +Comparator:LMV7271 +Comparator:LMV7272 +Comparator:LMV7275 +Comparator:LP2901D +Comparator:LT1011 +Comparator:LT1016 +Comparator:LT1116 +Comparator:LT1711xMS8 +Comparator:LTC6752xMS8-2 +Comparator:LTC6752xS5 +Comparator:LTC6752xSC6-1 +Comparator:LTC6752xSC6-4 +Comparator:LTC6752xUD-3 +Comparator:LTC6754xSC6 +Comparator:LTC6754xUD +Comparator:MAX9031AU +Comparator:MAX9031AX +Comparator:MAX941xPA +Comparator:MAX941xSA +Comparator:MAX941xUA +Comparator:MCP6561-OT +Comparator:MCP6561R +Comparator:MCP6561U +Comparator:MCP6561x-LT +Comparator:MCP6562 +Comparator:MCP6566 +Comparator:MCP6566R +Comparator:MCP6566U +Comparator:MCP6567 +Comparator:MCP6569 +Comparator:MCP65R41 +Comparator:MCP65R46 +Comparator:MIC845H +Comparator:MIC845L +Comparator:MIC845N +Comparator:TL3116 +Comparator:TL331 +Comparator:TLV3501AID +Comparator:TLV3501AIDBV +Comparator:TLV7031DBV +Comparator:TLV7041DBV +Comparator:TLV7041DCK +Comparator:TLV7041SDCK +Connector:4P2C +Connector:4P2C_Shielded +Connector:4P4C +Connector:4P4C_Shielded +Connector:6P2C +Connector:6P2C_Shielded +Connector:6P4C +Connector:6P4C_Shielded +Connector:6P6C +Connector:6P6C_Shielded +Connector:8P4C +Connector:8P4C_Shielded +Connector:8P8C +Connector:8P8C_LED +Connector:8P8C_LED_Shielded +Connector:8P8C_LED_Shielded_x2 +Connector:8P8C_Shielded +Connector:ATX-20 +Connector:ATX-24 +Connector:AVR-ISP-10 +Connector:AVR-ISP-6 +Connector:AVR-JTAG-10 +Connector:AVR-PDI-6 +Connector:AVR-TPI-6 +Connector:AVR-UPDI-6 +Connector:Barrel_Jack +Connector:Barrel_Jack_MountingPin +Connector:Barrel_Jack_Switch +Connector:Barrel_Jack_Switch_MountingPin +Connector:Barrel_Jack_Switch_Pin3Ring +Connector:Bus_ISA_16bit +Connector:Bus_ISA_8bit +Connector:Bus_M.2_Socket_A +Connector:Bus_M.2_Socket_B +Connector:Bus_M.2_Socket_E +Connector:Bus_M.2_Socket_M +Connector:Bus_PCI_32bit_5V +Connector:Bus_PCI_32bit_Universal +Connector:Bus_PCI_Express_Mini +Connector:Bus_PCI_Express_x1 +Connector:Bus_PCI_Express_x16 +Connector:Bus_PCI_Express_x4 +Connector:Bus_PCI_Express_x8 +Connector:CUI_PD-30 +Connector:CUI_PD-30S +Connector:CoaxialSwitch_Testpoint +Connector:Conn_01x01_Pin +Connector:Conn_01x01_Socket +Connector:Conn_01x02_Pin +Connector:Conn_01x02_Socket +Connector:Conn_01x03_Pin +Connector:Conn_01x03_Socket +Connector:Conn_01x04_Pin +Connector:Conn_01x04_Socket +Connector:Conn_01x05_Pin +Connector:Conn_01x05_Socket +Connector:Conn_01x06_Pin +Connector:Conn_01x06_Socket +Connector:Conn_01x07_Pin +Connector:Conn_01x07_Socket +Connector:Conn_01x08_Pin +Connector:Conn_01x08_Socket +Connector:Conn_01x09_Pin +Connector:Conn_01x09_Socket +Connector:Conn_01x10_Pin +Connector:Conn_01x10_Socket +Connector:Conn_01x11_Pin +Connector:Conn_01x11_Socket +Connector:Conn_01x12_Pin +Connector:Conn_01x12_Socket +Connector:Conn_01x13_Pin +Connector:Conn_01x13_Socket +Connector:Conn_01x14_Pin +Connector:Conn_01x14_Socket +Connector:Conn_01x15_Pin +Connector:Conn_01x15_Socket +Connector:Conn_01x16_Pin +Connector:Conn_01x16_Socket +Connector:Conn_01x17_Pin +Connector:Conn_01x17_Socket +Connector:Conn_01x18_Pin +Connector:Conn_01x18_Socket +Connector:Conn_01x19_Pin +Connector:Conn_01x19_Socket +Connector:Conn_01x20_Pin +Connector:Conn_01x20_Socket +Connector:Conn_01x21_Pin +Connector:Conn_01x21_Socket +Connector:Conn_01x22_Pin +Connector:Conn_01x22_Socket +Connector:Conn_01x23_Pin +Connector:Conn_01x23_Socket +Connector:Conn_01x24_Pin +Connector:Conn_01x24_Socket +Connector:Conn_01x25_Pin +Connector:Conn_01x25_Socket +Connector:Conn_01x26_Pin +Connector:Conn_01x26_Socket +Connector:Conn_01x27_Pin +Connector:Conn_01x27_Socket +Connector:Conn_01x28_Pin +Connector:Conn_01x28_Socket +Connector:Conn_01x29_Pin +Connector:Conn_01x29_Socket +Connector:Conn_01x30_Pin +Connector:Conn_01x30_Socket +Connector:Conn_01x31_Pin +Connector:Conn_01x31_Socket +Connector:Conn_01x32_Pin +Connector:Conn_01x32_Socket +Connector:Conn_01x33_Pin +Connector:Conn_01x33_Socket +Connector:Conn_01x34_Pin +Connector:Conn_01x34_Socket +Connector:Conn_01x35_Pin +Connector:Conn_01x35_Socket +Connector:Conn_01x36_Pin +Connector:Conn_01x36_Socket +Connector:Conn_01x37_Pin +Connector:Conn_01x37_Socket +Connector:Conn_01x38_Pin +Connector:Conn_01x38_Socket +Connector:Conn_01x39_Pin +Connector:Conn_01x39_Socket +Connector:Conn_01x40_Pin +Connector:Conn_01x40_Socket +Connector:Conn_01x41_Pin +Connector:Conn_01x41_Socket +Connector:Conn_01x42_Pin +Connector:Conn_01x42_Socket +Connector:Conn_01x43_Pin +Connector:Conn_01x43_Socket +Connector:Conn_01x44_Pin +Connector:Conn_01x44_Socket +Connector:Conn_01x45_Pin +Connector:Conn_01x45_Socket +Connector:Conn_01x46_Pin +Connector:Conn_01x46_Socket +Connector:Conn_01x47_Pin +Connector:Conn_01x47_Socket +Connector:Conn_01x48_Pin +Connector:Conn_01x48_Socket +Connector:Conn_01x49_Pin +Connector:Conn_01x49_Socket +Connector:Conn_01x50_Pin +Connector:Conn_01x50_Socket +Connector:Conn_01x51_Pin +Connector:Conn_01x51_Socket +Connector:Conn_01x52_Pin +Connector:Conn_01x52_Socket +Connector:Conn_01x53_Pin +Connector:Conn_01x53_Socket +Connector:Conn_01x54_Pin +Connector:Conn_01x54_Socket +Connector:Conn_01x55_Pin +Connector:Conn_01x55_Socket +Connector:Conn_01x56_Pin +Connector:Conn_01x56_Socket +Connector:Conn_01x57_Pin +Connector:Conn_01x57_Socket +Connector:Conn_01x58_Pin +Connector:Conn_01x58_Socket +Connector:Conn_01x59_Pin +Connector:Conn_01x59_Socket +Connector:Conn_01x60_Pin +Connector:Conn_01x60_Socket +Connector:Conn_15X4 +Connector:Conn_ARM_Cortex_Debug_ETM_20 +Connector:Conn_ARM_JTAG_SWD_10 +Connector:Conn_ARM_JTAG_SWD_20 +Connector:Conn_ARM_SWD_TagConnect_TC2030 +Connector:Conn_ARM_SWD_TagConnect_TC2030-NL +Connector:Conn_Coaxial +Connector:Conn_Coaxial_Power +Connector:Conn_Coaxial_Small +Connector:Conn_Coaxial_x2 +Connector:Conn_Coaxial_x2_Isolated +Connector:Conn_PIC_ICSP_ICD +Connector:Conn_Plug_2P +Connector:Conn_Plug_3P_Protected +Connector:Conn_Receptacle_2P +Connector:Conn_Receptacle_3P_Protected +Connector:Conn_ST_STDC14 +Connector:Conn_Shielded_Pair +Connector:Conn_Triaxial +Connector:Conn_Triaxial_Same_Side +Connector:DA15_Pins +Connector:DA15_Pins_MountingHoles +Connector:DA15_Socket +Connector:DA15_Socket_MountingHoles +Connector:DB25_Pins +Connector:DB25_Pins_MountingHoles +Connector:DB25_Socket +Connector:DB25_Socket_MountingHoles +Connector:DC37_Pins +Connector:DC37_Pins_MountingHoles +Connector:DC37_Socket +Connector:DC37_Socket_MountingHoles +Connector:DE15_Pins_HighDensity +Connector:DE15_Pins_HighDensity_MountingHoles +Connector:DE15_Socket_HighDensity +Connector:DE15_Socket_HighDensity_MountingHoles +Connector:DE9_Pins +Connector:DE9_Pins_MountingHoles +Connector:DE9_Socket +Connector:DE9_Socket_MountingHoles +Connector:DIN-3 +Connector:DIN-4 +Connector:DIN-5 +Connector:DIN-5_180degree +Connector:DIN-6 +Connector:DIN-7 +Connector:DIN-7_CenterPin7 +Connector:DIN-8 +Connector:DIN41612_01x32_A +Connector:DIN41612_02x05_AB_EvenPins +Connector:DIN41612_02x05_AC_EvenPins +Connector:DIN41612_02x05_AE_EvenPins +Connector:DIN41612_02x05_ZB_EvenPins +Connector:DIN41612_02x08_AB_EvenPins +Connector:DIN41612_02x08_AC_EvenPins +Connector:DIN41612_02x08_AE_EvenPins +Connector:DIN41612_02x08_ZB_EvenPins +Connector:DIN41612_02x10_AB +Connector:DIN41612_02x10_AC +Connector:DIN41612_02x10_AE +Connector:DIN41612_02x10_ZB +Connector:DIN41612_02x16_AB +Connector:DIN41612_02x16_AB_EvenPins +Connector:DIN41612_02x16_AC +Connector:DIN41612_02x16_AC_EvenPins +Connector:DIN41612_02x16_AE +Connector:DIN41612_02x16_AE_EvenPins +Connector:DIN41612_02x16_ZB +Connector:DIN41612_02x16_ZB_EvenPins +Connector:DIN41612_02x32_AB +Connector:DIN41612_02x32_AC +Connector:DIN41612_02x32_AE +Connector:DIN41612_02x32_ZB +Connector:DVI-D_Dual_Link +Connector:DVI-I_Dual_Link +Connector:ExpressCard +Connector:HDMI_A +Connector:HDMI_A_1.4 +Connector:HDMI_B +Connector:HDMI_C_1.3 +Connector:HDMI_C_1.4 +Connector:HDMI_D_1.4 +Connector:HDMI_E +Connector:IEC61076-2_M8_A-coding_01x03_Plug_Shielded +Connector:IEC61076-2_M8_A-coding_01x03_Receptacle_Shielded +Connector:IEC61076-2_M8_A-coding_01x04_Plug_Shielded +Connector:IEC61076-2_M8_A-coding_01x04_Receptacle_Shielded +Connector:IEC_60320_C13_Plug +Connector:IEC_60320_C14_Receptacle +Connector:IEC_60320_C5_Plug +Connector:IEC_60320_C6_Receptacle +Connector:IEC_60320_C7_Plug +Connector:IEC_60320_C8_Receptacle +Connector:IEEE1394a +Connector:JAE_SIM_Card_SF72S006 +Connector:Jack-DC +Connector:LEMO2 +Connector:LEMO4 +Connector:LEMO5 +Connector:LEMO6 +Connector:MXM3.0 +Connector:Micro_SD_Card +Connector:Micro_SD_Card_Det1 +Connector:Micro_SD_Card_Det2 +Connector:Micro_SD_Card_Det_Hirose_DM3AT +Connector:Microsemi_FlashPro-JTAG-10 +Connector:Mini-DIN-3 +Connector:Mini-DIN-4 +Connector:Mini-DIN-5 +Connector:Mini-DIN-6 +Connector:Mini-DIN-7 +Connector:Mini-DIN-8 +Connector:RJ10 +Connector:RJ10_Shielded +Connector:RJ11 +Connector:RJ11_Shielded +Connector:RJ12 +Connector:RJ12_Shielded +Connector:RJ13 +Connector:RJ13_Shielded +Connector:RJ14 +Connector:RJ14_Shielded +Connector:RJ18 +Connector:RJ18_Shielded +Connector:RJ22 +Connector:RJ22_Shielded +Connector:RJ25 +Connector:RJ25_Shielded +Connector:RJ31 +Connector:RJ31_Shielded +Connector:RJ32 +Connector:RJ32_Shielded +Connector:RJ33 +Connector:RJ33_Shielded +Connector:RJ34 +Connector:RJ34_Shielded +Connector:RJ35 +Connector:RJ35_Shielded +Connector:RJ38 +Connector:RJ38_Shielded +Connector:RJ41 +Connector:RJ41_Shielded +Connector:RJ45 +Connector:RJ45_Abracon_ARJP11A-MASA-B-A-EMU2 +Connector:RJ45_Amphenol_RJMG1BD3B8K1ANR +Connector:RJ45_Bel_SI-60062-F +Connector:RJ45_Bel_V895-1001-AW +Connector:RJ45_Halo_HFJ11-x2450E-LxxRL +Connector:RJ45_Halo_HFJ11-x2450ERL +Connector:RJ45_Halo_HFJ11-x2450HRL +Connector:RJ45_Hanrun_HR911105A_Horizontal +Connector:RJ45_JK00177 +Connector:RJ45_JK0654219 +Connector:RJ45_Kycon_G7LX-A88S7-BP-GY +Connector:RJ45_LED +Connector:RJ45_LED_Shielded +Connector:RJ45_LED_Shielded_x2 +Connector:RJ45_Pulse_JXD6-0001NL +Connector:RJ45_RB1-125B8G1A +Connector:RJ45_Shielded +Connector:RJ45_Wuerth_74980111211 +Connector:RJ45_Wuerth_7499010121A +Connector:RJ45_Wuerth_7499010211A +Connector:RJ45_Wuerth_7499151120 +Connector:RJ48 +Connector:RJ48_Shielded +Connector:RJ49 +Connector:RJ49_Shielded +Connector:RJ61 +Connector:RJ61_Shielded +Connector:RJ9 +Connector:RJ9_Shielded +Connector:Raspberry_Pi_4 +Connector:SCART-F +Connector:SD_Card_Device +Connector:SD_Card_Receptacle +Connector:SIM_Card +Connector:SIM_Card_Shielded +Connector:SODIMM-200 +Connector:SODIMM-200_Split +Connector:Samtec_ASP-134486-01 +Connector:Samtec_ASP-134602-01 +Connector:Screw_Terminal_01x01 +Connector:Screw_Terminal_01x02 +Connector:Screw_Terminal_01x03 +Connector:Screw_Terminal_01x04 +Connector:Screw_Terminal_01x05 +Connector:Screw_Terminal_01x06 +Connector:Screw_Terminal_01x07 +Connector:Screw_Terminal_01x08 +Connector:Screw_Terminal_01x09 +Connector:Screw_Terminal_01x10 +Connector:Screw_Terminal_01x11 +Connector:Screw_Terminal_01x12 +Connector:Screw_Terminal_01x13 +Connector:Screw_Terminal_01x14 +Connector:Screw_Terminal_01x15 +Connector:Screw_Terminal_01x16 +Connector:Screw_Terminal_01x17 +Connector:Screw_Terminal_01x18 +Connector:Screw_Terminal_01x19 +Connector:Screw_Terminal_01x20 +Connector:TC2030 +Connector:TC2050 +Connector:TC2070 +Connector:TestPoint +Connector:TestPoint_2Pole +Connector:TestPoint_Alt +Connector:TestPoint_Flag +Connector:TestPoint_Probe +Connector:TestPoint_Small +Connector:UEXT_Host +Connector:UEXT_Slave +Connector:USB3_A +Connector:USB3_A_Stacked +Connector:USB3_B +Connector:USB3_B_Micro +Connector:USB_A +Connector:USB_A_Stacked +Connector:USB_B +Connector:USB_B_Micro +Connector:USB_B_Mini +Connector:USB_C_Plug +Connector:USB_C_Plug_USB2.0 +Connector:USB_C_Receptacle +Connector:USB_C_Receptacle_PowerOnly_24P +Connector:USB_C_Receptacle_PowerOnly_6P +Connector:USB_C_Receptacle_USB2.0_14P +Connector:USB_C_Receptacle_USB2.0_16P +Connector:USB_OTG +Connector_Audio:AudioJack2 +Connector_Audio:AudioJack2_Dual_Ground_Switch +Connector_Audio:AudioJack2_Dual_Switch +Connector_Audio:AudioJack2_Ground +Connector_Audio:AudioJack2_Ground_Switch +Connector_Audio:AudioJack2_Ground_SwitchT +Connector_Audio:AudioJack2_Switch +Connector_Audio:AudioJack2_SwitchT +Connector_Audio:AudioJack3 +Connector_Audio:AudioJack3_Dual_Ground_Switch +Connector_Audio:AudioJack3_Dual_Switch +Connector_Audio:AudioJack3_Ground +Connector_Audio:AudioJack3_Ground_Switch +Connector_Audio:AudioJack3_Ground_SwitchTR +Connector_Audio:AudioJack3_Switch +Connector_Audio:AudioJack3_SwitchT +Connector_Audio:AudioJack3_SwitchTR +Connector_Audio:AudioJack4 +Connector_Audio:AudioJack4_Ground +Connector_Audio:AudioJack4_Ground_SwitchTR1 +Connector_Audio:AudioJack4_SwitchT1 +Connector_Audio:AudioJack4_SwitchTR1 +Connector_Audio:AudioJack5 +Connector_Audio:AudioJack5_Ground +Connector_Audio:AudioPlug2 +Connector_Audio:AudioPlug3 +Connector_Audio:AudioPlug4 +Connector_Audio:NC3FAAH +Connector_Audio:NC3FAAH-0 +Connector_Audio:NC3FAAH1 +Connector_Audio:NC3FAAH1-0 +Connector_Audio:NC3FAAH1-DA +Connector_Audio:NC3FAAH2 +Connector_Audio:NC3FAAH2-0 +Connector_Audio:NC3FAAV +Connector_Audio:NC3FAAV-0 +Connector_Audio:NC3FAAV1 +Connector_Audio:NC3FAAV1-0 +Connector_Audio:NC3FAAV1-DA +Connector_Audio:NC3FAAV2 +Connector_Audio:NC3FAAV2-0 +Connector_Audio:NC3FAH +Connector_Audio:NC3FAH-0 +Connector_Audio:NC3FAH1 +Connector_Audio:NC3FAH1-0 +Connector_Audio:NC3FAH1-DA +Connector_Audio:NC3FAH2 +Connector_Audio:NC3FAH2-0 +Connector_Audio:NC3FAH2-DA +Connector_Audio:NC3FAHL-0 +Connector_Audio:NC3FAHL1 +Connector_Audio:NC3FAHL1-0 +Connector_Audio:NC3FAHR-0 +Connector_Audio:NC3FAHR1 +Connector_Audio:NC3FAHR1-0 +Connector_Audio:NC3FAHR2 +Connector_Audio:NC3FAHR2-0 +Connector_Audio:NC3FAV +Connector_Audio:NC3FAV-0 +Connector_Audio:NC3FAV1 +Connector_Audio:NC3FAV1-0 +Connector_Audio:NC3FAV1-DA +Connector_Audio:NC3FAV2 +Connector_Audio:NC3FAV2-0 +Connector_Audio:NC3FAV2-DA +Connector_Audio:NC3FBH1 +Connector_Audio:NC3FBH1-B +Connector_Audio:NC3FBH1-DA +Connector_Audio:NC3FBH1-E +Connector_Audio:NC3FBH2 +Connector_Audio:NC3FBH2-B +Connector_Audio:NC3FBH2-DA +Connector_Audio:NC3FBH2-E +Connector_Audio:NC3FBHL1 +Connector_Audio:NC3FBV1 +Connector_Audio:NC3FBV1-0 +Connector_Audio:NC3FBV1-B +Connector_Audio:NC3FBV1-DA +Connector_Audio:NC3FBV2 +Connector_Audio:NC3FBV2-B +Connector_Audio:NC3FBV2-DA +Connector_Audio:NC3FBV2-SW +Connector_Audio:NC3MAAH +Connector_Audio:NC3MAAH-0 +Connector_Audio:NC3MAAH-1 +Connector_Audio:NC3MAAV +Connector_Audio:NC3MAAV-0 +Connector_Audio:NC3MAAV-1 +Connector_Audio:NC3MAFH-PH +Connector_Audio:NC3MAH +Connector_Audio:NC3MAH-0 +Connector_Audio:NC3MAHL +Connector_Audio:NC3MAHR +Connector_Audio:NC3MAMH-PH +Connector_Audio:NC3MAV +Connector_Audio:NC3MAV-0 +Connector_Audio:NC3MBH +Connector_Audio:NC3MBH-0 +Connector_Audio:NC3MBH-1 +Connector_Audio:NC3MBH-B +Connector_Audio:NC3MBH-E +Connector_Audio:NC3MBHL +Connector_Audio:NC3MBHL-B +Connector_Audio:NC3MBHR +Connector_Audio:NC3MBHR-B +Connector_Audio:NC3MBV +Connector_Audio:NC3MBV-0 +Connector_Audio:NC3MBV-1 +Connector_Audio:NC3MBV-B +Connector_Audio:NC3MBV-E +Connector_Audio:NC3MBV-SW +Connector_Audio:NC4FAH +Connector_Audio:NC4FAH-0 +Connector_Audio:NC4FAV +Connector_Audio:NC4FAV-0 +Connector_Audio:NC4FBH +Connector_Audio:NC4FBV +Connector_Audio:NC4MAH +Connector_Audio:NC4MAV +Connector_Audio:NC4MBH +Connector_Audio:NC4MBV +Connector_Audio:NC5FAH +Connector_Audio:NC5FAH-0 +Connector_Audio:NC5FAH-DA +Connector_Audio:NC5FAV +Connector_Audio:NC5FAV-DA +Connector_Audio:NC5FAV-SW +Connector_Audio:NC5FBH +Connector_Audio:NC5FBH-B +Connector_Audio:NC5FBV +Connector_Audio:NC5FBV-B +Connector_Audio:NC5FBV-SW +Connector_Audio:NC5MAH +Connector_Audio:NC5MAV +Connector_Audio:NC5MAV-SW +Connector_Audio:NC5MBH +Connector_Audio:NC5MBH-B +Connector_Audio:NC5MBV +Connector_Audio:NC5MBV-B +Connector_Audio:NC5MBV-SW +Connector_Audio:NCJ10FI-H +Connector_Audio:NCJ10FI-H-0 +Connector_Audio:NCJ10FI-V +Connector_Audio:NCJ10FI-V-0 +Connector_Audio:NCJ5FI-H +Connector_Audio:NCJ5FI-H-0 +Connector_Audio:NCJ5FI-V +Connector_Audio:NCJ5FI-V-0 +Connector_Audio:NCJ6FA-H +Connector_Audio:NCJ6FA-H-0 +Connector_Audio:NCJ6FA-H-DA +Connector_Audio:NCJ6FA-V +Connector_Audio:NCJ6FA-V-0 +Connector_Audio:NCJ6FA-V-DA +Connector_Audio:NCJ6FI-H +Connector_Audio:NCJ6FI-H-0 +Connector_Audio:NCJ6FI-V +Connector_Audio:NCJ6FI-V-0 +Connector_Audio:NCJ9FI-H +Connector_Audio:NCJ9FI-H-0 +Connector_Audio:NCJ9FI-V +Connector_Audio:NCJ9FI-V-0 +Connector_Audio:NJ2FD-V +Connector_Audio:NJ3FD-V +Connector_Audio:NJ5FD-V +Connector_Audio:NJ6FD-V +Connector_Audio:NJ6TB-V +Connector_Audio:NL2MDXX-H-3 +Connector_Audio:NL2MDXX-V +Connector_Audio:NL4MDXX-H-2 +Connector_Audio:NL4MDXX-H-3 +Connector_Audio:NL4MDXX-V +Connector_Audio:NL4MDXX-V-2 +Connector_Audio:NL4MDXX-V-3 +Connector_Audio:NL8MDXX-V +Connector_Audio:NL8MDXX-V-3 +Connector_Audio:NLJ2MDXX-H +Connector_Audio:NLJ2MDXX-V +Connector_Audio:NLT4MD-V +Connector_Audio:NMJ4HCD2 +Connector_Audio:NMJ4HFD2 +Connector_Audio:NMJ4HFD3 +Connector_Audio:NMJ4HHD2 +Connector_Audio:NMJ6HCD2 +Connector_Audio:NMJ6HCD3 +Connector_Audio:NMJ6HFD2 +Connector_Audio:NMJ6HFD2-AU +Connector_Audio:NMJ6HFD3 +Connector_Audio:NMJ6HFD4 +Connector_Audio:NMJ6HHD2 +Connector_Audio:NRJ3HF-1 +Connector_Audio:NRJ4HF +Connector_Audio:NRJ4HF-1 +Connector_Audio:NRJ4HH +Connector_Audio:NRJ4HH-1 +Connector_Audio:NRJ6HF +Connector_Audio:NRJ6HF-1 +Connector_Audio:NRJ6HF-1-AU +Connector_Audio:NRJ6HF-AU +Connector_Audio:NRJ6HH +Connector_Audio:NRJ6HH-1 +Connector_Audio:NRJ6HH-AU +Connector_Audio:NRJ6HM-1 +Connector_Audio:NRJ6HM-1-AU +Connector_Audio:NRJ6HM-1-PRE +Connector_Audio:NSJ12HC +Connector_Audio:NSJ12HF-1 +Connector_Audio:NSJ12HH-1 +Connector_Audio:NSJ12HL +Connector_Audio:NSJ8HC +Connector_Audio:NSJ8HL +Connector_Audio:SpeakON_NL2 +Connector_Audio:SpeakON_NL2_AudioJack2_Combo +Connector_Audio:SpeakON_NL4 +Connector_Audio:SpeakON_NL4_Switch +Connector_Audio:SpeakON_NL8 +Connector_Audio:XLR3 +Connector_Audio:XLR3_AudioJack2_Combo +Connector_Audio:XLR3_AudioJack2_Combo_Ground +Connector_Audio:XLR3_AudioJack3_Combo +Connector_Audio:XLR3_AudioJack3_Combo_Ground +Connector_Audio:XLR3_AudioJack3_Combo_GroundSwitch_Switch +Connector_Audio:XLR3_AudioJack3_Combo_Ground_Switch +Connector_Audio:XLR3_AudioJack3_Combo_Switch +Connector_Audio:XLR3_Ground +Connector_Audio:XLR3_Ground_Switched +Connector_Audio:XLR3_Switched +Connector_Audio:XLR4 +Connector_Audio:XLR4_Ground +Connector_Audio:XLR5 +Connector_Audio:XLR5_Ground +Connector_Audio:XLR5_Ground_Switched +Connector_Audio:XLR6 +Connector_Generic:Conn_01x01 +Connector_Generic:Conn_01x02 +Connector_Generic:Conn_01x03 +Connector_Generic:Conn_01x04 +Connector_Generic:Conn_01x05 +Connector_Generic:Conn_01x06 +Connector_Generic:Conn_01x07 +Connector_Generic:Conn_01x08 +Connector_Generic:Conn_01x09 +Connector_Generic:Conn_01x10 +Connector_Generic:Conn_01x11 +Connector_Generic:Conn_01x12 +Connector_Generic:Conn_01x13 +Connector_Generic:Conn_01x14 +Connector_Generic:Conn_01x15 +Connector_Generic:Conn_01x16 +Connector_Generic:Conn_01x17 +Connector_Generic:Conn_01x18 +Connector_Generic:Conn_01x19 +Connector_Generic:Conn_01x20 +Connector_Generic:Conn_01x21 +Connector_Generic:Conn_01x22 +Connector_Generic:Conn_01x23 +Connector_Generic:Conn_01x24 +Connector_Generic:Conn_01x25 +Connector_Generic:Conn_01x26 +Connector_Generic:Conn_01x27 +Connector_Generic:Conn_01x28 +Connector_Generic:Conn_01x29 +Connector_Generic:Conn_01x30 +Connector_Generic:Conn_01x31 +Connector_Generic:Conn_01x32 +Connector_Generic:Conn_01x33 +Connector_Generic:Conn_01x34 +Connector_Generic:Conn_01x35 +Connector_Generic:Conn_01x36 +Connector_Generic:Conn_01x37 +Connector_Generic:Conn_01x38 +Connector_Generic:Conn_01x39 +Connector_Generic:Conn_01x40 +Connector_Generic:Conn_01x41 +Connector_Generic:Conn_01x42 +Connector_Generic:Conn_01x43 +Connector_Generic:Conn_01x44 +Connector_Generic:Conn_01x45 +Connector_Generic:Conn_01x46 +Connector_Generic:Conn_01x47 +Connector_Generic:Conn_01x48 +Connector_Generic:Conn_01x49 +Connector_Generic:Conn_01x50 +Connector_Generic:Conn_01x51 +Connector_Generic:Conn_01x52 +Connector_Generic:Conn_01x53 +Connector_Generic:Conn_01x54 +Connector_Generic:Conn_01x55 +Connector_Generic:Conn_01x56 +Connector_Generic:Conn_01x57 +Connector_Generic:Conn_01x58 +Connector_Generic:Conn_01x59 +Connector_Generic:Conn_01x60 +Connector_Generic:Conn_02x01 +Connector_Generic:Conn_02x01_Row_Letter_First +Connector_Generic:Conn_02x01_Row_Letter_Last +Connector_Generic:Conn_02x02_Counter_Clockwise +Connector_Generic:Conn_02x02_Odd_Even +Connector_Generic:Conn_02x02_Row_Letter_First +Connector_Generic:Conn_02x02_Row_Letter_Last +Connector_Generic:Conn_02x02_Top_Bottom +Connector_Generic:Conn_02x03_Counter_Clockwise +Connector_Generic:Conn_02x03_Odd_Even +Connector_Generic:Conn_02x03_Row_Letter_First +Connector_Generic:Conn_02x03_Row_Letter_Last +Connector_Generic:Conn_02x03_Top_Bottom +Connector_Generic:Conn_02x04_Counter_Clockwise +Connector_Generic:Conn_02x04_Odd_Even +Connector_Generic:Conn_02x04_Row_Letter_First +Connector_Generic:Conn_02x04_Row_Letter_Last +Connector_Generic:Conn_02x04_Top_Bottom +Connector_Generic:Conn_02x05_Counter_Clockwise +Connector_Generic:Conn_02x05_Odd_Even +Connector_Generic:Conn_02x05_Row_Letter_First +Connector_Generic:Conn_02x05_Row_Letter_Last +Connector_Generic:Conn_02x05_Top_Bottom +Connector_Generic:Conn_02x06_Counter_Clockwise +Connector_Generic:Conn_02x06_Odd_Even +Connector_Generic:Conn_02x06_Row_Letter_First +Connector_Generic:Conn_02x06_Row_Letter_Last +Connector_Generic:Conn_02x06_Top_Bottom +Connector_Generic:Conn_02x07_Counter_Clockwise +Connector_Generic:Conn_02x07_Odd_Even +Connector_Generic:Conn_02x07_Row_Letter_First +Connector_Generic:Conn_02x07_Row_Letter_Last +Connector_Generic:Conn_02x07_Top_Bottom +Connector_Generic:Conn_02x08_Counter_Clockwise +Connector_Generic:Conn_02x08_Odd_Even +Connector_Generic:Conn_02x08_Row_Letter_First +Connector_Generic:Conn_02x08_Row_Letter_Last +Connector_Generic:Conn_02x08_Top_Bottom +Connector_Generic:Conn_02x09_Counter_Clockwise +Connector_Generic:Conn_02x09_Odd_Even +Connector_Generic:Conn_02x09_Row_Letter_First +Connector_Generic:Conn_02x09_Row_Letter_Last +Connector_Generic:Conn_02x09_Top_Bottom +Connector_Generic:Conn_02x10_Counter_Clockwise +Connector_Generic:Conn_02x10_Odd_Even +Connector_Generic:Conn_02x10_Row_Letter_First +Connector_Generic:Conn_02x10_Row_Letter_Last +Connector_Generic:Conn_02x10_Top_Bottom +Connector_Generic:Conn_02x11_Counter_Clockwise +Connector_Generic:Conn_02x11_Odd_Even +Connector_Generic:Conn_02x11_Row_Letter_First +Connector_Generic:Conn_02x11_Row_Letter_Last +Connector_Generic:Conn_02x11_Top_Bottom +Connector_Generic:Conn_02x12_Counter_Clockwise +Connector_Generic:Conn_02x12_Odd_Even +Connector_Generic:Conn_02x12_Row_Letter_First +Connector_Generic:Conn_02x12_Row_Letter_Last +Connector_Generic:Conn_02x12_Top_Bottom +Connector_Generic:Conn_02x13_Counter_Clockwise +Connector_Generic:Conn_02x13_Odd_Even +Connector_Generic:Conn_02x13_Row_Letter_First +Connector_Generic:Conn_02x13_Row_Letter_Last +Connector_Generic:Conn_02x13_Top_Bottom +Connector_Generic:Conn_02x14_Counter_Clockwise +Connector_Generic:Conn_02x14_Odd_Even +Connector_Generic:Conn_02x14_Row_Letter_First +Connector_Generic:Conn_02x14_Row_Letter_Last +Connector_Generic:Conn_02x14_Top_Bottom +Connector_Generic:Conn_02x15_Counter_Clockwise +Connector_Generic:Conn_02x15_Odd_Even +Connector_Generic:Conn_02x15_Row_Letter_First +Connector_Generic:Conn_02x15_Row_Letter_Last +Connector_Generic:Conn_02x15_Top_Bottom +Connector_Generic:Conn_02x16_Counter_Clockwise +Connector_Generic:Conn_02x16_Odd_Even +Connector_Generic:Conn_02x16_Row_Letter_First +Connector_Generic:Conn_02x16_Row_Letter_Last +Connector_Generic:Conn_02x16_Top_Bottom +Connector_Generic:Conn_02x17_Counter_Clockwise +Connector_Generic:Conn_02x17_Odd_Even +Connector_Generic:Conn_02x17_Row_Letter_First +Connector_Generic:Conn_02x17_Row_Letter_Last +Connector_Generic:Conn_02x17_Top_Bottom +Connector_Generic:Conn_02x18_Counter_Clockwise +Connector_Generic:Conn_02x18_Odd_Even +Connector_Generic:Conn_02x18_Row_Letter_First +Connector_Generic:Conn_02x18_Row_Letter_Last +Connector_Generic:Conn_02x18_Top_Bottom +Connector_Generic:Conn_02x19_Counter_Clockwise +Connector_Generic:Conn_02x19_Odd_Even +Connector_Generic:Conn_02x19_Row_Letter_First +Connector_Generic:Conn_02x19_Row_Letter_Last +Connector_Generic:Conn_02x19_Top_Bottom +Connector_Generic:Conn_02x20_Counter_Clockwise +Connector_Generic:Conn_02x20_Odd_Even +Connector_Generic:Conn_02x20_Row_Letter_First +Connector_Generic:Conn_02x20_Row_Letter_Last +Connector_Generic:Conn_02x20_Top_Bottom +Connector_Generic:Conn_02x21_Counter_Clockwise +Connector_Generic:Conn_02x21_Odd_Even +Connector_Generic:Conn_02x21_Row_Letter_First +Connector_Generic:Conn_02x21_Row_Letter_Last +Connector_Generic:Conn_02x21_Top_Bottom +Connector_Generic:Conn_02x22_Counter_Clockwise +Connector_Generic:Conn_02x22_Odd_Even +Connector_Generic:Conn_02x22_Row_Letter_First +Connector_Generic:Conn_02x22_Row_Letter_Last +Connector_Generic:Conn_02x22_Top_Bottom +Connector_Generic:Conn_02x23_Counter_Clockwise +Connector_Generic:Conn_02x23_Odd_Even +Connector_Generic:Conn_02x23_Row_Letter_First +Connector_Generic:Conn_02x23_Row_Letter_Last +Connector_Generic:Conn_02x23_Top_Bottom +Connector_Generic:Conn_02x24_Counter_Clockwise +Connector_Generic:Conn_02x24_Odd_Even +Connector_Generic:Conn_02x24_Row_Letter_First +Connector_Generic:Conn_02x24_Row_Letter_Last +Connector_Generic:Conn_02x24_Top_Bottom +Connector_Generic:Conn_02x25_Counter_Clockwise +Connector_Generic:Conn_02x25_Odd_Even +Connector_Generic:Conn_02x25_Row_Letter_First +Connector_Generic:Conn_02x25_Row_Letter_Last +Connector_Generic:Conn_02x25_Top_Bottom +Connector_Generic:Conn_02x26_Counter_Clockwise +Connector_Generic:Conn_02x26_Odd_Even +Connector_Generic:Conn_02x26_Row_Letter_First +Connector_Generic:Conn_02x26_Row_Letter_Last +Connector_Generic:Conn_02x26_Top_Bottom +Connector_Generic:Conn_02x27_Counter_Clockwise +Connector_Generic:Conn_02x27_Odd_Even +Connector_Generic:Conn_02x27_Row_Letter_First +Connector_Generic:Conn_02x27_Row_Letter_Last +Connector_Generic:Conn_02x27_Top_Bottom +Connector_Generic:Conn_02x28_Counter_Clockwise +Connector_Generic:Conn_02x28_Odd_Even +Connector_Generic:Conn_02x28_Row_Letter_First +Connector_Generic:Conn_02x28_Row_Letter_Last +Connector_Generic:Conn_02x28_Top_Bottom +Connector_Generic:Conn_02x29_Counter_Clockwise +Connector_Generic:Conn_02x29_Odd_Even +Connector_Generic:Conn_02x29_Row_Letter_First +Connector_Generic:Conn_02x29_Row_Letter_Last +Connector_Generic:Conn_02x29_Top_Bottom +Connector_Generic:Conn_02x30_Counter_Clockwise +Connector_Generic:Conn_02x30_Odd_Even +Connector_Generic:Conn_02x30_Row_Letter_First +Connector_Generic:Conn_02x30_Row_Letter_Last +Connector_Generic:Conn_02x30_Top_Bottom +Connector_Generic:Conn_02x31_Counter_Clockwise +Connector_Generic:Conn_02x31_Odd_Even +Connector_Generic:Conn_02x31_Row_Letter_First +Connector_Generic:Conn_02x31_Row_Letter_Last +Connector_Generic:Conn_02x31_Top_Bottom +Connector_Generic:Conn_02x32_Counter_Clockwise +Connector_Generic:Conn_02x32_Odd_Even +Connector_Generic:Conn_02x32_Row_Letter_First +Connector_Generic:Conn_02x32_Row_Letter_Last +Connector_Generic:Conn_02x32_Top_Bottom +Connector_Generic:Conn_02x33_Counter_Clockwise +Connector_Generic:Conn_02x33_Odd_Even +Connector_Generic:Conn_02x33_Row_Letter_First +Connector_Generic:Conn_02x33_Row_Letter_Last +Connector_Generic:Conn_02x33_Top_Bottom +Connector_Generic:Conn_02x34_Counter_Clockwise +Connector_Generic:Conn_02x34_Odd_Even +Connector_Generic:Conn_02x34_Row_Letter_First +Connector_Generic:Conn_02x34_Row_Letter_Last +Connector_Generic:Conn_02x34_Top_Bottom +Connector_Generic:Conn_02x35_Counter_Clockwise +Connector_Generic:Conn_02x35_Odd_Even +Connector_Generic:Conn_02x35_Row_Letter_First +Connector_Generic:Conn_02x35_Row_Letter_Last +Connector_Generic:Conn_02x35_Top_Bottom +Connector_Generic:Conn_02x36_Counter_Clockwise +Connector_Generic:Conn_02x36_Odd_Even +Connector_Generic:Conn_02x36_Row_Letter_First +Connector_Generic:Conn_02x36_Row_Letter_Last +Connector_Generic:Conn_02x36_Top_Bottom +Connector_Generic:Conn_02x37_Counter_Clockwise +Connector_Generic:Conn_02x37_Odd_Even +Connector_Generic:Conn_02x37_Row_Letter_First +Connector_Generic:Conn_02x37_Row_Letter_Last +Connector_Generic:Conn_02x37_Top_Bottom +Connector_Generic:Conn_02x38_Counter_Clockwise +Connector_Generic:Conn_02x38_Odd_Even +Connector_Generic:Conn_02x38_Row_Letter_First +Connector_Generic:Conn_02x38_Row_Letter_Last +Connector_Generic:Conn_02x38_Top_Bottom +Connector_Generic:Conn_02x39_Counter_Clockwise +Connector_Generic:Conn_02x39_Odd_Even +Connector_Generic:Conn_02x39_Row_Letter_First +Connector_Generic:Conn_02x39_Row_Letter_Last +Connector_Generic:Conn_02x39_Top_Bottom +Connector_Generic:Conn_02x40_Counter_Clockwise +Connector_Generic:Conn_02x40_Odd_Even +Connector_Generic:Conn_02x40_Row_Letter_First +Connector_Generic:Conn_02x40_Row_Letter_Last +Connector_Generic:Conn_02x40_Top_Bottom +Connector_Generic:Conn_02x41_Row_Letter_First +Connector_Generic:Conn_02x41_Row_Letter_Last +Connector_Generic:Conn_02x42_Row_Letter_First +Connector_Generic:Conn_02x42_Row_Letter_Last +Connector_Generic:Conn_02x43_Row_Letter_First +Connector_Generic:Conn_02x43_Row_Letter_Last +Connector_Generic:Conn_02x44_Row_Letter_First +Connector_Generic:Conn_02x44_Row_Letter_Last +Connector_Generic:Conn_02x45_Row_Letter_First +Connector_Generic:Conn_02x45_Row_Letter_Last +Connector_Generic:Conn_02x46_Row_Letter_First +Connector_Generic:Conn_02x46_Row_Letter_Last +Connector_Generic:Conn_02x47_Row_Letter_First +Connector_Generic:Conn_02x47_Row_Letter_Last +Connector_Generic:Conn_02x48_Row_Letter_First +Connector_Generic:Conn_02x48_Row_Letter_Last +Connector_Generic:Conn_02x49_Row_Letter_First +Connector_Generic:Conn_02x49_Row_Letter_Last +Connector_Generic:Conn_02x50_Row_Letter_First +Connector_Generic:Conn_02x50_Row_Letter_Last +Connector_Generic:Conn_02x51_Row_Letter_First +Connector_Generic:Conn_02x51_Row_Letter_Last +Connector_Generic:Conn_02x52_Row_Letter_First +Connector_Generic:Conn_02x52_Row_Letter_Last +Connector_Generic:Conn_02x53_Row_Letter_First +Connector_Generic:Conn_02x53_Row_Letter_Last +Connector_Generic:Conn_02x54_Row_Letter_First +Connector_Generic:Conn_02x54_Row_Letter_Last +Connector_Generic:Conn_02x55_Row_Letter_First +Connector_Generic:Conn_02x55_Row_Letter_Last +Connector_Generic:Conn_02x56_Row_Letter_First +Connector_Generic:Conn_02x56_Row_Letter_Last +Connector_Generic:Conn_02x57_Row_Letter_First +Connector_Generic:Conn_02x57_Row_Letter_Last +Connector_Generic:Conn_02x58_Row_Letter_First +Connector_Generic:Conn_02x58_Row_Letter_Last +Connector_Generic:Conn_02x59_Row_Letter_First +Connector_Generic:Conn_02x59_Row_Letter_Last +Connector_Generic:Conn_02x60_Row_Letter_First +Connector_Generic:Conn_02x60_Row_Letter_Last +Connector_Generic:Conn_2Rows-05Pins +Connector_Generic:Conn_2Rows-07Pins +Connector_Generic:Conn_2Rows-09Pins +Connector_Generic:Conn_2Rows-11Pins +Connector_Generic:Conn_2Rows-13Pins +Connector_Generic:Conn_2Rows-15Pins +Connector_Generic:Conn_2Rows-17Pins +Connector_Generic:Conn_2Rows-19Pins +Connector_Generic:Conn_2Rows-21Pins +Connector_Generic:Conn_2Rows-23Pins +Connector_Generic:Conn_2Rows-25Pins +Connector_Generic:Conn_2Rows-27Pins +Connector_Generic:Conn_2Rows-29Pins +Connector_Generic:Conn_2Rows-31Pins +Connector_Generic:Conn_2Rows-33Pins +Connector_Generic:Conn_2Rows-35Pins +Connector_Generic:Conn_2Rows-37Pins +Connector_Generic:Conn_2Rows-39Pins +Connector_Generic:Conn_2Rows-41Pins +Connector_Generic:Conn_2Rows-43Pins +Connector_Generic:Conn_2Rows-45Pins +Connector_Generic:Conn_2Rows-47Pins +Connector_Generic:Conn_2Rows-49Pins +Connector_Generic:Conn_2Rows-51Pins +Connector_Generic:Conn_2Rows-53Pins +Connector_Generic:Conn_2Rows-55Pins +Connector_Generic:Conn_2Rows-57Pins +Connector_Generic:Conn_2Rows-59Pins +Connector_Generic:Conn_2Rows-61Pins +Connector_Generic:Conn_2Rows-63Pins +Connector_Generic:Conn_2Rows-65Pins +Connector_Generic:Conn_2Rows-67Pins +Connector_Generic:Conn_2Rows-69Pins +Connector_Generic:Conn_2Rows-71Pins +Connector_Generic:Conn_2Rows-73Pins +Connector_Generic:Conn_2Rows-75Pins +Connector_Generic_MountingPin:Conn_01x01_MountingPin +Connector_Generic_MountingPin:Conn_01x02_MountingPin +Connector_Generic_MountingPin:Conn_01x03_MountingPin +Connector_Generic_MountingPin:Conn_01x04_MountingPin +Connector_Generic_MountingPin:Conn_01x05_MountingPin +Connector_Generic_MountingPin:Conn_01x06_MountingPin +Connector_Generic_MountingPin:Conn_01x07_MountingPin +Connector_Generic_MountingPin:Conn_01x08_MountingPin +Connector_Generic_MountingPin:Conn_01x09_MountingPin +Connector_Generic_MountingPin:Conn_01x10_MountingPin +Connector_Generic_MountingPin:Conn_01x11_MountingPin +Connector_Generic_MountingPin:Conn_01x12_MountingPin +Connector_Generic_MountingPin:Conn_01x13_MountingPin +Connector_Generic_MountingPin:Conn_01x14_MountingPin +Connector_Generic_MountingPin:Conn_01x15_MountingPin +Connector_Generic_MountingPin:Conn_01x16_MountingPin +Connector_Generic_MountingPin:Conn_01x17_MountingPin +Connector_Generic_MountingPin:Conn_01x18_MountingPin +Connector_Generic_MountingPin:Conn_01x19_MountingPin +Connector_Generic_MountingPin:Conn_01x20_MountingPin +Connector_Generic_MountingPin:Conn_01x21_MountingPin +Connector_Generic_MountingPin:Conn_01x22_MountingPin +Connector_Generic_MountingPin:Conn_01x23_MountingPin +Connector_Generic_MountingPin:Conn_01x24_MountingPin +Connector_Generic_MountingPin:Conn_01x25_MountingPin +Connector_Generic_MountingPin:Conn_01x26_MountingPin +Connector_Generic_MountingPin:Conn_01x27_MountingPin +Connector_Generic_MountingPin:Conn_01x28_MountingPin +Connector_Generic_MountingPin:Conn_01x29_MountingPin +Connector_Generic_MountingPin:Conn_01x30_MountingPin +Connector_Generic_MountingPin:Conn_01x31_MountingPin +Connector_Generic_MountingPin:Conn_01x32_MountingPin +Connector_Generic_MountingPin:Conn_01x33_MountingPin +Connector_Generic_MountingPin:Conn_01x34_MountingPin +Connector_Generic_MountingPin:Conn_01x35_MountingPin +Connector_Generic_MountingPin:Conn_01x36_MountingPin +Connector_Generic_MountingPin:Conn_01x37_MountingPin +Connector_Generic_MountingPin:Conn_01x38_MountingPin +Connector_Generic_MountingPin:Conn_01x39_MountingPin +Connector_Generic_MountingPin:Conn_01x40_MountingPin +Connector_Generic_MountingPin:Conn_01x41_MountingPin +Connector_Generic_MountingPin:Conn_01x42_MountingPin +Connector_Generic_MountingPin:Conn_01x43_MountingPin +Connector_Generic_MountingPin:Conn_01x44_MountingPin +Connector_Generic_MountingPin:Conn_01x45_MountingPin +Connector_Generic_MountingPin:Conn_01x46_MountingPin +Connector_Generic_MountingPin:Conn_01x47_MountingPin +Connector_Generic_MountingPin:Conn_01x48_MountingPin +Connector_Generic_MountingPin:Conn_01x49_MountingPin +Connector_Generic_MountingPin:Conn_01x50_MountingPin +Connector_Generic_MountingPin:Conn_01x51_MountingPin +Connector_Generic_MountingPin:Conn_01x52_MountingPin +Connector_Generic_MountingPin:Conn_01x53_MountingPin +Connector_Generic_MountingPin:Conn_01x54_MountingPin +Connector_Generic_MountingPin:Conn_01x55_MountingPin +Connector_Generic_MountingPin:Conn_01x56_MountingPin +Connector_Generic_MountingPin:Conn_01x57_MountingPin +Connector_Generic_MountingPin:Conn_01x58_MountingPin +Connector_Generic_MountingPin:Conn_01x59_MountingPin +Connector_Generic_MountingPin:Conn_01x60_MountingPin +Connector_Generic_MountingPin:Conn_02x01_MountingPin +Connector_Generic_MountingPin:Conn_02x01_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x01_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x02_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x02_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x02_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x02_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x02_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x03_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x03_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x03_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x03_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x03_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x04_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x04_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x04_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x04_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x04_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x05_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x05_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x05_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x05_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x05_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x06_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x06_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x06_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x06_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x06_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x07_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x07_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x07_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x07_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x07_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x08_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x08_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x08_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x08_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x08_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x09_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x09_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x09_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x09_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x09_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x10_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x10_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x10_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x10_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x10_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x11_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x11_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x11_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x11_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x11_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x12_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x12_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x12_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x12_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x12_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x13_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x13_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x13_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x13_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x13_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x14_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x14_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x14_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x14_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x14_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x15_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x15_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x15_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x15_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x15_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x16_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x16_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x16_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x16_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x16_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x17_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x17_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x17_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x17_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x17_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x18_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x18_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x18_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x18_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x18_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x19_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x19_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x19_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x19_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x19_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x20_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x20_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x20_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x20_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x20_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x21_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x21_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x21_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x21_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x21_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x22_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x22_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x22_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x22_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x22_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x23_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x23_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x23_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x23_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x23_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x24_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x24_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x24_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x24_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x24_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x25_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x25_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x25_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x25_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x25_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x26_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x26_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x26_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x26_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x26_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x27_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x27_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x27_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x27_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x27_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x28_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x28_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x28_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x28_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x28_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x29_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x29_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x29_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x29_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x29_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x30_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x30_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x30_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x30_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x30_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x31_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x31_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x31_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x31_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x31_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x32_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x32_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x32_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x32_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x32_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x33_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x33_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x33_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x33_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x33_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x34_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x34_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x34_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x34_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x34_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x35_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x35_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x35_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x35_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x35_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x36_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x36_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x36_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x36_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x36_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x37_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x37_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x37_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x37_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x37_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x38_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x38_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x38_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x38_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x38_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x39_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x39_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x39_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x39_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x39_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x40_Counter_Clockwise_MountingPin +Connector_Generic_MountingPin:Conn_02x40_Odd_Even_MountingPin +Connector_Generic_MountingPin:Conn_02x40_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x40_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x40_Top_Bottom_MountingPin +Connector_Generic_MountingPin:Conn_02x41_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x41_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x42_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x42_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x43_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x43_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x44_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x44_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x45_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x45_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x46_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x46_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x47_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x47_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x48_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x48_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x49_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x49_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x50_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x50_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x51_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x51_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x52_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x52_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x53_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x53_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x54_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x54_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x55_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x55_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x56_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x56_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x57_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x57_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x58_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x58_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x59_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x59_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_02x60_Row_Letter_First_MountingPin +Connector_Generic_MountingPin:Conn_02x60_Row_Letter_Last_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-05Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-07Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-09Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-11Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-13Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-15Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-17Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-19Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-21Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-23Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-25Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-27Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-29Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-31Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-33Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-35Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-37Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-39Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-41Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-43Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-45Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-47Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-49Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-51Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-53Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-55Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-57Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-59Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-61Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-63Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-65Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-67Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-69Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-71Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-73Pins_MountingPin +Connector_Generic_MountingPin:Conn_2Rows-75Pins_MountingPin +Connector_Generic_Shielded:Conn_01x01_Shielded +Connector_Generic_Shielded:Conn_01x02_Shielded +Connector_Generic_Shielded:Conn_01x03_Shielded +Connector_Generic_Shielded:Conn_01x04_Shielded +Connector_Generic_Shielded:Conn_01x05_Shielded +Connector_Generic_Shielded:Conn_01x06_Shielded +Connector_Generic_Shielded:Conn_01x07_Shielded +Connector_Generic_Shielded:Conn_01x08_Shielded +Connector_Generic_Shielded:Conn_01x09_Shielded +Connector_Generic_Shielded:Conn_01x10_Shielded +Connector_Generic_Shielded:Conn_01x11_Shielded +Connector_Generic_Shielded:Conn_01x12_Shielded +Connector_Generic_Shielded:Conn_01x13_Shielded +Connector_Generic_Shielded:Conn_01x14_Shielded +Connector_Generic_Shielded:Conn_01x15_Shielded +Connector_Generic_Shielded:Conn_01x16_Shielded +Connector_Generic_Shielded:Conn_01x17_Shielded +Connector_Generic_Shielded:Conn_01x18_Shielded +Connector_Generic_Shielded:Conn_01x19_Shielded +Connector_Generic_Shielded:Conn_01x20_Shielded +Connector_Generic_Shielded:Conn_01x21_Shielded +Connector_Generic_Shielded:Conn_01x22_Shielded +Connector_Generic_Shielded:Conn_01x23_Shielded +Connector_Generic_Shielded:Conn_01x24_Shielded +Connector_Generic_Shielded:Conn_01x25_Shielded +Connector_Generic_Shielded:Conn_01x26_Shielded +Connector_Generic_Shielded:Conn_01x27_Shielded +Connector_Generic_Shielded:Conn_01x28_Shielded +Connector_Generic_Shielded:Conn_01x29_Shielded +Connector_Generic_Shielded:Conn_01x30_Shielded +Connector_Generic_Shielded:Conn_01x31_Shielded +Connector_Generic_Shielded:Conn_01x32_Shielded +Connector_Generic_Shielded:Conn_01x33_Shielded +Connector_Generic_Shielded:Conn_01x34_Shielded +Connector_Generic_Shielded:Conn_01x35_Shielded +Connector_Generic_Shielded:Conn_01x36_Shielded +Connector_Generic_Shielded:Conn_01x37_Shielded +Connector_Generic_Shielded:Conn_01x38_Shielded +Connector_Generic_Shielded:Conn_01x39_Shielded +Connector_Generic_Shielded:Conn_01x40_Shielded +Connector_Generic_Shielded:Conn_01x41_Shielded +Connector_Generic_Shielded:Conn_01x42_Shielded +Connector_Generic_Shielded:Conn_01x43_Shielded +Connector_Generic_Shielded:Conn_01x44_Shielded +Connector_Generic_Shielded:Conn_01x45_Shielded +Connector_Generic_Shielded:Conn_01x46_Shielded +Connector_Generic_Shielded:Conn_01x47_Shielded +Connector_Generic_Shielded:Conn_01x48_Shielded +Connector_Generic_Shielded:Conn_01x49_Shielded +Connector_Generic_Shielded:Conn_01x50_Shielded +Connector_Generic_Shielded:Conn_01x51_Shielded +Connector_Generic_Shielded:Conn_01x52_Shielded +Connector_Generic_Shielded:Conn_01x53_Shielded +Connector_Generic_Shielded:Conn_01x54_Shielded +Connector_Generic_Shielded:Conn_01x55_Shielded +Connector_Generic_Shielded:Conn_01x56_Shielded +Connector_Generic_Shielded:Conn_01x57_Shielded +Connector_Generic_Shielded:Conn_01x58_Shielded +Connector_Generic_Shielded:Conn_01x59_Shielded +Connector_Generic_Shielded:Conn_01x60_Shielded +Connector_Generic_Shielded:Conn_02x01_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x01_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x01_Shielded +Connector_Generic_Shielded:Conn_02x02_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x02_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x02_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x02_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x02_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x03_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x03_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x03_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x03_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x03_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x04_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x04_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x04_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x04_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x04_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x05_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x05_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x05_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x05_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x05_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x06_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x06_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x06_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x06_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x06_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x07_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x07_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x07_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x07_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x07_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x08_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x08_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x08_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x08_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x08_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x09_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x09_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x09_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x09_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x09_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x10_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x10_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x10_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x10_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x10_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x11_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x11_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x11_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x11_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x11_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x12_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x12_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x12_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x12_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x12_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x13_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x13_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x13_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x13_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x13_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x14_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x14_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x14_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x14_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x14_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x15_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x15_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x15_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x15_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x15_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x16_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x16_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x16_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x16_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x16_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x17_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x17_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x17_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x17_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x17_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x18_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x18_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x18_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x18_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x18_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x19_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x19_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x19_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x19_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x19_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x20_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x20_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x20_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x20_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x20_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x21_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x21_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x21_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x21_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x21_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x22_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x22_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x22_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x22_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x22_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x23_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x23_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x23_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x23_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x23_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x24_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x24_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x24_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x24_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x24_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x25_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x25_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x25_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x25_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x25_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x26_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x26_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x26_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x26_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x26_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x27_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x27_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x27_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x27_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x27_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x28_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x28_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x28_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x28_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x28_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x29_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x29_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x29_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x29_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x29_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x30_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x30_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x30_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x30_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x30_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x31_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x31_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x31_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x31_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x31_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x32_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x32_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x32_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x32_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x32_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x33_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x33_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x33_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x33_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x33_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x34_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x34_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x34_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x34_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x34_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x35_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x35_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x35_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x35_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x35_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x36_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x36_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x36_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x36_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x36_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x37_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x37_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x37_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x37_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x37_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x38_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x38_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x38_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x38_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x38_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x39_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x39_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x39_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x39_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x39_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x40_Counter_Clockwise_Shielded +Connector_Generic_Shielded:Conn_02x40_Odd_Even_Shielded +Connector_Generic_Shielded:Conn_02x40_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x40_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x40_Top_Bottom_Shielded +Connector_Generic_Shielded:Conn_02x40_Top_Bottom_Shielded_1 +Connector_Generic_Shielded:Conn_02x41_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x41_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x42_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x42_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x43_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x43_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x44_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x44_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x45_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x45_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x46_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x46_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x47_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x47_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x48_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x48_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x49_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x49_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x50_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x50_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x51_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x51_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x52_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x52_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x53_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x53_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x54_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x54_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x55_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x55_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x56_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x56_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x57_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x57_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x58_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x58_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x59_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x59_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_02x60_Row_Letter_First_Shielded +Connector_Generic_Shielded:Conn_02x60_Row_Letter_Last_Shielded +Connector_Generic_Shielded:Conn_2Rows-05Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-07Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-09Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-11Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-13Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-15Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-17Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-19Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-21Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-23Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-25Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-27Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-29Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-31Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-33Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-35Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-37Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-39Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-41Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-43Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-45Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-47Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-49Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-51Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-53Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-55Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-57Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-59Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-61Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-63Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-65Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-67Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-69Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-71Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-73Pins_Shielded +Connector_Generic_Shielded:Conn_2Rows-75Pins_Shielded +Converter_ACDC:BAC05S05DC +Converter_ACDC:HLK-10M03 +Converter_ACDC:HLK-10M05 +Converter_ACDC:HLK-10M09 +Converter_ACDC:HLK-10M12 +Converter_ACDC:HLK-10M15 +Converter_ACDC:HLK-10M24 +Converter_ACDC:HLK-12M03A +Converter_ACDC:HLK-12M05A +Converter_ACDC:HLK-12M09A +Converter_ACDC:HLK-12M12A +Converter_ACDC:HLK-12M15A +Converter_ACDC:HLK-12M24A +Converter_ACDC:HLK-20M05 +Converter_ACDC:HLK-20M09 +Converter_ACDC:HLK-20M12 +Converter_ACDC:HLK-20M15 +Converter_ACDC:HLK-20M24 +Converter_ACDC:HLK-2M03 +Converter_ACDC:HLK-2M05 +Converter_ACDC:HLK-2M09 +Converter_ACDC:HLK-2M12 +Converter_ACDC:HLK-2M15 +Converter_ACDC:HLK-2M24 +Converter_ACDC:HLK-30M05 +Converter_ACDC:HLK-30M05C +Converter_ACDC:HLK-30M09 +Converter_ACDC:HLK-30M09C +Converter_ACDC:HLK-30M12 +Converter_ACDC:HLK-30M12C +Converter_ACDC:HLK-30M15 +Converter_ACDC:HLK-30M15C +Converter_ACDC:HLK-30M24 +Converter_ACDC:HLK-30M24C +Converter_ACDC:HLK-5M03 +Converter_ACDC:HLK-5M05 +Converter_ACDC:HLK-5M09 +Converter_ACDC:HLK-5M12 +Converter_ACDC:HLK-5M15 +Converter_ACDC:HLK-5M24 +Converter_ACDC:HLK-PM01 +Converter_ACDC:HLK-PM03 +Converter_ACDC:HLK-PM09 +Converter_ACDC:HLK-PM12 +Converter_ACDC:HLK-PM15 +Converter_ACDC:HLK-PM24 +Converter_ACDC:HS-40003 +Converter_ACDC:HS-40005 +Converter_ACDC:HS-40009 +Converter_ACDC:HS-40012 +Converter_ACDC:HS-40015 +Converter_ACDC:HS-40018 +Converter_ACDC:HS-40024 +Converter_ACDC:IRM-02-12 +Converter_ACDC:IRM-02-12S +Converter_ACDC:IRM-02-15 +Converter_ACDC:IRM-02-15S +Converter_ACDC:IRM-02-24 +Converter_ACDC:IRM-02-24S +Converter_ACDC:IRM-02-3.3 +Converter_ACDC:IRM-02-3.3S +Converter_ACDC:IRM-02-5 +Converter_ACDC:IRM-02-5S +Converter_ACDC:IRM-02-9 +Converter_ACDC:IRM-02-9S +Converter_ACDC:IRM-03-12 +Converter_ACDC:IRM-03-12S +Converter_ACDC:IRM-03-15 +Converter_ACDC:IRM-03-15S +Converter_ACDC:IRM-03-24 +Converter_ACDC:IRM-03-24S +Converter_ACDC:IRM-03-3.3 +Converter_ACDC:IRM-03-3.3S +Converter_ACDC:IRM-03-5 +Converter_ACDC:IRM-03-5S +Converter_ACDC:IRM-03-9 +Converter_ACDC:IRM-03-9S +Converter_ACDC:IRM-05-12 +Converter_ACDC:IRM-05-15 +Converter_ACDC:IRM-05-24 +Converter_ACDC:IRM-05-3.3 +Converter_ACDC:IRM-05-5 +Converter_ACDC:IRM-10-12 +Converter_ACDC:IRM-10-15 +Converter_ACDC:IRM-10-24 +Converter_ACDC:IRM-10-3.3 +Converter_ACDC:IRM-10-5 +Converter_ACDC:IRM-20-12 +Converter_ACDC:IRM-20-15 +Converter_ACDC:IRM-20-24 +Converter_ACDC:IRM-20-3.3 +Converter_ACDC:IRM-20-5 +Converter_ACDC:IRM-60-12 +Converter_ACDC:IRM-60-15 +Converter_ACDC:IRM-60-24 +Converter_ACDC:IRM-60-48 +Converter_ACDC:IRM-60-5 +Converter_ACDC:MFM-10-12 +Converter_ACDC:MFM-10-15 +Converter_ACDC:MFM-10-24 +Converter_ACDC:MFM-10-3.3 +Converter_ACDC:MFM-10-5 +Converter_ACDC:MFM-15-12 +Converter_ACDC:MFM-15-15 +Converter_ACDC:MFM-15-24 +Converter_ACDC:MFM-15-3.3 +Converter_ACDC:MFM-15-5 +Converter_ACDC:PBO-3-S12 +Converter_ACDC:PBO-3-S15 +Converter_ACDC:PBO-3-S24 +Converter_ACDC:PBO-3-S3.3 +Converter_ACDC:PBO-3-S5 +Converter_ACDC:PBO-3-S9 +Converter_ACDC:RAC01-05SGB +Converter_ACDC:RAC01-12SGB +Converter_ACDC:RAC01-24SGB +Converter_ACDC:RAC01-3.3SGB +Converter_ACDC:RAC01-xxSGB +Converter_ACDC:RAC04-05SGA +Converter_ACDC:RAC04-05SGB +Converter_ACDC:RAC04-09SGA +Converter_ACDC:RAC04-09SGB +Converter_ACDC:RAC04-12SGA +Converter_ACDC:RAC04-12SGB +Converter_ACDC:RAC04-15SGA +Converter_ACDC:RAC04-15SGB +Converter_ACDC:RAC04-24SGA +Converter_ACDC:RAC04-24SGB +Converter_ACDC:RAC04-3.3SGA +Converter_ACDC:RAC04-3.3SGB +Converter_ACDC:RAC04-xxSGA +Converter_ACDC:RAC04-xxSGB +Converter_ACDC:RAC05-05SK +Converter_ACDC:RAC05-12SK +Converter_ACDC:RAC05-15SK +Converter_ACDC:RAC05-24SK +Converter_ACDC:RAC05-3.3SK +Converter_ACDC:RAC20-05SK +Converter_ACDC:RAC20-12DK +Converter_ACDC:RAC20-12SK +Converter_ACDC:RAC20-15DK +Converter_ACDC:RAC20-15SK +Converter_ACDC:RAC20-24SK +Converter_ACDC:RAC20-48SK +Converter_ACDC:TMF05105 +Converter_ACDC:TMF05112 +Converter_ACDC:TMF05115 +Converter_ACDC:TMF05124 +Converter_ACDC:TMF10105 +Converter_ACDC:TMF10112 +Converter_ACDC:TMF10115 +Converter_ACDC:TMF10124 +Converter_ACDC:TMF20105 +Converter_ACDC:TMF20112 +Converter_ACDC:TMF20115 +Converter_ACDC:TMF20124 +Converter_ACDC:TMF30105 +Converter_ACDC:TMF30112 +Converter_ACDC:TMF30115 +Converter_ACDC:TMF30124 +Converter_ACDC:TMLM04103 +Converter_ACDC:TMLM04105 +Converter_ACDC:TMLM04109 +Converter_ACDC:TMLM04112 +Converter_ACDC:TMLM04115 +Converter_ACDC:TMLM04124 +Converter_ACDC:TMLM04225 +Converter_ACDC:TMLM04253 +Converter_ACDC:TMLM05103 +Converter_ACDC:TMLM05105 +Converter_ACDC:TMLM05112 +Converter_ACDC:TMLM05115 +Converter_ACDC:TMLM05124 +Converter_ACDC:TMLM10103 +Converter_ACDC:TMLM10105 +Converter_ACDC:TMLM10112 +Converter_ACDC:TMLM10115 +Converter_ACDC:TMLM10124 +Converter_ACDC:TMLM20103 +Converter_ACDC:TMLM20105 +Converter_ACDC:TMLM20112 +Converter_ACDC:TMLM20115 +Converter_ACDC:TMLM20124 +Converter_ACDC:TPP-15-103-D +Converter_ACDC:TPP-15-105-D +Converter_ACDC:TPP-15-109-D +Converter_ACDC:TPP-15-112-D +Converter_ACDC:TPP-15-115-D +Converter_ACDC:TPP-15-124-D +Converter_ACDC:TPP-15-136-D +Converter_ACDC:TPP-15-148-D +Converter_ACDC:VTX-214-010-103 +Converter_ACDC:VTX-214-010-105 +Converter_ACDC:VTX-214-010-106 +Converter_ACDC:VTX-214-010-107 +Converter_ACDC:VTX-214-010-108 +Converter_ACDC:VTX-214-010-109 +Converter_ACDC:VTX-214-010-110 +Converter_ACDC:VTX-214-010-112 +Converter_ACDC:VTX-214-010-115 +Converter_ACDC:VTX-214-010-118 +Converter_ACDC:VTX-214-010-124 +Converter_ACDC:VTX-214-010-148 +Converter_ACDC:VTX-214-015-103 +Converter_ACDC:VTX-214-015-105 +Converter_ACDC:VTX-214-015-106 +Converter_ACDC:VTX-214-015-107 +Converter_ACDC:VTX-214-015-109 +Converter_ACDC:VTX-214-015-112 +Converter_ACDC:VTX-214-015-115 +Converter_ACDC:VTX-214-015-118 +Converter_ACDC:VTX-214-015-124 +Converter_ACDC:VTX-214-015-148 +Converter_DCDC:ATA00A18S-L +Converter_DCDC:ATA00A36S-L +Converter_DCDC:ATA00AA18S-L +Converter_DCDC:ATA00AA36S-L +Converter_DCDC:ATA00B18S-L +Converter_DCDC:ATA00B36S-L +Converter_DCDC:ATA00BB18S-L +Converter_DCDC:ATA00BB36S-L +Converter_DCDC:ATA00C18S-L +Converter_DCDC:ATA00C36S-L +Converter_DCDC:ATA00CC18S-L +Converter_DCDC:ATA00CC36S-L +Converter_DCDC:ATA00F18S-L +Converter_DCDC:ATA00F36S-L +Converter_DCDC:ATA00H18S-L +Converter_DCDC:ATA00H36S-L +Converter_DCDC:Ag9905LP +Converter_DCDC:BD8314NUV +Converter_DCDC:IA0305D +Converter_DCDC:IA0305S +Converter_DCDC:IA0503D +Converter_DCDC:IA0503S +Converter_DCDC:IA0505D +Converter_DCDC:IA0505S +Converter_DCDC:IA0509D +Converter_DCDC:IA0509S +Converter_DCDC:IA0512D +Converter_DCDC:IA0512S +Converter_DCDC:IA0515D +Converter_DCDC:IA0515S +Converter_DCDC:IA0524D +Converter_DCDC:IA0524S +Converter_DCDC:IA1203D +Converter_DCDC:IA1203S +Converter_DCDC:IA1205D +Converter_DCDC:IA1205S +Converter_DCDC:IA1209D +Converter_DCDC:IA1209S +Converter_DCDC:IA1212D +Converter_DCDC:IA1212S +Converter_DCDC:IA1215D +Converter_DCDC:IA1215S +Converter_DCDC:IA1224D +Converter_DCDC:IA1224S +Converter_DCDC:IA2403D +Converter_DCDC:IA2403S +Converter_DCDC:IA2405D +Converter_DCDC:IA2405S +Converter_DCDC:IA2409D +Converter_DCDC:IA2409S +Converter_DCDC:IA2412D +Converter_DCDC:IA2412S +Converter_DCDC:IA2415D +Converter_DCDC:IA2415S +Converter_DCDC:IA2424D +Converter_DCDC:IA2424S +Converter_DCDC:IA4803D +Converter_DCDC:IA4803S +Converter_DCDC:IA4805D +Converter_DCDC:IA4805S +Converter_DCDC:IA4809D +Converter_DCDC:IA4809S +Converter_DCDC:IA4812D +Converter_DCDC:IA4812S +Converter_DCDC:IA4815D +Converter_DCDC:IA4815S +Converter_DCDC:IA4824D +Converter_DCDC:IA4824S +Converter_DCDC:IH0503D +Converter_DCDC:IH0503DH +Converter_DCDC:IH0503S +Converter_DCDC:IH0503SH +Converter_DCDC:IH0505D +Converter_DCDC:IH0505DH +Converter_DCDC:IH0505S +Converter_DCDC:IH0505SH +Converter_DCDC:IH0509D +Converter_DCDC:IH0509DH +Converter_DCDC:IH0509S +Converter_DCDC:IH0509SH +Converter_DCDC:IH0512D +Converter_DCDC:IH0512DH +Converter_DCDC:IH0512S +Converter_DCDC:IH0512SH +Converter_DCDC:IH0515D +Converter_DCDC:IH0515DH +Converter_DCDC:IH0515S +Converter_DCDC:IH0515SH +Converter_DCDC:IH0524D +Converter_DCDC:IH0524DH +Converter_DCDC:IH0524S +Converter_DCDC:IH0524SH +Converter_DCDC:IH1203D +Converter_DCDC:IH1203DH +Converter_DCDC:IH1203S +Converter_DCDC:IH1203SH +Converter_DCDC:IH1205D +Converter_DCDC:IH1205DH +Converter_DCDC:IH1205S +Converter_DCDC:IH1205SH +Converter_DCDC:IH1209D +Converter_DCDC:IH1209DH +Converter_DCDC:IH1209S +Converter_DCDC:IH1209SH +Converter_DCDC:IH1212D +Converter_DCDC:IH1212DH +Converter_DCDC:IH1212S +Converter_DCDC:IH1212SH +Converter_DCDC:IH1215D +Converter_DCDC:IH1215DH +Converter_DCDC:IH1215S +Converter_DCDC:IH1215SH +Converter_DCDC:IH1224D +Converter_DCDC:IH1224DH +Converter_DCDC:IH1224S +Converter_DCDC:IH1224SH +Converter_DCDC:IH2403D +Converter_DCDC:IH2403DH +Converter_DCDC:IH2403S +Converter_DCDC:IH2403SH +Converter_DCDC:IH2405D +Converter_DCDC:IH2405DH +Converter_DCDC:IH2405S +Converter_DCDC:IH2405SH +Converter_DCDC:IH2409D +Converter_DCDC:IH2409DH +Converter_DCDC:IH2409S +Converter_DCDC:IH2409SH +Converter_DCDC:IH2412D +Converter_DCDC:IH2412DH +Converter_DCDC:IH2412S +Converter_DCDC:IH2412SH +Converter_DCDC:IH2415D +Converter_DCDC:IH2415DH +Converter_DCDC:IH2415S +Converter_DCDC:IH2415SH +Converter_DCDC:IH2424D +Converter_DCDC:IH2424DH +Converter_DCDC:IH2424S +Converter_DCDC:IH2424SH +Converter_DCDC:IH4803D +Converter_DCDC:IH4803DH +Converter_DCDC:IH4803S +Converter_DCDC:IH4803SH +Converter_DCDC:IH4805D +Converter_DCDC:IH4805DH +Converter_DCDC:IH4805S +Converter_DCDC:IH4805SH +Converter_DCDC:IH4809D +Converter_DCDC:IH4809DH +Converter_DCDC:IH4809S +Converter_DCDC:IH4809SH +Converter_DCDC:IH4812D +Converter_DCDC:IH4812DH +Converter_DCDC:IH4812S +Converter_DCDC:IH4812SH +Converter_DCDC:IH4815D +Converter_DCDC:IH4815DH +Converter_DCDC:IH4815S +Converter_DCDC:IH4815SH +Converter_DCDC:IH4824D +Converter_DCDC:IH4824DH +Converter_DCDC:IH4824S +Converter_DCDC:IH4824SH +Converter_DCDC:ISU0205D12 +Converter_DCDC:ISU0205D15 +Converter_DCDC:ISU0205S05 +Converter_DCDC:ISU0205S12 +Converter_DCDC:ISU0205S15 +Converter_DCDC:ISU0205S24 +Converter_DCDC:ISU0224D12 +Converter_DCDC:ISU0224D15 +Converter_DCDC:ISU0224S05 +Converter_DCDC:ISU0224S12 +Converter_DCDC:ISU0224S15 +Converter_DCDC:ISU0224S24 +Converter_DCDC:ISU0248D12 +Converter_DCDC:ISU0248D15 +Converter_DCDC:ISU0248S05 +Converter_DCDC:ISU0248S12 +Converter_DCDC:ISU0248S15 +Converter_DCDC:ISU0248S24 +Converter_DCDC:ITQ2403SA +Converter_DCDC:ITQ2403SA-H +Converter_DCDC:ITQ2405S +Converter_DCDC:ITQ2405S-H +Converter_DCDC:ITQ2405SA +Converter_DCDC:ITQ2405SA-H +Converter_DCDC:ITQ2409SA +Converter_DCDC:ITQ2409SA-H +Converter_DCDC:ITQ2412S +Converter_DCDC:ITQ2412S-H +Converter_DCDC:ITQ2412SA +Converter_DCDC:ITQ2412SA-H +Converter_DCDC:ITQ2415S +Converter_DCDC:ITQ2415S-H +Converter_DCDC:ITQ2415SA +Converter_DCDC:ITQ2415SA-H +Converter_DCDC:ITQ2424SA +Converter_DCDC:ITQ2424SA-H +Converter_DCDC:ITQ4803SA +Converter_DCDC:ITQ4803SA-H +Converter_DCDC:ITQ4805S +Converter_DCDC:ITQ4805S-H +Converter_DCDC:ITQ4805SA +Converter_DCDC:ITQ4805SA-H +Converter_DCDC:ITQ4809SA +Converter_DCDC:ITQ4809SA-H +Converter_DCDC:ITQ4812S +Converter_DCDC:ITQ4812S-H +Converter_DCDC:ITQ4812SA +Converter_DCDC:ITQ4812SA-H +Converter_DCDC:ITQ4815S +Converter_DCDC:ITQ4815S-H +Converter_DCDC:ITQ4815SA +Converter_DCDC:ITQ4815SA-H +Converter_DCDC:ITQ4824SA +Converter_DCDC:ITQ4824SA-H +Converter_DCDC:ITX0503SA +Converter_DCDC:ITX0503SA-H +Converter_DCDC:ITX0503SA-HR +Converter_DCDC:ITX0503SA-R +Converter_DCDC:ITX0505S +Converter_DCDC:ITX0505S-H +Converter_DCDC:ITX0505S-HR +Converter_DCDC:ITX0505S-R +Converter_DCDC:ITX0505SA +Converter_DCDC:ITX0505SA-H +Converter_DCDC:ITX0505SA-HR +Converter_DCDC:ITX0505SA-R +Converter_DCDC:ITX0509SA +Converter_DCDC:ITX0509SA-H +Converter_DCDC:ITX0509SA-HR +Converter_DCDC:ITX0509SA-R +Converter_DCDC:ITX0512S +Converter_DCDC:ITX0512S-H +Converter_DCDC:ITX0512S-HR +Converter_DCDC:ITX0512S-R +Converter_DCDC:ITX0512SA +Converter_DCDC:ITX0512SA-H +Converter_DCDC:ITX0512SA-HR +Converter_DCDC:ITX0512SA-R +Converter_DCDC:ITX0515S +Converter_DCDC:ITX0515S-H +Converter_DCDC:ITX0515S-HR +Converter_DCDC:ITX0515S-R +Converter_DCDC:ITX0515SA +Converter_DCDC:ITX0515SA-H +Converter_DCDC:ITX0515SA-HR +Converter_DCDC:ITX0515SA-R +Converter_DCDC:ITX0524SA +Converter_DCDC:ITX0524SA-H +Converter_DCDC:ITX0524SA-HR +Converter_DCDC:ITX0524SA-R +Converter_DCDC:ITX1203SA +Converter_DCDC:ITX1203SA-H +Converter_DCDC:ITX1203SA-HR +Converter_DCDC:ITX1203SA-R +Converter_DCDC:ITX1205S +Converter_DCDC:ITX1205S-H +Converter_DCDC:ITX1205S-HR +Converter_DCDC:ITX1205S-R +Converter_DCDC:ITX1205SA +Converter_DCDC:ITX1205SA-H +Converter_DCDC:ITX1205SA-HR +Converter_DCDC:ITX1205SA-R +Converter_DCDC:ITX1209SA +Converter_DCDC:ITX1209SA-H +Converter_DCDC:ITX1209SA-HR +Converter_DCDC:ITX1209SA-R +Converter_DCDC:ITX1212S +Converter_DCDC:ITX1212S-H +Converter_DCDC:ITX1212S-HR +Converter_DCDC:ITX1212S-R +Converter_DCDC:ITX1212SA +Converter_DCDC:ITX1212SA-H +Converter_DCDC:ITX1212SA-HR +Converter_DCDC:ITX1212SA-R +Converter_DCDC:ITX1215S +Converter_DCDC:ITX1215S-H +Converter_DCDC:ITX1215S-HR +Converter_DCDC:ITX1215S-R +Converter_DCDC:ITX1215SA +Converter_DCDC:ITX1215SA-H +Converter_DCDC:ITX1215SA-HR +Converter_DCDC:ITX1215SA-R +Converter_DCDC:ITX1224SA +Converter_DCDC:ITX1224SA-H +Converter_DCDC:ITX1224SA-HR +Converter_DCDC:ITX1224SA-R +Converter_DCDC:ITX2403SA +Converter_DCDC:ITX2403SA-H +Converter_DCDC:ITX2403SA-HR +Converter_DCDC:ITX2403SA-R +Converter_DCDC:ITX2405S +Converter_DCDC:ITX2405S-H +Converter_DCDC:ITX2405S-HR +Converter_DCDC:ITX2405S-R +Converter_DCDC:ITX2405SA +Converter_DCDC:ITX2405SA-H +Converter_DCDC:ITX2405SA-HR +Converter_DCDC:ITX2405SA-R +Converter_DCDC:ITX2409SA +Converter_DCDC:ITX2409SA-H +Converter_DCDC:ITX2409SA-HR +Converter_DCDC:ITX2409SA-R +Converter_DCDC:ITX2412S +Converter_DCDC:ITX2412S-H +Converter_DCDC:ITX2412S-HR +Converter_DCDC:ITX2412S-R +Converter_DCDC:ITX2412SA +Converter_DCDC:ITX2412SA-H +Converter_DCDC:ITX2412SA-HR +Converter_DCDC:ITX2412SA-R +Converter_DCDC:ITX2415S +Converter_DCDC:ITX2415S-H +Converter_DCDC:ITX2415S-HR +Converter_DCDC:ITX2415S-R +Converter_DCDC:ITX2415SA +Converter_DCDC:ITX2415SA-H +Converter_DCDC:ITX2415SA-HR +Converter_DCDC:ITX2415SA-R +Converter_DCDC:ITX2424SA +Converter_DCDC:ITX2424SA-H +Converter_DCDC:ITX2424SA-HR +Converter_DCDC:ITX2424SA-R +Converter_DCDC:ITX4803SA +Converter_DCDC:ITX4803SA-H +Converter_DCDC:ITX4803SA-HR +Converter_DCDC:ITX4803SA-R +Converter_DCDC:ITX4805S +Converter_DCDC:ITX4805S-H +Converter_DCDC:ITX4805S-HR +Converter_DCDC:ITX4805S-R +Converter_DCDC:ITX4805SA +Converter_DCDC:ITX4805SA-H +Converter_DCDC:ITX4805SA-HR +Converter_DCDC:ITX4805SA-R +Converter_DCDC:ITX4809SA +Converter_DCDC:ITX4809SA-H +Converter_DCDC:ITX4809SA-HR +Converter_DCDC:ITX4809SA-R +Converter_DCDC:ITX4812S +Converter_DCDC:ITX4812S-H +Converter_DCDC:ITX4812S-HR +Converter_DCDC:ITX4812S-R +Converter_DCDC:ITX4812SA +Converter_DCDC:ITX4812SA-H +Converter_DCDC:ITX4812SA-HR +Converter_DCDC:ITX4812SA-R +Converter_DCDC:ITX4815S +Converter_DCDC:ITX4815S-H +Converter_DCDC:ITX4815S-HR +Converter_DCDC:ITX4815S-R +Converter_DCDC:ITX4815SA +Converter_DCDC:ITX4815SA-H +Converter_DCDC:ITX4815SA-HR +Converter_DCDC:ITX4815SA-R +Converter_DCDC:ITX4824SA +Converter_DCDC:ITX4824SA-H +Converter_DCDC:ITX4824SA-HR +Converter_DCDC:ITX4824SA-R +Converter_DCDC:JTD1524D05 +Converter_DCDC:JTD1524D12 +Converter_DCDC:JTD1524D15 +Converter_DCDC:JTD1524S05 +Converter_DCDC:JTD1524S12 +Converter_DCDC:JTD1524S15 +Converter_DCDC:JTD1524S3V3 +Converter_DCDC:JTD1548D05 +Converter_DCDC:JTD1548D12 +Converter_DCDC:JTD1548D15 +Converter_DCDC:JTD1548S05 +Converter_DCDC:JTD1548S12 +Converter_DCDC:JTD1548S15 +Converter_DCDC:JTD1548S3V3 +Converter_DCDC:JTD2024D05 +Converter_DCDC:JTD2024D12 +Converter_DCDC:JTD2024D15 +Converter_DCDC:JTD2024S05 +Converter_DCDC:JTD2024S12 +Converter_DCDC:JTD2024S15 +Converter_DCDC:JTD2024S3V3 +Converter_DCDC:JTD2048D05 +Converter_DCDC:JTD2048D12 +Converter_DCDC:JTD2048D15 +Converter_DCDC:JTD2048S05 +Converter_DCDC:JTD2048S12 +Converter_DCDC:JTD2048S15 +Converter_DCDC:JTD2048S3V3 +Converter_DCDC:JTE0624D03 +Converter_DCDC:JTE0624D05 +Converter_DCDC:JTE0624D12 +Converter_DCDC:JTE0624D15 +Converter_DCDC:JTE0624D24 +Converter_DCDC:LT1026 +Converter_DCDC:MEE1S0303SC +Converter_DCDC:MEE1S0305SC +Converter_DCDC:MEE1S0309SC +Converter_DCDC:MEE1S0312SC +Converter_DCDC:MEE1S0315SC +Converter_DCDC:MEE1S0503SC +Converter_DCDC:MEE1S0505SC +Converter_DCDC:MEE1S0509SC +Converter_DCDC:MEE1S0512SC +Converter_DCDC:MEE1S0515SC +Converter_DCDC:MEE1S1205SC +Converter_DCDC:MEE1S1209SC +Converter_DCDC:MEE1S1212SC +Converter_DCDC:MEE1S1215SC +Converter_DCDC:MEE1S1505SC +Converter_DCDC:MEE1S1509SC +Converter_DCDC:MEE1S1512SC +Converter_DCDC:MEE1S1515SC +Converter_DCDC:MEE1S2405SC +Converter_DCDC:MEE1S2409SC +Converter_DCDC:MEE1S2412SC +Converter_DCDC:MEE1S2415SC +Converter_DCDC:MEE3S0505SC +Converter_DCDC:MEE3S0509SC +Converter_DCDC:MEE3S0512SC +Converter_DCDC:MEE3S0515SC +Converter_DCDC:MEE3S1205SC +Converter_DCDC:MEE3S1209SC +Converter_DCDC:MEE3S1212SC +Converter_DCDC:MEE3S1215SC +Converter_DCDC:MGJ2D051505SC +Converter_DCDC:MGJ2D051509SC +Converter_DCDC:MGJ2D051515SC +Converter_DCDC:MGJ2D051802SC +Converter_DCDC:MGJ2D052003SC +Converter_DCDC:MGJ2D052005SC +Converter_DCDC:MGJ2D121505SC +Converter_DCDC:MGJ2D121509SC +Converter_DCDC:MGJ2D121802SC +Converter_DCDC:MGJ2D122003SC +Converter_DCDC:MGJ2D122005SC +Converter_DCDC:MGJ2D151505SC +Converter_DCDC:MGJ2D151509SC +Converter_DCDC:MGJ2D151515SC +Converter_DCDC:MGJ2D151802SC +Converter_DCDC:MGJ2D152003SC +Converter_DCDC:MGJ2D152005SC +Converter_DCDC:MGJ2D241505SC +Converter_DCDC:MGJ2D241509SC +Converter_DCDC:MGJ2D241709SC +Converter_DCDC:MGJ2D241802SC +Converter_DCDC:MGJ2D242003SC +Converter_DCDC:MGJ2D242005SC +Converter_DCDC:MGJ3T05150505MC +Converter_DCDC:MGJ3T12150505MC +Converter_DCDC:MGJ3T24150505MC +Converter_DCDC:MYRGPxx0060x21RC +Converter_DCDC:MYRGPxx0060x21RF +Converter_DCDC:NCS1S1203SC +Converter_DCDC:NCS1S1205SC +Converter_DCDC:NCS1S1212SC +Converter_DCDC:NCS1S2403SC +Converter_DCDC:NCS1S2405SC +Converter_DCDC:NCS1S2412SC +Converter_DCDC:NSD10-xxDyy +Converter_DCDC:NSD10-xxSyy +Converter_DCDC:OKI-78SR-12_1.0-W36-C +Converter_DCDC:OKI-78SR-12_1.0-W36H-C +Converter_DCDC:OKI-78SR-3.3_1.5-W36-C +Converter_DCDC:OKI-78SR-3.3_1.5-W36H-C +Converter_DCDC:OKI-78SR-5_1.5-W36-C +Converter_DCDC:OKI-78SR-5_1.5-W36H-C +Converter_DCDC:PTN78000H_EUS-5 +Converter_DCDC:PTN78000W_EUS-5 +Converter_DCDC:PTN78020H_EUK-7 +Converter_DCDC:PTN78020W_EUK-7 +Converter_DCDC:PTN78060H_EUW-7 +Converter_DCDC:PTN78060W_EUW-7 +Converter_DCDC:RPA60-2405SFW +Converter_DCDC:RPA60-2412SFW +Converter_DCDC:RPA60-2415SFW +Converter_DCDC:RPA60-2424SFW +Converter_DCDC:RPM3.3-1.0 +Converter_DCDC:RPM3.3-2.0 +Converter_DCDC:RPM3.3-3.0 +Converter_DCDC:RPM3.3-6.0 +Converter_DCDC:RPM5.0-1.0 +Converter_DCDC:RPM5.0-2.0 +Converter_DCDC:RPM5.0-3.0 +Converter_DCDC:RPM5.0-6.0 +Converter_DCDC:RPMH12-1.5 +Converter_DCDC:RPMH15-1.5 +Converter_DCDC:RPMH24-1.5 +Converter_DCDC:RPMH3.3-1.5 +Converter_DCDC:RPMH5.0-1.5 +Converter_DCDC:TBA1-0511E +Converter_DCDC:TBA1-0512E +Converter_DCDC:TBA1-0513E +Converter_DCDC:TBA1-0521E +Converter_DCDC:TBA1-0522E +Converter_DCDC:TBA1-0523E +Converter_DCDC:TBA1-1211E +Converter_DCDC:TBA1-1212E +Converter_DCDC:TBA1-1213E +Converter_DCDC:TBA1-1221E +Converter_DCDC:TBA1-1222E +Converter_DCDC:TBA1-1223E +Converter_DCDC:TBA1-2411E +Converter_DCDC:TBA1-2412E +Converter_DCDC:TBA1-2413E +Converter_DCDC:TBA1-2421E +Converter_DCDC:TBA1-2422E +Converter_DCDC:TBA1-2423E +Converter_DCDC:TBA2-0511 +Converter_DCDC:TBA2-0512 +Converter_DCDC:TBA2-0513 +Converter_DCDC:TBA2-0521 +Converter_DCDC:TBA2-0522 +Converter_DCDC:TBA2-0523 +Converter_DCDC:TBA2-1211 +Converter_DCDC:TBA2-1212 +Converter_DCDC:TBA2-1213 +Converter_DCDC:TBA2-1221 +Converter_DCDC:TBA2-1222 +Converter_DCDC:TBA2-1223 +Converter_DCDC:TBA2-2411 +Converter_DCDC:TBA2-2412 +Converter_DCDC:TBA2-2413 +Converter_DCDC:TBA2-2421 +Converter_DCDC:TBA2-2422 +Converter_DCDC:TBA2-2423 +Converter_DCDC:TC7662AxPA +Converter_DCDC:TC7662Bx0A +Converter_DCDC:TC7662BxPA +Converter_DCDC:TDU1-0511 +Converter_DCDC:TDU1-0512 +Converter_DCDC:TDU1-0513 +Converter_DCDC:TDU1-1211 +Converter_DCDC:TDU1-1212 +Converter_DCDC:TDU1-1213 +Converter_DCDC:TDU1-2411 +Converter_DCDC:TDU1-2412 +Converter_DCDC:TDU1-2413 +Converter_DCDC:TEA1-0505 +Converter_DCDC:TEA1-0505E +Converter_DCDC:TEA1-0505HI +Converter_DCDC:TEC2-1210WI +Converter_DCDC:TEC2-1211WI +Converter_DCDC:TEC2-1212WI +Converter_DCDC:TEC2-1213WI +Converter_DCDC:TEC2-1215WI +Converter_DCDC:TEC2-1219WI +Converter_DCDC:TEC2-2410WI +Converter_DCDC:TEC2-2411WI +Converter_DCDC:TEC2-2412WI +Converter_DCDC:TEC2-2413WI +Converter_DCDC:TEC2-2415WI +Converter_DCDC:TEC2-2419WI +Converter_DCDC:TEC2-4810WI +Converter_DCDC:TEC2-4811WI +Converter_DCDC:TEC2-4812WI +Converter_DCDC:TEC2-4813WI +Converter_DCDC:TEC2-4815WI +Converter_DCDC:TEC2-4819WI +Converter_DCDC:TEC3-2410UI +Converter_DCDC:TEC3-2411UI +Converter_DCDC:TEC3-2412UI +Converter_DCDC:TEC3-2413UI +Converter_DCDC:TEC3-2421UI +Converter_DCDC:TEC3-2422UI +Converter_DCDC:TEC3-2423UI +Converter_DCDC:TEL12-1211 +Converter_DCDC:TEL12-1212 +Converter_DCDC:TEL12-1213 +Converter_DCDC:TEL12-1215 +Converter_DCDC:TEL12-1222 +Converter_DCDC:TEL12-1223 +Converter_DCDC:TEL12-2411 +Converter_DCDC:TEL12-2411WI +Converter_DCDC:TEL12-2412 +Converter_DCDC:TEL12-2412WI +Converter_DCDC:TEL12-2413 +Converter_DCDC:TEL12-2413WI +Converter_DCDC:TEL12-2415 +Converter_DCDC:TEL12-2415WI +Converter_DCDC:TEL12-2422 +Converter_DCDC:TEL12-2422WI +Converter_DCDC:TEL12-2423 +Converter_DCDC:TEL12-2423WI +Converter_DCDC:TEL12-4811 +Converter_DCDC:TEL12-4811WI +Converter_DCDC:TEL12-4812 +Converter_DCDC:TEL12-4812WI +Converter_DCDC:TEL12-4813 +Converter_DCDC:TEL12-4813WI +Converter_DCDC:TEL12-4815 +Converter_DCDC:TEL12-4815WI +Converter_DCDC:TEL12-4822 +Converter_DCDC:TEL12-4822WI +Converter_DCDC:TEL12-4823 +Converter_DCDC:TEL12-4823WI +Converter_DCDC:TEN10-11010WIRH +Converter_DCDC:TEN10-11011WIRH +Converter_DCDC:TEN10-11012WIRH +Converter_DCDC:TEN10-11013WIRH +Converter_DCDC:TEN10-11015WIRH +Converter_DCDC:TEN10-11021WIRH +Converter_DCDC:TEN10-11022WIRH +Converter_DCDC:TEN10-11023WIRH +Converter_DCDC:TEN20-11011WIRH +Converter_DCDC:TEN20-11012WIRH +Converter_DCDC:TEN20-11013WIRH +Converter_DCDC:TEN20-11015WIRH +Converter_DCDC:TEN20-11021WIRH +Converter_DCDC:TEN20-11022WIRH +Converter_DCDC:TEN20-11023WIRH +Converter_DCDC:TEN20-2410WIN +Converter_DCDC:TEN20-2411WIN +Converter_DCDC:TEN20-2412WIN +Converter_DCDC:TEN20-2413WIN +Converter_DCDC:TEN20-2421WIN +Converter_DCDC:TEN20-2422WIN +Converter_DCDC:TEN20-2423WIN +Converter_DCDC:TEN20-4810WIN +Converter_DCDC:TEN20-4811WIN +Converter_DCDC:TEN20-4812WIN +Converter_DCDC:TEN20-4813WIN +Converter_DCDC:TEN20-4821WIN +Converter_DCDC:TEN20-4822WIN +Converter_DCDC:TEN20-4823WIN +Converter_DCDC:TEN3-11010WIRH +Converter_DCDC:TEN3-11011WIRH +Converter_DCDC:TEN3-11012WIRH +Converter_DCDC:TEN3-11013WIRH +Converter_DCDC:TEN3-11015WIRH +Converter_DCDC:TEN3-11021WIRH +Converter_DCDC:TEN3-11022WIRH +Converter_DCDC:TEN3-11023WIRH +Converter_DCDC:TEN40-11011WIRH +Converter_DCDC:TEN40-11012WIRH +Converter_DCDC:TEN40-11013WIRH +Converter_DCDC:TEN40-11015WIRH +Converter_DCDC:TEN40-11022WIRH +Converter_DCDC:TEN40-11023WIRH +Converter_DCDC:TEN6-11010WIRH +Converter_DCDC:TEN6-11011WIRH +Converter_DCDC:TEN6-11012WIRH +Converter_DCDC:TEN6-11013WIRH +Converter_DCDC:TEN6-11015WIRH +Converter_DCDC:TEN6-11021WIRH +Converter_DCDC:TEN6-11022WIRH +Converter_DCDC:TEN6-11023WIRH +Converter_DCDC:THB10-1211 +Converter_DCDC:THB10-1212 +Converter_DCDC:THB10-1222 +Converter_DCDC:THB10-1223 +Converter_DCDC:THB10-2411 +Converter_DCDC:THB10-2412 +Converter_DCDC:THB10-2422 +Converter_DCDC:THB10-2423 +Converter_DCDC:THB10-4811 +Converter_DCDC:THB10-4812 +Converter_DCDC:THB10-4822 +Converter_DCDC:THB10-4823 +Converter_DCDC:THR40-7211WI +Converter_DCDC:THR40-7212WI +Converter_DCDC:THR40-7213WI +Converter_DCDC:THR40-72154WI +Converter_DCDC:THR40-7215WI +Converter_DCDC:THR40-7222WI +Converter_DCDC:THR40-7223WI +Converter_DCDC:TMA-0505D +Converter_DCDC:TMA-0505S +Converter_DCDC:TMA-0512D +Converter_DCDC:TMA-0512S +Converter_DCDC:TMA-0515D +Converter_DCDC:TMA-0515S +Converter_DCDC:TMA-1205D +Converter_DCDC:TMA-1205S +Converter_DCDC:TMA-1212D +Converter_DCDC:TMA-1212S +Converter_DCDC:TMA-1215D +Converter_DCDC:TMA-1215S +Converter_DCDC:TMA-1505D +Converter_DCDC:TMA-1505S +Converter_DCDC:TMA-1512D +Converter_DCDC:TMA-1512S +Converter_DCDC:TMA-1515D +Converter_DCDC:TMA-1515S +Converter_DCDC:TMA-2405D +Converter_DCDC:TMA-2405S +Converter_DCDC:TMA-2412D +Converter_DCDC:TMA-2412S +Converter_DCDC:TMA-2415D +Converter_DCDC:TMA-2415S +Converter_DCDC:TME-0303S +Converter_DCDC:TME-0305S +Converter_DCDC:TME-0503S +Converter_DCDC:TME-0505S +Converter_DCDC:TME-0509S +Converter_DCDC:TME-0512S +Converter_DCDC:TME-0515S +Converter_DCDC:TME-1205S +Converter_DCDC:TME-1209S +Converter_DCDC:TME-1212S +Converter_DCDC:TME-1215S +Converter_DCDC:TME-2405S +Converter_DCDC:TME-2409S +Converter_DCDC:TME-2412S +Converter_DCDC:TME-2415S +Converter_DCDC:TMR-0510 +Converter_DCDC:TMR-0511 +Converter_DCDC:TMR-0512 +Converter_DCDC:TMR-1210 +Converter_DCDC:TMR-1211 +Converter_DCDC:TMR-1212 +Converter_DCDC:TMR-2410 +Converter_DCDC:TMR-2411 +Converter_DCDC:TMR-2412 +Converter_DCDC:TMR-4810 +Converter_DCDC:TMR-4811 +Converter_DCDC:TMR-4812 +Converter_DCDC:TMR2-2410WI +Converter_DCDC:TMR2-2411WI +Converter_DCDC:TMR2-2412WI +Converter_DCDC:TMR2-2413WI +Converter_DCDC:TMR2-4810WI +Converter_DCDC:TMR2-4811WI +Converter_DCDC:TMR2-4812WI +Converter_DCDC:TMR2-4813WI +Converter_DCDC:TMR4-2411WI +Converter_DCDC:TMR4-2412WI +Converter_DCDC:TMR4-2413WI +Converter_DCDC:TMR4-2415WI +Converter_DCDC:TMR4-2422WI +Converter_DCDC:TMR4-2423WI +Converter_DCDC:TMR4-4811WI +Converter_DCDC:TMR4-4812WI +Converter_DCDC:TMR4-4813WI +Converter_DCDC:TMR4-4815WI +Converter_DCDC:TMR4-4822WI +Converter_DCDC:TMR4-4823WI +Converter_DCDC:TMU3-0511 +Converter_DCDC:TMU3-0512 +Converter_DCDC:TMU3-0513 +Converter_DCDC:TMU3-1211 +Converter_DCDC:TMU3-1212 +Converter_DCDC:TMU3-1213 +Converter_DCDC:TMU3-2411 +Converter_DCDC:TMU3-2412 +Converter_DCDC:TMU3-2413 +Converter_DCDC:TPS43060RTE +Converter_DCDC:TPS54240DGQ +Converter_DCDC:TPS54240DRC +Converter_DCDC:TPS61022 +Converter_DCDC:TPSM53602RDA +Converter_DCDC:TPSM53603RDA +Converter_DCDC:TPSM53604RDA +Converter_DCDC:TRA3-0511 +Converter_DCDC:TRA3-0512 +Converter_DCDC:TRA3-0513 +Converter_DCDC:TRA3-0519 +Converter_DCDC:TRA3-1211 +Converter_DCDC:TRA3-1212 +Converter_DCDC:TRA3-1213 +Converter_DCDC:TRA3-1219 +Converter_DCDC:TRA3-2411 +Converter_DCDC:TRA3-2412 +Converter_DCDC:TRA3-2413 +Converter_DCDC:TRA3-2419 +Converter_DCDC:TRI1-0511 +Converter_DCDC:TRI1-0512 +Converter_DCDC:TRI1-0513 +Converter_DCDC:TRI1-1211 +Converter_DCDC:TRI1-1212 +Converter_DCDC:TRI1-1213 +Converter_DCDC:TRI1-2411 +Converter_DCDC:TRI1-2412 +Converter_DCDC:TRI1-2413 +CPLD_Altera:EP1210 +CPLD_Altera:EP1810 +CPLD_Altera:EP300 +CPLD_Altera:EP310 +CPLD_Altera:EP320 +CPLD_Altera:EP600 +CPLD_Altera:EP910 +CPLD_Altera:EPM1270F256 +CPLD_Altera:EPM1270M256 +CPLD_Altera:EPM1270T144 +CPLD_Altera:EPM2210F256 +CPLD_Altera:EPM2210F324 +CPLD_Altera:EPM240F100 +CPLD_Altera:EPM240M100 +CPLD_Altera:EPM240T100 +CPLD_Altera:EPM240ZM100 +CPLD_Altera:EPM240ZM68 +CPLD_Altera:EPM570F100 +CPLD_Altera:EPM570F256 +CPLD_Altera:EPM570M100 +CPLD_Altera:EPM570M256 +CPLD_Altera:EPM570T100 +CPLD_Altera:EPM570T144 +CPLD_Altera:EPM570ZM100 +CPLD_Altera:EPM570ZM144 +CPLD_Altera:EPM570ZM256 +CPLD_Microchip:ATF1502AS-xAx44 +CPLD_Microchip:ATF1502ASL-xAx44 +CPLD_Microchip:ATF1502ASV-xAx44 +CPLD_Microchip:ATF1504AS-xAx44 +CPLD_Microchip:ATF1504ASL-xAx44 +CPLD_Microchip:ATF1504ASV-xAx44 +CPLD_Microchip:ATF1504ASVL-xAx44 +CPLD_Renesas:SLG46826G +CPLD_Xilinx:XC7336 +CPLD_Xilinx:XC95108PC84 +CPLD_Xilinx:XC95108PQ100 +CPLD_Xilinx:XC95144PQ100 +CPLD_Xilinx:XC95144XL-TQ100 +CPLD_Xilinx:XC95144XL-TQ144 +CPLD_Xilinx:XC9536PC44 +CPLD_Xilinx:XC9572XL-TQ100 +CPLD_Xilinx:XC9572XL-VQ64 +CPLD_Xilinx:XCR3064-VQ100 +CPLD_Xilinx:XCR3064-VQ44 +CPLD_Xilinx:XCR3064XL-VQ100 +CPLD_Xilinx:XCR3064XL-VQ44 +CPLD_Xilinx:XCR3128-VQ100 +CPLD_Xilinx:XCR3128XL-VQ100 +CPLD_Xilinx:XCR3256-TQ144 +CPLD_Xilinx:XCR3256XL-TQ144 +CPU:CDP1802ACE +CPU:CDP1802ACEX +CPU:CDP1802BCE +CPU:CDP1802BCEX +CPU:P4080-BGA1295 +CPU:Z80CPU +CPU_NXP_6800:MC6800 +CPU_NXP_6800:MC6802 +CPU_NXP_6800:MC6809 +CPU_NXP_6800:MC6809E +CPU_NXP_6800:MC68A00 +CPU_NXP_6800:MC68A02 +CPU_NXP_6800:MC68A09 +CPU_NXP_6800:MC68A09E +CPU_NXP_6800:MC68B00 +CPU_NXP_6800:MC68B02 +CPU_NXP_6800:MC68B09 +CPU_NXP_6800:MC68B09E +CPU_NXP_68000:68000D +CPU_NXP_68000:68008D +CPU_NXP_68000:68010D +CPU_NXP_68000:MC68000FN +CPU_NXP_68000:MC68332 +CPU_NXP_IMX:MCIMX6D4AVT +CPU_NXP_IMX:MCIMX6D5EYM +CPU_NXP_IMX:MCIMX6D6AVT +CPU_NXP_IMX:MCIMX6D7CVT +CPU_NXP_IMX:MCIMX6DP4AVT +CPU_NXP_IMX:MCIMX6DP5EVT +CPU_NXP_IMX:MCIMX6DP5EYM +CPU_NXP_IMX:MCIMX6DP6AVT +CPU_NXP_IMX:MCIMX6DP7CVT +CPU_NXP_IMX:MCIMX6Q4AVT +CPU_NXP_IMX:MCIMX6Q5EYM +CPU_NXP_IMX:MCIMX6Q6AVT +CPU_NXP_IMX:MCIMX6Q7CVT +CPU_NXP_IMX:MCIMX6QP4AVT +CPU_NXP_IMX:MCIMX6QP5EVT +CPU_NXP_IMX:MCIMX6QP5EYM +CPU_NXP_IMX:MCIMX6QP6AVT +CPU_NXP_IMX:MCIMX6QP7CVT +CPU_PowerPC:MPC8641D +Device:Ammeter_AC +Device:Ammeter_DC +Device:Antenna +Device:Antenna_Chip +Device:Antenna_Dipole +Device:Antenna_Loop +Device:Antenna_Shield +Device:Battery +Device:Battery_Cell +Device:Buzzer +Device:C +Device:C_45deg +Device:C_Feedthrough +Device:C_Network04 +Device:C_Network05 +Device:C_Network06 +Device:C_Network07 +Device:C_Network08 +Device:C_Polarized +Device:C_Polarized_Series_2C +Device:C_Polarized_Small +Device:C_Polarized_Small_Series_2C +Device:C_Polarized_Small_US +Device:C_Polarized_Small_US_Series_2C +Device:C_Polarized_US +Device:C_Polarized_US_Series_2C +Device:C_Small +Device:C_Trim +Device:C_Trim_Differential +Device:C_Trim_Small +Device:C_Variable +Device:CircuitBreaker_1P +Device:CircuitBreaker_1P_US +Device:CircuitBreaker_2P +Device:CircuitBreaker_2P_US +Device:CircuitBreaker_3P +Device:CircuitBreaker_3P_US +Device:Crystal +Device:Crystal_GND2 +Device:Crystal_GND23 +Device:Crystal_GND23_Small +Device:Crystal_GND24 +Device:Crystal_GND24_Small +Device:Crystal_GND2_Small +Device:Crystal_GND3 +Device:Crystal_GND3_Small +Device:Crystal_Small +Device:D +Device:DIAC +Device:DIAC_Filled +Device:D_45deg +Device:D_45deg_Filled +Device:D_AAK +Device:D_Bridge_+-AA +Device:D_Bridge_+A-A +Device:D_Bridge_+AA- +Device:D_Bridge_-A+A +Device:D_Bridge_-AA+ +Device:D_Capacitance +Device:D_Capacitance_Filled +Device:D_Current-regulator +Device:D_Current-regulator_Small +Device:D_Dual_CommonAnode_AKK +Device:D_Dual_CommonAnode_AKK_Parallel +Device:D_Dual_CommonAnode_AKK_Split +Device:D_Dual_CommonAnode_KAK +Device:D_Dual_CommonAnode_KAK_Parallel +Device:D_Dual_CommonAnode_KAK_Split +Device:D_Dual_CommonAnode_KKA +Device:D_Dual_CommonAnode_KKA_Parallel +Device:D_Dual_CommonAnode_KKA_Split +Device:D_Dual_CommonCathode_AAK +Device:D_Dual_CommonCathode_AAK_Parallel +Device:D_Dual_CommonCathode_AAK_Split +Device:D_Dual_CommonCathode_AKA +Device:D_Dual_CommonCathode_AKA_Parallel +Device:D_Dual_CommonCathode_AKA_Split +Device:D_Dual_CommonCathode_KAA +Device:D_Dual_CommonCathode_KAA_Parallel +Device:D_Dual_CommonCathode_KAA_Split +Device:D_Dual_Series_ACK +Device:D_Dual_Series_ACK_Parallel +Device:D_Dual_Series_ACK_Split +Device:D_Dual_Series_AKC +Device:D_Dual_Series_AKC_Parallel +Device:D_Dual_Series_AKC_Split +Device:D_Dual_Series_CAK +Device:D_Dual_Series_CAK_Parallel +Device:D_Dual_Series_CAK_Split +Device:D_Dual_Series_CKA +Device:D_Dual_Series_CKA_Parallel +Device:D_Dual_Series_CKA_Split +Device:D_Dual_Series_KAC +Device:D_Dual_Series_KAC_Parallel +Device:D_Dual_Series_KAC_Split +Device:D_Dual_Series_KCA +Device:D_Dual_Series_KCA_Parallel +Device:D_Dual_Series_KCA_Split +Device:D_Filled +Device:D_KAA +Device:D_KAK +Device:D_KKA +Device:D_Laser_1A3C +Device:D_Laser_1C2A +Device:D_Laser_Photo_MType +Device:D_Laser_Photo_NType +Device:D_Laser_Photo_PType +Device:D_Photo +Device:D_Photo_Filled +Device:D_Radiation +Device:D_Radiation_Filled +Device:D_Schottky +Device:D_Schottky_AAK +Device:D_Schottky_AKA +Device:D_Schottky_AKK +Device:D_Schottky_Dual_CommonAnode_AKK +Device:D_Schottky_Dual_CommonAnode_AKK_Parallel +Device:D_Schottky_Dual_CommonAnode_AKK_Split +Device:D_Schottky_Dual_CommonAnode_KAK +Device:D_Schottky_Dual_CommonAnode_KAK_Parallel +Device:D_Schottky_Dual_CommonAnode_KAK_Split +Device:D_Schottky_Dual_CommonAnode_KKA +Device:D_Schottky_Dual_CommonAnode_KKA_Parallel +Device:D_Schottky_Dual_CommonAnode_KKA_Split +Device:D_Schottky_Dual_CommonCathode_AAK +Device:D_Schottky_Dual_CommonCathode_AAK_Parallel +Device:D_Schottky_Dual_CommonCathode_AAK_Split +Device:D_Schottky_Dual_CommonCathode_AKA +Device:D_Schottky_Dual_CommonCathode_AKA_Parallel +Device:D_Schottky_Dual_CommonCathode_AKA_Split +Device:D_Schottky_Dual_CommonCathode_KAA +Device:D_Schottky_Dual_CommonCathode_KAA_Parallel +Device:D_Schottky_Dual_CommonCathode_KAA_Split +Device:D_Schottky_Dual_Series_ACK +Device:D_Schottky_Dual_Series_ACK_Parallel +Device:D_Schottky_Dual_Series_ACK_Split +Device:D_Schottky_Dual_Series_AKC +Device:D_Schottky_Dual_Series_AKC_Parallel +Device:D_Schottky_Dual_Series_AKC_Split +Device:D_Schottky_Dual_Series_CAK +Device:D_Schottky_Dual_Series_CAK_Parallel +Device:D_Schottky_Dual_Series_CAK_Split +Device:D_Schottky_Dual_Series_CKA +Device:D_Schottky_Dual_Series_CKA_Parallel +Device:D_Schottky_Dual_Series_CKA_Split +Device:D_Schottky_Dual_Series_KAC +Device:D_Schottky_Dual_Series_KAC_Parallel +Device:D_Schottky_Dual_Series_KAC_Split +Device:D_Schottky_Dual_Series_KCA +Device:D_Schottky_Dual_Series_KCA_Parallel +Device:D_Schottky_Dual_Series_KCA_Split +Device:D_Schottky_Filled +Device:D_Schottky_KAA +Device:D_Schottky_KAK +Device:D_Schottky_KKA +Device:D_Schottky_Small +Device:D_Schottky_Small_Filled +Device:D_Shockley +Device:D_SiPM +Device:D_Small +Device:D_Small_Filled +Device:D_TVS +Device:D_TVS_Dual_AAC +Device:D_TVS_Dual_ACA +Device:D_TVS_Dual_CAA +Device:D_TVS_Filled +Device:D_TVS_Small +Device:D_TVS_Small_Filled +Device:D_TemperatureDependent +Device:D_TemperatureDependent_Filled +Device:D_Tunnel +Device:D_Tunnel_Filled +Device:D_Unitunnel +Device:D_Unitunnel_Filled +Device:D_Zener +Device:D_Zener_Dual_CommonAnode_AKK +Device:D_Zener_Dual_CommonAnode_AKK_Parallel +Device:D_Zener_Dual_CommonAnode_AKK_Split +Device:D_Zener_Dual_CommonAnode_KAK +Device:D_Zener_Dual_CommonAnode_KAK_Parallel +Device:D_Zener_Dual_CommonAnode_KAK_Split +Device:D_Zener_Dual_CommonAnode_KKA +Device:D_Zener_Dual_CommonAnode_KKA_Parallel +Device:D_Zener_Dual_CommonAnode_KKA_Split +Device:D_Zener_Dual_CommonCathode_AAK +Device:D_Zener_Dual_CommonCathode_AAK_Parallel +Device:D_Zener_Dual_CommonCathode_AAK_Split +Device:D_Zener_Dual_CommonCathode_AKA +Device:D_Zener_Dual_CommonCathode_AKA_Parallel +Device:D_Zener_Dual_CommonCathode_AKA_Split +Device:D_Zener_Dual_CommonCathode_KAA +Device:D_Zener_Dual_CommonCathode_KAA_Parallel +Device:D_Zener_Dual_CommonCathode_KAA_Split +Device:D_Zener_Filled +Device:D_Zener_Small +Device:D_Zener_Small_Filled +Device:DelayLine +Device:Earphone +Device:ElectromagneticActor +Device:FerriteBead +Device:FerriteBead_Small +Device:Filter_EMI_C +Device:Filter_EMI_CLC +Device:Filter_EMI_CommonMode +Device:Filter_EMI_LCL +Device:Filter_EMI_LL +Device:Filter_EMI_LLL +Device:Filter_EMI_LLLL +Device:Filter_EMI_LLLL_15263748 +Device:Filter_EMI_LLL_162534 +Device:Filter_EMI_LL_1423 +Device:FrequencyCounter +Device:Fuse +Device:Fuse_Polarized +Device:Fuse_Polarized_Small +Device:Fuse_Small +Device:GDT_2Pin +Device:GDT_3Pin +Device:Galvanometer +Device:HallGenerator +Device:Heater +Device:L +Device:LED +Device:LED_45deg +Device:LED_45deg_Filled +Device:LED_ABGR +Device:LED_ABRG +Device:LED_AGBR +Device:LED_AGRB +Device:LED_ARBG +Device:LED_ARGB +Device:LED_BAGR +Device:LED_BARG +Device:LED_BGAR +Device:LED_BGKR +Device:LED_BGRA +Device:LED_BGRK +Device:LED_BKGR +Device:LED_BKRG +Device:LED_BRAG +Device:LED_BRGA +Device:LED_BRGK +Device:LED_BRKG +Device:LED_Dual_AAK +Device:LED_Dual_AAKK +Device:LED_Dual_AKA +Device:LED_Dual_AKAK +Device:LED_Dual_AKKA +Device:LED_Dual_Bidirectional +Device:LED_Dual_KAK +Device:LED_Dual_KAKA +Device:LED_Dual_KKA +Device:LED_Filled +Device:LED_GABR +Device:LED_GARB +Device:LED_GBAR +Device:LED_GBKR +Device:LED_GBRA +Device:LED_GBRK +Device:LED_GKBR +Device:LED_GKRB +Device:LED_GRAB +Device:LED_GRBA +Device:LED_GRBK +Device:LED_GRKB +Device:LED_KBGR +Device:LED_KBRG +Device:LED_KGBR +Device:LED_KGRB +Device:LED_KRBG +Device:LED_KRGB +Device:LED_Pad +Device:LED_RABG +Device:LED_RAGB +Device:LED_RBAG +Device:LED_RBGA +Device:LED_RBGK +Device:LED_RBKG +Device:LED_RGAB +Device:LED_RGB +Device:LED_RGBA +Device:LED_RGBK +Device:LED_RGB_EP +Device:LED_RGKB +Device:LED_RKBG +Device:LED_RKGB +Device:LED_Series +Device:LED_Series_Pad +Device:LED_Small +Device:LED_Small_Filled +Device:L_45deg +Device:L_Coupled +Device:L_Coupled_1243 +Device:L_Coupled_1324 +Device:L_Coupled_1342 +Device:L_Coupled_1423 +Device:L_Coupled_Small +Device:L_Coupled_Small_1243 +Device:L_Coupled_Small_1324 +Device:L_Coupled_Small_1342 +Device:L_Coupled_Small_1423 +Device:L_Ferrite +Device:L_Ferrite_Coupled +Device:L_Ferrite_Coupled_1243 +Device:L_Ferrite_Coupled_1324 +Device:L_Ferrite_Coupled_1342 +Device:L_Ferrite_Coupled_1423 +Device:L_Ferrite_Coupled_Small +Device:L_Ferrite_Coupled_Small_1243 +Device:L_Ferrite_Coupled_Small_1324 +Device:L_Ferrite_Coupled_Small_1342 +Device:L_Ferrite_Coupled_Small_1423 +Device:L_Ferrite_Small +Device:L_Iron +Device:L_Iron_Coupled +Device:L_Iron_Coupled_1243 +Device:L_Iron_Coupled_1324 +Device:L_Iron_Coupled_1342 +Device:L_Iron_Coupled_1423 +Device:L_Iron_Coupled_Small +Device:L_Iron_Coupled_Small_1243 +Device:L_Iron_Coupled_Small_1324 +Device:L_Iron_Coupled_Small_1342 +Device:L_Iron_Coupled_Small_1423 +Device:L_Iron_Small +Device:L_Pack04 +Device:L_Small +Device:L_Trim +Device:Lamp +Device:Lamp_Flash +Device:Lamp_Neon +Device:Memristor +Device:Microphone +Device:Microphone_Condenser +Device:Microphone_Crystal +Device:Microphone_Ultrasound +Device:NetTie_2 +Device:NetTie_3 +Device:NetTie_3_Tee +Device:NetTie_4 +Device:NetTie_4_Cross +Device:Ohmmeter +Device:Opamp_Dual +Device:Opamp_Quad +Device:Oscilloscope +Device:PeltierElement +Device:Polyfuse +Device:Polyfuse_Small +Device:Q_NIGBT_CEG +Device:Q_NIGBT_CGE +Device:Q_NIGBT_ECG +Device:Q_NIGBT_ECGC +Device:Q_NIGBT_EGC +Device:Q_NIGBT_GCE +Device:Q_NIGBT_GCEC +Device:Q_NIGBT_GEC +Device:Q_NJFET_DGS +Device:Q_NJFET_DSG +Device:Q_NJFET_GDS +Device:Q_NJFET_GSD +Device:Q_NJFET_SDG +Device:Q_NJFET_SGD +Device:Q_NMOS +Device:Q_NMOS_Depletion +Device:Q_NPN +Device:Q_NPN_BRT +Device:Q_NPN_BRT_No_R2 +Device:Q_NPN_CurrentMirror +Device:Q_NPN_Darlington +Device:Q_NUJT_BEB +Device:Q_PJFET_DGS +Device:Q_PJFET_DSG +Device:Q_PJFET_GDS +Device:Q_PJFET_GSD +Device:Q_PJFET_SDG +Device:Q_PJFET_SGD +Device:Q_PMOS +Device:Q_PMOS_Depletion +Device:Q_PNP +Device:Q_PNP_BRT +Device:Q_PNP_BRT_No_R2 +Device:Q_PNP_CurrentMirror +Device:Q_PNP_Darlington +Device:Q_PUJT_BEB +Device:Q_Photo_NPN +Device:Q_Photo_NPN_CBE +Device:Q_Photo_NPN_CE +Device:Q_Photo_NPN_EBC +Device:Q_Photo_NPN_EC +Device:Q_SCR_AGK +Device:Q_SCR_AKG +Device:Q_SCR_GAK +Device:Q_SCR_GKA +Device:Q_SCR_KAG +Device:Q_SCR_KGA +Device:Q_Triac +Device:R +Device:RFShield_OnePiece +Device:RFShield_TwoPieces +Device:R_45deg +Device:R_Network03 +Device:R_Network03_Split +Device:R_Network03_US +Device:R_Network04 +Device:R_Network04_Split +Device:R_Network04_US +Device:R_Network05 +Device:R_Network05_Split +Device:R_Network05_US +Device:R_Network06 +Device:R_Network06_Split +Device:R_Network06_US +Device:R_Network07 +Device:R_Network07_Split +Device:R_Network07_US +Device:R_Network08 +Device:R_Network08_Split +Device:R_Network08_US +Device:R_Network09 +Device:R_Network09_Split +Device:R_Network09_US +Device:R_Network10 +Device:R_Network10_Split +Device:R_Network10_US +Device:R_Network11 +Device:R_Network11_Split +Device:R_Network11_US +Device:R_Network12 +Device:R_Network12_Split +Device:R_Network12_US +Device:R_Network13 +Device:R_Network13_Split +Device:R_Network13_US +Device:R_Network_Dividers_x02_SIP +Device:R_Network_Dividers_x03_SIP +Device:R_Network_Dividers_x04_SIP +Device:R_Network_Dividers_x05_SIP +Device:R_Network_Dividers_x06_SIP +Device:R_Network_Dividers_x07_SIP +Device:R_Network_Dividers_x08_SIP +Device:R_Network_Dividers_x09_SIP +Device:R_Network_Dividers_x10_SIP +Device:R_Network_Dividers_x11_SIP +Device:R_Pack02 +Device:R_Pack02_SIP +Device:R_Pack02_SIP_Split +Device:R_Pack02_Split +Device:R_Pack03 +Device:R_Pack03_SIP +Device:R_Pack03_SIP_Split +Device:R_Pack03_Split +Device:R_Pack04 +Device:R_Pack04_SIP +Device:R_Pack04_SIP_Split +Device:R_Pack04_Split +Device:R_Pack05 +Device:R_Pack05_SIP +Device:R_Pack05_SIP_Split +Device:R_Pack05_Split +Device:R_Pack06 +Device:R_Pack06_SIP +Device:R_Pack06_SIP_Split +Device:R_Pack06_Split +Device:R_Pack07 +Device:R_Pack07_SIP +Device:R_Pack07_SIP_Split +Device:R_Pack07_Split +Device:R_Pack08 +Device:R_Pack08_Split +Device:R_Pack09 +Device:R_Pack09_Split +Device:R_Pack10 +Device:R_Pack10_Split +Device:R_Pack11 +Device:R_Pack11_Split +Device:R_Photo +Device:R_Potentiometer +Device:R_Potentiometer_Dual +Device:R_Potentiometer_Dual_MountingPin +Device:R_Potentiometer_Dual_Separate +Device:R_Potentiometer_MountingPin +Device:R_Potentiometer_Small +Device:R_Potentiometer_Trim +Device:R_Potentiometer_Trim_US +Device:R_Potentiometer_US +Device:R_Shunt +Device:R_Shunt_US +Device:R_Small +Device:R_Small_US +Device:R_Trim +Device:R_US +Device:R_Variable +Device:R_Variable_US +Device:Resonator +Device:Resonator_Small +Device:RotaryEncoder +Device:RotaryEncoder_Switch +Device:RotaryEncoder_Switch_MP +Device:Solar_Cell +Device:Solar_Cells +Device:SparkGap +Device:Speaker +Device:Speaker_Crystal +Device:Speaker_Ultrasound +Device:Thermistor +Device:Thermistor_NTC +Device:Thermistor_NTC_3Wire +Device:Thermistor_NTC_4Wire +Device:Thermistor_NTC_US +Device:Thermistor_PTC +Device:Thermistor_PTC_3Wire +Device:Thermistor_PTC_4Wire +Device:Thermistor_PTC_US +Device:Thermistor_US +Device:Thermocouple +Device:Thermocouple_Alt +Device:Thermocouple_Block +Device:Transformer_1P_1S +Device:Transformer_1P_1S_SO8 +Device:Transformer_1P_2S +Device:Transformer_1P_SS +Device:Transformer_Audio +Device:Transformer_SP_1S +Device:Transformer_SP_2S +Device:Varistor +Device:Varistor_US +Device:VoltageDivider +Device:VoltageDivider_CenterPin1 +Device:VoltageDivider_CenterPin3 +Device:Voltmeter_AC +Device:Voltmeter_DC +Diode:1.5KExxA +Diode:1.5KExxCA +Diode:1.5SMCxxA +Diode:1.5SMCxxCA +Diode:1N4001 +Diode:1N4002 +Diode:1N4003 +Diode:1N4004 +Diode:1N4005 +Diode:1N4006 +Diode:1N4007 +Diode:1N4148 +Diode:1N4148W +Diode:1N4148WS +Diode:1N4148WT +Diode:1N4149 +Diode:1N4151 +Diode:1N4448 +Diode:1N4448W +Diode:1N4448WS +Diode:1N4448WT +Diode:1N47xxA +Diode:1N4933 +Diode:1N4934 +Diode:1N4935 +Diode:1N4936 +Diode:1N4937 +Diode:1N53xxB +Diode:1N5400 +Diode:1N5401 +Diode:1N5402 +Diode:1N5403 +Diode:1N5404 +Diode:1N5405 +Diode:1N5406 +Diode:1N5407 +Diode:1N5408 +Diode:1N5711 +Diode:1N5711UR +Diode:1N5712 +Diode:1N5712UR +Diode:1N5817 +Diode:1N5818 +Diode:1N5819 +Diode:1N5819WS +Diode:1N5820 +Diode:1N5821 +Diode:1N5822 +Diode:1N5908 +Diode:1N6263 +Diode:1N62xxA +Diode:1N62xxCA +Diode:1N630xA +Diode:1N630xCA +Diode:1N6857 +Diode:1N6857UR +Diode:1N6858 +Diode:1N6858UR +Diode:1N914 +Diode:1N914WT +Diode:1SS355VM +Diode:2BZX84Cxx +Diode:5KPxxA +Diode:5KPxxCA +Diode:B120-E3 +Diode:B130-E3 +Diode:B140-E3 +Diode:B150-E3 +Diode:B160-E3 +Diode:B220 +Diode:B230 +Diode:B240 +Diode:B250 +Diode:B260 +Diode:B320 +Diode:B330 +Diode:B340 +Diode:B350 +Diode:B360 +Diode:BA157 +Diode:BA158 +Diode:BA159 +Diode:BA243 +Diode:BA244 +Diode:BA282 +Diode:BA283 +Diode:BAR42FILM +Diode:BAR43FILM +Diode:BAS16TW +Diode:BAS16VY +Diode:BAS16W +Diode:BAS19 +Diode:BAS20 +Diode:BAS21 +Diode:BAS316 +Diode:BAS40-04 +Diode:BAS516 +Diode:BAT160A +Diode:BAT160C +Diode:BAT160S +Diode:BAT41 +Diode:BAT42 +Diode:BAT42W-V +Diode:BAT43 +Diode:BAT43W-V +Diode:BAT46 +Diode:BAT48JFILM +Diode:BAT48RL +Diode:BAT48ZFILM +Diode:BAT54A +Diode:BAT54ADW +Diode:BAT54AW +Diode:BAT54C +Diode:BAT54CW +Diode:BAT54J +Diode:BAT54S +Diode:BAT54SDW +Diode:BAT54SW +Diode:BAT54W +Diode:BAT60A +Diode:BAT85 +Diode:BAT86 +Diode:BAT86S +Diode:BAV16W +Diode:BAV17 +Diode:BAV18 +Diode:BAV19 +Diode:BAV199DW +Diode:BAV20 +Diode:BAV21 +Diode:BAV300 +Diode:BAV301 +Diode:BAV302 +Diode:BAV303 +Diode:BAV70 +Diode:BAV70M +Diode:BAV70S +Diode:BAV70T +Diode:BAV70W +Diode:BAV756S +Diode:BAV99 +Diode:BAV99S +Diode:BAW56DW +Diode:BAW56S +Diode:BAW75 +Diode:BAW76 +Diode:BAY93 +Diode:BYV79-100 +Diode:BYV79-150 +Diode:BYV79-200 +Diode:BZD27Cxx +Diode:BZM55Bxx +Diode:BZM55Cxx +Diode:BZT52Bxx +Diode:BZV55B10 +Diode:BZV55B11 +Diode:BZV55B12 +Diode:BZV55B13 +Diode:BZV55B15 +Diode:BZV55B16 +Diode:BZV55B18 +Diode:BZV55B20 +Diode:BZV55B22 +Diode:BZV55B24 +Diode:BZV55B27 +Diode:BZV55B2V4 +Diode:BZV55B2V7 +Diode:BZV55B30 +Diode:BZV55B33 +Diode:BZV55B36 +Diode:BZV55B39 +Diode:BZV55B3V0 +Diode:BZV55B3V3 +Diode:BZV55B3V6 +Diode:BZV55B3V9 +Diode:BZV55B43 +Diode:BZV55B47 +Diode:BZV55B4V3 +Diode:BZV55B4V7 +Diode:BZV55B51 +Diode:BZV55B56 +Diode:BZV55B5V1 +Diode:BZV55B5V6 +Diode:BZV55B62 +Diode:BZV55B68 +Diode:BZV55B6V2 +Diode:BZV55B6V8 +Diode:BZV55B75 +Diode:BZV55B7V5 +Diode:BZV55B8V2 +Diode:BZV55B9V1 +Diode:BZV55C10 +Diode:BZV55C11 +Diode:BZV55C12 +Diode:BZV55C13 +Diode:BZV55C15 +Diode:BZV55C16 +Diode:BZV55C18 +Diode:BZV55C20 +Diode:BZV55C22 +Diode:BZV55C24 +Diode:BZV55C27 +Diode:BZV55C2V4 +Diode:BZV55C2V7 +Diode:BZV55C30 +Diode:BZV55C33 +Diode:BZV55C36 +Diode:BZV55C39 +Diode:BZV55C3V0 +Diode:BZV55C3V3 +Diode:BZV55C3V6 +Diode:BZV55C3V9 +Diode:BZV55C43 +Diode:BZV55C47 +Diode:BZV55C4V3 +Diode:BZV55C4V7 +Diode:BZV55C51 +Diode:BZV55C56 +Diode:BZV55C5V1 +Diode:BZV55C5V6 +Diode:BZV55C62 +Diode:BZV55C68 +Diode:BZV55C6V2 +Diode:BZV55C6V8 +Diode:BZV55C75 +Diode:BZV55C7V5 +Diode:BZV55C8V2 +Diode:BZV55C9V1 +Diode:BZX384xxxx +Diode:BZX84Cxx +Diode:C3D02060A +Diode:C3D02060E +Diode:C3D02060F +Diode:C3D02065E +Diode:C3D03060A +Diode:C3D03060E +Diode:C3D03060F +Diode:C3D03065E +Diode:C3D04060A +Diode:C3D04060E +Diode:C3D04060F +Diode:C3D04065A +Diode:C3D04065E +Diode:C3D06060A +Diode:C3D06060F +Diode:C3D06060G +Diode:C3D06065A +Diode:C3D06065E +Diode:C3D06065I +Diode:C3D08060A +Diode:C3D08060G +Diode:C3D08065A +Diode:C3D08065E +Diode:C3D08065I +Diode:C3D10060A +Diode:C3D10060G +Diode:C3D10065A +Diode:C3D10065E +Diode:C3D10065I +Diode:C3D10170H +Diode:C3D12065A +Diode:C3D16060D +Diode:C3D16065A +Diode:C3D16065D +Diode:C3D1P7060Q +Diode:C3D20060D +Diode:C3D20065D +Diode:C3D25170H +Diode:C3D30065D +Diode:C4D02120A +Diode:C4D02120E +Diode:C4D05120A +Diode:C4D05120E +Diode:C4D08120A +Diode:C4D08120E +Diode:C4D10120A +Diode:C4D10120D +Diode:C4D10120E +Diode:C4D10120H +Diode:C4D15120A +Diode:C4D15120D +Diode:C4D15120H +Diode:C4D20120A +Diode:C4D20120D +Diode:C4D20120H +Diode:C4D30120D +Diode:C4D40120D +Diode:C5D50065D +Diode:CD4148W +Diode:CDBA3100-HF +Diode:CDBA340-HF +Diode:CDBA360-HF +Diode:CDBU40-HF +Diode:CSD01060A +Diode:CSD01060E +Diode:CVFD20065A +Diode:Central_Semi_CMKD4448 +Diode:Central_Semi_CMKD6001 +Diode:Comchip_ACDSV6-4448TI-G +Diode:Comchip_CDSV6-4148-G +Diode:Comchip_CDSV6-4448TI-G +Diode:DB3 +Diode:DB4 +Diode:DC34 +Diode:DSB2810 +Diode:DSB5712 +Diode:DZ2S030X0L +Diode:DZ2S033X0L +Diode:DZ2S036X0L +Diode:DZ2S039X0L +Diode:DZ2S047X0L +Diode:DZ2S051X0L +Diode:DZ2S056X0L +Diode:DZ2S068X0L +Diode:DZ2S082X0L +Diode:DZ2S100X0L +Diode:DZ2S110X0L +Diode:DZ2S120X0L +Diode:DZ2S130X0L +Diode:DZ2S150X0L +Diode:DZ2S160X0L +Diode:DZ2S180X0L +Diode:DZ2S200X0L +Diode:DZ2S360X0L +Diode:ESD131-B1-W0201 +Diode:ESD5Zxx +Diode:ESD9B3.3ST5G +Diode:ESD9B5.0ST5G +Diode:ESH2PB +Diode:ESH2PC +Diode:ESH2PD +Diode:HN2D02FU +Diode:IDDD04G65C6 +Diode:IDDD06G65C6 +Diode:IDDD08G65C6 +Diode:IDDD10G65C6 +Diode:IDDD12G65C6 +Diode:IDDD16G65C6 +Diode:IDDD20G65C6 +Diode:LL41 +Diode:LL4148 +Diode:LL42 +Diode:LL43 +Diode:LL4448 +Diode:MBR0520 +Diode:MBR0520LT +Diode:MBR0530 +Diode:MBR0540 +Diode:MBR0550 +Diode:MBR0560 +Diode:MBR0570 +Diode:MBR0580 +Diode:MBR1020VL +Diode:MBR340 +Diode:MBR735 +Diode:MBR745 +Diode:MBRA340 +Diode:MBRS340 +Diode:MCL4148 +Diode:MCL4448 +Diode:MM3Zxx +Diode:MM5Zxx +Diode:MMBD4148TW +Diode:MMBD4448HADW +Diode:MMBD4448HCQW +Diode:MMBD4448HTW +Diode:MMBZxx +Diode:MMSD4148 +Diode:MMSD914 +Diode:MRA4003T3G +Diode:MRA4004T3G +Diode:MRA4005T3G +Diode:MRA4006T3G +Diode:MRA4007T3G +Diode:NRVA4003T3G +Diode:NRVA4004T3G +Diode:NRVA4005T3G +Diode:NRVA4006T3G +Diode:NRVA4007T3G +Diode:NSR0340HT1G +Diode:PMEG030V030EPD +Diode:PMEG030V050EPD +Diode:PMEG040V030EPD +Diode:PMEG040V050EPD +Diode:PMEG045T050EPD +Diode:PMEG045T100EPD +Diode:PMEG045T150EIPD +Diode:PMEG045T150EPD +Diode:PMEG045V050EPD +Diode:PMEG045V100EPD +Diode:PMEG045V150EPD +Diode:PMEG050T150EPD +Diode:PMEG050V030EPD +Diode:PMEG050V150EPD +Diode:PMEG060V030EPD +Diode:PMEG060V050EPD +Diode:PMEG060V100EPD +Diode:PMEG10010ELR +Diode:PMEG10020AELR +Diode:PMEG10020ELR +Diode:PMEG100V060ELPD +Diode:PMEG100V080ELPD +Diode:PMEG100V100ELPD +Diode:PMEG1020EH +Diode:PMEG1020EJ +Diode:PMEG1030EH +Diode:PMEG1030EJ +Diode:PMEG2005EH +Diode:PMEG2005EJ +Diode:PMEG2010AEH +Diode:PMEG2010AEJ +Diode:PMEG2010AET +Diode:PMEG2010BER +Diode:PMEG2010EH +Diode:PMEG2010EJ +Diode:PMEG2010ER +Diode:PMEG2010ET +Diode:PMEG2015EH +Diode:PMEG2015EJ +Diode:PMEG2020EH +Diode:PMEG2020EJ +Diode:PMEG3002EJ +Diode:PMEG3005EH +Diode:PMEG3005EJ +Diode:PMEG3010BER +Diode:PMEG3010CEH +Diode:PMEG3010CEJ +Diode:PMEG3010EH +Diode:PMEG3010EJ +Diode:PMEG3010ER +Diode:PMEG3010ET +Diode:PMEG3015EH +Diode:PMEG3015EJ +Diode:PMEG3020BER +Diode:PMEG3020EH +Diode:PMEG3020EJ +Diode:PMEG3020ER +Diode:PMEG4002EJ +Diode:PMEG4005CEJ +Diode:PMEG4005EH +Diode:PMEG4005EJ +Diode:PMEG4010CEH +Diode:PMEG4010CEJ +Diode:PMEG4010EH +Diode:PMEG4010EJ +Diode:PMEG4010ER +Diode:PMEG4010ET +Diode:PMEG4020ER +Diode:PMEG4030ER +Diode:PMEG4050EP +Diode:PMEG40T10ER +Diode:PMEG40T20ER +Diode:PMEG40T30ER +Diode:PMEG45A10EPD +Diode:PMEG45T15EPD +Diode:PMEG45U10EPD +Diode:PMEG6002EJ +Diode:PMEG6010CEH +Diode:PMEG6010CEJ +Diode:PMEG6010ELR +Diode:PMEG6010ER +Diode:PMEG6020AELR +Diode:PMEG6020ELR +Diode:PMEG6020ER +Diode:PMEG6030EP +Diode:PMEG6045ETP +Diode:PMEG60T10ELR +Diode:PMEG60T20ELR +Diode:PMEG60T30ELR +Diode:PTVS10VZ1USK +Diode:PTVS12VZ1USK +Diode:PTVS15VZ1USK +Diode:PTVS18VZ1USK +Diode:PTVS20VZ1USK +Diode:PTVS22VZ1USK +Diode:PTVS26VZ1USK +Diode:PTVS5V0Z1USK +Diode:PTVS5V0Z1USKP +Diode:PTVS7V5Z1USK +Diode:Panasonic_MA5J002E +Diode:RF01VM2S +Diode:Rohm_UMN1N +Diode:Rohm_UMP11N +Diode:S2JTR +Diode:SB120 +Diode:SB130 +Diode:SB140 +Diode:SB150 +Diode:SB160 +Diode:SB5H100 +Diode:SB5H90 +Diode:SD05_SOD323 +Diode:SD103ATW +Diode:SD12_SOD323 +Diode:SD15_SOD323 +Diode:SD24_SOD323 +Diode:SD36_SOD323 +Diode:SM15T36A +Diode:SM15T36CA +Diode:SM15T6V8A +Diode:SM15T6V8CA +Diode:SM15T7V5A +Diode:SM15T7V5CA +Diode:SM2000 +Diode:SM4001 +Diode:SM4002 +Diode:SM4003 +Diode:SM4004 +Diode:SM4005 +Diode:SM4006 +Diode:SM4007 +Diode:SM5059 +Diode:SM5060 +Diode:SM5061 +Diode:SM5062 +Diode:SM5063 +Diode:SM513 +Diode:SM516 +Diode:SM518 +Diode:SM5908 +Diode:SM6T100A +Diode:SM6T10A +Diode:SM6T12A +Diode:SM6T150A +Diode:SM6T15A +Diode:SM6T18A +Diode:SM6T200A +Diode:SM6T220A +Diode:SM6T22A +Diode:SM6T24A +Diode:SM6T27A +Diode:SM6T30A +Diode:SM6T33A +Diode:SM6T36A +Diode:SM6T39A +Diode:SM6T56A +Diode:SM6T68A +Diode:SM6T6V8A +Diode:SM6T75A +Diode:SM6T7V5A +Diode:SM712_SOT23 +Diode:SMAJ100A +Diode:SMAJ100CA +Diode:SMAJ10A +Diode:SMAJ10CA +Diode:SMAJ110A +Diode:SMAJ110CA +Diode:SMAJ11A +Diode:SMAJ11CA +Diode:SMAJ120A +Diode:SMAJ120CA +Diode:SMAJ12A +Diode:SMAJ12CA +Diode:SMAJ130A +Diode:SMAJ130CA +Diode:SMAJ13A +Diode:SMAJ13CA +Diode:SMAJ14A +Diode:SMAJ14CA +Diode:SMAJ150A +Diode:SMAJ150CA +Diode:SMAJ15A +Diode:SMAJ15CA +Diode:SMAJ160A +Diode:SMAJ160CA +Diode:SMAJ16A +Diode:SMAJ16CA +Diode:SMAJ170A +Diode:SMAJ170CA +Diode:SMAJ17A +Diode:SMAJ17CA +Diode:SMAJ180A +Diode:SMAJ180CA +Diode:SMAJ188A +Diode:SMAJ188CA +Diode:SMAJ18A +Diode:SMAJ18CA +Diode:SMAJ200A +Diode:SMAJ200CA +Diode:SMAJ20A +Diode:SMAJ20CA +Diode:SMAJ220A +Diode:SMAJ220CA +Diode:SMAJ22A +Diode:SMAJ22CA +Diode:SMAJ24A +Diode:SMAJ24CA +Diode:SMAJ250A +Diode:SMAJ250CA +Diode:SMAJ26A +Diode:SMAJ26CA +Diode:SMAJ28A +Diode:SMAJ28CA +Diode:SMAJ300A +Diode:SMAJ300CA +Diode:SMAJ30A +Diode:SMAJ30CA +Diode:SMAJ33A +Diode:SMAJ33CA +Diode:SMAJ350A +Diode:SMAJ350CA +Diode:SMAJ36A +Diode:SMAJ36CA +Diode:SMAJ400A +Diode:SMAJ400CA +Diode:SMAJ40A +Diode:SMAJ40CA +Diode:SMAJ43A +Diode:SMAJ43CA +Diode:SMAJ440A +Diode:SMAJ440CA +Diode:SMAJ45A +Diode:SMAJ45CA +Diode:SMAJ48A +Diode:SMAJ48CA +Diode:SMAJ5.0A +Diode:SMAJ5.0CA +Diode:SMAJ51A +Diode:SMAJ51CA +Diode:SMAJ54A +Diode:SMAJ54CA +Diode:SMAJ58A +Diode:SMAJ58CA +Diode:SMAJ6.0A +Diode:SMAJ6.0CA +Diode:SMAJ6.5A +Diode:SMAJ6.5CA +Diode:SMAJ60A +Diode:SMAJ60CA +Diode:SMAJ64A +Diode:SMAJ64CA +Diode:SMAJ7.0A +Diode:SMAJ7.0CA +Diode:SMAJ7.5A +Diode:SMAJ7.5CA +Diode:SMAJ70A +Diode:SMAJ70CA +Diode:SMAJ75A +Diode:SMAJ75CA +Diode:SMAJ78A +Diode:SMAJ78CA +Diode:SMAJ8.0A +Diode:SMAJ8.0CA +Diode:SMAJ8.5A +Diode:SMAJ8.5CA +Diode:SMAJ85A +Diode:SMAJ85CA +Diode:SMAJ9.0A +Diode:SMAJ9.0CA +Diode:SMAJ90A +Diode:SMAJ90CA +Diode:SMF10A +Diode:SMF11A +Diode:SMF12A +Diode:SMF13A +Diode:SMF14A +Diode:SMF15A +Diode:SMF16A +Diode:SMF17A +Diode:SMF18A +Diode:SMF20A +Diode:SMF22A +Diode:SMF24A +Diode:SMF26A +Diode:SMF28A +Diode:SMF30A +Diode:SMF33A +Diode:SMF36A +Diode:SMF40A +Diode:SMF43A +Diode:SMF45A +Diode:SMF48A +Diode:SMF51A +Diode:SMF54A +Diode:SMF58A +Diode:SMF5V0A +Diode:SMF6V0A +Diode:SMF6V5A +Diode:SMF7V0A +Diode:SMF7V5A +Diode:SMF8V0A +Diode:SMF8V5A +Diode:SMF9V0A +Diode:SMZxxx +Diode:SS110 +Diode:SS1150 +Diode:SS12 +Diode:SS1200 +Diode:SS13 +Diode:SS14 +Diode:SS15 +Diode:SS16 +Diode:SS18 +Diode:SS210 +Diode:SS2150 +Diode:SS22 +Diode:SS2200 +Diode:SS23 +Diode:SS24 +Diode:SS25 +Diode:SS26 +Diode:SS28 +Diode:SS310 +Diode:SS3150 +Diode:SS32 +Diode:SS3200 +Diode:SS33 +Diode:SS34 +Diode:SS35 +Diode:SS36 +Diode:SS38 +Diode:STBR3008WY +Diode:STBR3012WY +Diode:STBR6008WY +Diode:STBR6012WY +Diode:STTH2002D +Diode:STTH2002G +Diode:STTH212S +Diode:STTH212U +Diode:SZESD9B5.0ST5G +Diode:Toshiba_HN1D01FU +Diode:UF5400 +Diode:UF5401 +Diode:UF5402 +Diode:UF5403 +Diode:UF5404 +Diode:UF5405 +Diode:UF5406 +Diode:UF5407 +Diode:UF5408 +Diode:US1A +Diode:US1B +Diode:US1D +Diode:US1G +Diode:US1J +Diode:US1K +Diode:US1M +Diode:US2AA +Diode:US2BA +Diode:US2DA +Diode:US2FA +Diode:US2GA +Diode:US2JA +Diode:US2KA +Diode:US2MA +Diode:VS-6ESU06 +Diode:Z1SMAxxx +Diode:Z2SMBxxx +Diode:Z3SMCxxx +Diode:ZMDxx +Diode:ZMMxx +Diode:ZMYxx +Diode:ZPDxx +Diode:ZPYxx +Diode:ZYxxx +Diode_Bridge:ABS10 +Diode_Bridge:ABS2 +Diode_Bridge:ABS4 +Diode_Bridge:ABS6 +Diode_Bridge:ABS8 +Diode_Bridge:B125C1500G +Diode_Bridge:B125C2300-1500A +Diode_Bridge:B125C2300-1500B +Diode_Bridge:B125C3x00-2200A +Diode_Bridge:B125C5000-3x00A +Diode_Bridge:B125C800DM +Diode_Bridge:B125R +Diode_Bridge:B250C1500G +Diode_Bridge:B250C2300-1500A +Diode_Bridge:B250C2300-1500B +Diode_Bridge:B250C3x00-2200A +Diode_Bridge:B250C5000-3x00A +Diode_Bridge:B250C800DM +Diode_Bridge:B250R +Diode_Bridge:B380C1500G +Diode_Bridge:B380C2300-1500A +Diode_Bridge:B380C2300-1500B +Diode_Bridge:B380C3x00-2200A +Diode_Bridge:B380C5000-3x00A +Diode_Bridge:B380C800DM +Diode_Bridge:B380R +Diode_Bridge:B40C1500G +Diode_Bridge:B40C2300-1500A +Diode_Bridge:B40C2300-1500B +Diode_Bridge:B40C3x00-2200A +Diode_Bridge:B40C5000-3x00A +Diode_Bridge:B40C800DM +Diode_Bridge:B40R +Diode_Bridge:B500C2300-1500A +Diode_Bridge:B500C3x00-2200A +Diode_Bridge:B500C5000-3x00A +Diode_Bridge:B500R +Diode_Bridge:B700C2300-1500B +Diode_Bridge:B80C1500G +Diode_Bridge:B80C2300-1500A +Diode_Bridge:B80C2300-1500B +Diode_Bridge:B80C3x00-2200A +Diode_Bridge:B80C5000-3x00A +Diode_Bridge:B80C800DM +Diode_Bridge:B80R +Diode_Bridge:DF005M +Diode_Bridge:DF005S +Diode_Bridge:DF01M +Diode_Bridge:DF01S +Diode_Bridge:DF01S1 +Diode_Bridge:DF02M +Diode_Bridge:DF02S +Diode_Bridge:DF04M +Diode_Bridge:DF04S +Diode_Bridge:DF06M +Diode_Bridge:DF06S +Diode_Bridge:DF08M +Diode_Bridge:DF08S +Diode_Bridge:DF10M +Diode_Bridge:DF10S +Diode_Bridge:DMA40U1800GU +Diode_Bridge:DNA40U2200GU +Diode_Bridge:GBU4A +Diode_Bridge:GBU4B +Diode_Bridge:GBU4D +Diode_Bridge:GBU4G +Diode_Bridge:GBU4J +Diode_Bridge:GBU4K +Diode_Bridge:GBU4M +Diode_Bridge:GBU6A +Diode_Bridge:GBU6B +Diode_Bridge:GBU6D +Diode_Bridge:GBU6G +Diode_Bridge:GBU6J +Diode_Bridge:GBU6K +Diode_Bridge:GBU6M +Diode_Bridge:GBU8A +Diode_Bridge:GBU8B +Diode_Bridge:GBU8D +Diode_Bridge:GBU8G +Diode_Bridge:GBU8J +Diode_Bridge:GBU8K +Diode_Bridge:GBU8M +Diode_Bridge:GUO40-08NO1 +Diode_Bridge:GUO40-12NO1 +Diode_Bridge:GUO40-16NO1 +Diode_Bridge:KBPC15005T +Diode_Bridge:KBPC15005W +Diode_Bridge:KBPC1501T +Diode_Bridge:KBPC1501W +Diode_Bridge:KBPC1502T +Diode_Bridge:KBPC1502W +Diode_Bridge:KBPC1504T +Diode_Bridge:KBPC1504W +Diode_Bridge:KBPC1506T +Diode_Bridge:KBPC1506W +Diode_Bridge:KBPC1508T +Diode_Bridge:KBPC1508W +Diode_Bridge:KBPC1510T +Diode_Bridge:KBPC1510W +Diode_Bridge:KBPC25005T +Diode_Bridge:KBPC25005W +Diode_Bridge:KBPC2501T +Diode_Bridge:KBPC2501W +Diode_Bridge:KBPC2502T +Diode_Bridge:KBPC2502W +Diode_Bridge:KBPC2504T +Diode_Bridge:KBPC2504W +Diode_Bridge:KBPC2506T +Diode_Bridge:KBPC2506W +Diode_Bridge:KBPC2508T +Diode_Bridge:KBPC2508W +Diode_Bridge:KBPC2510T +Diode_Bridge:KBPC2510W +Diode_Bridge:KBPC35005T +Diode_Bridge:KBPC35005W +Diode_Bridge:KBPC3501T +Diode_Bridge:KBPC3501W +Diode_Bridge:KBPC3502T +Diode_Bridge:KBPC3502W +Diode_Bridge:KBPC3504T +Diode_Bridge:KBPC3504W +Diode_Bridge:KBPC3506T +Diode_Bridge:KBPC3506W +Diode_Bridge:KBPC3508T +Diode_Bridge:KBPC3508W +Diode_Bridge:KBPC3510T +Diode_Bridge:KBPC3510W +Diode_Bridge:KBPC50005T +Diode_Bridge:KBPC50005W +Diode_Bridge:KBPC5001T +Diode_Bridge:KBPC5001W +Diode_Bridge:KBPC5002T +Diode_Bridge:KBPC5002W +Diode_Bridge:KBPC5004T +Diode_Bridge:KBPC5004W +Diode_Bridge:KBPC5006T +Diode_Bridge:KBPC5006W +Diode_Bridge:KBPC5008T +Diode_Bridge:KBPC5008W +Diode_Bridge:KBPC5010T +Diode_Bridge:KBPC5010W +Diode_Bridge:KBU4A +Diode_Bridge:KBU4B +Diode_Bridge:KBU4D +Diode_Bridge:KBU4G +Diode_Bridge:KBU4J +Diode_Bridge:KBU4K +Diode_Bridge:KBU4M +Diode_Bridge:KBU6A +Diode_Bridge:KBU6B +Diode_Bridge:KBU6D +Diode_Bridge:KBU6G +Diode_Bridge:KBU6J +Diode_Bridge:KBU6K +Diode_Bridge:KBU6M +Diode_Bridge:KBU8A +Diode_Bridge:KBU8B +Diode_Bridge:KBU8D +Diode_Bridge:KBU8G +Diode_Bridge:KBU8J +Diode_Bridge:KBU8K +Diode_Bridge:KBU8M +Diode_Bridge:MB2S +Diode_Bridge:MB4S +Diode_Bridge:MB6S +Diode_Bridge:MBL104S +Diode_Bridge:MBL106S +Diode_Bridge:MBL108S +Diode_Bridge:MBL110S +Diode_Bridge:MDB10S +Diode_Bridge:MDB6S +Diode_Bridge:MDB8S +Diode_Bridge:RB151 +Diode_Bridge:RB152 +Diode_Bridge:RB153 +Diode_Bridge:RB154 +Diode_Bridge:RB155 +Diode_Bridge:RB156 +Diode_Bridge:RB157 +Diode_Bridge:RMB2S +Diode_Bridge:RMB4S +Diode_Bridge:SC35VB160S-G +Diode_Bridge:SC35VB80S-G +Diode_Bridge:VS-KBPC1005 +Diode_Bridge:VS-KBPC101 +Diode_Bridge:VS-KBPC102 +Diode_Bridge:VS-KBPC104 +Diode_Bridge:VS-KBPC106 +Diode_Bridge:VS-KBPC108 +Diode_Bridge:VS-KBPC110 +Diode_Bridge:VS-KBPC6005 +Diode_Bridge:VS-KBPC601 +Diode_Bridge:VS-KBPC602 +Diode_Bridge:VS-KBPC604 +Diode_Bridge:VS-KBPC606 +Diode_Bridge:VS-KBPC608 +Diode_Bridge:VS-KBPC610 +Diode_Bridge:VS-KBPC8005 +Diode_Bridge:VS-KBPC801 +Diode_Bridge:VS-KBPC802 +Diode_Bridge:VS-KBPC804 +Diode_Bridge:VS-KBPC806 +Diode_Bridge:VS-KBPC808 +Diode_Bridge:VS-KBPC810 +Diode_Bridge:W005G +Diode_Bridge:W01G +Diode_Bridge:W02G +Diode_Bridge:W04G +Diode_Bridge:W06G +Diode_Bridge:W08G +Diode_Bridge:W10G +Diode_Laser:PL520 +Diode_Laser:PLT5_450B +Diode_Laser:PLT5_488 +Diode_Laser:PLT5_510 +Diode_Laser:SPL_PL90 +Display_Character:AD-121F2 +Display_Character:CA56-12CGKWA +Display_Character:CA56-12EWA +Display_Character:CA56-12SEKWA +Display_Character:CA56-12SRWA +Display_Character:CA56-12SURKWA +Display_Character:CA56-12SYKWA +Display_Character:CC56-12CGKWA +Display_Character:CC56-12EWA +Display_Character:CC56-12GWA +Display_Character:CC56-12SEKWA +Display_Character:CC56-12SRWA +Display_Character:CC56-12SURKWA +Display_Character:CC56-12SYKWA +Display_Character:CC56-12YWA +Display_Character:D148K +Display_Character:D168K +Display_Character:D198K +Display_Character:D1X8K-14BL +Display_Character:DA04-11CGKWA +Display_Character:DA04-11EWA +Display_Character:DA04-11GWA +Display_Character:DA04-11SEKWA +Display_Character:DA04-11SRWA +Display_Character:DA04-11SURKWA +Display_Character:DA04-11SYKWA +Display_Character:DA56-11CGKWA +Display_Character:DA56-11EWA +Display_Character:DA56-11GWA +Display_Character:DA56-11SEKWA +Display_Character:DA56-11SRWA +Display_Character:DA56-11SURKWA +Display_Character:DA56-11SYKWA +Display_Character:DA56-11YWA +Display_Character:DC56-11CGKWA +Display_Character:DC56-11EWA +Display_Character:DC56-11GWA +Display_Character:DC56-11SEKWA +Display_Character:DC56-11SRWA +Display_Character:DC56-11SURKWA +Display_Character:DC56-11SYKWA +Display_Character:DC56-11YWA +Display_Character:DE113-XX-XX +Display_Character:DE114-RS-20 +Display_Character:DE122-XX-XX +Display_Character:DE170-XX-XX +Display_Character:EA_T123X-I2C +Display_Character:ELD-426SYGWA +Display_Character:HDSM-441B +Display_Character:HDSM-443B +Display_Character:HDSM-541B +Display_Character:HDSM-543B +Display_Character:HDSP-7401 +Display_Character:HDSP-7403 +Display_Character:HDSP-7501 +Display_Character:HDSP-7503 +Display_Character:HDSP-7507 +Display_Character:HDSP-7508 +Display_Character:HDSP-7801 +Display_Character:HDSP-7803 +Display_Character:HDSP-7807 +Display_Character:HDSP-7808 +Display_Character:HDSP-A151 +Display_Character:HDSP-A153 +Display_Character:HDSP-A401 +Display_Character:HDSP-A403 +Display_Character:HY1602E +Display_Character:KCSA02-105 +Display_Character:KCSA02-106 +Display_Character:KCSA02-107 +Display_Character:KCSA02-123 +Display_Character:KCSA02-136 +Display_Character:KCSC02-105 +Display_Character:KCSC02-106 +Display_Character:KCSC02-107 +Display_Character:KCSC02-123 +Display_Character:KCSC02-136 +Display_Character:LCD-016N002L +Display_Character:LM16255K +Display_Character:LTC-4627JD +Display_Character:LTC-4627JD-01 +Display_Character:LTC-4627JF +Display_Character:LTC-4627JG +Display_Character:LTC-4627JR +Display_Character:LTC-4627JS +Display_Character:LTS-6960HR +Display_Character:LTS-6980HR +Display_Character:MAN3410A +Display_Character:MAN3420A +Display_Character:MAN3440A +Display_Character:MAN3610A +Display_Character:MAN3620A +Display_Character:MAN3630A +Display_Character:MAN3640A +Display_Character:MAN3810A +Display_Character:MAN3820A +Display_Character:MAN3840A +Display_Character:MAN71A +Display_Character:MAN72A +Display_Character:MAN73A +Display_Character:MAN74A +Display_Character:NHD-0420H1Z +Display_Character:NHD-C0220BIZ +Display_Character:NHD-C0220BIZ-FSRGB +Display_Character:RC1602A +Display_Character:RC1602A-GHW-ESX +Display_Character:SA15-11EWA +Display_Character:SA15-11GWA +Display_Character:SA15-11SRWA +Display_Character:SA39-11EWA +Display_Character:SA39-11GWA +Display_Character:SA39-11SRWA +Display_Character:SA39-11YWA +Display_Character:SA39-12EWA +Display_Character:SA39-12GWA +Display_Character:SA39-12SRWA +Display_Character:SA39-12YWA +Display_Character:SBC18-11EGWA +Display_Character:SBC18-11SURKCGKWA +Display_Character:SC39-11EWA +Display_Character:SC39-11GWA +Display_Character:SC39-11SRWA +Display_Character:SC39-11YWA +Display_Character:SC39-12EWA +Display_Character:SC39-12GWA +Display_Character:SC39-12SRWA +Display_Character:SC39-12YWA +Display_Character:SM420561N +Display_Character:WC1602A +Display_Graphic:AG12864E +Display_Graphic:EA_DOGL128X-6 +Display_Graphic:EA_DOGM128X-6 +Display_Graphic:EA_DOGS104B-A +Display_Graphic:EA_DOGXL160-7 +Display_Graphic:EA_eDIP128B-6LW +Display_Graphic:EA_eDIP128B-6LWTP +Display_Graphic:EA_eDIP128W-6LW +Display_Graphic:EA_eDIP128W-6LWTP +Display_Graphic:EA_eDIP160B-7LW +Display_Graphic:EA_eDIP160B-7LWTP +Display_Graphic:EA_eDIP160W-7LW +Display_Graphic:EA_eDIP160W-7LWTP +Display_Graphic:EA_eDIP240B-7LW +Display_Graphic:EA_eDIP240B-7LWTP +Display_Graphic:EA_eDIP240J-7LA +Display_Graphic:EA_eDIP240J-7LATP +Display_Graphic:EA_eDIP240J-7LW +Display_Graphic:EA_eDIP240J-7LWTP +Display_Graphic:EA_eDIP320B-8LW +Display_Graphic:EA_eDIP320B-8LWTP +Display_Graphic:EA_eDIP320J-8LA +Display_Graphic:EA_eDIP320J-8LATP +Display_Graphic:EA_eDIP320J-8LW +Display_Graphic:EA_eDIP320J-8LWTP +Display_Graphic:EA_eDIPTFT32-A +Display_Graphic:EA_eDIPTFT32-ATP +Display_Graphic:EA_eDIPTFT43-A +Display_Graphic:EA_eDIPTFT43-ATC +Display_Graphic:EA_eDIPTFT43-ATP +Display_Graphic:EA_eDIPTFT43-ATS +Display_Graphic:EA_eDIPTFT57-A +Display_Graphic:EA_eDIPTFT57-ATP +Display_Graphic:EA_eDIPTFT70-A +Display_Graphic:EA_eDIPTFT70-ATC +Display_Graphic:EA_eDIPTFT70-ATP +Display_Graphic:ERM19264 +Display_Graphic:NHD-C12832A1Z-FSRGB +Display_Graphic:OLED-128O064D +Driver_Display:82720 +Driver_Display:ADS7843E +Driver_Display:ADS7843E-2K5 +Driver_Display:ADS7843EG4 +Driver_Display:ADS7843IDBQRQ1 +Driver_Display:AY0438X-L +Driver_Display:AY0438X-P +Driver_Display:CR2013-MI2120 +Driver_Display:XPT2046QF +Driver_Display:XPT2046TS +Driver_FET:1EDN7550B +Driver_FET:1EDN8550B +Driver_FET:2ED1323S12P +Driver_FET:2ED1324S12P +Driver_FET:2ED21824S06J +Driver_FET:2EDL23N06PJXUMA1 +Driver_FET:ACPL-336J +Driver_FET:ACPL-P343 +Driver_FET:ACPL-W343 +Driver_FET:AN34092B +Driver_FET:BSP75N +Driver_FET:BSP76 +Driver_FET:BTS4140N +Driver_FET:EL7202CN +Driver_FET:EL7202CS +Driver_FET:EL7212CN +Driver_FET:EL7212CS +Driver_FET:EL7222CN +Driver_FET:EL7222CS +Driver_FET:FAN3111C +Driver_FET:FAN3111E +Driver_FET:FAN3268 +Driver_FET:FAN3278 +Driver_FET:FAN7371 +Driver_FET:FAN7388 +Driver_FET:FAN7842 +Driver_FET:FAN7888 +Driver_FET:FL5150MX +Driver_FET:FL5160MX +Driver_FET:HCPL-3120 +Driver_FET:HCPL-314J +Driver_FET:HIP2100_DFN +Driver_FET:HIP2100_EPSOIC +Driver_FET:HIP2100_QFN +Driver_FET:HIP2100_SOIC +Driver_FET:HIP2101_DFN +Driver_FET:HIP2101_EPSOIC +Driver_FET:HIP2101_QFN +Driver_FET:HIP2101_SOIC +Driver_FET:HIP4080A +Driver_FET:HIP4081A +Driver_FET:HIP4082xB +Driver_FET:HIP4082xP +Driver_FET:ICL7667 +Driver_FET:IR2010 +Driver_FET:IR2010S +Driver_FET:IR2011 +Driver_FET:IR2085S +Driver_FET:IR2101 +Driver_FET:IR2102 +Driver_FET:IR2103 +Driver_FET:IR2104 +Driver_FET:IR2106 +Driver_FET:IR21064 +Driver_FET:IR2108 +Driver_FET:IR21084 +Driver_FET:IR2109 +Driver_FET:IR21091 +Driver_FET:IR21094 +Driver_FET:IR2110 +Driver_FET:IR2110S +Driver_FET:IR2111 +Driver_FET:IR2112 +Driver_FET:IR2112S +Driver_FET:IR2113 +Driver_FET:IR2113S +Driver_FET:IR2114S +Driver_FET:IR2133 +Driver_FET:IR2133S +Driver_FET:IR2135 +Driver_FET:IR2135S +Driver_FET:IR2153 +Driver_FET:IR21531 +Driver_FET:IR2155 +Driver_FET:IR2181 +Driver_FET:IR21814 +Driver_FET:IR2183 +Driver_FET:IR21834 +Driver_FET:IR2184 +Driver_FET:IR21844 +Driver_FET:IR2213 +Driver_FET:IR2213S +Driver_FET:IR2214S +Driver_FET:IR2233 +Driver_FET:IR2233S +Driver_FET:IR2235 +Driver_FET:IR2235S +Driver_FET:IR2301 +Driver_FET:IR2302 +Driver_FET:IR2304 +Driver_FET:IR2308 +Driver_FET:IR25602S +Driver_FET:IR25603 +Driver_FET:IR25604S +Driver_FET:IR25607S +Driver_FET:IR7106S +Driver_FET:IR7184S +Driver_FET:IR7304S +Driver_FET:IRS2001 +Driver_FET:IRS2001M +Driver_FET:IRS2003 +Driver_FET:IRS2004 +Driver_FET:IRS2005M +Driver_FET:IRS2005S +Driver_FET:IRS2008S +Driver_FET:IRS2011 +Driver_FET:IRS2101 +Driver_FET:IRS2103 +Driver_FET:IRS2104 +Driver_FET:IRS2106 +Driver_FET:IRS21064 +Driver_FET:IRS2108 +Driver_FET:IRS21084 +Driver_FET:IRS2109 +Driver_FET:IRS21091 +Driver_FET:IRS21094 +Driver_FET:IRS2110 +Driver_FET:IRS2110S +Driver_FET:IRS2111 +Driver_FET:IRS2112 +Driver_FET:IRS2112S +Driver_FET:IRS2113 +Driver_FET:IRS2113M +Driver_FET:IRS2113S +Driver_FET:IRS21531D +Driver_FET:IRS2153D +Driver_FET:IRS2181 +Driver_FET:IRS21814 +Driver_FET:IRS21814M +Driver_FET:IRS2183 +Driver_FET:IRS21834 +Driver_FET:IRS2184 +Driver_FET:IRS21844 +Driver_FET:IRS21844M +Driver_FET:IRS2186 +Driver_FET:IRS21864 +Driver_FET:IRS21867S +Driver_FET:IRS2301S +Driver_FET:IRS2302S +Driver_FET:IRS2304 +Driver_FET:IRS2308 +Driver_FET:IRS25606S +Driver_FET:IRS2890DS +Driver_FET:ITS711L1 +Driver_FET:ITS716G +Driver_FET:ITS724G +Driver_FET:L6491 +Driver_FET:LF2190N +Driver_FET:LM2105D +Driver_FET:LM2105DSG +Driver_FET:LM5109AMA +Driver_FET:LM5109ASD +Driver_FET:LM5109BMA +Driver_FET:LM5109BSD +Driver_FET:LM5109MA +Driver_FET:LMG1020YFF +Driver_FET:LTC4440EMS8 +Driver_FET:LTC4440ES6 +Driver_FET:LTC4440IMS8 +Driver_FET:LTC4440IS6 +Driver_FET:MAX15012AxSA +Driver_FET:MAX15012BxSA +Driver_FET:MAX15012CxSA +Driver_FET:MAX15012DxSA +Driver_FET:MAX15013AxSA +Driver_FET:MAX15013BxSA +Driver_FET:MAX15013CxSA +Driver_FET:MAX15013DxSA +Driver_FET:MC33152 +Driver_FET:MC34152 +Driver_FET:MCP1415 +Driver_FET:MCP1415R +Driver_FET:MCP1416 +Driver_FET:MCP1416R +Driver_FET:MCP14A0303xMNY +Driver_FET:MCP14A0304xMNY +Driver_FET:MCP14A0305xMNY +Driver_FET:MCP14A0901xMNY +Driver_FET:MCP14A0902xMNY +Driver_FET:MCP14A1201xMNY +Driver_FET:MCP14A1202xMNY +Driver_FET:MIC4426 +Driver_FET:MIC4427 +Driver_FET:MIC4428 +Driver_FET:MIC4604YM +Driver_FET:NCD5702 +Driver_FET:NCV8402xST +Driver_FET:PE29101 +Driver_FET:PE29102 +Driver_FET:PM8834 +Driver_FET:PM8834M +Driver_FET:SM72295MA +Driver_FET:STGAP1AS +Driver_FET:STGAP2SCM +Driver_FET:STGAP2SM +Driver_FET:TC4421 +Driver_FET:TC4422 +Driver_FET:TC4426xOA +Driver_FET:TC4427xOA +Driver_FET:TC4428xOA +Driver_FET:TLP250 +Driver_FET:UCC21520ADW +Driver_FET:UCC21520DW +Driver_FET:UCC27511ADBV +Driver_FET:UCC27714D +Driver_FET:ZXGD3001E6 +Driver_FET:ZXGD3002E6 +Driver_FET:ZXGD3003E6 +Driver_FET:ZXGD3004E6 +Driver_FET:ZXGD3006E6 +Driver_FET:ZXGD3009E6 +Driver_Haptic:DRV2510-Q1 +Driver_Haptic:DRV2605LDGS +Driver_LED:AL8860MP +Driver_LED:AL8860WT +Driver_LED:AP3019AKTR +Driver_LED:AP3019AKTTR +Driver_LED:BCR430UW6 +Driver_LED:CH455G +Driver_LED:CH455H +Driver_LED:CH455K +Driver_LED:CL220K4-G +Driver_LED:CL220N5-G +Driver_LED:DIO5661CD6 +Driver_LED:DIO5661ST6 +Driver_LED:DIO5661TST6 +Driver_LED:HT1632C-52LQFP +Driver_LED:HV9921N8-G +Driver_LED:HV9922N8-G +Driver_LED:HV9923N8-G +Driver_LED:HV9925SG-G +Driver_LED:HV9930LG-G +Driver_LED:HV9931LG-G +Driver_LED:HV9961LG-G +Driver_LED:HV9961NG-G +Driver_LED:HV9967BK7-G +Driver_LED:HV9967BMG-G +Driver_LED:HV9972LG-G +Driver_LED:IS31FL3216 +Driver_LED:IS31FL3216A +Driver_LED:IS31FL3218-GR +Driver_LED:IS31FL3218-QF +Driver_LED:IS31FL3236-TQ +Driver_LED:IS31FL3236A-TQ +Driver_LED:IS31FL3731-QF +Driver_LED:IS31FL3731-SA +Driver_LED:IS31FL3733-QF +Driver_LED:IS31FL3733-TQ +Driver_LED:IS31FL3736 +Driver_LED:IS31FL3737 +Driver_LED:IS31LT3360 +Driver_LED:KTD2026 +Driver_LED:KTD2027 +Driver_LED:KTD2061xxUAC +Driver_LED:LED1642GWPTR +Driver_LED:LED1642GWQTR +Driver_LED:LED1642GWTTR +Driver_LED:LED1642GWXTTR +Driver_LED:LED5000 +Driver_LED:LM3914N +Driver_LED:LM3914V +Driver_LED:LP5036 +Driver_LED:LP8868XQDMT +Driver_LED:LT3465 +Driver_LED:LT3465A +Driver_LED:LT3755xMSE +Driver_LED:LT3755xMSE-1 +Driver_LED:LT3755xMSE-2 +Driver_LED:LT3755xUD +Driver_LED:LT3755xUD-1 +Driver_LED:LT3755xUD-2 +Driver_LED:LT3756xMSE +Driver_LED:LT3756xMSE-1 +Driver_LED:LT3756xMSE-2 +Driver_LED:LT3756xUD +Driver_LED:LT3756xUD-1 +Driver_LED:LT3756xUD-2 +Driver_LED:LT8391xFE +Driver_LED:MAX7219 +Driver_LED:MAX7221xNG +Driver_LED:MAX7221xRG +Driver_LED:MAX7221xWG +Driver_LED:MBI5252GFN +Driver_LED:MBI5252GP +Driver_LED:MC14495P +Driver_LED:MCP1643xMS +Driver_LED:MCP1662-xOT +Driver_LED:MP3362GJ +Driver_LED:MPQ2483DQ +Driver_LED:MPQ3362GJ-AEC1 +Driver_LED:NCP5623DTBR2G +Driver_LED:NCR401U +Driver_LED:PCA9531PW +Driver_LED:PCA9635 +Driver_LED:PCA9685BS +Driver_LED:PCA9685PW +Driver_LED:PCA9745BTW +Driver_LED:RCD-24 +Driver_LED:ST1CC40DR +Driver_LED:ST1CC40PUR +Driver_LED:STP08CP05B +Driver_LED:STP08CP05M +Driver_LED:STP08CP05T +Driver_LED:STP08CP05XT +Driver_LED:STP16CP05M +Driver_LED:STP16CP05P +Driver_LED:STP16CP05T +Driver_LED:STP16CP05XT +Driver_LED:STP16CPC26M +Driver_LED:STP16CPC26P +Driver_LED:STP16CPC26T +Driver_LED:STP16CPC26X +Driver_LED:TCA6507RUE +Driver_LED:TLC59108xPW +Driver_LED:TLC5916 +Driver_LED:TLC5917 +Driver_LED:TLC5940NT +Driver_LED:TLC5940PWP +Driver_LED:TLC5947DAP +Driver_LED:TLC5947RHB +Driver_LED:TLC5949PWP +Driver_LED:TLC5951DAP +Driver_LED:TLC5951RHA +Driver_LED:TLC5951RTA +Driver_LED:TLC5957RTQ +Driver_LED:TLC5971PWP +Driver_LED:TLC5971RGE +Driver_LED:TLC5973 +Driver_LED:TPS61165DBV +Driver_LED:TPS92692PWP +Driver_LED:WS2811 +Driver_LED:iC-HTG +Driver_Motor:A4950E +Driver_Motor:A4950K +Driver_Motor:A4952_LY +Driver_Motor:A4953_LJ +Driver_Motor:A4954 +Driver_Motor:AMT49413 +Driver_Motor:DRV8212P +Driver_Motor:DRV8308 +Driver_Motor:DRV8412 +Driver_Motor:DRV8432 +Driver_Motor:DRV8461SPWP +Driver_Motor:DRV8662 +Driver_Motor:DRV8711 +Driver_Motor:DRV8800PWP +Driver_Motor:DRV8800RTY +Driver_Motor:DRV8801PWP +Driver_Motor:DRV8801RTY +Driver_Motor:DRV8833PW +Driver_Motor:DRV8833PWP +Driver_Motor:DRV8833RTY +Driver_Motor:DRV8837 +Driver_Motor:DRV8837C +Driver_Motor:DRV8838 +Driver_Motor:DRV8847PWP +Driver_Motor:DRV8847PWR +Driver_Motor:DRV8847RTE +Driver_Motor:DRV8847SPWR +Driver_Motor:DRV8848 +Driver_Motor:DRV8870DDA +Driver_Motor:DRV8871DDA +Driver_Motor:DRV8872DDA +Driver_Motor:EMC2301-x-ACZL +Driver_Motor:EMC2302-x-AIZL +Driver_Motor:EMC2303-x-KP +Driver_Motor:EMC2305-x-AP +Driver_Motor:L293 +Driver_Motor:L293D +Driver_Motor:L293E +Driver_Motor:L297 +Driver_Motor:L298HN +Driver_Motor:L298N +Driver_Motor:L298P +Driver_Motor:LMD18200 +Driver_Motor:MAX22201 +Driver_Motor:MAX22202 +Driver_Motor:MAX22207 +Driver_Motor:MP6536DU +Driver_Motor:PAC5527QM +Driver_Motor:PG001M +Driver_Motor:Pololu_Breakout_A4988 +Driver_Motor:Pololu_Breakout_DRV8825 +Driver_Motor:SLA7042M +Driver_Motor:SLA7044M +Driver_Motor:SLA7070MPRT +Driver_Motor:SLA7071MPRT +Driver_Motor:SLA7072MPRT +Driver_Motor:SLA7073MPRT +Driver_Motor:SLA7075MPRT +Driver_Motor:SLA7076MPRT +Driver_Motor:SLA7077MPRT +Driver_Motor:SLA7078MPRT +Driver_Motor:SN754410NE +Driver_Motor:STK672-040-E +Driver_Motor:STK672-080-E +Driver_Motor:STSPIN220 +Driver_Motor:STSPIN230 +Driver_Motor:STSPIN233 +Driver_Motor:STSPIN240 +Driver_Motor:TB6612FNG +Driver_Motor:TC78H670FTG +Driver_Motor:TMC2041-LA +Driver_Motor:TMC2100-LA +Driver_Motor:TMC2100-TA +Driver_Motor:TMC2130-LA +Driver_Motor:TMC2130-TA +Driver_Motor:TMC2160 +Driver_Motor:TMC2202-WA +Driver_Motor:TMC2208-LA +Driver_Motor:TMC2224-LA +Driver_Motor:TMC2226-SA +Driver_Motor:TMC262 +Driver_Motor:TMC2660 +Driver_Motor:TMC5130A-TA +Driver_Motor:TMC5160A-TA +Driver_Motor:VNH2SP30 +Driver_Motor:VNH5019A-E +Driver_Motor:ZXBM5210-S +Driver_Motor:ZXBM5210-SP +Driver_Relay:DRV8860 +Driver_Relay:DRV8860_PWPR +Driver_Relay:MAX4820xUP +Driver_Relay:MAX4821xUP +Driver_Relay:TPL9201_TSSOP +Driver_TEC:MAX1968xUI +Driver_TEC:MAX1969xUI +DSP_AnalogDevices:ADAU1450 +DSP_AnalogDevices:ADAU1451 +DSP_AnalogDevices:ADAU1452 +DSP_AnalogDevices:ADAU1701 +DSP_AnalogDevices:ADAU1702 +DSP_Freescale:DSP96002 +DSP_Microchip_DSPIC33:DSPIC33EP256MU810-xPT +DSP_Microchip_DSPIC33:DSPIC33FJ128GP204 +DSP_Microchip_DSPIC33:DSPIC33FJ128GP804 +DSP_Microchip_DSPIC33:DSPIC33FJ128MC204 +DSP_Microchip_DSPIC33:DSPIC33FJ128MC510A +DSP_Microchip_DSPIC33:DSPIC33FJ128MC710A +DSP_Microchip_DSPIC33:DSPIC33FJ128MC804 +DSP_Microchip_DSPIC33:DSPIC33FJ256MC510A +DSP_Microchip_DSPIC33:DSPIC33FJ256MC710A +DSP_Microchip_DSPIC33:DSPIC33FJ32GP304 +DSP_Microchip_DSPIC33:DSPIC33FJ32MC304 +DSP_Microchip_DSPIC33:DSPIC33FJ64GP204 +DSP_Microchip_DSPIC33:DSPIC33FJ64GP306A-IMR +DSP_Microchip_DSPIC33:DSPIC33FJ64GP804 +DSP_Microchip_DSPIC33:DSPIC33FJ64MC204 +DSP_Microchip_DSPIC33:DSPIC33FJ64MC510A +DSP_Microchip_DSPIC33:DSPIC33FJ64MC710A +DSP_Microchip_DSPIC33:DSPIC33FJ64MC802-xSP +DSP_Microchip_DSPIC33:DSPIC33FJ64MC804 +DSP_Motorola:DSP56301 +DSP_Texas:TMS320LF2406PZ +Fiber_Optic:AFBR-1624Z +Fiber_Optic:AFBR-2624Z +Filter:0603USB-142 +Filter:0603USB-222 +Filter:0603USB-251 +Filter:0603USB-601 +Filter:0603USB-951 +Filter:0850BM14E0016 +Filter:0900FM15K0039 +Filter:1FP41-4R +Filter:1FP42-3R +Filter:1FP44-2R +Filter:1FP45-0R +Filter:1FP45-1R +Filter:1FP61-4R +Filter:1FP62-3R +Filter:1FP64-2R +Filter:1FP65-0R +Filter:1FP65-1R +Filter:B39162B8813P810 +Filter:BNX025 +Filter:Choke_CommonMode_FerriteCore_1234 +Filter:Choke_CommonMode_FerriteCore_1243 +Filter:Choke_CommonMode_FerriteCore_1324 +Filter:Choke_CommonMode_FerriteCore_1342 +Filter:Choke_CommonMode_FerriteCore_1423 +Filter:Choke_CommonMode_PulseElectronics_PH9455x105NL +Filter:Choke_CommonMode_PulseElectronics_PH9455x155NL +Filter:Choke_CommonMode_PulseElectronics_PH9455x156NL +Filter:Choke_CommonMode_PulseElectronics_PH9455x205NL +Filter:Choke_CommonMode_PulseElectronics_PH9455x356NL +Filter:Choke_CommonMode_PulseElectronics_PH9455x405NL +Filter:Choke_CommonMode_PulseElectronics_PH9455x705NL +Filter:Choke_CommonMode_PulseElectronics_PH9455x826NL +Filter:Choke_Schaffner_RN102-0.3-02-12M +Filter:Choke_Schaffner_RN102-0.3-02-22M +Filter:Choke_Schaffner_RN102-0.6-02-4M4 +Filter:Choke_Schaffner_RN102-1-02-3M0 +Filter:Choke_Schaffner_RN102-1.5-02-1M6 +Filter:Choke_Schaffner_RN102-2-02-1M1 +Filter:Choke_Wurth_WE-CNSW_744232090 +Filter:FN405-0.5-02 +Filter:FN405-1-02 +Filter:FN405-10-02 +Filter:FN405-3-02 +Filter:FN405-6-02 +Filter:FN406-0.5-02 +Filter:FN406-1-02 +Filter:FN406-3-02 +Filter:FN406-6-02 +Filter:FN406-8.4-02 +Filter:FN406B-0.5-02 +Filter:FN406B-1-02 +Filter:FN406B-3-02 +Filter:FN406B-6-02 +Filter:FN406B-8.4-02 +Filter:LTC1562xG-2 +Filter:LTC1562xxG +Filter:P300PL104M275xC222 +Filter:P300PL104M275xC332 +Filter:P300PL104M275xC472 +Filter:P300PL154M275xC222 +Filter:P300PL154M275xC332 +Filter:P300PL154M275xC472 +Filter:SAFFA1G58KA0F0A +Filter:SAFFA1G96FN0F0A +Filter:SAFFA2G14FA0F0A +Filter:SAFFA881MFL0F0A +Filter:SAFFA942MFM0F0A +Filter:SAFFB1G58KA0F0A +Filter:SAFFB1G96FN0F0A +Filter:SAFFB2G14FA0F0A +Filter:SAFFB881MFL0F0A +Filter:SAFFB942MFM0F0A +Filter:SF14-1575F5UUA1 +Filter:SF14-1575F5UUC1 +FPGA_CologneChip_GateMate:CCGM1A1 +FPGA_Efinix_Trion:T8Q144xx +FPGA_Lattice:ICE40HX1K-TQ144 +FPGA_Lattice:ICE40HX4K-BG121 +FPGA_Lattice:ICE40HX4K-TQ144 +FPGA_Lattice:ICE40HX8K-BG121 +FPGA_Lattice:ICE40UL1K-SWG16 +FPGA_Lattice:ICE40UP5K-SG48ITR +FPGA_Lattice:ICE5LP1K-SG48 +FPGA_Lattice:LFE5U-85F-6BG381x +FPGA_Lattice:LFE5U-85F-6BG756x +FPGA_Lattice:LFE5U-85F-7BG381x +FPGA_Lattice:LFE5U-85F-7BG756x +FPGA_Lattice:LFE5U-85F-8BG381x +FPGA_Lattice:LFE5U-85F-8BG756x +FPGA_Lattice:LFE5UM-85F-6BG381x +FPGA_Lattice:LFE5UM-85F-6BG756x +FPGA_Lattice:LFE5UM-85F-7BG381x +FPGA_Lattice:LFE5UM-85F-7BG756x +FPGA_Lattice:LFE5UM-85F-8BG381x +FPGA_Lattice:LFE5UM-85F-8BG756x +FPGA_Lattice:LFE5UM5G-85F-8BG381x +FPGA_Lattice:LFE5UM5G-85F-8BG756x +FPGA_Lattice:LFXP2-5E-5TN144 +FPGA_Lattice:LFXP2-5E-6TN144 +FPGA_Lattice:LFXP2-5E-7TN144 +FPGA_Microsemi:A3P030-VQG100 +FPGA_Microsemi:A3P060-VQG100 +FPGA_Microsemi:A3P1000-PQG208 +FPGA_Microsemi:A3P125-PQG208 +FPGA_Microsemi:A3P125-VQG100 +FPGA_Microsemi:A3P250-PQG208 +FPGA_Microsemi:A3P250-VQG100 +FPGA_Microsemi:A3P400-PQG208 +FPGA_Microsemi:A3P600-PQG208 +FPGA_Microsemi:ACT1020PL44 +FPGA_Microsemi:ACT1020PL68 +FPGA_Microsemi:ACT1225PL84 +FPGA_Microsemi:EX128-TQ100 +FPGA_Microsemi:EX128-TQ64 +FPGA_Microsemi:EX256-TQ100 +FPGA_Microsemi:EX64-TQ100 +FPGA_Microsemi:EX64-TQ64 +FPGA_Microsemi:M2GL090T-FG484 +FPGA_Xilinx:XC2018-PC68 +FPGA_Xilinx:XC2018-PC84 +FPGA_Xilinx:XC2064-PC68 +FPGA_Xilinx:XC2C256-TQ144 +FPGA_Xilinx:XC2C256-VQ100 +FPGA_Xilinx:XC2S100TQ144 +FPGA_Xilinx:XC2S150PQ208 +FPGA_Xilinx:XC2S200PQ208 +FPGA_Xilinx:XC2S300PQ208 +FPGA_Xilinx:XC2S400FT256 +FPGA_Xilinx:XC2S50-PQ208 +FPGA_Xilinx:XC2S64A-xQFG48 +FPGA_Xilinx:XC3020-PC68 +FPGA_Xilinx:XC3030-PC44 +FPGA_Xilinx:XC3030-PC68 +FPGA_Xilinx:XC3030-PC84 +FPGA_Xilinx:XC3030-VQ100 +FPGA_Xilinx:XC3042-PC84 +FPGA_Xilinx:XC3042-VQ100 +FPGA_Xilinx:XC3S1400A-FG484 +FPGA_Xilinx:XC3S200AN-FT256 +FPGA_Xilinx:XC3S400-FG320 +FPGA_Xilinx:XC3S400-PQ208 +FPGA_Xilinx:XC3S50-VQ100 +FPGA_Xilinx:XC3S50AN-TQG144 +FPGA_Xilinx:XC4003-PC84 +FPGA_Xilinx:XC4003-VQ100 +FPGA_Xilinx:XC4004-PQ160 +FPGA_Xilinx:XC4005-PC84 +FPGA_Xilinx:XC4005-PG156 +FPGA_Xilinx:XC4005-PQ100 +FPGA_Xilinx:XC4005-PQ160 +FPGA_Xilinx:XC6SLX25T-BG484 +FPGA_Xilinx:XCV150_BG352 +FPGA_Xilinx_Artix7:XC7A100T-CSG324 +FPGA_Xilinx_Artix7:XC7A100T-FGG484 +FPGA_Xilinx_Artix7:XC7A100T-FGG676 +FPGA_Xilinx_Artix7:XC7A100T-FTG256 +FPGA_Xilinx_Artix7:XC7A15T-CPG236 +FPGA_Xilinx_Artix7:XC7A15T-CSG324 +FPGA_Xilinx_Artix7:XC7A15T-CSG325 +FPGA_Xilinx_Artix7:XC7A15T-FGG484 +FPGA_Xilinx_Artix7:XC7A15T-FTG256 +FPGA_Xilinx_Artix7:XC7A200T-FBG484 +FPGA_Xilinx_Artix7:XC7A200T-FBG676 +FPGA_Xilinx_Artix7:XC7A200T-FFG1156 +FPGA_Xilinx_Artix7:XC7A200T-SBG484 +FPGA_Xilinx_Artix7:XC7A35T-CPG236 +FPGA_Xilinx_Artix7:XC7A35T-CSG324 +FPGA_Xilinx_Artix7:XC7A35T-CSG325 +FPGA_Xilinx_Artix7:XC7A35T-FGG484 +FPGA_Xilinx_Artix7:XC7A35T-FTG256 +FPGA_Xilinx_Artix7:XC7A50T-CPG236 +FPGA_Xilinx_Artix7:XC7A50T-CSG324 +FPGA_Xilinx_Artix7:XC7A50T-CSG325 +FPGA_Xilinx_Artix7:XC7A50T-FGG484 +FPGA_Xilinx_Artix7:XC7A50T-FTG256 +FPGA_Xilinx_Artix7:XC7A75T-CSG324 +FPGA_Xilinx_Artix7:XC7A75T-FGG484 +FPGA_Xilinx_Artix7:XC7A75T-FGG676 +FPGA_Xilinx_Artix7:XC7A75T-FTG256 +FPGA_Xilinx_Kintex7:XC7K160T-FBG484 +FPGA_Xilinx_Kintex7:XC7K160T-FBG676 +FPGA_Xilinx_Kintex7:XC7K160T-FFG676 +FPGA_Xilinx_Kintex7:XC7K325T-FBG676 +FPGA_Xilinx_Kintex7:XC7K325T-FBG900 +FPGA_Xilinx_Kintex7:XC7K325T-FFG676 +FPGA_Xilinx_Kintex7:XC7K325T-FFG900 +FPGA_Xilinx_Kintex7:XC7K355T-FFG901 +FPGA_Xilinx_Kintex7:XC7K410T-FBG676 +FPGA_Xilinx_Kintex7:XC7K410T-FBG900 +FPGA_Xilinx_Kintex7:XC7K410T-FFG676 +FPGA_Xilinx_Kintex7:XC7K410T-FFG900 +FPGA_Xilinx_Kintex7:XC7K420T-FFG1156 +FPGA_Xilinx_Kintex7:XC7K420T-FFG901 +FPGA_Xilinx_Kintex7:XC7K480T-FFG1156 +FPGA_Xilinx_Kintex7:XC7K480T-FFG901 +FPGA_Xilinx_Kintex7:XC7K70T-FBG484 +FPGA_Xilinx_Kintex7:XC7K70T-FBG676 +FPGA_Xilinx_Spartan6:XC6SLX100-CSG484 +FPGA_Xilinx_Spartan6:XC6SLX100-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX100-FGG676 +FPGA_Xilinx_Spartan6:XC6SLX100T-CSG484 +FPGA_Xilinx_Spartan6:XC6SLX100T-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX100T-FGG676 +FPGA_Xilinx_Spartan6:XC6SLX100T-FGG900 +FPGA_Xilinx_Spartan6:XC6SLX150-CSG484 +FPGA_Xilinx_Spartan6:XC6SLX150-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX150-FGG676 +FPGA_Xilinx_Spartan6:XC6SLX150-FGG900 +FPGA_Xilinx_Spartan6:XC6SLX150T-CSG484 +FPGA_Xilinx_Spartan6:XC6SLX150T-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX150T-FGG676 +FPGA_Xilinx_Spartan6:XC6SLX150T-FGG900 +FPGA_Xilinx_Spartan6:XC6SLX16-CPG196 +FPGA_Xilinx_Spartan6:XC6SLX16-CSG225 +FPGA_Xilinx_Spartan6:XC6SLX16-CSG324 +FPGA_Xilinx_Spartan6:XC6SLX16-FTG256 +FPGA_Xilinx_Spartan6:XC6SLX25-CSG324 +FPGA_Xilinx_Spartan6:XC6SLX25-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX25-FTG256 +FPGA_Xilinx_Spartan6:XC6SLX25T-CSG324 +FPGA_Xilinx_Spartan6:XC6SLX25T-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX4-CPG196 +FPGA_Xilinx_Spartan6:XC6SLX4-CSG225 +FPGA_Xilinx_Spartan6:XC6SLX4-TQG144 +FPGA_Xilinx_Spartan6:XC6SLX45-CSG324 +FPGA_Xilinx_Spartan6:XC6SLX45-CSG484 +FPGA_Xilinx_Spartan6:XC6SLX45-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX45-FGG676 +FPGA_Xilinx_Spartan6:XC6SLX45T-CSG324 +FPGA_Xilinx_Spartan6:XC6SLX45T-CSG484 +FPGA_Xilinx_Spartan6:XC6SLX45T-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX75-CSG484 +FPGA_Xilinx_Spartan6:XC6SLX75-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX75-FGG676 +FPGA_Xilinx_Spartan6:XC6SLX75T-CSG484 +FPGA_Xilinx_Spartan6:XC6SLX75T-FGG484 +FPGA_Xilinx_Spartan6:XC6SLX75T-FGG676 +FPGA_Xilinx_Spartan6:XC6SLX9-CPG196 +FPGA_Xilinx_Spartan6:XC6SLX9-CSG225 +FPGA_Xilinx_Spartan6:XC6SLX9-CSG324 +FPGA_Xilinx_Spartan6:XC6SLX9-FTG256 +FPGA_Xilinx_Spartan6:XC6SLX9-TQG144 +FPGA_Xilinx_Virtex5:XC5VFX100T-FF1136 +FPGA_Xilinx_Virtex5:XC5VFX100T-FF1738 +FPGA_Xilinx_Virtex5:XC5VFX130T-FF1738 +FPGA_Xilinx_Virtex5:XC5VFX200T-FF1738 +FPGA_Xilinx_Virtex5:XC5VFX30T-FF665 +FPGA_Xilinx_Virtex5:XC5VFX70T-FF1136 +FPGA_Xilinx_Virtex5:XC5VFX70T-FF665 +FPGA_Xilinx_Virtex5:XC5VLX110-FF1153 +FPGA_Xilinx_Virtex5:XC5VLX110-FF1760 +FPGA_Xilinx_Virtex5:XC5VLX110-FF676 +FPGA_Xilinx_Virtex5:XC5VLX110T-FF1136 +FPGA_Xilinx_Virtex5:XC5VLX110T-FF1738 +FPGA_Xilinx_Virtex5:XC5VLX155-FF1153 +FPGA_Xilinx_Virtex5:XC5VLX155-FF1760 +FPGA_Xilinx_Virtex5:XC5VLX155T-FF1136 +FPGA_Xilinx_Virtex5:XC5VLX155T-FF1738 +FPGA_Xilinx_Virtex5:XC5VLX20T-FF323 +FPGA_Xilinx_Virtex5:XC5VLX220-FF1760 +FPGA_Xilinx_Virtex5:XC5VLX220T-FF1738 +FPGA_Xilinx_Virtex5:XC5VLX30-FF324 +FPGA_Xilinx_Virtex5:XC5VLX30-FF676 +FPGA_Xilinx_Virtex5:XC5VLX30T-FF323 +FPGA_Xilinx_Virtex5:XC5VLX30T-FF665 +FPGA_Xilinx_Virtex5:XC5VLX330-FF1760 +FPGA_Xilinx_Virtex5:XC5VLX330T-FF1738 +FPGA_Xilinx_Virtex5:XC5VLX50-FF1153 +FPGA_Xilinx_Virtex5:XC5VLX50-FF324 +FPGA_Xilinx_Virtex5:XC5VLX50-FF676 +FPGA_Xilinx_Virtex5:XC5VLX50T-FF1136 +FPGA_Xilinx_Virtex5:XC5VLX50T-FF665 +FPGA_Xilinx_Virtex5:XC5VLX85-FF1153 +FPGA_Xilinx_Virtex5:XC5VLX85-FF676 +FPGA_Xilinx_Virtex5:XC5VLX85T-FF1136 +FPGA_Xilinx_Virtex5:XC5VSX240T-FF1738 +FPGA_Xilinx_Virtex5:XC5VSX35T-FF665 +FPGA_Xilinx_Virtex5:XC5VSX50T-FF1136 +FPGA_Xilinx_Virtex5:XC5VSX50T-FF665 +FPGA_Xilinx_Virtex5:XC5VSX95T-FF1136 +FPGA_Xilinx_Virtex5:XC5VTX150T-FF1156 +FPGA_Xilinx_Virtex5:XC5VTX150T-FF1759 +FPGA_Xilinx_Virtex5:XC5VTX240T-FF1759 +FPGA_Xilinx_Virtex6:XC6VHX250T-FF1154 +FPGA_Xilinx_Virtex6:XC6VHX255T-FF1155 +FPGA_Xilinx_Virtex6:XC6VHX255T-FF1923 +FPGA_Xilinx_Virtex6:XC6VHX380T-FF1154 +FPGA_Xilinx_Virtex6:XC6VHX380T-FF1155 +FPGA_Xilinx_Virtex6:XC6VHX380T-FF1923 +FPGA_Xilinx_Virtex6:XC6VHX380T-FF1924 +FPGA_Xilinx_Virtex6:XC6VHX565T-FF1923 +FPGA_Xilinx_Virtex6:XC6VHX565T-FF1924 +FPGA_Xilinx_Virtex6:XC6VLX130T-FF1156 +FPGA_Xilinx_Virtex6:XC6VLX130T-FF484 +FPGA_Xilinx_Virtex6:XC6VLX130T-FF784 +FPGA_Xilinx_Virtex6:XC6VLX195T-FF1156 +FPGA_Xilinx_Virtex6:XC6VLX195T-FF784 +FPGA_Xilinx_Virtex6:XC6VLX240T-FF1156 +FPGA_Xilinx_Virtex6:XC6VLX240T-FF1759 +FPGA_Xilinx_Virtex6:XC6VLX240T-FF784 +FPGA_Xilinx_Virtex6:XC6VLX365T-FF1156 +FPGA_Xilinx_Virtex6:XC6VLX365T-FF1759 +FPGA_Xilinx_Virtex6:XC6VLX550T-FF1759 +FPGA_Xilinx_Virtex6:XC6VLX550T-FF1760 +FPGA_Xilinx_Virtex6:XC6VLX75T-FF484 +FPGA_Xilinx_Virtex6:XC6VLX75T-FF784 +FPGA_Xilinx_Virtex6:XC6VLX760-FF1760 +FPGA_Xilinx_Virtex6:XC6VSX315T-FF1156 +FPGA_Xilinx_Virtex6:XC6VSX315T-FF1759 +FPGA_Xilinx_Virtex6:XC6VSX475T-FF1156 +FPGA_Xilinx_Virtex6:XC6VSX475T-FF1759 +FPGA_Xilinx_Virtex7:XC7V2000T-FHG1761 +FPGA_Xilinx_Virtex7:XC7V2000T-FLG1925 +FPGA_Xilinx_Virtex7:XC7V585T-FFG1157 +FPGA_Xilinx_Virtex7:XC7V585T-FFG1761 +FPGA_Xilinx_Virtex7:XC7VH580T-FLG1155 +FPGA_Xilinx_Virtex7:XC7VH580T-FLG1931 +FPGA_Xilinx_Virtex7:XC7VH580T-HCG1155 +FPGA_Xilinx_Virtex7:XC7VH580T-HCG1931 +FPGA_Xilinx_Virtex7:XC7VH870T-FLG1932 +FPGA_Xilinx_Virtex7:XC7VH870T-HCG1932 +FPGA_Xilinx_Virtex7:XC7VX1140T-FLG1926 +FPGA_Xilinx_Virtex7:XC7VX1140T-FLG1928 +FPGA_Xilinx_Virtex7:XC7VX1140T-FLG1930 +FPGA_Xilinx_Virtex7:XC7VX330T-FFG1157 +FPGA_Xilinx_Virtex7:XC7VX330T-FFG1761 +FPGA_Xilinx_Virtex7:XC7VX415T-FFG1157 +FPGA_Xilinx_Virtex7:XC7VX415T-FFG1158 +FPGA_Xilinx_Virtex7:XC7VX415T-FFG1927 +FPGA_Xilinx_Virtex7:XC7VX485T-FFG1157 +FPGA_Xilinx_Virtex7:XC7VX485T-FFG1158 +FPGA_Xilinx_Virtex7:XC7VX485T-FFG1761 +FPGA_Xilinx_Virtex7:XC7VX485T-FFG1927 +FPGA_Xilinx_Virtex7:XC7VX485T-FFG1930 +FPGA_Xilinx_Virtex7:XC7VX550T-FFG1158 +FPGA_Xilinx_Virtex7:XC7VX550T-FFG1927 +FPGA_Xilinx_Virtex7:XC7VX690T-FFG1157 +FPGA_Xilinx_Virtex7:XC7VX690T-FFG1158 +FPGA_Xilinx_Virtex7:XC7VX690T-FFG1761 +FPGA_Xilinx_Virtex7:XC7VX690T-FFG1926 +FPGA_Xilinx_Virtex7:XC7VX690T-FFG1927 +FPGA_Xilinx_Virtex7:XC7VX690T-FFG1930 +FPGA_Xilinx_Virtex7:XC7VX980T-FFG1926 +FPGA_Xilinx_Virtex7:XC7VX980T-FFG1928 +FPGA_Xilinx_Virtex7:XC7VX980T-FFG1930 +GPU:MC6845 +GPU:MC68A45 +GPU:MC68B45 +Graphic:Logo_Open_Hardware_Large +Graphic:Logo_Open_Hardware_Small +Graphic:SYM_Arrow45_Large +Graphic:SYM_Arrow45_Normal +Graphic:SYM_Arrow45_Small +Graphic:SYM_Arrow45_Tiny +Graphic:SYM_Arrow45_XLarge +Graphic:SYM_Arrow_Large +Graphic:SYM_Arrow_Normal +Graphic:SYM_Arrow_Small +Graphic:SYM_Arrow_Tiny +Graphic:SYM_Arrow_XLarge +Graphic:SYM_ESD_Large +Graphic:SYM_ESD_Small +Graphic:SYM_Earth_Protective_Large +Graphic:SYM_Earth_Protective_Small +Graphic:SYM_EasterEgg_42x60mm +Graphic:SYM_Flash_Large +Graphic:SYM_Flash_Small +Graphic:SYM_Flash_XLarge +Graphic:SYM_Hot_Large +Graphic:SYM_Hot_Small +Graphic:SYM_LASER_Large +Graphic:SYM_LASER_Small +Graphic:SYM_Magnet_Large +Graphic:SYM_Magnet_Small +Graphic:SYM_Radio_Waves_Large +Graphic:SYM_Radio_Waves_Small +Graphic:SYM_Radioactive_Large +Graphic:SYM_Radioactive_Radiation_Small +Interface:5PB1108PGxx +Interface:6821 +Interface:6822 +Interface:68230 +Interface:68681 +Interface:68901_PLCC +Interface:8237 +Interface:8255 +Interface:8255A +Interface:8259 +Interface:8259A +Interface:8259A-2 +Interface:8288 +Interface:82C55A +Interface:82C55A_PLCC +Interface:88SE9125C0-NAA +Interface:AD9833xRM +Interface:AD9834 +Interface:AD9850 +Interface:AD9851 +Interface:AD9910 +Interface:AD9912 +Interface:AD9951 +Interface:AD9954 +Interface:AM26LS31CD +Interface:AM26LS31CDB +Interface:AM26LS31CN +Interface:AM26LS31MJ +Interface:AM26LS31xNS +Interface:AM26LV32xD +Interface:AM26LV32xNS +Interface:CDCLVP1102RGT +Interface:CH376T +Interface:DS90C124 +Interface:DS90C241 +Interface:DS90C402 +Interface:DS90LV011A +Interface:DS90LV027A +Interface:FD1771 +Interface:FIN1019M +Interface:FIN1019MTC +Interface:HT12D +Interface:HT12E +Interface:LTC1518 +Interface:LTC1519 +Interface:LTC1688 +Interface:LTC1689 +Interface:LTC6957xDD-1 +Interface:LTC6957xDD-2 +Interface:LTC6957xDD-3 +Interface:LTC6957xDD-4 +Interface:LTC6957xMS-1 +Interface:LTC6957xMS-2 +Interface:LTC6957xMS-3 +Interface:LTC6957xMS-4 +Interface:MAX6816 +Interface:MC100EPT22D +Interface:MC100EPT22DT +Interface:MC100LVELT22D +Interface:MC100LVELT22DT +Interface:MC6840 +Interface:MC6843 +Interface:MC6844 +Interface:MC68A21 +Interface:MC68A40 +Interface:MC68A44 +Interface:MC68B21 +Interface:MC68B40 +Interface:MC68B44 +Interface:NB3N551MN +Interface:ONET1191PRGT +Interface:PCA9306 +Interface:PCA9306D +Interface:PCA9306DC +Interface:PCA9306DC1 +Interface:PCA9306DP +Interface:PCA9600D +Interface:PCA9600DP +Interface:PCA9615DP +Interface:PCI9030-PQFP176 +Interface:S5933_PQ160 +Interface:SI9986 +Interface:SLB9660xT +Interface:SLB9665xT +Interface:SN65LVDS047D +Interface:SN65LVDS047PW +Interface:SN65LVDS1D +Interface:SN65LVDS1DBV +Interface:SN65LVDS2D +Interface:SN65LVDS2DBV +Interface:SN65LVDT2D +Interface:SN65LVDT2DBV +Interface:SN74LV8153N +Interface:SN74LV8153PW +Interface:SN75160BDW +Interface:SN75160BN +Interface:TB5D1MD +Interface:TB5D1MDW +Interface:TB5D2H +Interface:TB5D2HDW +Interface:TB5R1D +Interface:TB5R1DW +Interface:TB5R2D +Interface:TB5R2DW +Interface:TCA9406DC +Interface:TCA9800 +Interface:TCA9801 +Interface:TCA9802 +Interface:TCA9803 +Interface:U2270B +Interface:WD2791 +Interface:WD2793 +Interface:WD2795 +Interface:WD2797 +Interface:Z8420 +Interface:Z84C20 +Interface_CAN_LIN:ADM3053 +Interface_CAN_LIN:ADM3057ExRW +Interface_CAN_LIN:CA-IF1042LVS +Interface_CAN_LIN:ISO1044BD +Interface_CAN_LIN:ISO1050DUB +Interface_CAN_LIN:ISOW1044 +Interface_CAN_LIN:LTC2875-DD +Interface_CAN_LIN:LTC2875-S8 +Interface_CAN_LIN:MCP2021A-xxxxMD +Interface_CAN_LIN:MCP2021A-xxxxP +Interface_CAN_LIN:MCP2021A-xxxxSN +Interface_CAN_LIN:MCP2022A-xxxxP +Interface_CAN_LIN:MCP2022A-xxxxSL +Interface_CAN_LIN:MCP2022A-xxxxST +Interface_CAN_LIN:MCP2050-330-EMQ +Interface_CAN_LIN:MCP2050-330-EP +Interface_CAN_LIN:MCP2050-330-ESL +Interface_CAN_LIN:MCP2050-500-EMQ +Interface_CAN_LIN:MCP2050-500-EP +Interface_CAN_LIN:MCP2050-500-ESL +Interface_CAN_LIN:MCP2515-xSO +Interface_CAN_LIN:MCP2515-xST +Interface_CAN_LIN:MCP2517FD-xJHA +Interface_CAN_LIN:MCP2517FD-xSL +Interface_CAN_LIN:MCP251863T-E-9PX +Interface_CAN_LIN:MCP251863T-H-SS +Interface_CAN_LIN:MCP2518FD-xQBB +Interface_CAN_LIN:MCP2542FDxMF +Interface_CAN_LIN:MCP2542WFDxMF +Interface_CAN_LIN:MCP2551-I-P +Interface_CAN_LIN:MCP2551-I-SN +Interface_CAN_LIN:MCP2557FD-xMF +Interface_CAN_LIN:MCP2557FD-xMNY +Interface_CAN_LIN:MCP2557FD-xSN +Interface_CAN_LIN:MCP2558FD-xMF +Interface_CAN_LIN:MCP2558FD-xMNY +Interface_CAN_LIN:MCP2558FD-xSN +Interface_CAN_LIN:MCP2561-E-MF +Interface_CAN_LIN:MCP2561-E-P +Interface_CAN_LIN:MCP2561-E-SN +Interface_CAN_LIN:MCP2561-H-MF +Interface_CAN_LIN:MCP2561-H-P +Interface_CAN_LIN:MCP2561-H-SN +Interface_CAN_LIN:MCP2562-E-MF +Interface_CAN_LIN:MCP2562-E-P +Interface_CAN_LIN:MCP2562-E-SN +Interface_CAN_LIN:MCP2562-H-MF +Interface_CAN_LIN:MCP2562-H-P +Interface_CAN_LIN:MCP2562-H-SN +Interface_CAN_LIN:MCP25625-x-SS +Interface_CAN_LIN:PCA82C251 +Interface_CAN_LIN:SN65HVD1050D +Interface_CAN_LIN:SN65HVD230 +Interface_CAN_LIN:SN65HVD231 +Interface_CAN_LIN:SN65HVD232 +Interface_CAN_LIN:SN65HVD233 +Interface_CAN_LIN:SN65HVD234 +Interface_CAN_LIN:SN65HVD235 +Interface_CAN_LIN:SN65HVD255D +Interface_CAN_LIN:SN65HVD256D +Interface_CAN_LIN:SN65HVD257D +Interface_CAN_LIN:TCAN1043xDxQ1 +Interface_CAN_LIN:TCAN330 +Interface_CAN_LIN:TCAN330G +Interface_CAN_LIN:TCAN332 +Interface_CAN_LIN:TCAN332G +Interface_CAN_LIN:TCAN334 +Interface_CAN_LIN:TCAN334G +Interface_CAN_LIN:TCAN337 +Interface_CAN_LIN:TCAN337G +Interface_CAN_LIN:TCAN4550RGY +Interface_CAN_LIN:TCAN4551RGYRQ1 +Interface_CAN_LIN:TJA1021T +Interface_CAN_LIN:TJA1021TK +Interface_CAN_LIN:TJA1029T +Interface_CAN_LIN:TJA1029TK +Interface_CAN_LIN:TJA1042T +Interface_CAN_LIN:TJA1042T-3 +Interface_CAN_LIN:TJA1042TK-3 +Interface_CAN_LIN:TJA1043T +Interface_CAN_LIN:TJA1043TK +Interface_CAN_LIN:TJA1049T +Interface_CAN_LIN:TJA1049T-3 +Interface_CAN_LIN:TJA1049TK +Interface_CAN_LIN:TJA1049TK-3 +Interface_CAN_LIN:TJA1051T +Interface_CAN_LIN:TJA1051T-3 +Interface_CAN_LIN:TJA1051T-E +Interface_CAN_LIN:TJA1051TK-3 +Interface_CAN_LIN:TJA1052i-1 +Interface_CAN_LIN:TJA1052i-2 +Interface_CAN_LIN:TJA1052i-5 +Interface_CAN_LIN:TJA1145T +Interface_CAN_LIN:TJA1145T-FD +Interface_CAN_LIN:TJA1145TK +Interface_CAN_LIN:TJA1145TK-FD +Interface_CurrentLoop:XTR111AxDGQ +Interface_CurrentLoop:XTR115U +Interface_CurrentLoop:XTR116U +Interface_Ethernet:DP83848C +Interface_Ethernet:DP83848I +Interface_Ethernet:ENC28J60x-ML +Interface_Ethernet:ENC28J60x-SO +Interface_Ethernet:ENC28J60x-SP +Interface_Ethernet:ENC28J60x-SS +Interface_Ethernet:ENC424J600-ML +Interface_Ethernet:ENC424J600-PT +Interface_Ethernet:KSZ8081MLX +Interface_Ethernet:KSZ8081RNA +Interface_Ethernet:KSZ8081RND +Interface_Ethernet:KSZ9031RNXCA +Interface_Ethernet:KSZ9563RNX +Interface_Ethernet:KSZ9893RNX +Interface_Ethernet:LAN7500-ABJZ +Interface_Ethernet:LAN8710A +Interface_Ethernet:LAN8720A +Interface_Ethernet:LAN8742A +Interface_Ethernet:LAN9303 +Interface_Ethernet:LAN9303i +Interface_Ethernet:LAN9512 +Interface_Ethernet:LAN9512i +Interface_Ethernet:LAN9513 +Interface_Ethernet:LAN9513i +Interface_Ethernet:LAN9514 +Interface_Ethernet:LAN9514i +Interface_Ethernet:RTL8211EG-VB-CG +Interface_Ethernet:VSC8541XMV-0x +Interface_Ethernet:W5100 +Interface_Ethernet:W5100S-L +Interface_Ethernet:W5100S-Q +Interface_Ethernet:W5500 +Interface_Ethernet:W6100-L +Interface_Ethernet:W6100-Q +Interface_Ethernet:WGI210AT +Interface_Expansion:AS1115-BQFT +Interface_Expansion:AS1115-BSST +Interface_Expansion:AW9523B +Interface_Expansion:LTC4314xGN +Interface_Expansion:LTC4314xUDC +Interface_Expansion:LTC4316xDD +Interface_Expansion:LTC4317 +Interface_Expansion:MAX31910xUI +Interface_Expansion:MAX31911xUI +Interface_Expansion:MAX31912xUI +Interface_Expansion:MAX31913xUI +Interface_Expansion:MAX7325AEG+ +Interface_Expansion:MCP23008-xML +Interface_Expansion:MCP23008-xP +Interface_Expansion:MCP23008-xSO +Interface_Expansion:MCP23008-xSS +Interface_Expansion:MCP23017_ML +Interface_Expansion:MCP23017_SO +Interface_Expansion:MCP23017_SP +Interface_Expansion:MCP23017_SS +Interface_Expansion:MCP23S17_ML +Interface_Expansion:MCP23S17_SO +Interface_Expansion:MCP23S17_SP +Interface_Expansion:MCP23S17_SS +Interface_Expansion:P82B96 +Interface_Expansion:PCA9506BS +Interface_Expansion:PCA9516 +Interface_Expansion:PCA9536D +Interface_Expansion:PCA9536DP +Interface_Expansion:PCA9537 +Interface_Expansion:PCA9544AD +Interface_Expansion:PCA9544APW +Interface_Expansion:PCA9547BS +Interface_Expansion:PCA9547D +Interface_Expansion:PCA9547PW +Interface_Expansion:PCA9548ADB +Interface_Expansion:PCA9548ADW +Interface_Expansion:PCA9548APW +Interface_Expansion:PCA9548ARGE +Interface_Expansion:PCA9555D +Interface_Expansion:PCA9555DB +Interface_Expansion:PCA9555PW +Interface_Expansion:PCA9557BS +Interface_Expansion:PCA9557D +Interface_Expansion:PCA9557PW +Interface_Expansion:PCA9847PW +Interface_Expansion:PCAL6416AHF +Interface_Expansion:PCAL6416APW +Interface_Expansion:PCAL6534EV +Interface_Expansion:PCF8574AP +Interface_Expansion:PCF8574AT +Interface_Expansion:PCF8574ATS +Interface_Expansion:PCF8574P +Interface_Expansion:PCF8574T +Interface_Expansion:PCF8574TS +Interface_Expansion:PCF8575DBR +Interface_Expansion:PCF8584 +Interface_Expansion:PCF8591 +Interface_Expansion:STMPE1600 +Interface_Expansion:TCA9534 +Interface_Expansion:TCA9535DBR +Interface_Expansion:TCA9535DBT +Interface_Expansion:TCA9535MRGER +Interface_Expansion:TCA9535PWR +Interface_Expansion:TCA9535RGER +Interface_Expansion:TCA9535RTWR +Interface_Expansion:TCA9544A +Interface_Expansion:TCA9548AMRGER +Interface_Expansion:TCA9548APWR +Interface_Expansion:TCA9548ARGER +Interface_Expansion:TCA9554DB +Interface_Expansion:TCA9554DBQ +Interface_Expansion:TCA9554DW +Interface_Expansion:TCA9554PW +Interface_Expansion:TCA9555DBR +Interface_Expansion:TCA9555DBT +Interface_Expansion:TCA9555PWR +Interface_Expansion:TCA9555RGER +Interface_Expansion:TCA9555RTWR +Interface_Expansion:TPIC6595 +Interface_Expansion:XRA1201IG24 +Interface_Expansion:XRA1201IL24 +Interface_Expansion:XRA1201PIG24 +Interface_Expansion:XRA1201PIL24 +Interface_HDMI:ADV7611 +Interface_HDMI:TPD12S520DBT +Interface_HID:JoyWarrior24A10L +Interface_HID:JoyWarrior24A8L +Interface_HID:SpinWarrior24A3 +Interface_HID:SpinWarrior24R4 +Interface_HID:SpinWarrior24R6 +Interface_LineDriver:DS7820 +Interface_LineDriver:DS7830 +Interface_LineDriver:DS8830 +Interface_LineDriver:DS89C21 +Interface_LineDriver:EL7242C +Interface_LineDriver:MC3486N +Interface_LineDriver:MC3487DX +Interface_LineDriver:MC3487N +Interface_LineDriver:UA9637 +Interface_LineDriver:UA9638CD +Interface_LineDriver:UA9638CDE4 +Interface_LineDriver:UA9638CDG4 +Interface_LineDriver:UA9638CDR +Interface_LineDriver:UA9638CDRG4 +Interface_LineDriver:UA9638CP +Interface_LineDriver:UA9638CPE4 +Interface_Optical:IRM-H6xxT +Interface_Optical:IS471F +Interface_Optical:IS485 +Interface_Optical:IS486 +Interface_Optical:QSE159 +Interface_Optical:SFP +Interface_Optical:SFP+ +Interface_Optical:TSDP341xx +Interface_Optical:TSDP343xx +Interface_Optical:TSMP58000 +Interface_Optical:TSMP58138 +Interface_Optical:TSOP17xx +Interface_Optical:TSOP21xx +Interface_Optical:TSOP23xx +Interface_Optical:TSOP25xx +Interface_Optical:TSOP312xx +Interface_Optical:TSOP314xx +Interface_Optical:TSOP321xx +Interface_Optical:TSOP323xx +Interface_Optical:TSOP325xx +Interface_Optical:TSOP32S40F +Interface_Optical:TSOP331xx +Interface_Optical:TSOP333xx +Interface_Optical:TSOP335xx +Interface_Optical:TSOP341xx +Interface_Optical:TSOP343xx +Interface_Optical:TSOP345xx +Interface_Optical:TSOP348xx +Interface_Optical:TSOP34S40F +Interface_Optical:TSOP382xx +Interface_Optical:TSOP384xx +Interface_Optical:TSOP38G36 +Interface_Optical:TSOP41xx +Interface_Optical:TSOP43xx +Interface_Optical:TSOP45xx +Interface_Optical:TSOP531xx +Interface_Optical:TSOP533xx +Interface_Optical:TSOP535xx +Interface_Optical:TSOP581xx +Interface_Optical:TSOP582xx +Interface_Optical:TSOP583xx +Interface_Optical:TSOP584xx +Interface_Optical:TSOP585xx +Interface_Telecom:FX614 +Interface_Telecom:HT9170D +Interface_Telecom:Si3210 +Interface_UART:16450 +Interface_UART:16550 +Interface_UART:68C681 +Interface_UART:8250 +Interface_UART:8252 +Interface_UART:ADM101E +Interface_UART:ADM1491EBR +Interface_UART:ADM205 +Interface_UART:ADM206 +Interface_UART:ADM207 +Interface_UART:ADM208 +Interface_UART:ADM209 +Interface_UART:ADM211 +Interface_UART:ADM213 +Interface_UART:ADM222 +Interface_UART:ADM232A +Interface_UART:ADM242 +Interface_UART:ADM2481xRW +Interface_UART:ADM2483xRW +Interface_UART:ADM2484E +Interface_UART:ADM2582E +Interface_UART:ADM2587E +Interface_UART:ADM2682E +Interface_UART:ADM2687E +Interface_UART:ADM3488ExR +Interface_UART:ADM3490ExR +Interface_UART:ADM3491ExR +Interface_UART:AZ75232G +Interface_UART:AZ75232GS +Interface_UART:AZ75232M +Interface_UART:GD65232DB +Interface_UART:GD65232DW +Interface_UART:GD65232PW +Interface_UART:GD75232DB +Interface_UART:GD75232DW +Interface_UART:GD75232N +Interface_UART:GD75232PW +Interface_UART:ICL3232 +Interface_UART:ISL3172E +Interface_UART:ISL3175E +Interface_UART:ISL3178E +Interface_UART:ISL3280ExHZ +Interface_UART:ISL3281ExHZ +Interface_UART:ISL3282ExRHZ +Interface_UART:ISL3283ExHZ +Interface_UART:ISL3284ExHZ +Interface_UART:ISL3295xxH +Interface_UART:ISL3298xxRT +Interface_UART:ISL83491 +Interface_UART:ISO1500 +Interface_UART:ISO3082DW +Interface_UART:ISO3088DW +Interface_UART:LT1080 +Interface_UART:LT1785AxN8 +Interface_UART:LT1785AxS8 +Interface_UART:LT1785xN8 +Interface_UART:LT1785xS8 +Interface_UART:LT1791AxN8 +Interface_UART:LT1791AxS +Interface_UART:LT1791xN8 +Interface_UART:LT1791xS +Interface_UART:LTC2850xDD +Interface_UART:LTC2850xMS8 +Interface_UART:LTC2850xS8 +Interface_UART:LTC2851xDD +Interface_UART:LTC2851xMS8 +Interface_UART:LTC2851xS8 +Interface_UART:LTC2852xDD +Interface_UART:LTC2852xMS +Interface_UART:LTC2852xS +Interface_UART:LTC2856xDD-1 +Interface_UART:LTC2856xDD-2 +Interface_UART:LTC2856xMS8-1 +Interface_UART:LTC2856xMS8-2 +Interface_UART:LTC2857xDD-1 +Interface_UART:LTC2857xDD-2 +Interface_UART:LTC2857xMS8-1 +Interface_UART:LTC2857xMS8-2 +Interface_UART:LTC2858xDD-1 +Interface_UART:LTC2858xDD-2 +Interface_UART:LTC2858xMS-1 +Interface_UART:LTC2858xMS-2 +Interface_UART:LTC2861 +Interface_UART:MAX13432EESD +Interface_UART:MAX13432EETD +Interface_UART:MAX13433EESD +Interface_UART:MAX13433EETD +Interface_UART:MAX14783ExS +Interface_UART:MAX14830 +Interface_UART:MAX1487E +Interface_UART:MAX202 +Interface_UART:MAX232 +Interface_UART:MAX232I +Interface_UART:MAX238xNG+ +Interface_UART:MAX238xWG+ +Interface_UART:MAX3051 +Interface_UART:MAX3072E +Interface_UART:MAX3075E +Interface_UART:MAX3078E +Interface_UART:MAX3218 +Interface_UART:MAX3221 +Interface_UART:MAX3226 +Interface_UART:MAX3227 +Interface_UART:MAX3232 +Interface_UART:MAX3284E +Interface_UART:MAX3483 +Interface_UART:MAX3485 +Interface_UART:MAX3486 +Interface_UART:MAX3488xPA +Interface_UART:MAX3488xSA +Interface_UART:MAX3490xPA +Interface_UART:MAX3490xSA +Interface_UART:MAX481E +Interface_UART:MAX483E +Interface_UART:MAX485E +Interface_UART:MAX487E +Interface_UART:MAX488E +Interface_UART:MAX489E +Interface_UART:MAX490E +Interface_UART:MAX491E +Interface_UART:MC6850 +Interface_UART:MC68A50 +Interface_UART:MC68B50 +Interface_UART:SC16IS740 +Interface_UART:SC16IS750xBS +Interface_UART:SC16IS750xPW +Interface_UART:SC16IS752IBS +Interface_UART:SC16IS752IPW +Interface_UART:SC16IS760xBS +Interface_UART:SC16IS760xPW +Interface_UART:SC16IS762IBS +Interface_UART:SC16IS762IPW +Interface_UART:SN65HVD11HD +Interface_UART:SN65LBC176D +Interface_UART:SN65LBC176P +Interface_UART:SN65LBC176QD +Interface_UART:SN65LBC176QDR +Interface_UART:SN75176AD +Interface_UART:SN75176AP +Interface_UART:SN75LBC176D +Interface_UART:SN75LBC176P +Interface_UART:SNJ55LBC176JG +Interface_UART:SP3481CN +Interface_UART:SP3481CP +Interface_UART:SP3481EN +Interface_UART:SP3481EP +Interface_UART:SP3485CN +Interface_UART:SP3485CP +Interface_UART:SP3485EN +Interface_UART:SP3485EP +Interface_UART:SSP3085 +Interface_UART:ST202ExD +Interface_UART:ST232ExD +Interface_UART:ST485E +Interface_UART:THVD1400D +Interface_UART:THVD1420D +Interface_UART:THVD1450D +Interface_UART:THVD1450DR +Interface_UART:THVD1451D +Interface_UART:THVD1500 +Interface_UART:THVD8000 +Interface_UART:Z8530 +Interface_USB:ADUM3160 +Interface_USB:ADUM4160 +Interface_USB:AP33771 +Interface_USB:BQ24392 +Interface_USB:CH224K +Interface_USB:CH236D +Interface_USB:CH246D +Interface_USB:CH330N +Interface_USB:CH334R +Interface_USB:CH340C +Interface_USB:CH340E +Interface_USB:CH340G +Interface_USB:CH340K +Interface_USB:CH340N +Interface_USB:CH340T +Interface_USB:CH340X +Interface_USB:CH343G +Interface_USB:CH343P +Interface_USB:CH344Q +Interface_USB:CH9102F +Interface_USB:CP2102N-Axx-xQFN20 +Interface_USB:CP2102N-Axx-xQFN24 +Interface_USB:CP2102N-Axx-xQFN28 +Interface_USB:CP2104 +Interface_USB:CP2108-xxx-xM +Interface_USB:CP2112 +Interface_USB:CP2615-xx-xM +Interface_USB:CY7C65211-24LTXI +Interface_USB:CY7C65211A-24LTXI +Interface_USB:CY7C65213-28PVXI +Interface_USB:CY7C65213-32LTXI +Interface_USB:CY7C65213A-28PVXI +Interface_USB:CY7C65213A-32LTXI +Interface_USB:CY7C65215-32LTXI +Interface_USB:CY7C65215A-32LTXI +Interface_USB:CYPD3171-24LQXQ +Interface_USB:CYPD3174-16SXQ +Interface_USB:CYPD3174-24LQXQ +Interface_USB:CYPD3175-24LQXQ +Interface_USB:CYPD3177-24LQ +Interface_USB:FE1.1s +Interface_USB:FSUSB30MUX +Interface_USB:FSUSB42MUX +Interface_USB:FT200XD +Interface_USB:FT201XQ +Interface_USB:FT201XS +Interface_USB:FT220XQ +Interface_USB:FT220XS +Interface_USB:FT221XQ +Interface_USB:FT221XS +Interface_USB:FT2232D +Interface_USB:FT2232HL +Interface_USB:FT2232HQ +Interface_USB:FT230XQ +Interface_USB:FT230XS +Interface_USB:FT231XQ +Interface_USB:FT231XS +Interface_USB:FT232BM +Interface_USB:FT232H +Interface_USB:FT232RL +Interface_USB:FT234XD +Interface_USB:FT240XQ +Interface_USB:FT240XS +Interface_USB:FT245BM +Interface_USB:FT4222HQ +Interface_USB:FT4232H +Interface_USB:FT601Q +Interface_USB:FUSB302B01MPX +Interface_USB:FUSB302B10MPX +Interface_USB:FUSB302B11MPX +Interface_USB:FUSB302BMPX +Interface_USB:FUSB303BTMX +Interface_USB:FUSB307BMPX +Interface_USB:IP2721 +Interface_USB:MA8601 +Interface_USB:MCP2200-E-SS +Interface_USB:MCP2200-I-MQ +Interface_USB:MCP2200-I-SO +Interface_USB:MCP2200-I-SS +Interface_USB:MCP2200T-E-SS +Interface_USB:MCP2200T-I-MQ +Interface_USB:MCP2200T-I-SO +Interface_USB:MCP2200T-I-SS +Interface_USB:MCP2210x-MQ +Interface_USB:MCP2210x-SO +Interface_USB:MCP2210x-SS +Interface_USB:MCP2221AxML +Interface_USB:MCP2221AxP +Interface_USB:MCP2221AxSL +Interface_USB:MCP2221AxST +Interface_USB:MP5034GJ +Interface_USB:STULPI01A +Interface_USB:STULPI01B +Interface_USB:STUSB4500QTR +Interface_USB:TPS2500DRC +Interface_USB:TPS2501DRC +Interface_USB:TPS2513 +Interface_USB:TPS2513A +Interface_USB:TPS2514 +Interface_USB:TPS2514A +Interface_USB:TPS2560 +Interface_USB:TPS2561 +Interface_USB:TPS25730D +Interface_USB:TS3USB30EDGSR +Interface_USB:TS3USB30ERSWR +Interface_USB:TS3USBCA410 +Interface_USB:TS3USBCA420 +Interface_USB:TUSB2036 +Interface_USB:TUSB320 +Interface_USB:TUSB320I +Interface_USB:TUSB321 +Interface_USB:TUSB322I +Interface_USB:TUSB4041I +Interface_USB:TUSB7340 +Interface_USB:TUSB8041 +Interface_USB:UPD720202K8-7x1-BAA +Interface_USB:USB2504 +Interface_USB:USB2514B_Bi +Interface_USB:USB3250-ABZJ +Interface_USB:USB3300-EZK +Interface_USB:USB3341 +Interface_USB:USB3343 +Interface_USB:USB3346 +Interface_USB:USB3347 +Interface_USB:USB3740B-AI2 +Interface_USB:USB3740B-AI9 +Interface_USB:XR21B1424 +Isolator:4N25 +Isolator:4N26 +Isolator:4N27 +Isolator:4N28 +Isolator:4N35 +Isolator:4N36 +Isolator:4N37 +Isolator:6N135 +Isolator:6N135S +Isolator:6N136 +Isolator:6N136S +Isolator:6N137 +Isolator:6N138 +Isolator:6N139 +Isolator:ACPL-214-500E +Isolator:ADN4650 +Isolator:ADN4651 +Isolator:ADN4652 +Isolator:ADuM1200AR +Isolator:ADuM1200BR +Isolator:ADuM1200CR +Isolator:ADuM1200WS +Isolator:ADuM1200WT +Isolator:ADuM1200WU +Isolator:ADuM1201AR +Isolator:ADuM1201BR +Isolator:ADuM1201CR +Isolator:ADuM1201WS +Isolator:ADuM1201WT +Isolator:ADuM1201WU +Isolator:ADuM120N +Isolator:ADuM121N +Isolator:ADuM1250 +Isolator:ADuM1281 +Isolator:ADuM1300xRW +Isolator:ADuM1400xRW +Isolator:ADuM1401xRW +Isolator:ADuM1402xRW +Isolator:ADuM1410 +Isolator:ADuM1411 +Isolator:ADuM1412 +Isolator:ADuM260N +Isolator:ADuM261N +Isolator:ADuM262N +Isolator:ADuM263N +Isolator:ADuM3151 +Isolator:ADuM3152 +Isolator:ADuM3153 +Isolator:ADuM5211 +Isolator:ADuM5401 +Isolator:ADuM5402 +Isolator:ADuM5403 +Isolator:ADuM5404 +Isolator:ADuM5410 +Isolator:ADuM5411 +Isolator:ADuM5412 +Isolator:ADuM7640A +Isolator:ADuM7640C +Isolator:ADuM7641A +Isolator:ADuM7641C +Isolator:ADuM7642A +Isolator:ADuM7642C +Isolator:ADuM7643A +Isolator:ADuM7643C +Isolator:CNY17-1 +Isolator:CNY17-2 +Isolator:CNY17-3 +Isolator:CNY17-4 +Isolator:CPC-5002 +Isolator:EL814 +Isolator:EL817 +Isolator:FODM214 +Isolator:FODM214A +Isolator:FODM217A +Isolator:FODM217B +Isolator:FODM217C +Isolator:FODM217D +Isolator:H11AA1 +Isolator:H11L1 +Isolator:H11L2 +Isolator:H11L3 +Isolator:HCNW2201 +Isolator:HCNW2211 +Isolator:HCPL-0201 +Isolator:HCPL-0211 +Isolator:HCPL-0600 +Isolator:HCPL-0601 +Isolator:HCPL-0611 +Isolator:HCPL-061A +Isolator:HCPL-061N +Isolator:HCPL-0630 +Isolator:HCPL-0631 +Isolator:HCPL-063A +Isolator:HCPL-063N +Isolator:HCPL-0661 +Isolator:HCPL-2201 +Isolator:HCPL-2202 +Isolator:HCPL-2211 +Isolator:HCPL-2212 +Isolator:HCPL-2601 +Isolator:HCPL-2611 +Isolator:HCPL-261A +Isolator:HCPL-261N +Isolator:HCPL-2630 +Isolator:HCPL-2631 +Isolator:HCPL-263A +Isolator:HCPL-263N +Isolator:HCPL-4661 +Isolator:HCPL-9000 +Isolator:HCPL2730 +Isolator:HCPL2731 +Isolator:ILD74 +Isolator:ILQ74 +Isolator:ISO1211 +Isolator:ISO1212 +Isolator:ISO1540 +Isolator:ISO1541 +Isolator:ISO1642DWR +Isolator:ISO1643DWR +Isolator:ISO1644DWR +Isolator:ISO6731 +Isolator:ISO6740 +Isolator:ISO6741 +Isolator:ISO6742 +Isolator:ISO7320C +Isolator:ISO7320FC +Isolator:ISO7321C +Isolator:ISO7321FC +Isolator:ISO7330C +Isolator:ISO7330FC +Isolator:ISO7331C +Isolator:ISO7331FC +Isolator:ISO7340C +Isolator:ISO7340FC +Isolator:ISO7341C +Isolator:ISO7341FC +Isolator:ISO7342C +Isolator:ISO7342FC +Isolator:ISO7760DBQ +Isolator:ISO7760DW +Isolator:ISO7760FDBQ +Isolator:ISO7760FDW +Isolator:ISO7761DBQ +Isolator:ISO7761DW +Isolator:ISO7761FDBQ +Isolator:ISO7761FDW +Isolator:ISO7762DBQ +Isolator:ISO7762DW +Isolator:ISO7762FDBQ +Isolator:ISO7762FDW +Isolator:ISO7763DBQ +Isolator:ISO7763DW +Isolator:ISO7763FDBQ +Isolator:ISO7763FDW +Isolator:ISOW7740 +Isolator:ISOW7741 +Isolator:ISOW7742 +Isolator:ISOW7743 +Isolator:ISOW7744 +Isolator:LTV-247 +Isolator:LTV-352T +Isolator:LTV-354T +Isolator:LTV-355T +Isolator:LTV-356T +Isolator:LTV-357T +Isolator:LTV-358T +Isolator:LTV-814 +Isolator:LTV-817 +Isolator:LTV-817M +Isolator:LTV-817S +Isolator:LTV-824 +Isolator:LTV-827 +Isolator:LTV-827M +Isolator:LTV-827S +Isolator:LTV-844 +Isolator:LTV-847 +Isolator:LTV-847M +Isolator:LTV-847S +Isolator:MAX14850AEE+ +Isolator:MAX14850ASE+ +Isolator:MID400 +Isolator:MOCD207M +Isolator:MOCD208M +Isolator:MOCD211M +Isolator:MOCD213M +Isolator:MOCD217M +Isolator:NSL-32 +Isolator:PC3H4 +Isolator:PC3H4A +Isolator:PC817 +Isolator:PC827 +Isolator:PC837 +Isolator:PC847 +Isolator:PS8802-1 +Isolator:PS8802-2 +Isolator:SFH617A-1 +Isolator:SFH617A-1X001 +Isolator:SFH617A-1X006 +Isolator:SFH617A-1X007T +Isolator:SFH617A-1X016 +Isolator:SFH617A-2 +Isolator:SFH617A-2X001 +Isolator:SFH617A-2X006 +Isolator:SFH617A-2X009T +Isolator:SFH617A-2X016 +Isolator:SFH617A-2X017T +Isolator:SFH617A-2X019T +Isolator:SFH617A-3 +Isolator:SFH617A-3X001 +Isolator:SFH617A-3X006 +Isolator:SFH617A-3X007T +Isolator:SFH617A-3X016 +Isolator:SFH617A-3X017T +Isolator:SFH617A-4 +Isolator:SFH617A-4X001 +Isolator:SFH617A-4X006 +Isolator:SFH617A-4X016 +Isolator:SFH6206-1T +Isolator:SFH6206-2T +Isolator:SFH6206-2X001T +Isolator:SFH6206-3T +Isolator:SFH6206-3X001T +Isolator:SFH620A-1 +Isolator:SFH620A-1X001 +Isolator:SFH620A-1X006 +Isolator:SFH620A-2 +Isolator:SFH620A-2X001 +Isolator:SFH620A-2X006 +Isolator:SFH620A-2X007T +Isolator:SFH620A-2X016 +Isolator:SFH620A-2X017T +Isolator:SFH620A-3 +Isolator:SFH620A-3X001 +Isolator:SFH620A-3X006 +Isolator:SFH620A-3X016 +Isolator:Si8640BA-B-IU +Isolator:Si8640BB-B-IS +Isolator:Si8640BB-B-IS1 +Isolator:Si8640BB-B-IU +Isolator:Si8640BC-B-IS1 +Isolator:Si8640BD-B-IS +Isolator:Si8640BD-B-IS2 +Isolator:Si8640EB-B-IU +Isolator:Si8640EC-B-IS1 +Isolator:Si8640ED-B-IS +Isolator:Si8640ED-B-IS2 +Isolator:Si8641BA-B-IU +Isolator:Si8641BB-B-IS +Isolator:Si8641BB-B-IS1 +Isolator:Si8641BB-B-IU +Isolator:Si8641BC-B-IS1 +Isolator:Si8641BD-B-IS +Isolator:Si8641BD-B-IS2 +Isolator:Si8641EB-B-IU +Isolator:Si8641EC-B-IS1 +Isolator:Si8641ED-B-IS +Isolator:Si8641ED-B-IS2 +Isolator:Si8642BA-B-IU +Isolator:Si8642BB-B-IS +Isolator:Si8642BB-B-IS1 +Isolator:Si8642BB-B-IU +Isolator:Si8642BC-B-IS1 +Isolator:Si8642BD-B-IS +Isolator:Si8642BD-B-IS2 +Isolator:Si8642EA-B-IU +Isolator:Si8642EB-B-IU +Isolator:Si8642EC-B-IS1 +Isolator:Si8642ED-B-IS +Isolator:Si8642ED-B-IS2 +Isolator:Si8645BA-B-IU +Isolator:Si8645BB-B-IS +Isolator:Si8645BB-B-IS1 +Isolator:Si8645BB-B-IU +Isolator:Si8645BC-B-IS1 +Isolator:Si8645BD-B-IS +Isolator:Si8660BA-AS1 +Isolator:Si8660BA-B-IS1 +Isolator:Si8660BB-AS1 +Isolator:Si8660BB-AU +Isolator:Si8660BB-B-IS1 +Isolator:Si8660BB-B-IU +Isolator:Si8660BC-AS1 +Isolator:Si8660BC-B-IS1 +Isolator:Si8660BD-AS +Isolator:Si8660BD-B-IS +Isolator:Si8660EB-AU +Isolator:Si8660EB-B-IU +Isolator:Si8660EC-AS1 +Isolator:Si8660EC-B-IS1 +Isolator:Si8660ED-AS +Isolator:Si8660ED-B-IS +Isolator:Si8661BB-AS1 +Isolator:Si8661BB-AU +Isolator:Si8661BB-B-IS1 +Isolator:Si8661BB-B-IU +Isolator:Si8661BC-AS1 +Isolator:Si8661BC-B-IS1 +Isolator:Si8661BD-AS +Isolator:Si8661BD-AS2 +Isolator:Si8661BD-B-IS +Isolator:Si8661BD-B-IS2 +Isolator:Si8661EB-AU +Isolator:Si8661EB-B-IU +Isolator:Si8661EC-AS1 +Isolator:Si8661EC-B-IS1 +Isolator:Si8661ED-AS +Isolator:Si8661ED-B-IS +Isolator:Si8662BB-AS1 +Isolator:Si8662BB-AU +Isolator:Si8662BB-B-IS1 +Isolator:Si8662BB-B-IU +Isolator:Si8662BC-AS1 +Isolator:Si8662BC-B-IS1 +Isolator:Si8662BD-AS +Isolator:Si8662BD-B-IS +Isolator:Si8662EB-AU +Isolator:Si8662EB-B-IU +Isolator:Si8662EC-AS1 +Isolator:Si8662EC-B-IS1 +Isolator:Si8662ED-AS +Isolator:Si8662ED-B-IS +Isolator:Si8663BB-AS1 +Isolator:Si8663BB-AU +Isolator:Si8663BB-B-IS1 +Isolator:Si8663BB-B-IU +Isolator:Si8663BC-AS1 +Isolator:Si8663BC-B-IS1 +Isolator:Si8663BD-AS +Isolator:Si8663BD-B-IS +Isolator:Si8663EB-AU +Isolator:Si8663EB-B-IU +Isolator:Si8663EC-AS1 +Isolator:Si8663EC-B-IS1 +Isolator:Si8663ED-AS +Isolator:Si8663ED-B-IS +Isolator:TCMT1100 +Isolator:TCMT1101 +Isolator:TCMT1102 +Isolator:TCMT1103 +Isolator:TCMT1104 +Isolator:TCMT1105 +Isolator:TCMT1106 +Isolator:TCMT1107 +Isolator:TCMT1108 +Isolator:TCMT1109 +Isolator:TCMT1600 +Isolator:TCMT4100 +Isolator:TCMT4106 +Isolator:TCMT4600 +Isolator:TCMT4606 +Isolator:TIL111 +Isolator:TLP127 +Isolator:TLP130 +Isolator:TLP131 +Isolator:TLP137 +Isolator:TLP184 +Isolator:TLP184xSE +Isolator:TLP185 +Isolator:TLP185xSE +Isolator:TLP2310 +Isolator:TLP2703 +Isolator:TLP2745 +Isolator:TLP2748 +Isolator:TLP2761 +Isolator:TLP2767 +Isolator:TLP2768A +Isolator:TLP2770 +Isolator:TLP290 +Isolator:TLP290-4 +Isolator:TLP291 +Isolator:TLP291-4 +Isolator:TLP292 +Isolator:TLP292-4 +Isolator:TLP293 +Isolator:TLP293-4 +Isolator:TLP3021 +Isolator:TLP3022 +Isolator:TLP3023 +Isolator:TLP627 +Isolator:TLP627-2 +Isolator:TLP627-4 +Isolator:TLP785 +Isolator:TLP785F +Isolator:VO0600T +Isolator:VO0601T +Isolator:VO0611T +Isolator:VO0630T +Isolator:VO0631T +Isolator:VO0661T +Isolator:VO2601 +Isolator:VO2611 +Isolator:VO2630 +Isolator:VO2631 +Isolator:VO4661 +Isolator:VO615A +Isolator:VO615A-1 +Isolator:VO615A-2 +Isolator:VO615A-3 +Isolator:VO615A-4 +Isolator:VO615A-5 +Isolator:VO615A-6 +Isolator:VO615A-7 +Isolator:VO615A-8 +Isolator:VO615A-9 +Isolator:VOA300 +Isolator:VOS618A +Isolator:VTL5C +Isolator:VTL5Cx2 +Isolator:π120U30 +Isolator:π120U31 +Isolator_Analog:ACPL-C790 +Isolator_Analog:ACPL-C79A +Isolator_Analog:ACPL-C79B +Isolator_Analog:ACPL-C870 +Isolator_Analog:ACPL-C87A +Isolator_Analog:ACPL-C87B +Isolator_Analog:AMC3330 +Isolator_Analog:IL300 +Isolator_Analog:LOC112 +Isolator_Analog:LOC112P +Isolator_Analog:LOC112S +Jumper:Jumper_2_Bridged +Jumper:Jumper_2_Open +Jumper:Jumper_2_Small_Bridged +Jumper:Jumper_2_Small_Open +Jumper:Jumper_3_Bridged12 +Jumper:Jumper_3_Open +Jumper:SolderJumper_2_Bridged +Jumper:SolderJumper_2_Open +Jumper:SolderJumper_3_Bridged12 +Jumper:SolderJumper_3_Bridged123 +Jumper:SolderJumper_3_Open +LED:APA-106-F5 +LED:APA102 +LED:APA102-2020 +LED:APFA3010 +LED:ASMB-MTB0-0A3A2 +LED:ASMB-MTB1-0A3A2 +LED:ASMT-YTB7-0AA02 +LED:ASMT-YTC2-0AA02 +LED:CLS6B-FKW +LED:CLV1L-FKB +LED:CLX6F-FKC +LED:CQY99 +LED:HDSP-4830 +LED:HDSP-4830_2 +LED:HDSP-4832 +LED:HDSP-4832_2 +LED:HDSP-4836 +LED:HDSP-4836_2 +LED:HDSP-4840 +LED:HDSP-4840_2 +LED:HDSP-4850 +LED:HDSP-4850_2 +LED:HLCP-J100 +LED:HLCP-J100_2 +LED:IR204A +LED:IR26-21C_L110_TR8 +LED:IRL81A +LED:Inolux_IN-P55TATRGB +LED:Inolux_IN-PI554FCH +LED:Inolux_IN-PI556FCH +LED:LD271 +LED:LD274 +LED:LED_Cree_XHP50_12V +LED:LED_Cree_XHP50_6V +LED:LED_Cree_XHP70_12V +LED:LED_Cree_XHP70_6V +LED:LTST-C235KGKRKT +LED:LiteOn_LTST-E563C +LED:NeoPixel_THT +LED:QLS6A-FKW +LED:QLS6B-FKW +LED:SFH4346 +LED:SFH4356P +LED:SFH4546 +LED:SFH4550 +LED:SFH460 +LED:SFH480 +LED:SFH482 +LED:SK6805 +LED:SK6812 +LED:SK6812MINI +LED:SMLVN6RGB +LED:TSAL4400 +LED:WS2812 +LED:WS2812B +LED:WS2812B-2020 +LED:WS2812S +LED:WS2813 +LED:WS2822S +Logic_LevelTranslator:74LVC2T45DC +Logic_LevelTranslator:74LVCH2T45DC +Logic_LevelTranslator:FXMA108 +Logic_LevelTranslator:NCN4555MN +Logic_LevelTranslator:NLSV2T244D +Logic_LevelTranslator:NLSV2T244DM +Logic_LevelTranslator:NLSV2T244MU +Logic_LevelTranslator:SN74AUP1T34DCK +Logic_LevelTranslator:SN74AVC4T245PW +Logic_LevelTranslator:SN74AVC8T245PW +Logic_LevelTranslator:SN74LV1T125DBV +Logic_LevelTranslator:SN74LV1T125DCK +Logic_LevelTranslator:SN74LV1T34DBV +Logic_LevelTranslator:SN74LV1T34DCK +Logic_LevelTranslator:SN74LVC1T45DBV +Logic_LevelTranslator:SN74LVC1T45DCK +Logic_LevelTranslator:SN74LVC1T45DRL +Logic_LevelTranslator:SN74LVC245APW +Logic_LevelTranslator:SN74LVC2T45DCUR +Logic_LevelTranslator:SN74LVC2T45YZP +Logic_LevelTranslator:SN74LVC8T245 +Logic_LevelTranslator:TCA9517ADGK +Logic_LevelTranslator:TCA9517D +Logic_LevelTranslator:TXB0101DBV +Logic_LevelTranslator:TXB0101DCK +Logic_LevelTranslator:TXB0101DRL +Logic_LevelTranslator:TXB0101YZP +Logic_LevelTranslator:TXB0102DCT +Logic_LevelTranslator:TXB0102DCU +Logic_LevelTranslator:TXB0102YZP +Logic_LevelTranslator:TXB0104D +Logic_LevelTranslator:TXB0104PW +Logic_LevelTranslator:TXB0104RGY +Logic_LevelTranslator:TXB0104RUT +Logic_LevelTranslator:TXB0104YZT +Logic_LevelTranslator:TXB0104ZXU +Logic_LevelTranslator:TXB0106PW +Logic_LevelTranslator:TXB0106RGY +Logic_LevelTranslator:TXB0108DQSR +Logic_LevelTranslator:TXB0108PW +Logic_LevelTranslator:TXB0108RGY +Logic_LevelTranslator:TXB0304RUT +Logic_LevelTranslator:TXBN0304RUT +Logic_LevelTranslator:TXS0101DBV +Logic_LevelTranslator:TXS0101DCK +Logic_LevelTranslator:TXS0101DRL +Logic_LevelTranslator:TXS0101YZP +Logic_LevelTranslator:TXS0102DCT +Logic_LevelTranslator:TXS0102DCU +Logic_LevelTranslator:TXS0102DQE +Logic_LevelTranslator:TXS0102YZP +Logic_LevelTranslator:TXS0104ED +Logic_LevelTranslator:TXS0104EPW +Logic_LevelTranslator:TXS0108EPW +Logic_LevelTranslator:TXS02612RTW +Logic_Programmable:GAL16V8 +Logic_Programmable:PAL16L8 +Logic_Programmable:PAL20 +Logic_Programmable:PAL20L8 +Logic_Programmable:PAL20RS10 +Logic_Programmable:PAL24 +Logic_Programmable:PEEL22CV10AP +Logic_Programmable:PEEL22CV10AS +MCU_AnalogDevices:ADUC816 +MCU_AnalogDevices:MAX32660GTP +MCU_AnalogDevices:MAX32670GTL +MCU_Cypress:CY7C68013A-56LTX +MCU_Cypress:CY7C68013A-56PVX +MCU_Cypress:CY7C68014A-56LTX +MCU_Cypress:CY7C68014A-56PVX +MCU_Cypress:CY8C4127LQI-BL453 +MCU_Cypress:CY8C4127LQI-BL473 +MCU_Cypress:CY8C4127LQI-BL483 +MCU_Cypress:CY8C4127LQI-BL493 +MCU_Cypress:CY8C4245AXI-M445 +MCU_Cypress:CY8C4245AZI-M445 +MCU_Cypress:CY8C4246AXI-M445 +MCU_Cypress:CY8C4246AZI-M445 +MCU_Cypress:CY8C4246AZI-M475 +MCU_Cypress:CY8C4247AXI-M485 +MCU_Cypress:CY8C4247AZI-M475 +MCU_Cypress:CY8C4247AZI-M485 +MCU_Cypress:CY8C4247LQI-BL453 +MCU_Cypress:CY8C4247LQI-BL463 +MCU_Cypress:CY8C4247LQI-BL473 +MCU_Cypress:CY8C4247LQI-BL483 +MCU_Cypress:CY8C4247LQI-BL493 +MCU_Cypress:CY8C4247LQQ-BL483 +MCU_Cypress:CY8C4xx7LQI-4xx +MCU_Cypress:CYBL10161-56LQXI +MCU_Cypress:CYBL10162-56LQXI +MCU_Cypress:CYBL10163-56LQXI +MCU_Cypress:CYBL10461-56LQXI +MCU_Cypress:CYBL10462-56LQXI +MCU_Cypress:CYBL10463-56LQXI +MCU_Cypress:CYBL10561-56LQXI +MCU_Cypress:CYBL10562-56LQXI +MCU_Cypress:CYBL10563-56LQXI +MCU_Cypress:CYBL10563-56LQXQ +MCU_Cypress:CYBL10563-68FLXIT +MCU_Cypress:CYBL10563-68FNXIT +MCU_Cypress:CYBL10x6x-56LQxx +MCU_Dialog:DA14691 +MCU_Dialog:DA14695 +MCU_Espressif:ESP32-C3 +MCU_Espressif:ESP32-PICO-D4 +MCU_Espressif:ESP32-S2 +MCU_Espressif:ESP32-S3 +MCU_Espressif:ESP8266EX +MCU_Intel:80186 +MCU_Intel:80188 +MCU_Intel:8035 +MCU_Intel:8039 +MCU_Intel:8040 +MCU_Intel:8048 +MCU_Intel:8049 +MCU_Intel:8050 +MCU_Intel:8080 +MCU_Intel:8080A +MCU_Intel:8086_Max_Mode +MCU_Intel:8086_Min_Mode +MCU_Intel:8087 +MCU_Intel:8088 +MCU_Intel:8088_Max_Mode +MCU_Intel:8088_Min_Mode +MCU_Intel:80C186XL +MCU_Intel:80C188 +MCU_Intel:80C188XL +MCU_Intel:8748 +MCU_Intel:8749 +MCU_Intel:I386EX_PQFP +MCU_Intel:IA186XLPLC68IR2 +MCU_Intel:IA188XLPLC68IR2 +MCU_Intel:M80C186 +MCU_Intel:M80C186XL +MCU_Intel:P8031AH +MCU_Intel:P8051AH +MCU_Intel:P8052AH +MCU_Intel:P8751BH +MCU_Intel:P8752BH +MCU_Microchip_8051:AT89C2051-12P +MCU_Microchip_8051:AT89C2051-12S +MCU_Microchip_8051:AT89C2051-24P +MCU_Microchip_8051:AT89C2051-24S +MCU_Microchip_8051:AT89C4051-12P +MCU_Microchip_8051:AT89C4051-12S +MCU_Microchip_8051:AT89C4051-24P +MCU_Microchip_8051:AT89C4051-24S +MCU_Microchip_8051:AT89S2051-24P +MCU_Microchip_8051:AT89S2051-24S +MCU_Microchip_8051:AT89S4051-24P +MCU_Microchip_8051:AT89S4051-24S +MCU_Microchip_8051:AT89x51xxA +MCU_Microchip_8051:AT89x51xxJ +MCU_Microchip_8051:AT89x51xxP +MCU_Microchip_ATmega:ATmega128-16A +MCU_Microchip_ATmega:ATmega128-16M +MCU_Microchip_ATmega:ATmega1280-16A +MCU_Microchip_ATmega:ATmega1280-16C +MCU_Microchip_ATmega:ATmega1280V-8A +MCU_Microchip_ATmega:ATmega1280V-8C +MCU_Microchip_ATmega:ATmega1281-16A +MCU_Microchip_ATmega:ATmega1281-16M +MCU_Microchip_ATmega:ATmega1281V-8A +MCU_Microchip_ATmega:ATmega1281V-8M +MCU_Microchip_ATmega:ATmega1284-A +MCU_Microchip_ATmega:ATmega1284-M +MCU_Microchip_ATmega:ATmega1284-P +MCU_Microchip_ATmega:ATmega1284P-A +MCU_Microchip_ATmega:ATmega1284P-M +MCU_Microchip_ATmega:ATmega1284P-P +MCU_Microchip_ATmega:ATmega128A-A +MCU_Microchip_ATmega:ATmega128A-M +MCU_Microchip_ATmega:ATmega128L-8A +MCU_Microchip_ATmega:ATmega128L-8M +MCU_Microchip_ATmega:ATmega16-16A +MCU_Microchip_ATmega:ATmega16-16M +MCU_Microchip_ATmega:ATmega16-16P +MCU_Microchip_ATmega:ATmega162-16A +MCU_Microchip_ATmega:ATmega162-16M +MCU_Microchip_ATmega:ATmega162-16P +MCU_Microchip_ATmega:ATmega162V-8A +MCU_Microchip_ATmega:ATmega162V-8M +MCU_Microchip_ATmega:ATmega162V-8P +MCU_Microchip_ATmega:ATmega164A-A +MCU_Microchip_ATmega:ATmega164A-C +MCU_Microchip_ATmega:ATmega164A-M +MCU_Microchip_ATmega:ATmega164A-MC +MCU_Microchip_ATmega:ATmega164A-P +MCU_Microchip_ATmega:ATmega164P-20A +MCU_Microchip_ATmega:ATmega164P-20M +MCU_Microchip_ATmega:ATmega164P-20P +MCU_Microchip_ATmega:ATmega164PA-A +MCU_Microchip_ATmega:ATmega164PA-C +MCU_Microchip_ATmega:ATmega164PA-M +MCU_Microchip_ATmega:ATmega164PA-MC +MCU_Microchip_ATmega:ATmega164PA-P +MCU_Microchip_ATmega:ATmega164PV-10A +MCU_Microchip_ATmega:ATmega164PV-10M +MCU_Microchip_ATmega:ATmega164PV-10P +MCU_Microchip_ATmega:ATmega165A-A +MCU_Microchip_ATmega:ATmega165A-M +MCU_Microchip_ATmega:ATmega165P-16A +MCU_Microchip_ATmega:ATmega165P-16M +MCU_Microchip_ATmega:ATmega165PA-A +MCU_Microchip_ATmega:ATmega165PA-M +MCU_Microchip_ATmega:ATmega165PV-8A +MCU_Microchip_ATmega:ATmega165PV-8M +MCU_Microchip_ATmega:ATmega168-20A +MCU_Microchip_ATmega:ATmega168-20M +MCU_Microchip_ATmega:ATmega168-20P +MCU_Microchip_ATmega:ATmega168A-A +MCU_Microchip_ATmega:ATmega168A-CC +MCU_Microchip_ATmega:ATmega168A-M +MCU_Microchip_ATmega:ATmega168A-MM +MCU_Microchip_ATmega:ATmega168A-P +MCU_Microchip_ATmega:ATmega168P-20A +MCU_Microchip_ATmega:ATmega168P-20M +MCU_Microchip_ATmega:ATmega168P-20P +MCU_Microchip_ATmega:ATmega168PA-A +MCU_Microchip_ATmega:ATmega168PA-CC +MCU_Microchip_ATmega:ATmega168PA-M +MCU_Microchip_ATmega:ATmega168PA-MM +MCU_Microchip_ATmega:ATmega168PA-P +MCU_Microchip_ATmega:ATmega168PB-A +MCU_Microchip_ATmega:ATmega168PB-M +MCU_Microchip_ATmega:ATmega168PV-10A +MCU_Microchip_ATmega:ATmega168PV-10M +MCU_Microchip_ATmega:ATmega168PV-10P +MCU_Microchip_ATmega:ATmega168V-10A +MCU_Microchip_ATmega:ATmega168V-10M +MCU_Microchip_ATmega:ATmega168V-10P +MCU_Microchip_ATmega:ATmega169A-A +MCU_Microchip_ATmega:ATmega169A-M +MCU_Microchip_ATmega:ATmega169A-MC +MCU_Microchip_ATmega:ATmega169P-16A +MCU_Microchip_ATmega:ATmega169P-16M +MCU_Microchip_ATmega:ATmega169P-16MC +MCU_Microchip_ATmega:ATmega169PA-A +MCU_Microchip_ATmega:ATmega169PA-M +MCU_Microchip_ATmega:ATmega169PA-MC +MCU_Microchip_ATmega:ATmega169PV-8A +MCU_Microchip_ATmega:ATmega169PV-8M +MCU_Microchip_ATmega:ATmega169PV-8MC +MCU_Microchip_ATmega:ATmega16A-A +MCU_Microchip_ATmega:ATmega16A-M +MCU_Microchip_ATmega:ATmega16A-P +MCU_Microchip_ATmega:ATmega16L-8A +MCU_Microchip_ATmega:ATmega16L-8M +MCU_Microchip_ATmega:ATmega16L-8P +MCU_Microchip_ATmega:ATmega16M1-A +MCU_Microchip_ATmega:ATmega16M1-M +MCU_Microchip_ATmega:ATmega16U2-A +MCU_Microchip_ATmega:ATmega16U2-M +MCU_Microchip_ATmega:ATmega16U4-A +MCU_Microchip_ATmega:ATmega16U4-M +MCU_Microchip_ATmega:ATmega16U4RC-A +MCU_Microchip_ATmega:ATmega16U4RC-M +MCU_Microchip_ATmega:ATmega2560-16A +MCU_Microchip_ATmega:ATmega2560-16C +MCU_Microchip_ATmega:ATmega2560V-8A +MCU_Microchip_ATmega:ATmega2560V-8C +MCU_Microchip_ATmega:ATmega2561-16A +MCU_Microchip_ATmega:ATmega2561-16M +MCU_Microchip_ATmega:ATmega2561V-8A +MCU_Microchip_ATmega:ATmega2561V-8M +MCU_Microchip_ATmega:ATmega32-16A +MCU_Microchip_ATmega:ATmega32-16M +MCU_Microchip_ATmega:ATmega32-16P +MCU_Microchip_ATmega:ATmega3208-A +MCU_Microchip_ATmega:ATmega3208-M +MCU_Microchip_ATmega:ATmega3208-X +MCU_Microchip_ATmega:ATmega3209-A +MCU_Microchip_ATmega:ATmega3209-M +MCU_Microchip_ATmega:ATmega324A-A +MCU_Microchip_ATmega:ATmega324A-C +MCU_Microchip_ATmega:ATmega324A-M +MCU_Microchip_ATmega:ATmega324A-MC +MCU_Microchip_ATmega:ATmega324A-P +MCU_Microchip_ATmega:ATmega324P-20A +MCU_Microchip_ATmega:ATmega324P-20M +MCU_Microchip_ATmega:ATmega324P-20P +MCU_Microchip_ATmega:ATmega324PA-A +MCU_Microchip_ATmega:ATmega324PA-C +MCU_Microchip_ATmega:ATmega324PA-M +MCU_Microchip_ATmega:ATmega324PA-MC +MCU_Microchip_ATmega:ATmega324PA-P +MCU_Microchip_ATmega:ATmega324PB-A +MCU_Microchip_ATmega:ATmega324PB-M +MCU_Microchip_ATmega:ATmega324PV-10A +MCU_Microchip_ATmega:ATmega324PV-10M +MCU_Microchip_ATmega:ATmega324PV-10P +MCU_Microchip_ATmega:ATmega325-16A +MCU_Microchip_ATmega:ATmega325-16M +MCU_Microchip_ATmega:ATmega3250-16A +MCU_Microchip_ATmega:ATmega3250A-A +MCU_Microchip_ATmega:ATmega3250P-20A +MCU_Microchip_ATmega:ATmega3250PA-A +MCU_Microchip_ATmega:ATmega3250PV-10A +MCU_Microchip_ATmega:ATmega3250V-8A +MCU_Microchip_ATmega:ATmega325A-A +MCU_Microchip_ATmega:ATmega325A-M +MCU_Microchip_ATmega:ATmega325P-20A +MCU_Microchip_ATmega:ATmega325P-20M +MCU_Microchip_ATmega:ATmega325PA-A +MCU_Microchip_ATmega:ATmega325PA-M +MCU_Microchip_ATmega:ATmega325PV-10A +MCU_Microchip_ATmega:ATmega325PV-10M +MCU_Microchip_ATmega:ATmega325V-8A +MCU_Microchip_ATmega:ATmega325V-8M +MCU_Microchip_ATmega:ATmega328-A +MCU_Microchip_ATmega:ATmega328-M +MCU_Microchip_ATmega:ATmega328-MM +MCU_Microchip_ATmega:ATmega328-P +MCU_Microchip_ATmega:ATmega328P-A +MCU_Microchip_ATmega:ATmega328P-M +MCU_Microchip_ATmega:ATmega328P-MM +MCU_Microchip_ATmega:ATmega328P-P +MCU_Microchip_ATmega:ATmega328PB-A +MCU_Microchip_ATmega:ATmega328PB-M +MCU_Microchip_ATmega:ATmega329-16A +MCU_Microchip_ATmega:ATmega329-16M +MCU_Microchip_ATmega:ATmega3290-16A +MCU_Microchip_ATmega:ATmega3290A-A +MCU_Microchip_ATmega:ATmega3290P-20A +MCU_Microchip_ATmega:ATmega3290PA-A +MCU_Microchip_ATmega:ATmega3290PV-10A +MCU_Microchip_ATmega:ATmega3290V-8A +MCU_Microchip_ATmega:ATmega329A-A +MCU_Microchip_ATmega:ATmega329A-M +MCU_Microchip_ATmega:ATmega329P-20A +MCU_Microchip_ATmega:ATmega329P-20M +MCU_Microchip_ATmega:ATmega329PA-A +MCU_Microchip_ATmega:ATmega329PA-M +MCU_Microchip_ATmega:ATmega329PV-10A +MCU_Microchip_ATmega:ATmega329PV-10M +MCU_Microchip_ATmega:ATmega329V-8A +MCU_Microchip_ATmega:ATmega329V-8M +MCU_Microchip_ATmega:ATmega32A-A +MCU_Microchip_ATmega:ATmega32A-M +MCU_Microchip_ATmega:ATmega32A-P +MCU_Microchip_ATmega:ATmega32L-8A +MCU_Microchip_ATmega:ATmega32L-8M +MCU_Microchip_ATmega:ATmega32L-8P +MCU_Microchip_ATmega:ATmega32M1-A +MCU_Microchip_ATmega:ATmega32M1-M +MCU_Microchip_ATmega:ATmega32U2-A +MCU_Microchip_ATmega:ATmega32U2-M +MCU_Microchip_ATmega:ATmega32U4-A +MCU_Microchip_ATmega:ATmega32U4-M +MCU_Microchip_ATmega:ATmega32U4RC-A +MCU_Microchip_ATmega:ATmega32U4RC-M +MCU_Microchip_ATmega:ATmega406-1AA +MCU_Microchip_ATmega:ATmega48-20A +MCU_Microchip_ATmega:ATmega48-20M +MCU_Microchip_ATmega:ATmega48-20MM +MCU_Microchip_ATmega:ATmega48-20P +MCU_Microchip_ATmega:ATmega4808-A +MCU_Microchip_ATmega:ATmega4808-M +MCU_Microchip_ATmega:ATmega4808-X +MCU_Microchip_ATmega:ATmega4809-A +MCU_Microchip_ATmega:ATmega4809-M +MCU_Microchip_ATmega:ATmega48A-A +MCU_Microchip_ATmega:ATmega48A-CC +MCU_Microchip_ATmega:ATmega48A-M +MCU_Microchip_ATmega:ATmega48A-MM +MCU_Microchip_ATmega:ATmega48A-P +MCU_Microchip_ATmega:ATmega48P-20A +MCU_Microchip_ATmega:ATmega48P-20M +MCU_Microchip_ATmega:ATmega48P-20MM +MCU_Microchip_ATmega:ATmega48P-20P +MCU_Microchip_ATmega:ATmega48PA-A +MCU_Microchip_ATmega:ATmega48PA-CC +MCU_Microchip_ATmega:ATmega48PA-M +MCU_Microchip_ATmega:ATmega48PA-MM +MCU_Microchip_ATmega:ATmega48PA-P +MCU_Microchip_ATmega:ATmega48PB-A +MCU_Microchip_ATmega:ATmega48PB-M +MCU_Microchip_ATmega:ATmega48PV-10A +MCU_Microchip_ATmega:ATmega48PV-10M +MCU_Microchip_ATmega:ATmega48PV-10MM +MCU_Microchip_ATmega:ATmega48PV-10P +MCU_Microchip_ATmega:ATmega48V-10A +MCU_Microchip_ATmega:ATmega48V-10M +MCU_Microchip_ATmega:ATmega48V-10MM +MCU_Microchip_ATmega:ATmega48V-10P +MCU_Microchip_ATmega:ATmega64-16A +MCU_Microchip_ATmega:ATmega64-16M +MCU_Microchip_ATmega:ATmega640-16A +MCU_Microchip_ATmega:ATmega640-16C +MCU_Microchip_ATmega:ATmega640V-8A +MCU_Microchip_ATmega:ATmega640V-8C +MCU_Microchip_ATmega:ATmega644-20A +MCU_Microchip_ATmega:ATmega644-20M +MCU_Microchip_ATmega:ATmega644-20P +MCU_Microchip_ATmega:ATmega644A-A +MCU_Microchip_ATmega:ATmega644A-M +MCU_Microchip_ATmega:ATmega644A-P +MCU_Microchip_ATmega:ATmega644P-20A +MCU_Microchip_ATmega:ATmega644P-20M +MCU_Microchip_ATmega:ATmega644P-20P +MCU_Microchip_ATmega:ATmega644PA-A +MCU_Microchip_ATmega:ATmega644PA-M +MCU_Microchip_ATmega:ATmega644PA-P +MCU_Microchip_ATmega:ATmega644PV-10A +MCU_Microchip_ATmega:ATmega644PV-10M +MCU_Microchip_ATmega:ATmega644PV-10P +MCU_Microchip_ATmega:ATmega644V-10A +MCU_Microchip_ATmega:ATmega644V-10M +MCU_Microchip_ATmega:ATmega644V-10P +MCU_Microchip_ATmega:ATmega645-16A +MCU_Microchip_ATmega:ATmega645-16M +MCU_Microchip_ATmega:ATmega6450-16A +MCU_Microchip_ATmega:ATmega6450A-A +MCU_Microchip_ATmega:ATmega6450P-A +MCU_Microchip_ATmega:ATmega6450V-8A +MCU_Microchip_ATmega:ATmega645A-A +MCU_Microchip_ATmega:ATmega645A-M +MCU_Microchip_ATmega:ATmega645P-A +MCU_Microchip_ATmega:ATmega645P-M +MCU_Microchip_ATmega:ATmega645V-8A +MCU_Microchip_ATmega:ATmega645V-8M +MCU_Microchip_ATmega:ATmega649-16A +MCU_Microchip_ATmega:ATmega649-16M +MCU_Microchip_ATmega:ATmega6490-16A +MCU_Microchip_ATmega:ATmega6490A-A +MCU_Microchip_ATmega:ATmega6490P-A +MCU_Microchip_ATmega:ATmega6490V-8A +MCU_Microchip_ATmega:ATmega649A-A +MCU_Microchip_ATmega:ATmega649A-M +MCU_Microchip_ATmega:ATmega649P-A +MCU_Microchip_ATmega:ATmega649P-M +MCU_Microchip_ATmega:ATmega649V-8A +MCU_Microchip_ATmega:ATmega649V-8M +MCU_Microchip_ATmega:ATmega64A-A +MCU_Microchip_ATmega:ATmega64A-M +MCU_Microchip_ATmega:ATmega64L-8A +MCU_Microchip_ATmega:ATmega64L-8M +MCU_Microchip_ATmega:ATmega64M1-A +MCU_Microchip_ATmega:ATmega64M1-M +MCU_Microchip_ATmega:ATmega8-16A +MCU_Microchip_ATmega:ATmega8-16M +MCU_Microchip_ATmega:ATmega8-16P +MCU_Microchip_ATmega:ATmega8515-16A +MCU_Microchip_ATmega:ATmega8515-16J +MCU_Microchip_ATmega:ATmega8515-16M +MCU_Microchip_ATmega:ATmega8515-16P +MCU_Microchip_ATmega:ATmega8515L-8A +MCU_Microchip_ATmega:ATmega8515L-8J +MCU_Microchip_ATmega:ATmega8515L-8M +MCU_Microchip_ATmega:ATmega8515L-8P +MCU_Microchip_ATmega:ATmega8535-16A +MCU_Microchip_ATmega:ATmega8535-16J +MCU_Microchip_ATmega:ATmega8535-16M +MCU_Microchip_ATmega:ATmega8535-16P +MCU_Microchip_ATmega:ATmega8535L-8A +MCU_Microchip_ATmega:ATmega8535L-8J +MCU_Microchip_ATmega:ATmega8535L-8M +MCU_Microchip_ATmega:ATmega8535L-8P +MCU_Microchip_ATmega:ATmega88-20A +MCU_Microchip_ATmega:ATmega88-20M +MCU_Microchip_ATmega:ATmega88-20P +MCU_Microchip_ATmega:ATmega88A-A +MCU_Microchip_ATmega:ATmega88A-CC +MCU_Microchip_ATmega:ATmega88A-M +MCU_Microchip_ATmega:ATmega88A-MM +MCU_Microchip_ATmega:ATmega88A-P +MCU_Microchip_ATmega:ATmega88P-20A +MCU_Microchip_ATmega:ATmega88P-20M +MCU_Microchip_ATmega:ATmega88P-20P +MCU_Microchip_ATmega:ATmega88PA-A +MCU_Microchip_ATmega:ATmega88PA-CC +MCU_Microchip_ATmega:ATmega88PA-M +MCU_Microchip_ATmega:ATmega88PA-MM +MCU_Microchip_ATmega:ATmega88PA-P +MCU_Microchip_ATmega:ATmega88PB-A +MCU_Microchip_ATmega:ATmega88PB-M +MCU_Microchip_ATmega:ATmega88PV-10A +MCU_Microchip_ATmega:ATmega88PV-10M +MCU_Microchip_ATmega:ATmega88PV-10P +MCU_Microchip_ATmega:ATmega88V-10A +MCU_Microchip_ATmega:ATmega88V-10M +MCU_Microchip_ATmega:ATmega88V-10P +MCU_Microchip_ATmega:ATmega8A-A +MCU_Microchip_ATmega:ATmega8A-M +MCU_Microchip_ATmega:ATmega8A-P +MCU_Microchip_ATmega:ATmega8L-8A +MCU_Microchip_ATmega:ATmega8L-8M +MCU_Microchip_ATmega:ATmega8L-8P +MCU_Microchip_ATmega:ATmega8U2-A +MCU_Microchip_ATmega:ATmega8U2-M +MCU_Microchip_ATmega:ATxmega128A1-A +MCU_Microchip_ATmega:ATxmega128A1-C +MCU_Microchip_ATmega:ATxmega128A1-C7 +MCU_Microchip_ATmega:ATxmega128A1U-A +MCU_Microchip_ATmega:ATxmega128A1U-C +MCU_Microchip_ATmega:ATxmega128A1U-C7 +MCU_Microchip_ATmega:ATxmega128A3-A +MCU_Microchip_ATmega:ATxmega128A3-M +MCU_Microchip_ATmega:ATxmega128A3U-A +MCU_Microchip_ATmega:ATxmega128A3U-M +MCU_Microchip_ATmega:ATxmega128A4U-A +MCU_Microchip_ATmega:ATxmega128A4U-C +MCU_Microchip_ATmega:ATxmega128A4U-M +MCU_Microchip_ATmega:ATxmega128B1-A +MCU_Microchip_ATmega:ATxmega128B1-C +MCU_Microchip_ATmega:ATxmega128B3-A +MCU_Microchip_ATmega:ATxmega128B3-M +MCU_Microchip_ATmega:ATxmega128B3-MC +MCU_Microchip_ATmega:ATxmega128C3-A +MCU_Microchip_ATmega:ATxmega128C3-M +MCU_Microchip_ATmega:ATxmega128D3-A +MCU_Microchip_ATmega:ATxmega128D3-M +MCU_Microchip_ATmega:ATxmega128D4-A +MCU_Microchip_ATmega:ATxmega128D4-C +MCU_Microchip_ATmega:ATxmega128D4-M +MCU_Microchip_ATmega:ATxmega16A4U-A +MCU_Microchip_ATmega:ATxmega16A4U-C +MCU_Microchip_ATmega:ATxmega16A4U-M +MCU_Microchip_ATmega:ATxmega16C4-A +MCU_Microchip_ATmega:ATxmega16C4-C +MCU_Microchip_ATmega:ATxmega16C4-M +MCU_Microchip_ATmega:ATxmega16D4-A +MCU_Microchip_ATmega:ATxmega16D4-C +MCU_Microchip_ATmega:ATxmega16D4-M +MCU_Microchip_ATmega:ATxmega16E5-A +MCU_Microchip_ATmega:ATxmega16E5-M +MCU_Microchip_ATmega:ATxmega16E5-M4 +MCU_Microchip_ATmega:ATxmega192A3-A +MCU_Microchip_ATmega:ATxmega192A3-M +MCU_Microchip_ATmega:ATxmega192A3U-A +MCU_Microchip_ATmega:ATxmega192A3U-M +MCU_Microchip_ATmega:ATxmega192C3-A +MCU_Microchip_ATmega:ATxmega192C3-M +MCU_Microchip_ATmega:ATxmega192D3-A +MCU_Microchip_ATmega:ATxmega192D3-M +MCU_Microchip_ATmega:ATxmega256A3-A +MCU_Microchip_ATmega:ATxmega256A3-M +MCU_Microchip_ATmega:ATxmega256A3B-A +MCU_Microchip_ATmega:ATxmega256A3B-M +MCU_Microchip_ATmega:ATxmega256A3BU-A +MCU_Microchip_ATmega:ATxmega256A3BU-M +MCU_Microchip_ATmega:ATxmega256A3U-A +MCU_Microchip_ATmega:ATxmega256A3U-M +MCU_Microchip_ATmega:ATxmega256C3-A +MCU_Microchip_ATmega:ATxmega256C3-M +MCU_Microchip_ATmega:ATxmega256D3-A +MCU_Microchip_ATmega:ATxmega256D3-M +MCU_Microchip_ATmega:ATxmega32A4U-A +MCU_Microchip_ATmega:ATxmega32A4U-C +MCU_Microchip_ATmega:ATxmega32A4U-M +MCU_Microchip_ATmega:ATxmega32C3-A +MCU_Microchip_ATmega:ATxmega32C3-M +MCU_Microchip_ATmega:ATxmega32C4-A +MCU_Microchip_ATmega:ATxmega32C4-C +MCU_Microchip_ATmega:ATxmega32C4-M +MCU_Microchip_ATmega:ATxmega32D3-A +MCU_Microchip_ATmega:ATxmega32D3-M +MCU_Microchip_ATmega:ATxmega32D4-A +MCU_Microchip_ATmega:ATxmega32D4-C +MCU_Microchip_ATmega:ATxmega32D4-M +MCU_Microchip_ATmega:ATxmega32E5-A +MCU_Microchip_ATmega:ATxmega32E5-M +MCU_Microchip_ATmega:ATxmega32E5-M4 +MCU_Microchip_ATmega:ATxmega384C3-A +MCU_Microchip_ATmega:ATxmega384C3-M +MCU_Microchip_ATmega:ATxmega384D3-A +MCU_Microchip_ATmega:ATxmega384D3-M +MCU_Microchip_ATmega:ATxmega64A1-A +MCU_Microchip_ATmega:ATxmega64A1-C +MCU_Microchip_ATmega:ATxmega64A1-C7 +MCU_Microchip_ATmega:ATxmega64A1U-A +MCU_Microchip_ATmega:ATxmega64A1U-C +MCU_Microchip_ATmega:ATxmega64A1U-C7 +MCU_Microchip_ATmega:ATxmega64A3-A +MCU_Microchip_ATmega:ATxmega64A3-M +MCU_Microchip_ATmega:ATxmega64A3U-A +MCU_Microchip_ATmega:ATxmega64A3U-M +MCU_Microchip_ATmega:ATxmega64A4U-A +MCU_Microchip_ATmega:ATxmega64A4U-C +MCU_Microchip_ATmega:ATxmega64A4U-M +MCU_Microchip_ATmega:ATxmega64B1-A +MCU_Microchip_ATmega:ATxmega64B1-C +MCU_Microchip_ATmega:ATxmega64B3-A +MCU_Microchip_ATmega:ATxmega64B3-M +MCU_Microchip_ATmega:ATxmega64C3-A +MCU_Microchip_ATmega:ATxmega64C3-M +MCU_Microchip_ATmega:ATxmega64D3-A +MCU_Microchip_ATmega:ATxmega64D3-M +MCU_Microchip_ATmega:ATxmega64D4-A +MCU_Microchip_ATmega:ATxmega64D4-C +MCU_Microchip_ATmega:ATxmega64D4-M +MCU_Microchip_ATmega:ATxmega8E5-A +MCU_Microchip_ATmega:ATxmega8E5-M +MCU_Microchip_ATmega:ATxmega8E5-M4 +MCU_Microchip_ATtiny:ATtiny10-MA +MCU_Microchip_ATtiny:ATtiny10-TS +MCU_Microchip_ATtiny:ATtiny102-M +MCU_Microchip_ATtiny:ATtiny102-SS +MCU_Microchip_ATtiny:ATtiny104-SS +MCU_Microchip_ATtiny:ATtiny13-20M +MCU_Microchip_ATtiny:ATtiny13-20MM +MCU_Microchip_ATtiny:ATtiny13-20P +MCU_Microchip_ATtiny:ATtiny13-20S +MCU_Microchip_ATtiny:ATtiny13-20SS +MCU_Microchip_ATtiny:ATtiny13A-M +MCU_Microchip_ATtiny:ATtiny13A-MM +MCU_Microchip_ATtiny:ATtiny13A-P +MCU_Microchip_ATtiny:ATtiny13A-S +MCU_Microchip_ATtiny:ATtiny13A-SS +MCU_Microchip_ATtiny:ATtiny13V-10M +MCU_Microchip_ATtiny:ATtiny13V-10MM +MCU_Microchip_ATtiny:ATtiny13V-10P +MCU_Microchip_ATtiny:ATtiny13V-10S +MCU_Microchip_ATtiny:ATtiny13V-10SS +MCU_Microchip_ATtiny:ATtiny1604-SS +MCU_Microchip_ATtiny:ATtiny1606-M +MCU_Microchip_ATtiny:ATtiny1606-S +MCU_Microchip_ATtiny:ATtiny1607-M +MCU_Microchip_ATtiny:ATtiny1614-SS +MCU_Microchip_ATtiny:ATtiny1616-M +MCU_Microchip_ATtiny:ATtiny1616-S +MCU_Microchip_ATtiny:ATtiny1617-M +MCU_Microchip_ATtiny:ATtiny1624-SS +MCU_Microchip_ATtiny:ATtiny1624-X +MCU_Microchip_ATtiny:ATtiny1626-M +MCU_Microchip_ATtiny:ATtiny1626-S +MCU_Microchip_ATtiny:ATtiny1626-X +MCU_Microchip_ATtiny:ATtiny1627-M +MCU_Microchip_ATtiny:ATtiny1634-M +MCU_Microchip_ATtiny:ATtiny1634-S +MCU_Microchip_ATtiny:ATtiny167-M +MCU_Microchip_ATtiny:ATtiny167-S +MCU_Microchip_ATtiny:ATtiny167-X +MCU_Microchip_ATtiny:ATtiny20-CC +MCU_Microchip_ATtiny:ATtiny20-MM +MCU_Microchip_ATtiny:ATtiny20-SS +MCU_Microchip_ATtiny:ATtiny20-U +MCU_Microchip_ATtiny:ATtiny20-X +MCU_Microchip_ATtiny:ATtiny202-SS +MCU_Microchip_ATtiny:ATtiny204-SS +MCU_Microchip_ATtiny:ATtiny212-SS +MCU_Microchip_ATtiny:ATtiny214-SS +MCU_Microchip_ATtiny:ATtiny2313-20M +MCU_Microchip_ATtiny:ATtiny2313-20P +MCU_Microchip_ATtiny:ATtiny2313-20S +MCU_Microchip_ATtiny:ATtiny2313A-M +MCU_Microchip_ATtiny:ATtiny2313A-MM +MCU_Microchip_ATtiny:ATtiny2313A-P +MCU_Microchip_ATtiny:ATtiny2313A-S +MCU_Microchip_ATtiny:ATtiny2313V-10M +MCU_Microchip_ATtiny:ATtiny2313V-10P +MCU_Microchip_ATtiny:ATtiny2313V-10S +MCU_Microchip_ATtiny:ATtiny24-20M +MCU_Microchip_ATtiny:ATtiny24-20P +MCU_Microchip_ATtiny:ATtiny24-20SS +MCU_Microchip_ATtiny:ATtiny24A-CC +MCU_Microchip_ATtiny:ATtiny24A-M +MCU_Microchip_ATtiny:ATtiny24A-MM +MCU_Microchip_ATtiny:ATtiny24A-P +MCU_Microchip_ATtiny:ATtiny24A-SS +MCU_Microchip_ATtiny:ATtiny24V-10M +MCU_Microchip_ATtiny:ATtiny24V-10P +MCU_Microchip_ATtiny:ATtiny24V-10SS +MCU_Microchip_ATtiny:ATtiny25-20M +MCU_Microchip_ATtiny:ATtiny25-20P +MCU_Microchip_ATtiny:ATtiny25-20S +MCU_Microchip_ATtiny:ATtiny25-20SS +MCU_Microchip_ATtiny:ATtiny25V-10M +MCU_Microchip_ATtiny:ATtiny25V-10P +MCU_Microchip_ATtiny:ATtiny25V-10S +MCU_Microchip_ATtiny:ATtiny25V-10SS +MCU_Microchip_ATtiny:ATtiny26-16M +MCU_Microchip_ATtiny:ATtiny26-16P +MCU_Microchip_ATtiny:ATtiny26-16S +MCU_Microchip_ATtiny:ATtiny261A-M +MCU_Microchip_ATtiny:ATtiny261A-P +MCU_Microchip_ATtiny:ATtiny261A-S +MCU_Microchip_ATtiny:ATtiny261A-X +MCU_Microchip_ATtiny:ATtiny26L-8M +MCU_Microchip_ATtiny:ATtiny26L-8P +MCU_Microchip_ATtiny:ATtiny26L-8S +MCU_Microchip_ATtiny:ATtiny28L-4A +MCU_Microchip_ATtiny:ATtiny28L-4M +MCU_Microchip_ATtiny:ATtiny28L-4P +MCU_Microchip_ATtiny:ATtiny28V-1A +MCU_Microchip_ATtiny:ATtiny28V-1M +MCU_Microchip_ATtiny:ATtiny28V-1P +MCU_Microchip_ATtiny:ATtiny3216-M +MCU_Microchip_ATtiny:ATtiny3216-S +MCU_Microchip_ATtiny:ATtiny3217-M +MCU_Microchip_ATtiny:ATtiny3224-SS +MCU_Microchip_ATtiny:ATtiny3224-X +MCU_Microchip_ATtiny:ATtiny3226-M +MCU_Microchip_ATtiny:ATtiny3226-S +MCU_Microchip_ATtiny:ATtiny3226-X +MCU_Microchip_ATtiny:ATtiny3227-M +MCU_Microchip_ATtiny:ATtiny4-MA +MCU_Microchip_ATtiny:ATtiny4-TS +MCU_Microchip_ATtiny:ATtiny40-MM +MCU_Microchip_ATtiny:ATtiny40-S +MCU_Microchip_ATtiny:ATtiny40-X +MCU_Microchip_ATtiny:ATtiny402-SS +MCU_Microchip_ATtiny:ATtiny404-SS +MCU_Microchip_ATtiny:ATtiny406-M +MCU_Microchip_ATtiny:ATtiny406-S +MCU_Microchip_ATtiny:ATtiny412-SS +MCU_Microchip_ATtiny:ATtiny414-SS +MCU_Microchip_ATtiny:ATtiny416-M +MCU_Microchip_ATtiny:ATtiny416-S +MCU_Microchip_ATtiny:ATtiny417-M +MCU_Microchip_ATtiny:ATtiny424-SS +MCU_Microchip_ATtiny:ATtiny424-X +MCU_Microchip_ATtiny:ATtiny426-M +MCU_Microchip_ATtiny:ATtiny426-S +MCU_Microchip_ATtiny:ATtiny426-X +MCU_Microchip_ATtiny:ATtiny427-M +MCU_Microchip_ATtiny:ATtiny4313-M +MCU_Microchip_ATtiny:ATtiny4313-MM +MCU_Microchip_ATtiny:ATtiny4313-P +MCU_Microchip_ATtiny:ATtiny4313-S +MCU_Microchip_ATtiny:ATtiny43U-M +MCU_Microchip_ATtiny:ATtiny43U-S +MCU_Microchip_ATtiny:ATtiny44-20M +MCU_Microchip_ATtiny:ATtiny44-20P +MCU_Microchip_ATtiny:ATtiny44-20SS +MCU_Microchip_ATtiny:ATtiny441-M +MCU_Microchip_ATtiny:ATtiny441-MM +MCU_Microchip_ATtiny:ATtiny441-SS +MCU_Microchip_ATtiny:ATtiny44A-CC +MCU_Microchip_ATtiny:ATtiny44A-M +MCU_Microchip_ATtiny:ATtiny44A-MM +MCU_Microchip_ATtiny:ATtiny44A-P +MCU_Microchip_ATtiny:ATtiny44A-SS +MCU_Microchip_ATtiny:ATtiny44V-10M +MCU_Microchip_ATtiny:ATtiny44V-10P +MCU_Microchip_ATtiny:ATtiny44V-10SS +MCU_Microchip_ATtiny:ATtiny45-20M +MCU_Microchip_ATtiny:ATtiny45-20P +MCU_Microchip_ATtiny:ATtiny45-20S +MCU_Microchip_ATtiny:ATtiny45-20X +MCU_Microchip_ATtiny:ATtiny45V-10M +MCU_Microchip_ATtiny:ATtiny45V-10P +MCU_Microchip_ATtiny:ATtiny45V-10S +MCU_Microchip_ATtiny:ATtiny45V-10X +MCU_Microchip_ATtiny:ATtiny461-20M +MCU_Microchip_ATtiny:ATtiny461-20P +MCU_Microchip_ATtiny:ATtiny461-20S +MCU_Microchip_ATtiny:ATtiny461A-M +MCU_Microchip_ATtiny:ATtiny461A-P +MCU_Microchip_ATtiny:ATtiny461A-S +MCU_Microchip_ATtiny:ATtiny461A-X +MCU_Microchip_ATtiny:ATtiny461V-10M +MCU_Microchip_ATtiny:ATtiny461V-10P +MCU_Microchip_ATtiny:ATtiny461V-10S +MCU_Microchip_ATtiny:ATtiny48-A +MCU_Microchip_ATtiny:ATtiny48-CC +MCU_Microchip_ATtiny:ATtiny48-M +MCU_Microchip_ATtiny:ATtiny48-MM +MCU_Microchip_ATtiny:ATtiny48-P +MCU_Microchip_ATtiny:ATtiny5-MA +MCU_Microchip_ATtiny:ATtiny5-TS +MCU_Microchip_ATtiny:ATtiny804-SS +MCU_Microchip_ATtiny:ATtiny806-M +MCU_Microchip_ATtiny:ATtiny806-S +MCU_Microchip_ATtiny:ATtiny807-M +MCU_Microchip_ATtiny:ATtiny814-SS +MCU_Microchip_ATtiny:ATtiny816-M +MCU_Microchip_ATtiny:ATtiny816-S +MCU_Microchip_ATtiny:ATtiny817-M +MCU_Microchip_ATtiny:ATtiny824-SS +MCU_Microchip_ATtiny:ATtiny824-X +MCU_Microchip_ATtiny:ATtiny826-M +MCU_Microchip_ATtiny:ATtiny826-S +MCU_Microchip_ATtiny:ATtiny826-X +MCU_Microchip_ATtiny:ATtiny827-M +MCU_Microchip_ATtiny:ATtiny828-A +MCU_Microchip_ATtiny:ATtiny828-M +MCU_Microchip_ATtiny:ATtiny84-20M +MCU_Microchip_ATtiny:ATtiny84-20P +MCU_Microchip_ATtiny:ATtiny84-20SS +MCU_Microchip_ATtiny:ATtiny841-M +MCU_Microchip_ATtiny:ATtiny841-MM +MCU_Microchip_ATtiny:ATtiny841-SS +MCU_Microchip_ATtiny:ATtiny84A-CC +MCU_Microchip_ATtiny:ATtiny84A-M +MCU_Microchip_ATtiny:ATtiny84A-MM +MCU_Microchip_ATtiny:ATtiny84A-P +MCU_Microchip_ATtiny:ATtiny84A-SS +MCU_Microchip_ATtiny:ATtiny84V-10M +MCU_Microchip_ATtiny:ATtiny84V-10P +MCU_Microchip_ATtiny:ATtiny84V-10SS +MCU_Microchip_ATtiny:ATtiny85-20M +MCU_Microchip_ATtiny:ATtiny85-20P +MCU_Microchip_ATtiny:ATtiny85-20S +MCU_Microchip_ATtiny:ATtiny85V-10M +MCU_Microchip_ATtiny:ATtiny85V-10P +MCU_Microchip_ATtiny:ATtiny85V-10S +MCU_Microchip_ATtiny:ATtiny861-20M +MCU_Microchip_ATtiny:ATtiny861-20P +MCU_Microchip_ATtiny:ATtiny861-20S +MCU_Microchip_ATtiny:ATtiny861A-M +MCU_Microchip_ATtiny:ATtiny861A-P +MCU_Microchip_ATtiny:ATtiny861A-S +MCU_Microchip_ATtiny:ATtiny861A-X +MCU_Microchip_ATtiny:ATtiny861V-10M +MCU_Microchip_ATtiny:ATtiny861V-10P +MCU_Microchip_ATtiny:ATtiny861V-10S +MCU_Microchip_ATtiny:ATtiny87-M +MCU_Microchip_ATtiny:ATtiny87-S +MCU_Microchip_ATtiny:ATtiny87-X +MCU_Microchip_ATtiny:ATtiny88-A +MCU_Microchip_ATtiny:ATtiny88-CC +MCU_Microchip_ATtiny:ATtiny88-M +MCU_Microchip_ATtiny:ATtiny88-MM +MCU_Microchip_ATtiny:ATtiny88-P +MCU_Microchip_ATtiny:ATtiny9-MA +MCU_Microchip_ATtiny:ATtiny9-TS +MCU_Microchip_AVR:AT90CAN128-16A +MCU_Microchip_AVR:AT90CAN128-16M +MCU_Microchip_AVR:AT90CAN32-16A +MCU_Microchip_AVR:AT90CAN32-16M +MCU_Microchip_AVR:AT90CAN64-16A +MCU_Microchip_AVR:AT90CAN64-16M +MCU_Microchip_AVR:AT90PWM1-16M +MCU_Microchip_AVR:AT90PWM1-16S +MCU_Microchip_AVR:AT90USB1286-A +MCU_Microchip_AVR:AT90USB1286-M +MCU_Microchip_AVR:AT90USB1287-A +MCU_Microchip_AVR:AT90USB1287-M +MCU_Microchip_AVR:AT90USB162-16A +MCU_Microchip_AVR:AT90USB162-16M +MCU_Microchip_AVR:AT90USB646-A +MCU_Microchip_AVR:AT90USB646-M +MCU_Microchip_AVR:AT90USB647-A +MCU_Microchip_AVR:AT90USB647-M +MCU_Microchip_AVR:AT90USB82-16M +MCU_Microchip_AVR_Dx:AVR128DA28x-xSO +MCU_Microchip_AVR_Dx:AVR128DA28x-xSP +MCU_Microchip_AVR_Dx:AVR128DA28x-xSS +MCU_Microchip_AVR_Dx:AVR128DA32x-xPT +MCU_Microchip_AVR_Dx:AVR128DA32x-xRXB +MCU_Microchip_AVR_Dx:AVR128DA48x-x6LX +MCU_Microchip_AVR_Dx:AVR128DA48x-xPT +MCU_Microchip_AVR_Dx:AVR128DA64x-xMR +MCU_Microchip_AVR_Dx:AVR128DA64x-xPT +MCU_Microchip_AVR_Dx:AVR128DB28x-xSO +MCU_Microchip_AVR_Dx:AVR128DB28x-xSP +MCU_Microchip_AVR_Dx:AVR128DB28x-xSS +MCU_Microchip_AVR_Dx:AVR128DB32x-xPT +MCU_Microchip_AVR_Dx:AVR128DB32x-xRXB +MCU_Microchip_AVR_Dx:AVR128DB48x-x6LX +MCU_Microchip_AVR_Dx:AVR128DB48x-xPT +MCU_Microchip_AVR_Dx:AVR128DB64x-xMR +MCU_Microchip_AVR_Dx:AVR128DB64x-xPT +MCU_Microchip_AVR_Dx:AVR32DA28x-xSO +MCU_Microchip_AVR_Dx:AVR32DA28x-xSP +MCU_Microchip_AVR_Dx:AVR32DA28x-xSS +MCU_Microchip_AVR_Dx:AVR32DA32x-xPT +MCU_Microchip_AVR_Dx:AVR32DA32x-xRXB +MCU_Microchip_AVR_Dx:AVR32DA48x-x6LX +MCU_Microchip_AVR_Dx:AVR32DA48x-xPT +MCU_Microchip_AVR_Dx:AVR32DB28x-xSO +MCU_Microchip_AVR_Dx:AVR32DB28x-xSP +MCU_Microchip_AVR_Dx:AVR32DB28x-xSS +MCU_Microchip_AVR_Dx:AVR32DB32x-xPT +MCU_Microchip_AVR_Dx:AVR32DB32x-xRXB +MCU_Microchip_AVR_Dx:AVR32DB48x-x6LX +MCU_Microchip_AVR_Dx:AVR32DB48x-xPT +MCU_Microchip_AVR_Dx:AVR64DA28x-xSO +MCU_Microchip_AVR_Dx:AVR64DA28x-xSP +MCU_Microchip_AVR_Dx:AVR64DA28x-xSS +MCU_Microchip_AVR_Dx:AVR64DA32x-xPT +MCU_Microchip_AVR_Dx:AVR64DA32x-xRXB +MCU_Microchip_AVR_Dx:AVR64DA48x-x6LX +MCU_Microchip_AVR_Dx:AVR64DA48x-xPT +MCU_Microchip_AVR_Dx:AVR64DA64x-xMR +MCU_Microchip_AVR_Dx:AVR64DA64x-xPT +MCU_Microchip_AVR_Dx:AVR64DB28x-xSO +MCU_Microchip_AVR_Dx:AVR64DB28x-xSP +MCU_Microchip_AVR_Dx:AVR64DB28x-xSS +MCU_Microchip_AVR_Dx:AVR64DB32x-xPT +MCU_Microchip_AVR_Dx:AVR64DB32x-xRXB +MCU_Microchip_AVR_Dx:AVR64DB48x-x6LX +MCU_Microchip_AVR_Dx:AVR64DB48x-xPT +MCU_Microchip_AVR_Dx:AVR64DB64x-xMR +MCU_Microchip_AVR_Dx:AVR64DB64x-xPT +MCU_Microchip_PIC10:PIC10F200-IMC +MCU_Microchip_PIC10:PIC10F200-IOT +MCU_Microchip_PIC10:PIC10F200-IP +MCU_Microchip_PIC10:PIC10F202-IMC +MCU_Microchip_PIC10:PIC10F202-IOT +MCU_Microchip_PIC10:PIC10F202-IP +MCU_Microchip_PIC10:PIC10F204-IMC +MCU_Microchip_PIC10:PIC10F204-IOT +MCU_Microchip_PIC10:PIC10F204-IP +MCU_Microchip_PIC10:PIC10F206-IMC +MCU_Microchip_PIC10:PIC10F206-IOT +MCU_Microchip_PIC10:PIC10F206-IP +MCU_Microchip_PIC10:PIC10F220-IMC +MCU_Microchip_PIC10:PIC10F220-IOT +MCU_Microchip_PIC10:PIC10F220-IP +MCU_Microchip_PIC10:PIC10F222-IMC +MCU_Microchip_PIC10:PIC10F222-IOT +MCU_Microchip_PIC10:PIC10F222-IP +MCU_Microchip_PIC10:PIC10F320-IMC +MCU_Microchip_PIC10:PIC10F320-IOT +MCU_Microchip_PIC10:PIC10F320-IP +MCU_Microchip_PIC10:PIC10F322-IMC +MCU_Microchip_PIC10:PIC10F322-IOT +MCU_Microchip_PIC10:PIC10F322-IP +MCU_Microchip_PIC12:PIC12C508-xJW +MCU_Microchip_PIC12:PIC12C508-xP +MCU_Microchip_PIC12:PIC12C508-xSM +MCU_Microchip_PIC12:PIC12C508A-xJW +MCU_Microchip_PIC12:PIC12C508A-xP +MCU_Microchip_PIC12:PIC12C508A-xSM +MCU_Microchip_PIC12:PIC12C508A-xSN +MCU_Microchip_PIC12:PIC12C509-xJW +MCU_Microchip_PIC12:PIC12C509-xP +MCU_Microchip_PIC12:PIC12C509-xSM +MCU_Microchip_PIC12:PIC12C509A-xJW +MCU_Microchip_PIC12:PIC12C509A-xP +MCU_Microchip_PIC12:PIC12C509A-xSM +MCU_Microchip_PIC12:PIC12C509A-xSN +MCU_Microchip_PIC12:PIC12C671-xJW +MCU_Microchip_PIC12:PIC12C671-xP +MCU_Microchip_PIC12:PIC12C671-xSN +MCU_Microchip_PIC12:PIC12C672-xJW +MCU_Microchip_PIC12:PIC12C672-xP +MCU_Microchip_PIC12:PIC12C672-xSN +MCU_Microchip_PIC12:PIC12CE518-xJW +MCU_Microchip_PIC12:PIC12CE518-xP +MCU_Microchip_PIC12:PIC12CE518-xSM +MCU_Microchip_PIC12:PIC12CE518-xSN +MCU_Microchip_PIC12:PIC12CE519-xJW +MCU_Microchip_PIC12:PIC12CE519-xP +MCU_Microchip_PIC12:PIC12CE519-xSM +MCU_Microchip_PIC12:PIC12CE519-xSN +MCU_Microchip_PIC12:PIC12CE673-xJW +MCU_Microchip_PIC12:PIC12CE673-xP +MCU_Microchip_PIC12:PIC12CE674-xJW +MCU_Microchip_PIC12:PIC12CE674-xP +MCU_Microchip_PIC12:PIC12CR509A-xP +MCU_Microchip_PIC12:PIC12CR509A-xSM +MCU_Microchip_PIC12:PIC12CR509A-xSN +MCU_Microchip_PIC12:PIC12F1501-xMC +MCU_Microchip_PIC12:PIC12F1501-xMS +MCU_Microchip_PIC12:PIC12F1501-xP +MCU_Microchip_PIC12:PIC12F1501-xSN +MCU_Microchip_PIC12:PIC12F1822-xMC +MCU_Microchip_PIC12:PIC12F1822-xP +MCU_Microchip_PIC12:PIC12F1822-xSN +MCU_Microchip_PIC12:PIC12F1840-xMC +MCU_Microchip_PIC12:PIC12F1840-xP +MCU_Microchip_PIC12:PIC12F1840-xSN +MCU_Microchip_PIC12:PIC12F508-xMC +MCU_Microchip_PIC12:PIC12F508-xMS +MCU_Microchip_PIC12:PIC12F508-xP +MCU_Microchip_PIC12:PIC12F508-xSN +MCU_Microchip_PIC12:PIC12F509-xMC +MCU_Microchip_PIC12:PIC12F509-xMS +MCU_Microchip_PIC12:PIC12F509-xP +MCU_Microchip_PIC12:PIC12F509-xSN +MCU_Microchip_PIC12:PIC12F510-xMC +MCU_Microchip_PIC12:PIC12F510-xMS +MCU_Microchip_PIC12:PIC12F510-xP +MCU_Microchip_PIC12:PIC12F510-xSN +MCU_Microchip_PIC12:PIC12F519-xMC +MCU_Microchip_PIC12:PIC12F519-xMS +MCU_Microchip_PIC12:PIC12F519-xP +MCU_Microchip_PIC12:PIC12F519-xSN +MCU_Microchip_PIC12:PIC12F609-xMC +MCU_Microchip_PIC12:PIC12F609-xMS +MCU_Microchip_PIC12:PIC12F609-xP +MCU_Microchip_PIC12:PIC12F609-xSN +MCU_Microchip_PIC12:PIC12F615-xMC +MCU_Microchip_PIC12:PIC12F615-xMS +MCU_Microchip_PIC12:PIC12F615-xP +MCU_Microchip_PIC12:PIC12F615-xSN +MCU_Microchip_PIC12:PIC12F617-xMC +MCU_Microchip_PIC12:PIC12F617-xMS +MCU_Microchip_PIC12:PIC12F617-xP +MCU_Microchip_PIC12:PIC12F617-xSN +MCU_Microchip_PIC12:PIC12F629-xMC +MCU_Microchip_PIC12:PIC12F629-xMS +MCU_Microchip_PIC12:PIC12F629-xP +MCU_Microchip_PIC12:PIC12F629-xSN +MCU_Microchip_PIC12:PIC12F635-xMC +MCU_Microchip_PIC12:PIC12F635-xMS +MCU_Microchip_PIC12:PIC12F635-xP +MCU_Microchip_PIC12:PIC12F635-xSN +MCU_Microchip_PIC12:PIC12F675-xMC +MCU_Microchip_PIC12:PIC12F675-xMS +MCU_Microchip_PIC12:PIC12F675-xP +MCU_Microchip_PIC12:PIC12F675-xSN +MCU_Microchip_PIC12:PIC12F683-xMC +MCU_Microchip_PIC12:PIC12F683-xMS +MCU_Microchip_PIC12:PIC12F683-xP +MCU_Microchip_PIC12:PIC12F683-xSN +MCU_Microchip_PIC12:PIC12F752-xMC +MCU_Microchip_PIC12:PIC12F752-xP +MCU_Microchip_PIC12:PIC12F752-xSN +MCU_Microchip_PIC12:PIC12HV609-xMC +MCU_Microchip_PIC12:PIC12HV609-xMS +MCU_Microchip_PIC12:PIC12HV609-xP +MCU_Microchip_PIC12:PIC12HV609-xSN +MCU_Microchip_PIC12:PIC12HV615-xMC +MCU_Microchip_PIC12:PIC12HV615-xMS +MCU_Microchip_PIC12:PIC12HV615-xP +MCU_Microchip_PIC12:PIC12HV615-xSN +MCU_Microchip_PIC12:PIC12HV752-xMC +MCU_Microchip_PIC12:PIC12HV752-xP +MCU_Microchip_PIC12:PIC12HV752-xSN +MCU_Microchip_PIC12:PIC12LF1501-xMC +MCU_Microchip_PIC12:PIC12LF1501-xMS +MCU_Microchip_PIC12:PIC12LF1501-xP +MCU_Microchip_PIC12:PIC12LF1501-xSN +MCU_Microchip_PIC12:PIC12LF1822-xMC +MCU_Microchip_PIC12:PIC12LF1822-xP +MCU_Microchip_PIC12:PIC12LF1822-xSN +MCU_Microchip_PIC12:PIC12LF1840-xMC +MCU_Microchip_PIC12:PIC12LF1840-xP +MCU_Microchip_PIC12:PIC12LF1840-xSN +MCU_Microchip_PIC12:PIC12LF1840T48-xST +MCU_Microchip_PIC16:PIC16C505-IP +MCU_Microchip_PIC16:PIC16C505-ISL +MCU_Microchip_PIC16:PIC16C505-IST +MCU_Microchip_PIC16:PIC16F1454-IML +MCU_Microchip_PIC16:PIC16F1454-IP +MCU_Microchip_PIC16:PIC16F1454-ISL +MCU_Microchip_PIC16:PIC16F1454-ISS +MCU_Microchip_PIC16:PIC16F1454-IST +MCU_Microchip_PIC16:PIC16F1455-IML +MCU_Microchip_PIC16:PIC16F1455-IP +MCU_Microchip_PIC16:PIC16F1455-ISL +MCU_Microchip_PIC16:PIC16F1455-ISS +MCU_Microchip_PIC16:PIC16F1455-IST +MCU_Microchip_PIC16:PIC16F1459-IML +MCU_Microchip_PIC16:PIC16F1459-IP +MCU_Microchip_PIC16:PIC16F1459-ISO +MCU_Microchip_PIC16:PIC16F1459-ISS +MCU_Microchip_PIC16:PIC16F1459-IST +MCU_Microchip_PIC16:PIC16F1503-IMG +MCU_Microchip_PIC16:PIC16F1503-IP +MCU_Microchip_PIC16:PIC16F1503-ISL +MCU_Microchip_PIC16:PIC16F1503-IST +MCU_Microchip_PIC16:PIC16F1507-IML +MCU_Microchip_PIC16:PIC16F1507-IP +MCU_Microchip_PIC16:PIC16F1507-ISO +MCU_Microchip_PIC16:PIC16F1507-ISS +MCU_Microchip_PIC16:PIC16F1508-IML +MCU_Microchip_PIC16:PIC16F1508-IP +MCU_Microchip_PIC16:PIC16F1508-ISO +MCU_Microchip_PIC16:PIC16F1508-ISS +MCU_Microchip_PIC16:PIC16F1509-IML +MCU_Microchip_PIC16:PIC16F1509-IP +MCU_Microchip_PIC16:PIC16F1509-ISO +MCU_Microchip_PIC16:PIC16F1509-ISS +MCU_Microchip_PIC16:PIC16F1512-IMV +MCU_Microchip_PIC16:PIC16F1512-ISO +MCU_Microchip_PIC16:PIC16F1512-ISP +MCU_Microchip_PIC16:PIC16F1512-ISS +MCU_Microchip_PIC16:PIC16F1513-IMV +MCU_Microchip_PIC16:PIC16F1513-ISO +MCU_Microchip_PIC16:PIC16F1513-ISP +MCU_Microchip_PIC16:PIC16F1513-ISS +MCU_Microchip_PIC16:PIC16F1516-IMV +MCU_Microchip_PIC16:PIC16F1516-ISO +MCU_Microchip_PIC16:PIC16F1516-ISP +MCU_Microchip_PIC16:PIC16F1516-ISS +MCU_Microchip_PIC16:PIC16F1517-IMV +MCU_Microchip_PIC16:PIC16F1517-IP +MCU_Microchip_PIC16:PIC16F1517-IPT +MCU_Microchip_PIC16:PIC16F1518-IMV +MCU_Microchip_PIC16:PIC16F1518-ISO +MCU_Microchip_PIC16:PIC16F1518-ISP +MCU_Microchip_PIC16:PIC16F1518-ISS +MCU_Microchip_PIC16:PIC16F1519-IMV +MCU_Microchip_PIC16:PIC16F1519-IP +MCU_Microchip_PIC16:PIC16F1519-IPT +MCU_Microchip_PIC16:PIC16F1526-IMR +MCU_Microchip_PIC16:PIC16F1526-IPT +MCU_Microchip_PIC16:PIC16F1527-IMR +MCU_Microchip_PIC16:PIC16F1527-IPT +MCU_Microchip_PIC16:PIC16F15323-xSL +MCU_Microchip_PIC16:PIC16F15356-xML +MCU_Microchip_PIC16:PIC16F15356-xMV +MCU_Microchip_PIC16:PIC16F15356-xSO +MCU_Microchip_PIC16:PIC16F15356-xSP +MCU_Microchip_PIC16:PIC16F15356-xSS +MCU_Microchip_PIC16:PIC16F15375-xML +MCU_Microchip_PIC16:PIC16F15375-xMV +MCU_Microchip_PIC16:PIC16F15375-xP +MCU_Microchip_PIC16:PIC16F15375-xPT +MCU_Microchip_PIC16:PIC16F15376-xML +MCU_Microchip_PIC16:PIC16F15376-xMV +MCU_Microchip_PIC16:PIC16F15376-xP +MCU_Microchip_PIC16:PIC16F15376-xPT +MCU_Microchip_PIC16:PIC16F15385-xMV +MCU_Microchip_PIC16:PIC16F15385-xPT +MCU_Microchip_PIC16:PIC16F15386-xMV +MCU_Microchip_PIC16:PIC16F15386-xPT +MCU_Microchip_PIC16:PIC16F1619-xGZ +MCU_Microchip_PIC16:PIC16F1619-xML +MCU_Microchip_PIC16:PIC16F1619-xP +MCU_Microchip_PIC16:PIC16F1619-xSO +MCU_Microchip_PIC16:PIC16F1619-xSS +MCU_Microchip_PIC16:PIC16F1786-xML +MCU_Microchip_PIC16:PIC16F1786-xP +MCU_Microchip_PIC16:PIC16F1786-xSO +MCU_Microchip_PIC16:PIC16F1786-xSP +MCU_Microchip_PIC16:PIC16F1786-xSS +MCU_Microchip_PIC16:PIC16F1829-IML +MCU_Microchip_PIC16:PIC16F1829-IP +MCU_Microchip_PIC16:PIC16F1829-ISL +MCU_Microchip_PIC16:PIC16F1829-ISO +MCU_Microchip_PIC16:PIC16F1829-ISS +MCU_Microchip_PIC16:PIC16F1829-IST +MCU_Microchip_PIC16:PIC16F1829LIN-ESS +MCU_Microchip_PIC16:PIC16F18324-xSL +MCU_Microchip_PIC16:PIC16F18325-ISL +MCU_Microchip_PIC16:PIC16F18325-xGZ +MCU_Microchip_PIC16:PIC16F18325-xJQ +MCU_Microchip_PIC16:PIC16F18344-GZ +MCU_Microchip_PIC16:PIC16F18344-P +MCU_Microchip_PIC16:PIC16F18344-SO +MCU_Microchip_PIC16:PIC16F18344-SS +MCU_Microchip_PIC16:PIC16F18344-xSL +MCU_Microchip_PIC16:PIC16F18346-GZ +MCU_Microchip_PIC16:PIC16F18346-P +MCU_Microchip_PIC16:PIC16F18346-SO +MCU_Microchip_PIC16:PIC16F18346-SS_0 +MCU_Microchip_PIC16:PIC16F18854-xSO +MCU_Microchip_PIC16:PIC16F18855-xMV +MCU_Microchip_PIC16:PIC16F18855-xSO +MCU_Microchip_PIC16:PIC16F19195-x5LX +MCU_Microchip_PIC16:PIC16F19195-xMR +MCU_Microchip_PIC16:PIC16F19195-xPT +MCU_Microchip_PIC16:PIC16F19196-x5LX +MCU_Microchip_PIC16:PIC16F19196-xMR +MCU_Microchip_PIC16:PIC16F19196-xPT +MCU_Microchip_PIC16:PIC16F19197-x5LX +MCU_Microchip_PIC16:PIC16F19197-xMR +MCU_Microchip_PIC16:PIC16F19197-xPT +MCU_Microchip_PIC16:PIC16F1934-IML +MCU_Microchip_PIC16:PIC16F1934-IPT +MCU_Microchip_PIC16:PIC16F1937-IML +MCU_Microchip_PIC16:PIC16F1937-IPT +MCU_Microchip_PIC16:PIC16F1939-IML +MCU_Microchip_PIC16:PIC16F1939-IPT +MCU_Microchip_PIC16:PIC16F505-IMG +MCU_Microchip_PIC16:PIC16F505-IP +MCU_Microchip_PIC16:PIC16F505-ISL +MCU_Microchip_PIC16:PIC16F505-IST +MCU_Microchip_PIC16:PIC16F54-IP +MCU_Microchip_PIC16:PIC16F54-ISO +MCU_Microchip_PIC16:PIC16F54-ISS +MCU_Microchip_PIC16:PIC16F610-IML +MCU_Microchip_PIC16:PIC16F610-IP +MCU_Microchip_PIC16:PIC16F616-IML +MCU_Microchip_PIC16:PIC16F616-IP +MCU_Microchip_PIC16:PIC16F627-xxIP +MCU_Microchip_PIC16:PIC16F627-xxISO +MCU_Microchip_PIC16:PIC16F627-xxISS +MCU_Microchip_PIC16:PIC16F627A-IP +MCU_Microchip_PIC16:PIC16F627A-ISO +MCU_Microchip_PIC16:PIC16F627A-ISS +MCU_Microchip_PIC16:PIC16F628-xxIP +MCU_Microchip_PIC16:PIC16F628-xxISO +MCU_Microchip_PIC16:PIC16F628-xxISS +MCU_Microchip_PIC16:PIC16F628A-IP +MCU_Microchip_PIC16:PIC16F628A-ISO +MCU_Microchip_PIC16:PIC16F628A-ISS +MCU_Microchip_PIC16:PIC16F631-IP +MCU_Microchip_PIC16:PIC16F631-ISO +MCU_Microchip_PIC16:PIC16F631-ISS +MCU_Microchip_PIC16:PIC16F648A-IP +MCU_Microchip_PIC16:PIC16F648A-ISO +MCU_Microchip_PIC16:PIC16F648A-ISS +MCU_Microchip_PIC16:PIC16F677-IP +MCU_Microchip_PIC16:PIC16F677-ISO +MCU_Microchip_PIC16:PIC16F677-ISS +MCU_Microchip_PIC16:PIC16F684-IML +MCU_Microchip_PIC16:PIC16F684-IP +MCU_Microchip_PIC16:PIC16F685-IP +MCU_Microchip_PIC16:PIC16F685-ISO +MCU_Microchip_PIC16:PIC16F685-ISS +MCU_Microchip_PIC16:PIC16F687-IP +MCU_Microchip_PIC16:PIC16F687-ISO +MCU_Microchip_PIC16:PIC16F687-ISS +MCU_Microchip_PIC16:PIC16F689-IP +MCU_Microchip_PIC16:PIC16F689-ISO +MCU_Microchip_PIC16:PIC16F689-ISS +MCU_Microchip_PIC16:PIC16F690-IP +MCU_Microchip_PIC16:PIC16F690-ISO +MCU_Microchip_PIC16:PIC16F690-ISS +MCU_Microchip_PIC16:PIC16F716-IP +MCU_Microchip_PIC16:PIC16F716-ISO +MCU_Microchip_PIC16:PIC16F716-ISS +MCU_Microchip_PIC16:PIC16F73-IML +MCU_Microchip_PIC16:PIC16F73-ISO +MCU_Microchip_PIC16:PIC16F73-ISP +MCU_Microchip_PIC16:PIC16F73-ISS +MCU_Microchip_PIC16:PIC16F74-IP +MCU_Microchip_PIC16:PIC16F76-IML +MCU_Microchip_PIC16:PIC16F76-ISO +MCU_Microchip_PIC16:PIC16F76-ISP +MCU_Microchip_PIC16:PIC16F76-ISS +MCU_Microchip_PIC16:PIC16F77-IP +MCU_Microchip_PIC16:PIC16F818-IML +MCU_Microchip_PIC16:PIC16F818-IP +MCU_Microchip_PIC16:PIC16F818-ISO +MCU_Microchip_PIC16:PIC16F818-ISS +MCU_Microchip_PIC16:PIC16F819-IML +MCU_Microchip_PIC16:PIC16F819-IP +MCU_Microchip_PIC16:PIC16F819-ISO +MCU_Microchip_PIC16:PIC16F819-ISS +MCU_Microchip_PIC16:PIC16F83-XXP +MCU_Microchip_PIC16:PIC16F83-XXSO +MCU_Microchip_PIC16:PIC16F84-XXP +MCU_Microchip_PIC16:PIC16F84-XXSO +MCU_Microchip_PIC16:PIC16F84A-XXP +MCU_Microchip_PIC16:PIC16F84A-XXSO +MCU_Microchip_PIC16:PIC16F84A-XXSS +MCU_Microchip_PIC16:PIC16F870-ISO +MCU_Microchip_PIC16:PIC16F870-ISP +MCU_Microchip_PIC16:PIC16F870-ISS +MCU_Microchip_PIC16:PIC16F871-IL +MCU_Microchip_PIC16:PIC16F871-IP +MCU_Microchip_PIC16:PIC16F871-IPT +MCU_Microchip_PIC16:PIC16F873-XXISO +MCU_Microchip_PIC16:PIC16F873-XXISP +MCU_Microchip_PIC16:PIC16F874-XXIP +MCU_Microchip_PIC16:PIC16F874A-IP +MCU_Microchip_PIC16:PIC16F874A-IPT +MCU_Microchip_PIC16:PIC16F876-XXISO +MCU_Microchip_PIC16:PIC16F876-XXISP +MCU_Microchip_PIC16:PIC16F877-XXIP +MCU_Microchip_PIC16:PIC16F877A-IP +MCU_Microchip_PIC16:PIC16F877A-IPT +MCU_Microchip_PIC16:PIC16F88-IML +MCU_Microchip_PIC16:PIC16F88-IP +MCU_Microchip_PIC16:PIC16F882-IP +MCU_Microchip_PIC16:PIC16F883-IP +MCU_Microchip_PIC16:PIC16F884-IP +MCU_Microchip_PIC16:PIC16F886-IP +MCU_Microchip_PIC16:PIC16F887-IP +MCU_Microchip_PIC16:PIC16LF15356-xML +MCU_Microchip_PIC16:PIC16LF15356-xMV +MCU_Microchip_PIC16:PIC16LF15356-xSO +MCU_Microchip_PIC16:PIC16LF15356-xSP +MCU_Microchip_PIC16:PIC16LF15356-xSS +MCU_Microchip_PIC16:PIC16LF15375-xML +MCU_Microchip_PIC16:PIC16LF15375-xMV +MCU_Microchip_PIC16:PIC16LF15375-xP +MCU_Microchip_PIC16:PIC16LF15375-xPT +MCU_Microchip_PIC16:PIC16LF15376-xML +MCU_Microchip_PIC16:PIC16LF15376-xMV +MCU_Microchip_PIC16:PIC16LF15376-xP +MCU_Microchip_PIC16:PIC16LF15376-xPT +MCU_Microchip_PIC16:PIC16LF15385-xMV +MCU_Microchip_PIC16:PIC16LF15385-xPT +MCU_Microchip_PIC16:PIC16LF15386-xMV +MCU_Microchip_PIC16:PIC16LF15386-xPT +MCU_Microchip_PIC16:PIC16LF1786-xML +MCU_Microchip_PIC16:PIC16LF1786-xP +MCU_Microchip_PIC16:PIC16LF1786-xSO +MCU_Microchip_PIC16:PIC16LF1786-xSP +MCU_Microchip_PIC16:PIC16LF1786-xSS +MCU_Microchip_PIC16:PIC16LF18325-ISL +MCU_Microchip_PIC16:PIC16LF18325-xGZ +MCU_Microchip_PIC16:PIC16LF18325-xJQ +MCU_Microchip_PIC16:PIC16LF1904-IP +MCU_Microchip_PIC16:PIC16LF1907-IP +MCU_Microchip_PIC16:PIC16LF19195-x5LX +MCU_Microchip_PIC16:PIC16LF19195-xMR +MCU_Microchip_PIC16:PIC16LF19195-xPT +MCU_Microchip_PIC16:PIC16LF19196-x5LX +MCU_Microchip_PIC16:PIC16LF19196-xMR +MCU_Microchip_PIC16:PIC16LF19196-xPT +MCU_Microchip_PIC16:PIC16LF19197-x5LX +MCU_Microchip_PIC16:PIC16LF19197-xMR +MCU_Microchip_PIC16:PIC16LF19197-xPT +MCU_Microchip_PIC18:PIC18F1220-SO +MCU_Microchip_PIC18:PIC18F1320-SO +MCU_Microchip_PIC18:PIC18F13K50-EP +MCU_Microchip_PIC18:PIC18F13K50-ESO +MCU_Microchip_PIC18:PIC18F13K50-ESS +MCU_Microchip_PIC18:PIC18F14K50-EP +MCU_Microchip_PIC18:PIC18F14K50-ESO +MCU_Microchip_PIC18:PIC18F14K50-ESS +MCU_Microchip_PIC18:PIC18F2331-IML +MCU_Microchip_PIC18:PIC18F2331-ISO +MCU_Microchip_PIC18:PIC18F2331-ISP +MCU_Microchip_PIC18:PIC18F23K20_ISS +MCU_Microchip_PIC18:PIC18F23K22-xSO +MCU_Microchip_PIC18:PIC18F23K22-xSP +MCU_Microchip_PIC18:PIC18F2420-xML +MCU_Microchip_PIC18:PIC18F2420-xSP +MCU_Microchip_PIC18:PIC18F2431-IML +MCU_Microchip_PIC18:PIC18F2431-ISO +MCU_Microchip_PIC18:PIC18F2431-ISP +MCU_Microchip_PIC18:PIC18F2450-IML +MCU_Microchip_PIC18:PIC18F2450-ISO +MCU_Microchip_PIC18:PIC18F2450-ISP +MCU_Microchip_PIC18:PIC18F2455-ISO +MCU_Microchip_PIC18:PIC18F2455-ISP +MCU_Microchip_PIC18:PIC18F24K20_ISS +MCU_Microchip_PIC18:PIC18F24K22-xSO +MCU_Microchip_PIC18:PIC18F24K22-xSP +MCU_Microchip_PIC18:PIC18F24K50-xML +MCU_Microchip_PIC18:PIC18F24K50-xSO +MCU_Microchip_PIC18:PIC18F24K50-xSP +MCU_Microchip_PIC18:PIC18F24K50-xSS +MCU_Microchip_PIC18:PIC18F2520-xML +MCU_Microchip_PIC18:PIC18F2520-xSP +MCU_Microchip_PIC18:PIC18F2550-ISO +MCU_Microchip_PIC18:PIC18F2550-ISP +MCU_Microchip_PIC18:PIC18F25K20_ISS +MCU_Microchip_PIC18:PIC18F25K22-xSO +MCU_Microchip_PIC18:PIC18F25K22-xSP +MCU_Microchip_PIC18:PIC18F25K50-xML +MCU_Microchip_PIC18:PIC18F25K50-xSO +MCU_Microchip_PIC18:PIC18F25K50-xSP +MCU_Microchip_PIC18:PIC18F25K50-xSS +MCU_Microchip_PIC18:PIC18F25K80_IML +MCU_Microchip_PIC18:PIC18F25K80_ISS +MCU_Microchip_PIC18:PIC18F25K83-xSP +MCU_Microchip_PIC18:PIC18F26K20_ISS +MCU_Microchip_PIC18:PIC18F26K22-xSO +MCU_Microchip_PIC18:PIC18F26K22-xSP +MCU_Microchip_PIC18:PIC18F26K80_IML +MCU_Microchip_PIC18:PIC18F26K80_ISS +MCU_Microchip_PIC18:PIC18F26K83-xSP +MCU_Microchip_PIC18:PIC18F27J53_ISS +MCU_Microchip_PIC18:PIC18F4331-IML +MCU_Microchip_PIC18:PIC18F4331-IP +MCU_Microchip_PIC18:PIC18F4331-IPT +MCU_Microchip_PIC18:PIC18F442-IP +MCU_Microchip_PIC18:PIC18F442-IPT +MCU_Microchip_PIC18:PIC18F4420-xP +MCU_Microchip_PIC18:PIC18F4431-IML +MCU_Microchip_PIC18:PIC18F4431-IP +MCU_Microchip_PIC18:PIC18F4431-IPT +MCU_Microchip_PIC18:PIC18F4450-IML +MCU_Microchip_PIC18:PIC18F4450-IP +MCU_Microchip_PIC18:PIC18F4450-IPT +MCU_Microchip_PIC18:PIC18F4455-IML +MCU_Microchip_PIC18:PIC18F4455-IP +MCU_Microchip_PIC18:PIC18F4455-IPT +MCU_Microchip_PIC18:PIC18F4458-IML +MCU_Microchip_PIC18:PIC18F4458-IP +MCU_Microchip_PIC18:PIC18F4458-IPT +MCU_Microchip_PIC18:PIC18F448-IP +MCU_Microchip_PIC18:PIC18F4480-IP +MCU_Microchip_PIC18:PIC18F44J10-IP +MCU_Microchip_PIC18:PIC18F452-IP +MCU_Microchip_PIC18:PIC18F452-IPT +MCU_Microchip_PIC18:PIC18F4520-xP +MCU_Microchip_PIC18:PIC18F4550-IML +MCU_Microchip_PIC18:PIC18F4550-IP +MCU_Microchip_PIC18:PIC18F4550-IPT +MCU_Microchip_PIC18:PIC18F4553-IML +MCU_Microchip_PIC18:PIC18F4553-IP +MCU_Microchip_PIC18:PIC18F4553-IPT +MCU_Microchip_PIC18:PIC18F458-IP +MCU_Microchip_PIC18:PIC18F4580-IP +MCU_Microchip_PIC18:PIC18F45J10-IP +MCU_Microchip_PIC18:PIC18F45K50_QFP +MCU_Microchip_PIC18:PIC18F45K80-IML +MCU_Microchip_PIC18:PIC18F45K80-IPT +MCU_Microchip_PIC18:PIC18F46K22-xPT +MCU_Microchip_PIC18:PIC18F46K80-IML +MCU_Microchip_PIC18:PIC18F46K80-IPT +MCU_Microchip_PIC18:PIC18F66J60-IPT +MCU_Microchip_PIC18:PIC18F66J65-IPT +MCU_Microchip_PIC18:PIC18F67J60-IPT +MCU_Microchip_PIC18:PIC18F87K22-xPT +MCU_Microchip_PIC18:PIC18F96J60-IPF +MCU_Microchip_PIC18:PIC18F96J60-IPT +MCU_Microchip_PIC18:PIC18F96J65-IPF +MCU_Microchip_PIC18:PIC18F96J65-IPT +MCU_Microchip_PIC18:PIC18F97J60-IPF +MCU_Microchip_PIC18:PIC18F97J60-IPT +MCU_Microchip_PIC18:PIC18LF1220-SO +MCU_Microchip_PIC18:PIC18LF1320-SO +MCU_Microchip_PIC18:PIC18LF13K50-EP +MCU_Microchip_PIC18:PIC18LF13K50-ESO +MCU_Microchip_PIC18:PIC18LF13K50-ESS +MCU_Microchip_PIC18:PIC18LF14K50-EP +MCU_Microchip_PIC18:PIC18LF14K50-ESO +MCU_Microchip_PIC18:PIC18LF14K50-ESS +MCU_Microchip_PIC18:PIC18LF2331-IML +MCU_Microchip_PIC18:PIC18LF2331-ISO +MCU_Microchip_PIC18:PIC18LF2331-ISP +MCU_Microchip_PIC18:PIC18LF2431-IML +MCU_Microchip_PIC18:PIC18LF2431-ISO +MCU_Microchip_PIC18:PIC18LF2431-ISP +MCU_Microchip_PIC18:PIC18LF2450-IML +MCU_Microchip_PIC18:PIC18LF2450-ISO +MCU_Microchip_PIC18:PIC18LF2450-ISP +MCU_Microchip_PIC18:PIC18LF2455-ISO +MCU_Microchip_PIC18:PIC18LF2455-ISP +MCU_Microchip_PIC18:PIC18LF24K50-xML +MCU_Microchip_PIC18:PIC18LF24K50-xSO +MCU_Microchip_PIC18:PIC18LF24K50-xSP +MCU_Microchip_PIC18:PIC18LF24K50-xSS +MCU_Microchip_PIC18:PIC18LF2550-ISO +MCU_Microchip_PIC18:PIC18LF2550-ISP +MCU_Microchip_PIC18:PIC18LF25K50-xML +MCU_Microchip_PIC18:PIC18LF25K50-xSO +MCU_Microchip_PIC18:PIC18LF25K50-xSP +MCU_Microchip_PIC18:PIC18LF25K50-xSS +MCU_Microchip_PIC18:PIC18LF25K80_IML +MCU_Microchip_PIC18:PIC18LF25K80_ISS +MCU_Microchip_PIC18:PIC18LF25K83-xSP +MCU_Microchip_PIC18:PIC18LF26K80_IML +MCU_Microchip_PIC18:PIC18LF26K80_ISS +MCU_Microchip_PIC18:PIC18LF26K83-xSP +MCU_Microchip_PIC18:PIC18LF4331-IML +MCU_Microchip_PIC18:PIC18LF4331-IP +MCU_Microchip_PIC18:PIC18LF4331-IPT +MCU_Microchip_PIC18:PIC18LF442-IP +MCU_Microchip_PIC18:PIC18LF442-IPT +MCU_Microchip_PIC18:PIC18LF4431-IML +MCU_Microchip_PIC18:PIC18LF4431-IP +MCU_Microchip_PIC18:PIC18LF4431-IPT +MCU_Microchip_PIC18:PIC18LF4450-IML +MCU_Microchip_PIC18:PIC18LF4450-IP +MCU_Microchip_PIC18:PIC18LF4450-IPT +MCU_Microchip_PIC18:PIC18LF4455-IML +MCU_Microchip_PIC18:PIC18LF4455-IP +MCU_Microchip_PIC18:PIC18LF4455-IPT +MCU_Microchip_PIC18:PIC18LF4458-IML +MCU_Microchip_PIC18:PIC18LF4458-IP +MCU_Microchip_PIC18:PIC18LF4458-IPT +MCU_Microchip_PIC18:PIC18LF448-IP +MCU_Microchip_PIC18:PIC18LF4480-IP +MCU_Microchip_PIC18:PIC18LF44J10-IP +MCU_Microchip_PIC18:PIC18LF452-IP +MCU_Microchip_PIC18:PIC18LF452-IPT +MCU_Microchip_PIC18:PIC18LF4550-IML +MCU_Microchip_PIC18:PIC18LF4550-IP +MCU_Microchip_PIC18:PIC18LF4550-IPT +MCU_Microchip_PIC18:PIC18LF4553-IML +MCU_Microchip_PIC18:PIC18LF4553-IP +MCU_Microchip_PIC18:PIC18LF4553-IPT +MCU_Microchip_PIC18:PIC18LF458-IP +MCU_Microchip_PIC18:PIC18LF4580-IP +MCU_Microchip_PIC18:PIC18LF45J10-IP +MCU_Microchip_PIC18:PIC18LF45K50_QFP +MCU_Microchip_PIC18:PIC18LF45K80-IML +MCU_Microchip_PIC18:PIC18LF45K80-IPT +MCU_Microchip_PIC18:PIC18LF46K80-IML +MCU_Microchip_PIC18:PIC18LF46K80-IPT +MCU_Microchip_PIC24:PIC24FJ128GA306-xMR +MCU_Microchip_PIC24:PIC24FJ128GA306-xPT +MCU_Microchip_PIC24:PIC24FJ256DA210-xBG +MCU_Microchip_PIC24:PIC24FJ256DA210-xPT +MCU_Microchip_PIC24:PIC24FJ64GA306-xMR +MCU_Microchip_PIC24:PIC24FJ64GA306-xPT +MCU_Microchip_PIC24:PIC24FV32KA304-IPT +MCU_Microchip_PIC32:PIC32MK1024GPD100-xPT +MCU_Microchip_PIC32:PIC32MK1024GPE100-xPT +MCU_Microchip_PIC32:PIC32MM0064GPL028x-ML +MCU_Microchip_PIC32:PIC32MX110F016D-IPT +MCU_Microchip_PIC32:PIC32MX120F032D-IPT +MCU_Microchip_PIC32:PIC32MX130F064D-IPT +MCU_Microchip_PIC32:PIC32MX150F128D-IPT +MCU_Microchip_PIC32:PIC32MX170F256D-IPT +MCU_Microchip_PIC32:PIC32MX210F016D-IPT +MCU_Microchip_PIC32:PIC32MX220F032D-IPT +MCU_Microchip_PIC32:PIC32MX230F064D-IPT +MCU_Microchip_PIC32:PIC32MX250F128D-IPT +MCU_Microchip_PIC32:PIC32MX270F256D-IPT +MCU_Microchip_PIC32:PIC32MX575F256H +MCU_Microchip_PIC32:PIC32MX575F512H +MCU_Microchip_PIC32:PIC32MX795F512L-80x-PF +MCU_Microchip_PIC32:PIC32MX795F512L-80x-PT +MCU_Microchip_SAMA:ATSAMA5D21 +MCU_Microchip_SAMD:ATSAMD09C13A-SS +MCU_Microchip_SAMD:ATSAMD09D14A-M +MCU_Microchip_SAMD:ATSAMD10C13A-SS +MCU_Microchip_SAMD:ATSAMD10C14A-SS +MCU_Microchip_SAMD:ATSAMD10D13A-M +MCU_Microchip_SAMD:ATSAMD10D13A-SS +MCU_Microchip_SAMD:ATSAMD10D14A-M +MCU_Microchip_SAMD:ATSAMD10D14A-SS +MCU_Microchip_SAMD:ATSAMD10D14A-U +MCU_Microchip_SAMD:ATSAMD11C14A-SS +MCU_Microchip_SAMD:ATSAMD11D14A-M +MCU_Microchip_SAMD:ATSAMD11D14A-SS +MCU_Microchip_SAMD:ATSAMD11D14A-U +MCU_Microchip_SAMD:ATSAMD21E15A-A +MCU_Microchip_SAMD:ATSAMD21E15A-M +MCU_Microchip_SAMD:ATSAMD21E15B-A +MCU_Microchip_SAMD:ATSAMD21E15B-M +MCU_Microchip_SAMD:ATSAMD21E15L-A +MCU_Microchip_SAMD:ATSAMD21E15L-M +MCU_Microchip_SAMD:ATSAMD21E16A-A +MCU_Microchip_SAMD:ATSAMD21E16A-M +MCU_Microchip_SAMD:ATSAMD21E16B-A +MCU_Microchip_SAMD:ATSAMD21E16B-M +MCU_Microchip_SAMD:ATSAMD21E16L-A +MCU_Microchip_SAMD:ATSAMD21E16L-M +MCU_Microchip_SAMD:ATSAMD21E17A-A +MCU_Microchip_SAMD:ATSAMD21E17A-M +MCU_Microchip_SAMD:ATSAMD21E17D-A +MCU_Microchip_SAMD:ATSAMD21E17D-M +MCU_Microchip_SAMD:ATSAMD21E17L-A +MCU_Microchip_SAMD:ATSAMD21E17L-M +MCU_Microchip_SAMD:ATSAMD21E18A-A +MCU_Microchip_SAMD:ATSAMD21E18A-M +MCU_Microchip_SAMD:ATSAMD21G15A-A +MCU_Microchip_SAMD:ATSAMD21G15A-M +MCU_Microchip_SAMD:ATSAMD21G15B-A +MCU_Microchip_SAMD:ATSAMD21G15B-M +MCU_Microchip_SAMD:ATSAMD21G16A-A +MCU_Microchip_SAMD:ATSAMD21G16A-M +MCU_Microchip_SAMD:ATSAMD21G16B-A +MCU_Microchip_SAMD:ATSAMD21G16B-M +MCU_Microchip_SAMD:ATSAMD21G16L-M +MCU_Microchip_SAMD:ATSAMD21G17A-A +MCU_Microchip_SAMD:ATSAMD21G17A-M +MCU_Microchip_SAMD:ATSAMD21G17D-A +MCU_Microchip_SAMD:ATSAMD21G17D-M +MCU_Microchip_SAMD:ATSAMD21G17L-M +MCU_Microchip_SAMD:ATSAMD21G18A-A +MCU_Microchip_SAMD:ATSAMD21G18A-M +MCU_Microchip_SAMD:ATSAMD21J15A-A +MCU_Microchip_SAMD:ATSAMD21J15A-M +MCU_Microchip_SAMD:ATSAMD21J15B-A +MCU_Microchip_SAMD:ATSAMD21J15B-C +MCU_Microchip_SAMD:ATSAMD21J15B-M +MCU_Microchip_SAMD:ATSAMD21J16A-A +MCU_Microchip_SAMD:ATSAMD21J16A-M +MCU_Microchip_SAMD:ATSAMD21J16B-A +MCU_Microchip_SAMD:ATSAMD21J16B-C +MCU_Microchip_SAMD:ATSAMD21J16B-M +MCU_Microchip_SAMD:ATSAMD21J17A-A +MCU_Microchip_SAMD:ATSAMD21J17A-M +MCU_Microchip_SAMD:ATSAMD21J17D-A +MCU_Microchip_SAMD:ATSAMD21J17D-C +MCU_Microchip_SAMD:ATSAMD21J17D-M +MCU_Microchip_SAMD:ATSAMD21J18A-A +MCU_Microchip_SAMD:ATSAMD21J18A-M +MCU_Microchip_SAMD:ATSAMD51J18A-A +MCU_Microchip_SAMD:ATSAMD51J18A-M +MCU_Microchip_SAMD:ATSAMD51J19A-A +MCU_Microchip_SAMD:ATSAMD51J19A-M +MCU_Microchip_SAMD:ATSAMD51J20A-A +MCU_Microchip_SAMD:ATSAMD51J20A-M +MCU_Microchip_SAMD:ATSAMDA1E14B-A +MCU_Microchip_SAMD:ATSAMDA1E14B-M +MCU_Microchip_SAMD:ATSAMDA1E15B-A +MCU_Microchip_SAMD:ATSAMDA1E15B-M +MCU_Microchip_SAMD:ATSAMDA1E16B-A +MCU_Microchip_SAMD:ATSAMDA1E16B-M +MCU_Microchip_SAMD:ATSAMDA1G14B-A +MCU_Microchip_SAMD:ATSAMDA1G14B-M +MCU_Microchip_SAMD:ATSAMDA1G15B-A +MCU_Microchip_SAMD:ATSAMDA1G15B-M +MCU_Microchip_SAMD:ATSAMDA1G16B-A +MCU_Microchip_SAMD:ATSAMDA1G16B-M +MCU_Microchip_SAMD:ATSAMDA1J14B-A +MCU_Microchip_SAMD:ATSAMDA1J15B-A +MCU_Microchip_SAMD:ATSAMDA1J16B-A +MCU_Microchip_SAME:ATSAME51J18A-A +MCU_Microchip_SAME:ATSAME51J19A-A +MCU_Microchip_SAME:ATSAME51J20A-A +MCU_Microchip_SAME:ATSAME53J18A-M +MCU_Microchip_SAME:ATSAME53J19A-M +MCU_Microchip_SAME:ATSAME53J20A-M +MCU_Microchip_SAME:ATSAME54N19A-A +MCU_Microchip_SAME:ATSAME70J19A-AN +MCU_Microchip_SAME:ATSAME70J20A-AN +MCU_Microchip_SAME:ATSAME70J21A-AN +MCU_Microchip_SAME:ATSAME70N19A-AN +MCU_Microchip_SAME:ATSAME70N20A-AN +MCU_Microchip_SAME:ATSAME70N21A-AN +MCU_Microchip_SAME:ATSAME70Q19A-AN +MCU_Microchip_SAME:ATSAME70Q20A-AN +MCU_Microchip_SAME:ATSAME70Q21A-AN +MCU_Microchip_SAML:ATSAML21E15B-AUT +MCU_Microchip_SAML:ATSAML21E15B-MUT +MCU_Microchip_SAML:ATSAML21E16B-AUT +MCU_Microchip_SAML:ATSAML21E16B-MUT +MCU_Microchip_SAML:ATSAML21E17B-AUT +MCU_Microchip_SAML:ATSAML21E17B-MUT +MCU_Microchip_SAML:ATSAML21E18B-AUT +MCU_Microchip_SAML:ATSAML21E18B-MUT +MCU_Microchip_SAML:ATSAML21G16B-AUT +MCU_Microchip_SAML:ATSAML21G16B-MUT +MCU_Microchip_SAML:ATSAML21G17B-AUT +MCU_Microchip_SAML:ATSAML21G17B-MUT +MCU_Microchip_SAML:ATSAML21G18B-AUT +MCU_Microchip_SAML:ATSAML21G18B-MUT +MCU_Microchip_SAML:ATSAML21J16B-AUT +MCU_Microchip_SAML:ATSAML21J16B-MUT +MCU_Microchip_SAML:ATSAML21J17B-AUT +MCU_Microchip_SAML:ATSAML21J17B-MUT +MCU_Microchip_SAML:ATSAML21J18B-AUT +MCU_Microchip_SAML:ATSAML21J18B-MUT +MCU_Microchip_SAMV:ATSAMV71Q19B-A +MCU_Microchip_SAMV:ATSAMV71Q20B-A +MCU_Microchip_SAMV:ATSAMV71Q21B-A +MCU_Module:Adafruit_Feather_32u4_BluefruitLE +MCU_Module:Adafruit_Feather_Generic +MCU_Module:Adafruit_Feather_HUZZAH32_ESP32 +MCU_Module:Adafruit_Feather_HUZZAH_ESP8266 +MCU_Module:Adafruit_Feather_M0_Adalogger +MCU_Module:Adafruit_Feather_M0_Basic_Proto +MCU_Module:Adafruit_Feather_M0_BluefruitLE +MCU_Module:Adafruit_Feather_M0_Express +MCU_Module:Adafruit_Feather_M0_RFM69HCW_Packet_Radio +MCU_Module:Adafruit_Feather_M0_RFM9x_LoRa_Radio +MCU_Module:Adafruit_Feather_M0_Wifi +MCU_Module:Adafruit_Feather_WICED_Wifi +MCU_Module:Adafruit_HUZZAH_ESP8266_breakout +MCU_Module:Arduino_Leonardo +MCU_Module:Arduino_Nano_ESP32 +MCU_Module:Arduino_Nano_Every +MCU_Module:Arduino_Nano_RP2040_Connect +MCU_Module:Arduino_Nano_v2.x +MCU_Module:Arduino_Nano_v3.x +MCU_Module:Arduino_UNO_R2 +MCU_Module:Arduino_UNO_R3 +MCU_Module:CHIP +MCU_Module:CHIP-PRO +MCU_Module:Carambola2 +MCU_Module:Electrosmith_Daisy_Seed_Rev4 +MCU_Module:Google_Coral +MCU_Module:Maple_Mini +MCU_Module:NUCLEO144-F207ZG +MCU_Module:NUCLEO144-F412ZG +MCU_Module:NUCLEO144-F413ZH +MCU_Module:NUCLEO144-F429ZI +MCU_Module:NUCLEO144-F439ZI +MCU_Module:NUCLEO144-F446ZE +MCU_Module:NUCLEO144-F722ZE +MCU_Module:NUCLEO144-F746ZG +MCU_Module:NUCLEO144-F756ZG +MCU_Module:NUCLEO144-F767ZI +MCU_Module:NUCLEO144-H743ZI +MCU_Module:NUCLEO64-F411RE +MCU_Module:OPOS6UL +MCU_Module:OPOS6UL_NANO +MCU_Module:Olimex_MOD-WIFI-ESP8266-DEV +MCU_Module:Omega2+ +MCU_Module:Omega2S +MCU_Module:Omega2S+ +MCU_Module:PocketBeagle +MCU_Module:RaspberryPi-CM1 +MCU_Module:RaspberryPi-CM3 +MCU_Module:RaspberryPi-CM3+ +MCU_Module:RaspberryPi-CM3+L +MCU_Module:RaspberryPi-CM3-L +MCU_Module:RaspberryPi_Pico +MCU_Module:RaspberryPi_Pico_Debug +MCU_Module:RaspberryPi_Pico_Extensive +MCU_Module:RaspberryPi_Pico_W +MCU_Module:RaspberryPi_Pico_W_Debug +MCU_Module:RaspberryPi_Pico_W_Extensive +MCU_Module:Sipeed-M1 +MCU_Module:Sipeed-M1W +MCU_Module:VisionSOM-6UL +MCU_Module:VisionSOM-6ULL +MCU_Module:VisionSOM-RT +MCU_Module:VisionSOM-STM32MP1 +MCU_Nordic:nRF51x22-QFxx +MCU_Nordic:nRF52810-QCxx +MCU_Nordic:nRF52810-QFxx +MCU_Nordic:nRF52811-QCxx +MCU_Nordic:nRF52820-QDxx +MCU_Nordic:nRF52832-QFxx +MCU_Nordic:nRF52833_QDxx +MCU_Nordic:nRF52833_QIxx +MCU_Nordic:nRF52840 +MCU_Nordic:nRF5340-QKxx +MCU_Nordic:nRF9160-SIxA +MCU_NXP_ColdFire:MCF5211CAE66 +MCU_NXP_ColdFire:MCF5212CAE66 +MCU_NXP_ColdFire:MCF5213-LQFP100 +MCU_NXP_ColdFire:MCF5282 +MCU_NXP_ColdFire:MCF5328-BGA256 +MCU_NXP_ColdFire:MCF5407 +MCU_NXP_HC11:68HC11 +MCU_NXP_HC11:68HC11A8 +MCU_NXP_HC11:68HC11F1 +MCU_NXP_HC11:68HC11_PLCC +MCU_NXP_HC11:68HC711_PLCC +MCU_NXP_HC11:MC68HC11A0CC +MCU_NXP_HC11:MC68HC11A1CC +MCU_NXP_HC11:MC68HC11A7CC +MCU_NXP_HC11:MC68HC11A8CC +MCU_NXP_HC11:MC68HC11F1CC +MCU_NXP_HC12:MC68HC812A4 +MCU_NXP_HC12:MC68HC912 +MCU_NXP_HCS12:MC9S12DT256 +MCU_NXP_Kinetis:MK20DN128VFM5 +MCU_NXP_Kinetis:MK20DN32VFM5 +MCU_NXP_Kinetis:MK20DN64VFM5 +MCU_NXP_Kinetis:MK20DX128VFM5 +MCU_NXP_Kinetis:MK20DX32VFM5 +MCU_NXP_Kinetis:MK20DX64VFM5 +MCU_NXP_Kinetis:MK20FN1M0VMD12 +MCU_NXP_Kinetis:MK20FX512VMD12 +MCU_NXP_Kinetis:MK26FN2M0VMD18 +MCU_NXP_Kinetis:MKE02Z16VLC4 +MCU_NXP_Kinetis:MKE02Z16VLD4 +MCU_NXP_Kinetis:MKE02Z32VLC4 +MCU_NXP_Kinetis:MKE02Z32VLD4 +MCU_NXP_Kinetis:MKE02Z32VLH4 +MCU_NXP_Kinetis:MKE02Z32VQH4 +MCU_NXP_Kinetis:MKE02Z64VLC4 +MCU_NXP_Kinetis:MKE02Z64VLD4 +MCU_NXP_Kinetis:MKE02Z64VLH4 +MCU_NXP_Kinetis:MKE02Z64VQH4 +MCU_NXP_Kinetis:MKE16Z64VLF4 +MCU_NXP_Kinetis:MKL02Z16VFG4 +MCU_NXP_Kinetis:MKL02Z16VFK4 +MCU_NXP_Kinetis:MKL02Z16VFM4 +MCU_NXP_Kinetis:MKL02Z32CAF4 +MCU_NXP_Kinetis:MKL02Z32VFG4 +MCU_NXP_Kinetis:MKL02Z32VFK4 +MCU_NXP_Kinetis:MKL02Z32VFM4 +MCU_NXP_Kinetis:MKL02Z8VFG4 +MCU_NXP_Kinetis:MKL03Z16VFG4 +MCU_NXP_Kinetis:MKL03Z16VFK4 +MCU_NXP_Kinetis:MKL03Z32CAF4 +MCU_NXP_Kinetis:MKL03Z32CBF4 +MCU_NXP_Kinetis:MKL03Z32VFG4 +MCU_NXP_Kinetis:MKL03Z32VFK4 +MCU_NXP_Kinetis:MKL03Z8VFG4 +MCU_NXP_Kinetis:MKL03Z8VFK4 +MCU_NXP_Kinetis:MKL04Z16VFK4 +MCU_NXP_Kinetis:MKL04Z16VFM4 +MCU_NXP_Kinetis:MKL04Z16VLC4 +MCU_NXP_Kinetis:MKL04Z16VLF4 +MCU_NXP_Kinetis:MKL04Z32VFK4 +MCU_NXP_Kinetis:MKL04Z32VFM4 +MCU_NXP_Kinetis:MKL04Z32VLC4 +MCU_NXP_Kinetis:MKL04Z32VLF4 +MCU_NXP_Kinetis:MKL04Z8VFK4 +MCU_NXP_Kinetis:MKL04Z8VFM4 +MCU_NXP_Kinetis:MKL04Z8VLC4 +MCU_NXP_Kinetis:MKL05Z16VFK4 +MCU_NXP_Kinetis:MKL05Z16VFM4 +MCU_NXP_Kinetis:MKL05Z16VLC4 +MCU_NXP_Kinetis:MKL05Z16VLF4 +MCU_NXP_Kinetis:MKL05Z32VFK4 +MCU_NXP_Kinetis:MKL05Z32VFM4 +MCU_NXP_Kinetis:MKL05Z32VLC4 +MCU_NXP_Kinetis:MKL05Z32VLF4 +MCU_NXP_Kinetis:MKL05Z8VFK4 +MCU_NXP_Kinetis:MKL05Z8VFM4 +MCU_NXP_Kinetis:MKL05Z8VLC4 +MCU_NXP_Kinetis:MKL16Z128VFM4 +MCU_NXP_Kinetis:MKL16Z128VFT4 +MCU_NXP_Kinetis:MKL16Z128VLH4 +MCU_NXP_Kinetis:MKL16Z256VLH4 +MCU_NXP_Kinetis:MKL16Z256VMP4 +MCU_NXP_Kinetis:MKL16Z32VFM4 +MCU_NXP_Kinetis:MKL16Z32VFT4 +MCU_NXP_Kinetis:MKL16Z32VLH4 +MCU_NXP_Kinetis:MKL16Z64VFM4 +MCU_NXP_Kinetis:MKL16Z64VFT4 +MCU_NXP_Kinetis:MKL16Z64VLH4 +MCU_NXP_Kinetis:MKL17Z128VFM4 +MCU_NXP_Kinetis:MKL17Z128VFT4 +MCU_NXP_Kinetis:MKL17Z128VLH4 +MCU_NXP_Kinetis:MKL17Z128VMP4 +MCU_NXP_Kinetis:MKL17Z256CAL4 +MCU_NXP_Kinetis:MKL17Z256VFM4 +MCU_NXP_Kinetis:MKL17Z256VFT4 +MCU_NXP_Kinetis:MKL17Z256VLH4 +MCU_NXP_Kinetis:MKL17Z256VMP4 +MCU_NXP_Kinetis:MKL17Z32VDA4 +MCU_NXP_Kinetis:MKL17Z32VFM4 +MCU_NXP_Kinetis:MKL17Z32VFT4 +MCU_NXP_Kinetis:MKL17Z32VLH4 +MCU_NXP_Kinetis:MKL17Z32VMP4 +MCU_NXP_Kinetis:MKL17Z64VDA4 +MCU_NXP_Kinetis:MKL17Z64VFM4 +MCU_NXP_Kinetis:MKL17Z64VFT4 +MCU_NXP_Kinetis:MKL17Z64VLH4 +MCU_NXP_Kinetis:MKL17Z64VMP4 +MCU_NXP_Kinetis:MKL24Z32VFM4 +MCU_NXP_Kinetis:MKL24Z32VFT4 +MCU_NXP_Kinetis:MKL24Z32VLH4 +MCU_NXP_Kinetis:MKL24Z32VLK4 +MCU_NXP_Kinetis:MKL24Z64VFM4 +MCU_NXP_Kinetis:MKL24Z64VFT4 +MCU_NXP_Kinetis:MKL24Z64VLH4 +MCU_NXP_Kinetis:MKL24Z64VLK4 +MCU_NXP_Kinetis:MKL25Z128VFM4 +MCU_NXP_Kinetis:MKL25Z128VFT4 +MCU_NXP_Kinetis:MKL25Z128VLH4 +MCU_NXP_Kinetis:MKL25Z128VLK4 +MCU_NXP_Kinetis:MKL25Z32VFM4 +MCU_NXP_Kinetis:MKL25Z32VFT4 +MCU_NXP_Kinetis:MKL25Z32VLH4 +MCU_NXP_Kinetis:MKL25Z32VLK4 +MCU_NXP_Kinetis:MKL25Z64VFM4 +MCU_NXP_Kinetis:MKL25Z64VFT4 +MCU_NXP_Kinetis:MKL25Z64VLH4 +MCU_NXP_Kinetis:MKL25Z64VLK4 +MCU_NXP_Kinetis:MKL26Z128CAL4 +MCU_NXP_Kinetis:MKL26Z128VFM4 +MCU_NXP_Kinetis:MKL26Z128VFT4 +MCU_NXP_Kinetis:MKL26Z128VLH4 +MCU_NXP_Kinetis:MKL26Z128VLL4 +MCU_NXP_Kinetis:MKL26Z128VMC4 +MCU_NXP_Kinetis:MKL26Z256VLH4 +MCU_NXP_Kinetis:MKL26Z256VLL4 +MCU_NXP_Kinetis:MKL26Z256VMC4 +MCU_NXP_Kinetis:MKL26Z256VMP4 +MCU_NXP_Kinetis:MKL26Z32VFM4 +MCU_NXP_Kinetis:MKL26Z32VFT4 +MCU_NXP_Kinetis:MKL26Z32VLH4 +MCU_NXP_Kinetis:MKL26Z64VFM4 +MCU_NXP_Kinetis:MKL26Z64VFT4 +MCU_NXP_Kinetis:MKL26Z64VLH4 +MCU_NXP_Kinetis:MKL27Z128VFT4 +MCU_NXP_Kinetis:MKL27Z128VLH4 +MCU_NXP_Kinetis:MKL27Z256VFT4 +MCU_NXP_Kinetis:MKL27Z256VLH4 +MCU_NXP_Kinetis:MKL27Z32VFM4 +MCU_NXP_Kinetis:MKL27Z32VFT4 +MCU_NXP_Kinetis:MKL27Z32VLH4 +MCU_NXP_Kinetis:MKL27Z64VFM4 +MCU_NXP_Kinetis:MKL27Z64VFT4 +MCU_NXP_Kinetis:MKL27Z64VLH4 +MCU_NXP_Kinetis:MKL28Z512VDC7 +MCU_NXP_Kinetis:MKL28Z512VLL7 +MCU_NXP_Kinetis:MKL43Z128VLH4 +MCU_NXP_Kinetis:MKL43Z128VMP4 +MCU_NXP_Kinetis:MKL43Z256VLH4 +MCU_NXP_Kinetis:MKL43Z256VMP4 +MCU_NXP_Kinetis:MKL46Z128VLH4 +MCU_NXP_Kinetis:MKL46Z128VLL4 +MCU_NXP_Kinetis:MKL46Z128VMC4 +MCU_NXP_Kinetis:MKL46Z256VLH4 +MCU_NXP_Kinetis:MKL46Z256VLL4 +MCU_NXP_Kinetis:MKL46Z256VMC4 +MCU_NXP_Kinetis:MKL46Z256VMP4 +MCU_NXP_Kinetis:MKV11Z128VLF7 +MCU_NXP_Kinetis:MKV11Z128VLH7 +MCU_NXP_Kinetis:MKW21Z256VHT +MCU_NXP_Kinetis:MKW21Z512VHT +MCU_NXP_Kinetis:MKW31Z256VHT +MCU_NXP_Kinetis:MKW31Z512VHT +MCU_NXP_Kinetis:MKW41Z256VHT +MCU_NXP_Kinetis:MKW41Z512VHT +MCU_NXP_LPC:LPC1102UK +MCU_NXP_LPC:LPC1104UK +MCU_NXP_LPC:LPC1111FHN33-101 +MCU_NXP_LPC:LPC1111FHN33-102 +MCU_NXP_LPC:LPC1111FHN33-103 +MCU_NXP_LPC:LPC1111FHN33-201 +MCU_NXP_LPC:LPC1111FHN33-202 +MCU_NXP_LPC:LPC1111FHN33-203 +MCU_NXP_LPC:LPC1111JHN33-103 +MCU_NXP_LPC:LPC1111JHN33-203 +MCU_NXP_LPC:LPC1112FHI33-102 +MCU_NXP_LPC:LPC1112FHI33-202 +MCU_NXP_LPC:LPC1112FHI33-203 +MCU_NXP_LPC:LPC1112FHN33-101 +MCU_NXP_LPC:LPC1112FHN33-102 +MCU_NXP_LPC:LPC1112FHN33-103 +MCU_NXP_LPC:LPC1112FHN33-201 +MCU_NXP_LPC:LPC1112FHN33-202 +MCU_NXP_LPC:LPC1112FHN33-203 +MCU_NXP_LPC:LPC1112JHI33-203 +MCU_NXP_LPC:LPC1112JHN33-103 +MCU_NXP_LPC:LPC1112JHN33-203 +MCU_NXP_LPC:LPC1113FBD48-301 +MCU_NXP_LPC:LPC1113FBD48-302 +MCU_NXP_LPC:LPC1113FBD48-303 +MCU_NXP_LPC:LPC1113FHN33-201 +MCU_NXP_LPC:LPC1113FHN33-202 +MCU_NXP_LPC:LPC1113FHN33-203 +MCU_NXP_LPC:LPC1113FHN33-301 +MCU_NXP_LPC:LPC1113FHN33-302 +MCU_NXP_LPC:LPC1113FHN33-303 +MCU_NXP_LPC:LPC1113JBD48-303 +MCU_NXP_LPC:LPC1113JHN33-203 +MCU_NXP_LPC:LPC1113JHN33-303 +MCU_NXP_LPC:LPC1114FBD48-301 +MCU_NXP_LPC:LPC1114FBD48-302 +MCU_NXP_LPC:LPC1114FBD48-303 +MCU_NXP_LPC:LPC1114FBD48-323 +MCU_NXP_LPC:LPC1114FBD48-333 +MCU_NXP_LPC:LPC1114FHI33-302 +MCU_NXP_LPC:LPC1114FHI33-303 +MCU_NXP_LPC:LPC1114FHN33-201 +MCU_NXP_LPC:LPC1114FHN33-202 +MCU_NXP_LPC:LPC1114FHN33-203 +MCU_NXP_LPC:LPC1114FHN33-301 +MCU_NXP_LPC:LPC1114FHN33-302 +MCU_NXP_LPC:LPC1114FHN33-303 +MCU_NXP_LPC:LPC1114FHN33-333 +MCU_NXP_LPC:LPC1114JBD48-303 +MCU_NXP_LPC:LPC1114JBD48-323 +MCU_NXP_LPC:LPC1114JBD48-333 +MCU_NXP_LPC:LPC1114JHI33-303 +MCU_NXP_LPC:LPC1114JHN33-203 +MCU_NXP_LPC:LPC1115FBD48-303 +MCU_NXP_LPC:LPC1115JBD48-303 +MCU_NXP_LPC:LPC11E12FBD48-201 +MCU_NXP_LPC:LPC11E13FBD48-301 +MCU_NXP_LPC:LPC11E14FBD48-401 +MCU_NXP_LPC:LPC11U12FBD48-201 +MCU_NXP_LPC:LPC11U13FBD48-201 +MCU_NXP_LPC:LPC11U14FBD48-201 +MCU_NXP_LPC:LPC11U22FBD48-301 +MCU_NXP_LPC:LPC11U23FBD48-301 +MCU_NXP_LPC:LPC11U24FBD48-301 +MCU_NXP_LPC:LPC11U24FBD48-401 +MCU_NXP_LPC:LPC11U34FBD48-311 +MCU_NXP_LPC:LPC11U34FBD48-421 +MCU_NXP_LPC:LPC11U35FBD48-401 +MCU_NXP_LPC:LPC11U36FBD48-401 +MCU_NXP_LPC:LPC11U37FBD48-401_ +MCU_NXP_LPC:LPC1224FBD48-101 +MCU_NXP_LPC:LPC1224FBD48-121 +MCU_NXP_LPC:LPC1224FBD64-101 +MCU_NXP_LPC:LPC1224FBD64-121 +MCU_NXP_LPC:LPC1225FBD48-301 +MCU_NXP_LPC:LPC1225FBD48-321 +MCU_NXP_LPC:LPC1225FBD64-301 +MCU_NXP_LPC:LPC1225FBD64-321 +MCU_NXP_LPC:LPC1226FBD48-301 +MCU_NXP_LPC:LPC1226FBD64-301 +MCU_NXP_LPC:LPC1227FBD48-301 +MCU_NXP_LPC:LPC1227FBD64-301 +MCU_NXP_LPC:LPC1763FBD100 +MCU_NXP_LPC:LPC1764FBD100 +MCU_NXP_LPC:LPC1765FBD100 +MCU_NXP_LPC:LPC1766FBD100 +MCU_NXP_LPC:LPC1767FBD100 +MCU_NXP_LPC:LPC1768FBD100 +MCU_NXP_LPC:LPC1769FBD100 +MCU_NXP_LPC:LPC2141FBD64 +MCU_NXP_LPC:LPC2142FBD64 +MCU_NXP_LPC:LPC2144FBD64 +MCU_NXP_LPC:LPC2146FBD64 +MCU_NXP_LPC:LPC2148FBD64 +MCU_NXP_LPC:LPC811M001JDH16 +MCU_NXP_LPC:LPC812M001JDH16 +MCU_NXP_LPC:LPC812M101JD20 +MCU_NXP_LPC:LPC812M101JDH20 +MCU_NXP_LPC:LPC812M101JTB16 +MCU_NXP_LPC:LPC822M101JDH20 +MCU_NXP_LPC:LPC822M101JHI33 +MCU_NXP_LPC:LPC824M201JDH20 +MCU_NXP_LPC:LPC824M201JHI33 +MCU_NXP_LPC:LPC832M101FDH20 +MCU_NXP_LPC:LPC834M101FHI33 +MCU_NXP_MAC7100:MAC7101 +MCU_NXP_MAC7100:MAC7111 +MCU_NXP_MCore:MMC2114CFCPU +MCU_NXP_NTAG:NHS3100 +MCU_NXP_S08:MC9S08AC128xFDE +MCU_NXP_S08:MC9S08AC128xFGE +MCU_NXP_S08:MC9S08AC128xFUE +MCU_NXP_S08:MC9S08AC128xLKE +MCU_NXP_S08:MC9S08AC128xPUE +MCU_NXP_S08:MC9S08AC16xFDE +MCU_NXP_S08:MC9S08AC16xFGE +MCU_NXP_S08:MC9S08AC16xFJE +MCU_NXP_S08:MC9S08AC32xFDE +MCU_NXP_S08:MC9S08AC32xFGE +MCU_NXP_S08:MC9S08AC32xFJE +MCU_NXP_S08:MC9S08AC32xFUE +MCU_NXP_S08:MC9S08AC32xPUE +MCU_NXP_S08:MC9S08AC48xFDE +MCU_NXP_S08:MC9S08AC48xFGE +MCU_NXP_S08:MC9S08AC48xFJE +MCU_NXP_S08:MC9S08AC48xFUE +MCU_NXP_S08:MC9S08AC48xPUE +MCU_NXP_S08:MC9S08AC60xFDE +MCU_NXP_S08:MC9S08AC60xFGE +MCU_NXP_S08:MC9S08AC60xFJE +MCU_NXP_S08:MC9S08AC60xFUE +MCU_NXP_S08:MC9S08AC60xPUE +MCU_NXP_S08:MC9S08AC8xFDE +MCU_NXP_S08:MC9S08AC8xFGE +MCU_NXP_S08:MC9S08AC8xFJE +MCU_NXP_S08:MC9S08AC96xFDE +MCU_NXP_S08:MC9S08AC96xFGE +MCU_NXP_S08:MC9S08AC96xFUE +MCU_NXP_S08:MC9S08AC96xLKE +MCU_NXP_S08:MC9S08AC96xPUE +MCU_NXP_S08:MC9S08AW16AE0xLC +MCU_NXP_S08:MC9S08AW16xFDE +MCU_NXP_S08:MC9S08AW16xFGE +MCU_NXP_S08:MC9S08AW16xFUE +MCU_NXP_S08:MC9S08AW16xPUE +MCU_NXP_S08:MC9S08AW32xFDE +MCU_NXP_S08:MC9S08AW32xFGE +MCU_NXP_S08:MC9S08AW32xFUE +MCU_NXP_S08:MC9S08AW32xPUE +MCU_NXP_S08:MC9S08AW48xFDE +MCU_NXP_S08:MC9S08AW48xFGE +MCU_NXP_S08:MC9S08AW48xFUE +MCU_NXP_S08:MC9S08AW48xPUE +MCU_NXP_S08:MC9S08AW60xFDE +MCU_NXP_S08:MC9S08AW60xFGE +MCU_NXP_S08:MC9S08AW60xFUE +MCU_NXP_S08:MC9S08AW60xPUE +MCU_NXP_S08:MC9S08AW8AE0xLC +MCU_NXP_S08:MC9S08DN16xLC +MCU_NXP_S08:MC9S08DN16xLF +MCU_NXP_S08:MC9S08DN32xLC +MCU_NXP_S08:MC9S08DN32xLF +MCU_NXP_S08:MC9S08DN32xLH +MCU_NXP_S08:MC9S08DN48xLC +MCU_NXP_S08:MC9S08DN48xLF +MCU_NXP_S08:MC9S08DN48xLH +MCU_NXP_S08:MC9S08DN60xLC +MCU_NXP_S08:MC9S08DN60xLF +MCU_NXP_S08:MC9S08DN60xLH +MCU_NXP_S08:MC9S08DV16xLC +MCU_NXP_S08:MC9S08DV16xLF +MCU_NXP_S08:MC9S08DV32xLC +MCU_NXP_S08:MC9S08DV32xLF +MCU_NXP_S08:MC9S08DV32xLH +MCU_NXP_S08:MC9S08DV48xLC +MCU_NXP_S08:MC9S08DV48xLF +MCU_NXP_S08:MC9S08DV48xLH +MCU_NXP_S08:MC9S08DV60xLC +MCU_NXP_S08:MC9S08DV60xLF +MCU_NXP_S08:MC9S08DV60xLH +MCU_NXP_S08:MC9S08DZ128xLF +MCU_NXP_S08:MC9S08DZ128xLH +MCU_NXP_S08:MC9S08DZ128xLL +MCU_NXP_S08:MC9S08DZ16xLC +MCU_NXP_S08:MC9S08DZ16xLF +MCU_NXP_S08:MC9S08DZ32xLC +MCU_NXP_S08:MC9S08DZ32xLF +MCU_NXP_S08:MC9S08DZ32xLH +MCU_NXP_S08:MC9S08DZ48xLC +MCU_NXP_S08:MC9S08DZ48xLF +MCU_NXP_S08:MC9S08DZ48xLH +MCU_NXP_S08:MC9S08DZ60xLC +MCU_NXP_S08:MC9S08DZ60xLF +MCU_NXP_S08:MC9S08DZ60xLH +MCU_NXP_S08:MC9S08DZ96xLF +MCU_NXP_S08:MC9S08DZ96xLH +MCU_NXP_S08:MC9S08DZ96xLL +MCU_NXP_S08:MC9S08EL16xTJ +MCU_NXP_S08:MC9S08EL16xTL +MCU_NXP_S08:MC9S08EL32xTJ +MCU_NXP_S08:MC9S08EL32xTL +MCU_NXP_S08:MC9S08FL16xLC +MCU_NXP_S08:MC9S08FL8xLC +MCU_NXP_S08:MC9S08JM16xGT +MCU_NXP_S08:MC9S08JM16xLC +MCU_NXP_S08:MC9S08JM16xLD +MCU_NXP_S08:MC9S08JM32xGT +MCU_NXP_S08:MC9S08JM32xLD +MCU_NXP_S08:MC9S08JM32xLH +MCU_NXP_S08:MC9S08JM60xGT +MCU_NXP_S08:MC9S08JM60xLD +MCU_NXP_S08:MC9S08JM60xLH +MCU_NXP_S08:MC9S08JM8xGT +MCU_NXP_S08:MC9S08JM8xLC +MCU_NXP_S08:MC9S08JM8xLD +MCU_NXP_S08:MC9S08JS16CFK +MCU_NXP_S08:MC9S08JS16CWJ +MCU_NXP_S08:MC9S08JS8CFK +MCU_NXP_S08:MC9S08JS8CWJ +MCU_NXP_S08:MC9S08LG32J0xLF +MCU_NXP_S08:MC9S08LG32J0xLH +MCU_NXP_S08:MC9S08LG32J0xLK +MCU_NXP_S08:MC9S08MP16xLC +MCU_NXP_S08:MC9S08MP16xLF +MCU_NXP_S08:MC9S08MP16xWL +MCU_NXP_S08:MC9S08QA2CDNE +MCU_NXP_S08:MC9S08QA2CFQE +MCU_NXP_S08:MC9S08QA2CPAE +MCU_NXP_S08:MC9S08QA4CDNE +MCU_NXP_S08:MC9S08QA4CFQE +MCU_NXP_S08:MC9S08QA4CPAE +MCU_NXP_S08:MC9S08QB4xGK +MCU_NXP_S08:MC9S08QB4xTG +MCU_NXP_S08:MC9S08QB4xWL +MCU_NXP_S08:MC9S08QB8xGK +MCU_NXP_S08:MC9S08QB8xTG +MCU_NXP_S08:MC9S08QB8xWL +MCU_NXP_S08:MC9S08QD2xPC +MCU_NXP_S08:MC9S08QD2xSC +MCU_NXP_S08:MC9S08QD4xPC +MCU_NXP_S08:MC9S08QD4xSC +MCU_NXP_S08:MC9S08QG4xDNE +MCU_NXP_S08:MC9S08QG4xDTE +MCU_NXP_S08:MC9S08QG4xFKE +MCU_NXP_S08:MC9S08QG4xFQE +MCU_NXP_S08:MC9S08QG4xPAE +MCU_NXP_S08:MC9S08QG8xDNE +MCU_NXP_S08:MC9S08QG8xDTE +MCU_NXP_S08:MC9S08QG8xFKE +MCU_NXP_S08:MC9S08QG8xFQE +MCU_NXP_S08:MC9S08QG8xPBE +MCU_NXP_S08:MC9S08SC4xTG +MCU_NXP_S08:MC9S08SE4xRL +MCU_NXP_S08:MC9S08SE4xTG +MCU_NXP_S08:MC9S08SE4xWL +MCU_NXP_S08:MC9S08SE8xRL +MCU_NXP_S08:MC9S08SE8xTG +MCU_NXP_S08:MC9S08SE8xWL +MCU_NXP_S08:MC9S08SF4xTG +MCU_NXP_S08:MC9S08SF4xTJ +MCU_NXP_S08:MC9S08SG16xTG +MCU_NXP_S08:MC9S08SG16xTJ +MCU_NXP_S08:MC9S08SG16xTL +MCU_NXP_S08:MC9S08SG32xTG +MCU_NXP_S08:MC9S08SG32xTJ +MCU_NXP_S08:MC9S08SG32xTL +MCU_NXP_S08:MC9S08SG4xSC +MCU_NXP_S08:MC9S08SG4xTG +MCU_NXP_S08:MC9S08SG4xTJ +MCU_NXP_S08:MC9S08SG8xSC +MCU_NXP_S08:MC9S08SG8xTG +MCU_NXP_S08:MC9S08SG8xTJ +MCU_NXP_S08:MC9S08SH16xTG +MCU_NXP_S08:MC9S08SH16xTJ +MCU_NXP_S08:MC9S08SH16xTL +MCU_NXP_S08:MC9S08SH16xWL +MCU_NXP_S08:MC9S08SH32xTG +MCU_NXP_S08:MC9S08SH32xTJ +MCU_NXP_S08:MC9S08SH32xTL +MCU_NXP_S08:MC9S08SH32xWL +MCU_NXP_S08:MC9S08SH4xFK +MCU_NXP_S08:MC9S08SH4xPJ +MCU_NXP_S08:MC9S08SH4xSC +MCU_NXP_S08:MC9S08SH4xTG +MCU_NXP_S08:MC9S08SH4xTJ +MCU_NXP_S08:MC9S08SH4xWJ +MCU_NXP_S08:MC9S08SH8xFK +MCU_NXP_S08:MC9S08SH8xPJ +MCU_NXP_S08:MC9S08SH8xSC +MCU_NXP_S08:MC9S08SH8xTG +MCU_NXP_S08:MC9S08SH8xTJ +MCU_NXP_S08:MC9S08SH8xWJ +MCU_NXP_S08:MC9S08SL16xTJ +MCU_NXP_S08:MC9S08SL16xTL +MCU_NXP_S08:MC9S08SL32xTJ +MCU_NXP_S08:MC9S08SL32xTL +MCU_NXP_S08:MC9S08SV16CLC +MCU_NXP_S08:MC9S08SV8CLC +MCU_Parallax:P8X32A-D40 +MCU_Parallax:P8X32A-M44 +MCU_Parallax:P8X32A-Q44 +MCU_Puya:PY32F002AF15P +MCU_RaspberryPi:RP2040 +MCU_Renesas_Synergy_S1:R7FS12878xA01CFL +MCU_SiFive:FE310-G000 +MCU_SiFive:FE310-G002 +MCU_SiFive:FU540-C000 +MCU_SiliconLabs:C8051F320-GQ +MCU_SiliconLabs:C8051F321-GM +MCU_SiliconLabs:C8051F380-GQ +MCU_SiliconLabs:C8051F381-GM +MCU_SiliconLabs:C8051F381-GQ +MCU_SiliconLabs:C8051F382-GQ +MCU_SiliconLabs:C8051F383-GM +MCU_SiliconLabs:C8051F383-GQ +MCU_SiliconLabs:C8051F384-GQ +MCU_SiliconLabs:C8051F385-GM +MCU_SiliconLabs:C8051F385-GQ +MCU_SiliconLabs:C8051F386-GQ +MCU_SiliconLabs:C8051F387-GM +MCU_SiliconLabs:C8051F387-GQ +MCU_SiliconLabs:C8051F38C-GM +MCU_SiliconLabs:C8051F38C-GQ +MCU_SiliconLabs:EFM32G230F128G-E-QFN64 +MCU_SiliconLabs:EFM32HG108F32G-C-QFN24 +MCU_SiliconLabs:EFM32HG108F64G-C-QFN24 +MCU_SiliconLabs:EFM32HG308F32G-C-QFN24 +MCU_SiliconLabs:EFM32HG308F64G-C-QFN24 +MCU_SiliconLabs:EFM32ZG108F16-B-QFN24 +MCU_SiliconLabs:EFM32ZG108F32-B-QFN24 +MCU_SiliconLabs:EFM32ZG108F4-B-QFN24 +MCU_SiliconLabs:EFM32ZG108F8-B-QFN24 +MCU_SiliconLabs:EFM32ZG110F16-B-QFN24 +MCU_SiliconLabs:EFM32ZG110F32-B-QFN24 +MCU_SiliconLabs:EFM32ZG110F4-B-QFN24 +MCU_SiliconLabs:EFM32ZG110F8-B-QFN24 +MCU_SiliconLabs:EFM8BB10F2A-A-QFN20 +MCU_SiliconLabs:EFM8BB10F2G-A-QFN20 +MCU_SiliconLabs:EFM8BB10F2I-A-QFN20 +MCU_SiliconLabs:EFM8BB10F4A-A-QFN20 +MCU_SiliconLabs:EFM8BB10F4G-A-QFN20 +MCU_SiliconLabs:EFM8BB10F4I-A-QFN20 +MCU_SiliconLabs:EFM8BB10F8A-A-QFN20 +MCU_SiliconLabs:EFM8BB10F8G-A-QFN20 +MCU_SiliconLabs:EFM8BB10F8G-A-QSOP24 +MCU_SiliconLabs:EFM8BB10F8G-A-SOIC16 +MCU_SiliconLabs:EFM8BB10F8I-A-QFN20 +MCU_SiliconLabs:EFM8BB10F8I-A-QSOP24 +MCU_SiliconLabs:EFM8BB10F8I-A-SOIC16 +MCU_SiliconLabs:EFM8LB12F32E-C-QFP32 +MCU_SiliconLabs:EFM8LB12F64E-C-QFP32 +MCU_SiliconLabs:EFM8UB30F40G-A-QFN20 +MCU_SiliconLabs:EFM8UB31F40G-A-QFN24 +MCU_SiliconLabs:EFM8UB31F40G-A-QSOP24 +MCU_SiliconLabs:EFR32xG23xxxxF512xM48 +MCU_STC:IAP15W205S-35x-SOP16 +MCU_STC:IRC15W207S-35x-SOP16 +MCU_STC:STC15W201S-35x-SOP16 +MCU_STC:STC15W202S-35x-SOP16 +MCU_STC:STC15W203S-35x-SOP16 +MCU_STC:STC15W204S-35x-SOP16 +MCU_STC:STC8G1K04-38I-TSSOP20 +MCU_STC:STC8G1K08-38I-TSSOP20 +MCU_STC:STC8G1K08A-36I-DFN8 +MCU_STC:STC8G1K17-38I-TSSOP20 +MCU_ST_STM32C0:STM32C011D6Yx +MCU_ST_STM32C0:STM32C011F4Px +MCU_ST_STM32C0:STM32C011F4Ux +MCU_ST_STM32C0:STM32C011F6Px +MCU_ST_STM32C0:STM32C011F6Ux +MCU_ST_STM32C0:STM32C011F_4-6_Px +MCU_ST_STM32C0:STM32C011F_4-6_Ux +MCU_ST_STM32C0:STM32C011J4Mx +MCU_ST_STM32C0:STM32C011J6Mx +MCU_ST_STM32C0:STM32C011J_4-6_Mx +MCU_ST_STM32C0:STM32C031C4Tx +MCU_ST_STM32C0:STM32C031C4Ux +MCU_ST_STM32C0:STM32C031C6Tx +MCU_ST_STM32C0:STM32C031C6Ux +MCU_ST_STM32C0:STM32C031C_4-6_Tx +MCU_ST_STM32C0:STM32C031C_4-6_Ux +MCU_ST_STM32C0:STM32C031F4Px +MCU_ST_STM32C0:STM32C031F6Px +MCU_ST_STM32C0:STM32C031F_4-6_Px +MCU_ST_STM32C0:STM32C031G4Ux +MCU_ST_STM32C0:STM32C031G6Ux +MCU_ST_STM32C0:STM32C031G_4-6_Ux +MCU_ST_STM32C0:STM32C031K4Tx +MCU_ST_STM32C0:STM32C031K4Ux +MCU_ST_STM32C0:STM32C031K6Tx +MCU_ST_STM32C0:STM32C031K6Ux +MCU_ST_STM32C0:STM32C031K_4-6_Tx +MCU_ST_STM32C0:STM32C031K_4-6_Ux +MCU_ST_STM32C0:STM32C071C8Tx +MCU_ST_STM32C0:STM32C071C8TxN +MCU_ST_STM32C0:STM32C071C8Ux +MCU_ST_STM32C0:STM32C071C8UxN +MCU_ST_STM32C0:STM32C071CBTx +MCU_ST_STM32C0:STM32C071CBTxN +MCU_ST_STM32C0:STM32C071CBUx +MCU_ST_STM32C0:STM32C071CBUxN +MCU_ST_STM32C0:STM32C071F8Px +MCU_ST_STM32C0:STM32C071F8PxN +MCU_ST_STM32C0:STM32C071FBPx +MCU_ST_STM32C0:STM32C071FBPxN +MCU_ST_STM32C0:STM32C071G8Ux +MCU_ST_STM32C0:STM32C071G8UxN +MCU_ST_STM32C0:STM32C071GBUx +MCU_ST_STM32C0:STM32C071GBUxN +MCU_ST_STM32C0:STM32C071K8Tx +MCU_ST_STM32C0:STM32C071K8TxN +MCU_ST_STM32C0:STM32C071K8Ux +MCU_ST_STM32C0:STM32C071K8UxN +MCU_ST_STM32C0:STM32C071KBTx +MCU_ST_STM32C0:STM32C071KBTxN +MCU_ST_STM32C0:STM32C071KBUx +MCU_ST_STM32C0:STM32C071KBUxN +MCU_ST_STM32C0:STM32C071R8Tx +MCU_ST_STM32C0:STM32C071R8TxN +MCU_ST_STM32C0:STM32C071RBIxN +MCU_ST_STM32C0:STM32C071RBTx +MCU_ST_STM32C0:STM32C071RBTxN +MCU_ST_STM32F0:STM32F030C6Tx +MCU_ST_STM32F0:STM32F030C8Tx +MCU_ST_STM32F0:STM32F030CCTx +MCU_ST_STM32F0:STM32F030F4Px +MCU_ST_STM32F0:STM32F030K6Tx +MCU_ST_STM32F0:STM32F030R8Tx +MCU_ST_STM32F0:STM32F030RCTx +MCU_ST_STM32F0:STM32F031C4Tx +MCU_ST_STM32F0:STM32F031C6Tx +MCU_ST_STM32F0:STM32F031C_4-6_Tx +MCU_ST_STM32F0:STM32F031E6Yx +MCU_ST_STM32F0:STM32F031F4Px +MCU_ST_STM32F0:STM32F031F6Px +MCU_ST_STM32F0:STM32F031F_4-6_Px +MCU_ST_STM32F0:STM32F031G4Ux +MCU_ST_STM32F0:STM32F031G6Ux +MCU_ST_STM32F0:STM32F031G_4-6_Ux +MCU_ST_STM32F0:STM32F031K4Ux +MCU_ST_STM32F0:STM32F031K6Tx +MCU_ST_STM32F0:STM32F031K6Ux +MCU_ST_STM32F0:STM32F031K_4-6_Ux +MCU_ST_STM32F0:STM32F038C6Tx +MCU_ST_STM32F0:STM32F038E6Yx +MCU_ST_STM32F0:STM32F038F6Px +MCU_ST_STM32F0:STM32F038G6Ux +MCU_ST_STM32F0:STM32F038K6Ux +MCU_ST_STM32F0:STM32F042C4Tx +MCU_ST_STM32F0:STM32F042C4Ux +MCU_ST_STM32F0:STM32F042C6Tx +MCU_ST_STM32F0:STM32F042C6Ux +MCU_ST_STM32F0:STM32F042C_4-6_Tx +MCU_ST_STM32F0:STM32F042C_4-6_Ux +MCU_ST_STM32F0:STM32F042F4Px +MCU_ST_STM32F0:STM32F042F6Px +MCU_ST_STM32F0:STM32F042G4Ux +MCU_ST_STM32F0:STM32F042G6Ux +MCU_ST_STM32F0:STM32F042G_4-6_Ux +MCU_ST_STM32F0:STM32F042K4Tx +MCU_ST_STM32F0:STM32F042K4Ux +MCU_ST_STM32F0:STM32F042K6Tx +MCU_ST_STM32F0:STM32F042K6Ux +MCU_ST_STM32F0:STM32F042K_4-6_Tx +MCU_ST_STM32F0:STM32F042K_4-6_Ux +MCU_ST_STM32F0:STM32F042T6Yx +MCU_ST_STM32F0:STM32F048C6Ux +MCU_ST_STM32F0:STM32F048G6Ux +MCU_ST_STM32F0:STM32F048T6Yx +MCU_ST_STM32F0:STM32F051C4Tx +MCU_ST_STM32F0:STM32F051C4Ux +MCU_ST_STM32F0:STM32F051C6Tx +MCU_ST_STM32F0:STM32F051C6Ux +MCU_ST_STM32F0:STM32F051C8Tx +MCU_ST_STM32F0:STM32F051C8Ux +MCU_ST_STM32F0:STM32F051K4Tx +MCU_ST_STM32F0:STM32F051K4Ux +MCU_ST_STM32F0:STM32F051K6Tx +MCU_ST_STM32F0:STM32F051K6Ux +MCU_ST_STM32F0:STM32F051K8Tx +MCU_ST_STM32F0:STM32F051K8Ux +MCU_ST_STM32F0:STM32F051R4Tx +MCU_ST_STM32F0:STM32F051R6Tx +MCU_ST_STM32F0:STM32F051R8Hx +MCU_ST_STM32F0:STM32F051R8Tx +MCU_ST_STM32F0:STM32F051T8Yx +MCU_ST_STM32F0:STM32F058C8Ux +MCU_ST_STM32F0:STM32F058R8Hx +MCU_ST_STM32F0:STM32F058R8Tx +MCU_ST_STM32F0:STM32F058T8Yx +MCU_ST_STM32F0:STM32F070C6Tx +MCU_ST_STM32F0:STM32F070CBTx +MCU_ST_STM32F0:STM32F070F6Px +MCU_ST_STM32F0:STM32F070RBTx +MCU_ST_STM32F0:STM32F071C8Tx +MCU_ST_STM32F0:STM32F071C8Ux +MCU_ST_STM32F0:STM32F071CBTx +MCU_ST_STM32F0:STM32F071CBUx +MCU_ST_STM32F0:STM32F071CBYx +MCU_ST_STM32F0:STM32F071C_8-B_Tx +MCU_ST_STM32F0:STM32F071C_8-B_Ux +MCU_ST_STM32F0:STM32F071RBTx +MCU_ST_STM32F0:STM32F071V8Hx +MCU_ST_STM32F0:STM32F071V8Tx +MCU_ST_STM32F0:STM32F071VBHx +MCU_ST_STM32F0:STM32F071VBTx +MCU_ST_STM32F0:STM32F071V_8-B_Hx +MCU_ST_STM32F0:STM32F071V_8-B_Tx +MCU_ST_STM32F0:STM32F072C8Tx +MCU_ST_STM32F0:STM32F072C8Ux +MCU_ST_STM32F0:STM32F072CBTx +MCU_ST_STM32F0:STM32F072CBUx +MCU_ST_STM32F0:STM32F072CBYx +MCU_ST_STM32F0:STM32F072C_8-B_Tx +MCU_ST_STM32F0:STM32F072C_8-B_Ux +MCU_ST_STM32F0:STM32F072R8Tx +MCU_ST_STM32F0:STM32F072RBHx +MCU_ST_STM32F0:STM32F072RBIx +MCU_ST_STM32F0:STM32F072RBTx +MCU_ST_STM32F0:STM32F072R_8-B_Tx +MCU_ST_STM32F0:STM32F072V8Hx +MCU_ST_STM32F0:STM32F072V8Tx +MCU_ST_STM32F0:STM32F072VBHx +MCU_ST_STM32F0:STM32F072VBTx +MCU_ST_STM32F0:STM32F072V_8-B_Hx +MCU_ST_STM32F0:STM32F072V_8-B_Tx +MCU_ST_STM32F0:STM32F078CBTx +MCU_ST_STM32F0:STM32F078CBUx +MCU_ST_STM32F0:STM32F078CBYx +MCU_ST_STM32F0:STM32F078RBHx +MCU_ST_STM32F0:STM32F078RBTx +MCU_ST_STM32F0:STM32F078VBHx +MCU_ST_STM32F0:STM32F078VBTx +MCU_ST_STM32F0:STM32F091CBTx +MCU_ST_STM32F0:STM32F091CBUx +MCU_ST_STM32F0:STM32F091CCTx +MCU_ST_STM32F0:STM32F091CCUx +MCU_ST_STM32F0:STM32F091C_B-C_Tx +MCU_ST_STM32F0:STM32F091C_B-C_Ux +MCU_ST_STM32F0:STM32F091RBTx +MCU_ST_STM32F0:STM32F091RCHx +MCU_ST_STM32F0:STM32F091RCTx +MCU_ST_STM32F0:STM32F091RCYx +MCU_ST_STM32F0:STM32F091R_B-C_Tx +MCU_ST_STM32F0:STM32F091VBTx +MCU_ST_STM32F0:STM32F091VCHx +MCU_ST_STM32F0:STM32F091VCTx +MCU_ST_STM32F0:STM32F091V_B-C_Tx +MCU_ST_STM32F0:STM32F098CCTx +MCU_ST_STM32F0:STM32F098CCUx +MCU_ST_STM32F0:STM32F098RCHx +MCU_ST_STM32F0:STM32F098RCTx +MCU_ST_STM32F0:STM32F098RCYx +MCU_ST_STM32F0:STM32F098VCHx +MCU_ST_STM32F0:STM32F098VCTx +MCU_ST_STM32F1:STM32F100C4Tx +MCU_ST_STM32F1:STM32F100C6Tx +MCU_ST_STM32F1:STM32F100C8Tx +MCU_ST_STM32F1:STM32F100CBTx +MCU_ST_STM32F1:STM32F100C_4-6_Tx +MCU_ST_STM32F1:STM32F100C_8-B_Tx +MCU_ST_STM32F1:STM32F100R4Hx +MCU_ST_STM32F1:STM32F100R4Tx +MCU_ST_STM32F1:STM32F100R6Hx +MCU_ST_STM32F1:STM32F100R6Tx +MCU_ST_STM32F1:STM32F100R8Hx +MCU_ST_STM32F1:STM32F100R8Tx +MCU_ST_STM32F1:STM32F100RBHx +MCU_ST_STM32F1:STM32F100RBTx +MCU_ST_STM32F1:STM32F100RCTx +MCU_ST_STM32F1:STM32F100RDTx +MCU_ST_STM32F1:STM32F100RETx +MCU_ST_STM32F1:STM32F100R_4-6_Hx +MCU_ST_STM32F1:STM32F100R_4-6_Tx +MCU_ST_STM32F1:STM32F100R_8-B_Hx +MCU_ST_STM32F1:STM32F100R_8-B_Tx +MCU_ST_STM32F1:STM32F100R_C-D-E_Tx +MCU_ST_STM32F1:STM32F100V8Tx +MCU_ST_STM32F1:STM32F100VBTx +MCU_ST_STM32F1:STM32F100VCTx +MCU_ST_STM32F1:STM32F100VDTx +MCU_ST_STM32F1:STM32F100VETx +MCU_ST_STM32F1:STM32F100V_8-B_Tx +MCU_ST_STM32F1:STM32F100V_C-D-E_Tx +MCU_ST_STM32F1:STM32F100ZCTx +MCU_ST_STM32F1:STM32F100ZDTx +MCU_ST_STM32F1:STM32F100ZETx +MCU_ST_STM32F1:STM32F100Z_C-D-E_Tx +MCU_ST_STM32F1:STM32F101C4Tx +MCU_ST_STM32F1:STM32F101C6Tx +MCU_ST_STM32F1:STM32F101C8Tx +MCU_ST_STM32F1:STM32F101C8Ux +MCU_ST_STM32F1:STM32F101CBTx +MCU_ST_STM32F1:STM32F101CBUx +MCU_ST_STM32F1:STM32F101C_4-6_Tx +MCU_ST_STM32F1:STM32F101C_8-B_Tx +MCU_ST_STM32F1:STM32F101C_8-B_Ux +MCU_ST_STM32F1:STM32F101R4Tx +MCU_ST_STM32F1:STM32F101R6Tx +MCU_ST_STM32F1:STM32F101R8Tx +MCU_ST_STM32F1:STM32F101RBHx +MCU_ST_STM32F1:STM32F101RBTx +MCU_ST_STM32F1:STM32F101RCTx +MCU_ST_STM32F1:STM32F101RDTx +MCU_ST_STM32F1:STM32F101RETx +MCU_ST_STM32F1:STM32F101RFTx +MCU_ST_STM32F1:STM32F101RGTx +MCU_ST_STM32F1:STM32F101R_4-6_Tx +MCU_ST_STM32F1:STM32F101R_8-B_Tx +MCU_ST_STM32F1:STM32F101R_C-D-E_Tx +MCU_ST_STM32F1:STM32F101R_F-G_Tx +MCU_ST_STM32F1:STM32F101T4Ux +MCU_ST_STM32F1:STM32F101T6Ux +MCU_ST_STM32F1:STM32F101T8Ux +MCU_ST_STM32F1:STM32F101TBUx +MCU_ST_STM32F1:STM32F101T_4-6_Ux +MCU_ST_STM32F1:STM32F101T_8-B_Ux +MCU_ST_STM32F1:STM32F101V8Tx +MCU_ST_STM32F1:STM32F101VBTx +MCU_ST_STM32F1:STM32F101VCTx +MCU_ST_STM32F1:STM32F101VDTx +MCU_ST_STM32F1:STM32F101VETx +MCU_ST_STM32F1:STM32F101VFTx +MCU_ST_STM32F1:STM32F101VGTx +MCU_ST_STM32F1:STM32F101V_8-B_Tx +MCU_ST_STM32F1:STM32F101V_C-D-E_Tx +MCU_ST_STM32F1:STM32F101V_F-G_Tx +MCU_ST_STM32F1:STM32F101ZCTx +MCU_ST_STM32F1:STM32F101ZDTx +MCU_ST_STM32F1:STM32F101ZETx +MCU_ST_STM32F1:STM32F101ZFTx +MCU_ST_STM32F1:STM32F101ZGTx +MCU_ST_STM32F1:STM32F101Z_C-D-E_Tx +MCU_ST_STM32F1:STM32F101Z_F-G_Tx +MCU_ST_STM32F1:STM32F102C4Tx +MCU_ST_STM32F1:STM32F102C6Tx +MCU_ST_STM32F1:STM32F102C8Tx +MCU_ST_STM32F1:STM32F102CBTx +MCU_ST_STM32F1:STM32F102C_4-6_Tx +MCU_ST_STM32F1:STM32F102C_8-B_Tx +MCU_ST_STM32F1:STM32F102R4Tx +MCU_ST_STM32F1:STM32F102R6Tx +MCU_ST_STM32F1:STM32F102R8Tx +MCU_ST_STM32F1:STM32F102RBTx +MCU_ST_STM32F1:STM32F102R_4-6_Tx +MCU_ST_STM32F1:STM32F102R_8-B_Tx +MCU_ST_STM32F1:STM32F103C4Tx +MCU_ST_STM32F1:STM32F103C6Tx +MCU_ST_STM32F1:STM32F103C6Ux +MCU_ST_STM32F1:STM32F103C8Tx +MCU_ST_STM32F1:STM32F103CBTx +MCU_ST_STM32F1:STM32F103CBUx +MCU_ST_STM32F1:STM32F103C_4-6_Tx +MCU_ST_STM32F1:STM32F103C_8-B_Tx +MCU_ST_STM32F1:STM32F103R4Hx +MCU_ST_STM32F1:STM32F103R4Tx +MCU_ST_STM32F1:STM32F103R6Hx +MCU_ST_STM32F1:STM32F103R6Tx +MCU_ST_STM32F1:STM32F103R8Hx +MCU_ST_STM32F1:STM32F103R8Tx +MCU_ST_STM32F1:STM32F103RBHx +MCU_ST_STM32F1:STM32F103RBTx +MCU_ST_STM32F1:STM32F103RCTx +MCU_ST_STM32F1:STM32F103RCYx +MCU_ST_STM32F1:STM32F103RDTx +MCU_ST_STM32F1:STM32F103RDYx +MCU_ST_STM32F1:STM32F103RETx +MCU_ST_STM32F1:STM32F103REYx +MCU_ST_STM32F1:STM32F103RFTx +MCU_ST_STM32F1:STM32F103RGTx +MCU_ST_STM32F1:STM32F103R_4-6_Hx +MCU_ST_STM32F1:STM32F103R_4-6_Tx +MCU_ST_STM32F1:STM32F103R_8-B_Hx +MCU_ST_STM32F1:STM32F103R_8-B_Tx +MCU_ST_STM32F1:STM32F103R_C-D-E_Tx +MCU_ST_STM32F1:STM32F103R_C-D-E_Yx +MCU_ST_STM32F1:STM32F103R_F-G_Tx +MCU_ST_STM32F1:STM32F103T4Ux +MCU_ST_STM32F1:STM32F103T6Ux +MCU_ST_STM32F1:STM32F103T8Ux +MCU_ST_STM32F1:STM32F103TBUx +MCU_ST_STM32F1:STM32F103T_4-6_Ux +MCU_ST_STM32F1:STM32F103T_8-B_Ux +MCU_ST_STM32F1:STM32F103V8Hx +MCU_ST_STM32F1:STM32F103V8Tx +MCU_ST_STM32F1:STM32F103VBHx +MCU_ST_STM32F1:STM32F103VBIx +MCU_ST_STM32F1:STM32F103VBTx +MCU_ST_STM32F1:STM32F103VCHx +MCU_ST_STM32F1:STM32F103VCTx +MCU_ST_STM32F1:STM32F103VDHx +MCU_ST_STM32F1:STM32F103VDTx +MCU_ST_STM32F1:STM32F103VEHx +MCU_ST_STM32F1:STM32F103VETx +MCU_ST_STM32F1:STM32F103VFTx +MCU_ST_STM32F1:STM32F103VGTx +MCU_ST_STM32F1:STM32F103V_8-B_Hx +MCU_ST_STM32F1:STM32F103V_8-B_Tx +MCU_ST_STM32F1:STM32F103V_C-D-E_Hx +MCU_ST_STM32F1:STM32F103V_C-D-E_Tx +MCU_ST_STM32F1:STM32F103V_F-G_Tx +MCU_ST_STM32F1:STM32F103ZCHx +MCU_ST_STM32F1:STM32F103ZCTx +MCU_ST_STM32F1:STM32F103ZDHx +MCU_ST_STM32F1:STM32F103ZDTx +MCU_ST_STM32F1:STM32F103ZEHx +MCU_ST_STM32F1:STM32F103ZETx +MCU_ST_STM32F1:STM32F103ZFHx +MCU_ST_STM32F1:STM32F103ZFTx +MCU_ST_STM32F1:STM32F103ZGHx +MCU_ST_STM32F1:STM32F103ZGTx +MCU_ST_STM32F1:STM32F103Z_C-D-E_Hx +MCU_ST_STM32F1:STM32F103Z_C-D-E_Tx +MCU_ST_STM32F1:STM32F103Z_F-G_Hx +MCU_ST_STM32F1:STM32F103Z_F-G_Tx +MCU_ST_STM32F1:STM32F105R8Tx +MCU_ST_STM32F1:STM32F105RBTx +MCU_ST_STM32F1:STM32F105RCTx +MCU_ST_STM32F1:STM32F105R_8-B-C_Tx +MCU_ST_STM32F1:STM32F105V8Hx +MCU_ST_STM32F1:STM32F105V8Tx +MCU_ST_STM32F1:STM32F105VBHx +MCU_ST_STM32F1:STM32F105VBTx +MCU_ST_STM32F1:STM32F105VCTx +MCU_ST_STM32F1:STM32F105V_8-B-C_Tx +MCU_ST_STM32F1:STM32F105V_8-B_Hx +MCU_ST_STM32F1:STM32F107RBTx +MCU_ST_STM32F1:STM32F107RCTx +MCU_ST_STM32F1:STM32F107R_B-C_Tx +MCU_ST_STM32F1:STM32F107VBTx +MCU_ST_STM32F1:STM32F107VCHx +MCU_ST_STM32F1:STM32F107VCTx +MCU_ST_STM32F1:STM32F107V_B-C_Tx +MCU_ST_STM32F2:STM32F205RBTx +MCU_ST_STM32F2:STM32F205RCTx +MCU_ST_STM32F2:STM32F205RETx +MCU_ST_STM32F2:STM32F205REYx +MCU_ST_STM32F2:STM32F205RFTx +MCU_ST_STM32F2:STM32F205RGEx +MCU_ST_STM32F2:STM32F205RGTx +MCU_ST_STM32F2:STM32F205RGYx +MCU_ST_STM32F2:STM32F205R_B-C-E-F-G_Tx +MCU_ST_STM32F2:STM32F205R_E-G_Yx +MCU_ST_STM32F2:STM32F205VBTx +MCU_ST_STM32F2:STM32F205VCTx +MCU_ST_STM32F2:STM32F205VETx +MCU_ST_STM32F2:STM32F205VFTx +MCU_ST_STM32F2:STM32F205VGTx +MCU_ST_STM32F2:STM32F205V_B-C-E-F-G_Tx +MCU_ST_STM32F2:STM32F205ZCTx +MCU_ST_STM32F2:STM32F205ZETx +MCU_ST_STM32F2:STM32F205ZFTx +MCU_ST_STM32F2:STM32F205ZGTx +MCU_ST_STM32F2:STM32F205Z_C-E-F-G_Tx +MCU_ST_STM32F2:STM32F207ICHx +MCU_ST_STM32F2:STM32F207ICTx +MCU_ST_STM32F2:STM32F207IEHx +MCU_ST_STM32F2:STM32F207IETx +MCU_ST_STM32F2:STM32F207IFHx +MCU_ST_STM32F2:STM32F207IFTx +MCU_ST_STM32F2:STM32F207IGHx +MCU_ST_STM32F2:STM32F207IGTx +MCU_ST_STM32F2:STM32F207I_C-E-F-G_Hx +MCU_ST_STM32F2:STM32F207I_C-E-F-G_Tx +MCU_ST_STM32F2:STM32F207VCTx +MCU_ST_STM32F2:STM32F207VETx +MCU_ST_STM32F2:STM32F207VFTx +MCU_ST_STM32F2:STM32F207VGTx +MCU_ST_STM32F2:STM32F207V_C-E-F-G_Tx +MCU_ST_STM32F2:STM32F207ZCTx +MCU_ST_STM32F2:STM32F207ZETx +MCU_ST_STM32F2:STM32F207ZFTx +MCU_ST_STM32F2:STM32F207ZGTx +MCU_ST_STM32F2:STM32F207Z_C-E-F-G_Tx +MCU_ST_STM32F2:STM32F215RETx +MCU_ST_STM32F2:STM32F215RGTx +MCU_ST_STM32F2:STM32F215R_E-G_Tx +MCU_ST_STM32F2:STM32F215VETx +MCU_ST_STM32F2:STM32F215VGTx +MCU_ST_STM32F2:STM32F215V_E-G_Tx +MCU_ST_STM32F2:STM32F215ZETx +MCU_ST_STM32F2:STM32F215ZGTx +MCU_ST_STM32F2:STM32F215Z_E-G_Tx +MCU_ST_STM32F2:STM32F217IEHx +MCU_ST_STM32F2:STM32F217IETx +MCU_ST_STM32F2:STM32F217IGHx +MCU_ST_STM32F2:STM32F217IGTx +MCU_ST_STM32F2:STM32F217I_E-G_Hx +MCU_ST_STM32F2:STM32F217I_E-G_Tx +MCU_ST_STM32F2:STM32F217VETx +MCU_ST_STM32F2:STM32F217VGTx +MCU_ST_STM32F2:STM32F217V_E-G_Tx +MCU_ST_STM32F2:STM32F217ZETx +MCU_ST_STM32F2:STM32F217ZGTx +MCU_ST_STM32F2:STM32F217Z_E-G_Tx +MCU_ST_STM32F3:STM32F301C6Tx +MCU_ST_STM32F3:STM32F301C8Tx +MCU_ST_STM32F3:STM32F301C8Yx +MCU_ST_STM32F3:STM32F301C_6-8_Tx +MCU_ST_STM32F3:STM32F301K6Tx +MCU_ST_STM32F3:STM32F301K6Ux +MCU_ST_STM32F3:STM32F301K8Tx +MCU_ST_STM32F3:STM32F301K8Ux +MCU_ST_STM32F3:STM32F301K_6-8_Tx +MCU_ST_STM32F3:STM32F301K_6-8_Ux +MCU_ST_STM32F3:STM32F301R6Tx +MCU_ST_STM32F3:STM32F301R8Tx +MCU_ST_STM32F3:STM32F301R_6-8_Tx +MCU_ST_STM32F3:STM32F302C6Tx +MCU_ST_STM32F3:STM32F302C8Tx +MCU_ST_STM32F3:STM32F302C8Yx +MCU_ST_STM32F3:STM32F302CBTx +MCU_ST_STM32F3:STM32F302CCTx +MCU_ST_STM32F3:STM32F302C_6-8_Tx +MCU_ST_STM32F3:STM32F302C_B-C_Tx +MCU_ST_STM32F3:STM32F302K6Ux +MCU_ST_STM32F3:STM32F302K8Ux +MCU_ST_STM32F3:STM32F302K_6-8_Ux +MCU_ST_STM32F3:STM32F302R6Tx +MCU_ST_STM32F3:STM32F302R8Tx +MCU_ST_STM32F3:STM32F302RBTx +MCU_ST_STM32F3:STM32F302RCTx +MCU_ST_STM32F3:STM32F302RDTx +MCU_ST_STM32F3:STM32F302RETx +MCU_ST_STM32F3:STM32F302R_6-8_Tx +MCU_ST_STM32F3:STM32F302R_B-C_Tx +MCU_ST_STM32F3:STM32F302R_D-E_Tx +MCU_ST_STM32F3:STM32F302VBTx +MCU_ST_STM32F3:STM32F302VCTx +MCU_ST_STM32F3:STM32F302VCYx +MCU_ST_STM32F3:STM32F302VDHx +MCU_ST_STM32F3:STM32F302VDTx +MCU_ST_STM32F3:STM32F302VEHx +MCU_ST_STM32F3:STM32F302VETx +MCU_ST_STM32F3:STM32F302V_B-C_Tx +MCU_ST_STM32F3:STM32F302V_D-E_Hx +MCU_ST_STM32F3:STM32F302V_D-E_Tx +MCU_ST_STM32F3:STM32F302ZDTx +MCU_ST_STM32F3:STM32F302ZETx +MCU_ST_STM32F3:STM32F302Z_D-E_Tx +MCU_ST_STM32F3:STM32F303C6Tx +MCU_ST_STM32F3:STM32F303C8Tx +MCU_ST_STM32F3:STM32F303C8Yx +MCU_ST_STM32F3:STM32F303CBTx +MCU_ST_STM32F3:STM32F303CCTx +MCU_ST_STM32F3:STM32F303C_6-8_Tx +MCU_ST_STM32F3:STM32F303C_B-C_Tx +MCU_ST_STM32F3:STM32F303K6Tx +MCU_ST_STM32F3:STM32F303K6Ux +MCU_ST_STM32F3:STM32F303K8Tx +MCU_ST_STM32F3:STM32F303K8Ux +MCU_ST_STM32F3:STM32F303K_6-8_Tx +MCU_ST_STM32F3:STM32F303K_6-8_Ux +MCU_ST_STM32F3:STM32F303R6Tx +MCU_ST_STM32F3:STM32F303R8Tx +MCU_ST_STM32F3:STM32F303RBTx +MCU_ST_STM32F3:STM32F303RCTx +MCU_ST_STM32F3:STM32F303RDTx +MCU_ST_STM32F3:STM32F303RETx +MCU_ST_STM32F3:STM32F303R_6-8_Tx +MCU_ST_STM32F3:STM32F303R_B-C_Tx +MCU_ST_STM32F3:STM32F303R_D-E_Tx +MCU_ST_STM32F3:STM32F303VBTx +MCU_ST_STM32F3:STM32F303VCTx +MCU_ST_STM32F3:STM32F303VCYx +MCU_ST_STM32F3:STM32F303VDHx +MCU_ST_STM32F3:STM32F303VDTx +MCU_ST_STM32F3:STM32F303VEHx +MCU_ST_STM32F3:STM32F303VETx +MCU_ST_STM32F3:STM32F303VEYx +MCU_ST_STM32F3:STM32F303V_B-C_Tx +MCU_ST_STM32F3:STM32F303V_D-E_Hx +MCU_ST_STM32F3:STM32F303V_D-E_Tx +MCU_ST_STM32F3:STM32F303ZDTx +MCU_ST_STM32F3:STM32F303ZETx +MCU_ST_STM32F3:STM32F303Z_D-E_Tx +MCU_ST_STM32F3:STM32F318C8Tx +MCU_ST_STM32F3:STM32F318C8Yx +MCU_ST_STM32F3:STM32F318K8Ux +MCU_ST_STM32F3:STM32F328C8Tx +MCU_ST_STM32F3:STM32F334C4Tx +MCU_ST_STM32F3:STM32F334C6Tx +MCU_ST_STM32F3:STM32F334C8Tx +MCU_ST_STM32F3:STM32F334C8Yx +MCU_ST_STM32F3:STM32F334C_4-6-8_Tx +MCU_ST_STM32F3:STM32F334K4Tx +MCU_ST_STM32F3:STM32F334K4Ux +MCU_ST_STM32F3:STM32F334K6Tx +MCU_ST_STM32F3:STM32F334K6Ux +MCU_ST_STM32F3:STM32F334K8Tx +MCU_ST_STM32F3:STM32F334K8Ux +MCU_ST_STM32F3:STM32F334K_4-6-8_Tx +MCU_ST_STM32F3:STM32F334K_4-6-8_Ux +MCU_ST_STM32F3:STM32F334R6Tx +MCU_ST_STM32F3:STM32F334R8Tx +MCU_ST_STM32F3:STM32F334R_6-8_Tx +MCU_ST_STM32F3:STM32F358CCTx +MCU_ST_STM32F3:STM32F358RCTx +MCU_ST_STM32F3:STM32F358VCTx +MCU_ST_STM32F3:STM32F373C8Tx +MCU_ST_STM32F3:STM32F373CBTx +MCU_ST_STM32F3:STM32F373CCTx +MCU_ST_STM32F3:STM32F373C_8-B-C_Tx +MCU_ST_STM32F3:STM32F373R8Tx +MCU_ST_STM32F3:STM32F373RBTx +MCU_ST_STM32F3:STM32F373RCTx +MCU_ST_STM32F3:STM32F373R_8-B-C_Tx +MCU_ST_STM32F3:STM32F373V8Hx +MCU_ST_STM32F3:STM32F373V8Tx +MCU_ST_STM32F3:STM32F373VBHx +MCU_ST_STM32F3:STM32F373VBTx +MCU_ST_STM32F3:STM32F373VCHx +MCU_ST_STM32F3:STM32F373VCTx +MCU_ST_STM32F3:STM32F373V_8-B-C_Hx +MCU_ST_STM32F3:STM32F373V_8-B-C_Tx +MCU_ST_STM32F3:STM32F378CCTx +MCU_ST_STM32F3:STM32F378RCTx +MCU_ST_STM32F3:STM32F378RCYx +MCU_ST_STM32F3:STM32F378VCHx +MCU_ST_STM32F3:STM32F378VCTx +MCU_ST_STM32F3:STM32F398VETx +MCU_ST_STM32F4:STM32F401CBUx +MCU_ST_STM32F4:STM32F401CBYx +MCU_ST_STM32F4:STM32F401CCFx +MCU_ST_STM32F4:STM32F401CCUx +MCU_ST_STM32F4:STM32F401CCYx +MCU_ST_STM32F4:STM32F401CDUx +MCU_ST_STM32F4:STM32F401CDYx +MCU_ST_STM32F4:STM32F401CEUx +MCU_ST_STM32F4:STM32F401CEYx +MCU_ST_STM32F4:STM32F401C_B-C_Ux +MCU_ST_STM32F4:STM32F401C_B-C_Yx +MCU_ST_STM32F4:STM32F401C_D-E_Ux +MCU_ST_STM32F4:STM32F401C_D-E_Yx +MCU_ST_STM32F4:STM32F401RBTx +MCU_ST_STM32F4:STM32F401RCTx +MCU_ST_STM32F4:STM32F401RDTx +MCU_ST_STM32F4:STM32F401RETx +MCU_ST_STM32F4:STM32F401R_B-C_Tx +MCU_ST_STM32F4:STM32F401R_D-E_Tx +MCU_ST_STM32F4:STM32F401VBHx +MCU_ST_STM32F4:STM32F401VBTx +MCU_ST_STM32F4:STM32F401VCHx +MCU_ST_STM32F4:STM32F401VCTx +MCU_ST_STM32F4:STM32F401VDHx +MCU_ST_STM32F4:STM32F401VDTx +MCU_ST_STM32F4:STM32F401VEHx +MCU_ST_STM32F4:STM32F401VETx +MCU_ST_STM32F4:STM32F401V_B-C_Hx +MCU_ST_STM32F4:STM32F401V_B-C_Tx +MCU_ST_STM32F4:STM32F401V_D-E_Hx +MCU_ST_STM32F4:STM32F401V_D-E_Tx +MCU_ST_STM32F4:STM32F405OEYx +MCU_ST_STM32F4:STM32F405OGYx +MCU_ST_STM32F4:STM32F405O_E-G_Yx +MCU_ST_STM32F4:STM32F405RGTx +MCU_ST_STM32F4:STM32F405VGTx +MCU_ST_STM32F4:STM32F405ZGTx +MCU_ST_STM32F4:STM32F407IEHx +MCU_ST_STM32F4:STM32F407IETx +MCU_ST_STM32F4:STM32F407IGHx +MCU_ST_STM32F4:STM32F407IGTx +MCU_ST_STM32F4:STM32F407I_E-G_Hx +MCU_ST_STM32F4:STM32F407I_E-G_Tx +MCU_ST_STM32F4:STM32F407VETx +MCU_ST_STM32F4:STM32F407VGTx +MCU_ST_STM32F4:STM32F407V_E-G_Tx +MCU_ST_STM32F4:STM32F407ZETx +MCU_ST_STM32F4:STM32F407ZGTx +MCU_ST_STM32F4:STM32F407Z_E-G_Tx +MCU_ST_STM32F4:STM32F410C8Tx +MCU_ST_STM32F4:STM32F410C8Ux +MCU_ST_STM32F4:STM32F410CBTx +MCU_ST_STM32F4:STM32F410CBUx +MCU_ST_STM32F4:STM32F410C_8-B_Tx +MCU_ST_STM32F4:STM32F410C_8-B_Ux +MCU_ST_STM32F4:STM32F410R8Ix +MCU_ST_STM32F4:STM32F410R8Tx +MCU_ST_STM32F4:STM32F410RBIx +MCU_ST_STM32F4:STM32F410RBTx +MCU_ST_STM32F4:STM32F410R_8-B_Ix +MCU_ST_STM32F4:STM32F410R_8-B_Tx +MCU_ST_STM32F4:STM32F410T8Yx +MCU_ST_STM32F4:STM32F410TBYx +MCU_ST_STM32F4:STM32F410T_8-B_Yx +MCU_ST_STM32F4:STM32F411CCUx +MCU_ST_STM32F4:STM32F411CCYx +MCU_ST_STM32F4:STM32F411CEUx +MCU_ST_STM32F4:STM32F411CEYx +MCU_ST_STM32F4:STM32F411C_C-E_Ux +MCU_ST_STM32F4:STM32F411C_C-E_Yx +MCU_ST_STM32F4:STM32F411RCTx +MCU_ST_STM32F4:STM32F411RETx +MCU_ST_STM32F4:STM32F411R_C-E_Tx +MCU_ST_STM32F4:STM32F411VCHx +MCU_ST_STM32F4:STM32F411VCTx +MCU_ST_STM32F4:STM32F411VEHx +MCU_ST_STM32F4:STM32F411VETx +MCU_ST_STM32F4:STM32F411V_C-E_Hx +MCU_ST_STM32F4:STM32F411V_C-E_Tx +MCU_ST_STM32F4:STM32F412CEUx +MCU_ST_STM32F4:STM32F412CGUx +MCU_ST_STM32F4:STM32F412C_E-G_Ux +MCU_ST_STM32F4:STM32F412RETx +MCU_ST_STM32F4:STM32F412REYx +MCU_ST_STM32F4:STM32F412REYxP +MCU_ST_STM32F4:STM32F412RGTx +MCU_ST_STM32F4:STM32F412RGYx +MCU_ST_STM32F4:STM32F412RGYxP +MCU_ST_STM32F4:STM32F412R_E-G_Tx +MCU_ST_STM32F4:STM32F412R_E-G_Yx +MCU_ST_STM32F4:STM32F412R_E-G_YxP +MCU_ST_STM32F4:STM32F412VEHx +MCU_ST_STM32F4:STM32F412VETx +MCU_ST_STM32F4:STM32F412VGHx +MCU_ST_STM32F4:STM32F412VGTx +MCU_ST_STM32F4:STM32F412V_E-G_Hx +MCU_ST_STM32F4:STM32F412V_E-G_Tx +MCU_ST_STM32F4:STM32F412ZEJx +MCU_ST_STM32F4:STM32F412ZETx +MCU_ST_STM32F4:STM32F412ZGJx +MCU_ST_STM32F4:STM32F412ZGTx +MCU_ST_STM32F4:STM32F412Z_E-G_Jx +MCU_ST_STM32F4:STM32F412Z_E-G_Tx +MCU_ST_STM32F4:STM32F413CGUx +MCU_ST_STM32F4:STM32F413CHUx +MCU_ST_STM32F4:STM32F413C_G-H_Ux +MCU_ST_STM32F4:STM32F413MGYx +MCU_ST_STM32F4:STM32F413MHYx +MCU_ST_STM32F4:STM32F413M_G-H_Yx +MCU_ST_STM32F4:STM32F413RGTx +MCU_ST_STM32F4:STM32F413RHTx +MCU_ST_STM32F4:STM32F413R_G-H_Tx +MCU_ST_STM32F4:STM32F413VGHx +MCU_ST_STM32F4:STM32F413VGTx +MCU_ST_STM32F4:STM32F413VHHx +MCU_ST_STM32F4:STM32F413VHTx +MCU_ST_STM32F4:STM32F413V_G-H_Hx +MCU_ST_STM32F4:STM32F413V_G-H_Tx +MCU_ST_STM32F4:STM32F413ZGJx +MCU_ST_STM32F4:STM32F413ZGTx +MCU_ST_STM32F4:STM32F413ZHJx +MCU_ST_STM32F4:STM32F413ZHTx +MCU_ST_STM32F4:STM32F413Z_G-H_Jx +MCU_ST_STM32F4:STM32F413Z_G-H_Tx +MCU_ST_STM32F4:STM32F415OGYx +MCU_ST_STM32F4:STM32F415RGTx +MCU_ST_STM32F4:STM32F415VGTx +MCU_ST_STM32F4:STM32F415ZGTx +MCU_ST_STM32F4:STM32F417IEHx +MCU_ST_STM32F4:STM32F417IETx +MCU_ST_STM32F4:STM32F417IGHx +MCU_ST_STM32F4:STM32F417IGTx +MCU_ST_STM32F4:STM32F417I_E-G_Hx +MCU_ST_STM32F4:STM32F417I_E-G_Tx +MCU_ST_STM32F4:STM32F417VETx +MCU_ST_STM32F4:STM32F417VGTx +MCU_ST_STM32F4:STM32F417V_E-G_Tx +MCU_ST_STM32F4:STM32F417ZETx +MCU_ST_STM32F4:STM32F417ZGTx +MCU_ST_STM32F4:STM32F417Z_E-G_Tx +MCU_ST_STM32F4:STM32F423CHUx +MCU_ST_STM32F4:STM32F423MHYx +MCU_ST_STM32F4:STM32F423RHTx +MCU_ST_STM32F4:STM32F423VHHx +MCU_ST_STM32F4:STM32F423VHTx +MCU_ST_STM32F4:STM32F423ZHJx +MCU_ST_STM32F4:STM32F423ZHTx +MCU_ST_STM32F4:STM32F427AGHx +MCU_ST_STM32F4:STM32F427AIHx +MCU_ST_STM32F4:STM32F427A_G-I_Hx +MCU_ST_STM32F4:STM32F427IGHx +MCU_ST_STM32F4:STM32F427IGTx +MCU_ST_STM32F4:STM32F427IIHx +MCU_ST_STM32F4:STM32F427IITx +MCU_ST_STM32F4:STM32F427I_G-I_Hx +MCU_ST_STM32F4:STM32F427I_G-I_Tx +MCU_ST_STM32F4:STM32F427VGTx +MCU_ST_STM32F4:STM32F427VITx +MCU_ST_STM32F4:STM32F427V_G-I_Tx +MCU_ST_STM32F4:STM32F427ZGTx +MCU_ST_STM32F4:STM32F427ZITx +MCU_ST_STM32F4:STM32F427Z_G-I_Tx +MCU_ST_STM32F4:STM32F429AGHx +MCU_ST_STM32F4:STM32F429AIHx +MCU_ST_STM32F4:STM32F429A_G-I_Hx +MCU_ST_STM32F4:STM32F429BETx +MCU_ST_STM32F4:STM32F429BGTx +MCU_ST_STM32F4:STM32F429BITx +MCU_ST_STM32F4:STM32F429B_E-G-I_Tx +MCU_ST_STM32F4:STM32F429IEHx +MCU_ST_STM32F4:STM32F429IETx +MCU_ST_STM32F4:STM32F429IGHx +MCU_ST_STM32F4:STM32F429IGTx +MCU_ST_STM32F4:STM32F429IIHx +MCU_ST_STM32F4:STM32F429IITx +MCU_ST_STM32F4:STM32F429I_E-G-I_Hx +MCU_ST_STM32F4:STM32F429I_E-G_Tx +MCU_ST_STM32F4:STM32F429NEHx +MCU_ST_STM32F4:STM32F429NGHx +MCU_ST_STM32F4:STM32F429NIHx +MCU_ST_STM32F4:STM32F429N_E-G_Hx +MCU_ST_STM32F4:STM32F429VETx +MCU_ST_STM32F4:STM32F429VGTx +MCU_ST_STM32F4:STM32F429VITx +MCU_ST_STM32F4:STM32F429V_E-G_Tx +MCU_ST_STM32F4:STM32F429ZETx +MCU_ST_STM32F4:STM32F429ZGTx +MCU_ST_STM32F4:STM32F429ZGYx +MCU_ST_STM32F4:STM32F429ZITx +MCU_ST_STM32F4:STM32F429ZIYx +MCU_ST_STM32F4:STM32F429Z_E-G_Tx +MCU_ST_STM32F4:STM32F437AIHx +MCU_ST_STM32F4:STM32F437IGHx +MCU_ST_STM32F4:STM32F437IGTx +MCU_ST_STM32F4:STM32F437IIHx +MCU_ST_STM32F4:STM32F437IITx +MCU_ST_STM32F4:STM32F437I_G-I_Hx +MCU_ST_STM32F4:STM32F437I_G-I_Tx +MCU_ST_STM32F4:STM32F437VGTx +MCU_ST_STM32F4:STM32F437VITx +MCU_ST_STM32F4:STM32F437V_G-I_Tx +MCU_ST_STM32F4:STM32F437ZGTx +MCU_ST_STM32F4:STM32F437ZITx +MCU_ST_STM32F4:STM32F437Z_G-I_Tx +MCU_ST_STM32F4:STM32F439AIHx +MCU_ST_STM32F4:STM32F439BGTx +MCU_ST_STM32F4:STM32F439BITx +MCU_ST_STM32F4:STM32F439B_G-I_Tx +MCU_ST_STM32F4:STM32F439IGHx +MCU_ST_STM32F4:STM32F439IGTx +MCU_ST_STM32F4:STM32F439IIHx +MCU_ST_STM32F4:STM32F439IITx +MCU_ST_STM32F4:STM32F439I_G-I_Hx +MCU_ST_STM32F4:STM32F439I_G-I_Tx +MCU_ST_STM32F4:STM32F439NGHx +MCU_ST_STM32F4:STM32F439NIHx +MCU_ST_STM32F4:STM32F439N_G-I_Hx +MCU_ST_STM32F4:STM32F439VGTx +MCU_ST_STM32F4:STM32F439VITx +MCU_ST_STM32F4:STM32F439V_G-I_Tx +MCU_ST_STM32F4:STM32F439ZGTx +MCU_ST_STM32F4:STM32F439ZGYx +MCU_ST_STM32F4:STM32F439ZITx +MCU_ST_STM32F4:STM32F439ZIYx +MCU_ST_STM32F4:STM32F439Z_G-I_Tx +MCU_ST_STM32F4:STM32F439Z_G-I_Yx +MCU_ST_STM32F4:STM32F446MCYx +MCU_ST_STM32F4:STM32F446MEYx +MCU_ST_STM32F4:STM32F446M_C-E_Yx +MCU_ST_STM32F4:STM32F446RCTx +MCU_ST_STM32F4:STM32F446RETx +MCU_ST_STM32F4:STM32F446R_C-E_Tx +MCU_ST_STM32F4:STM32F446VCTx +MCU_ST_STM32F4:STM32F446VETx +MCU_ST_STM32F4:STM32F446V_C-E_Tx +MCU_ST_STM32F4:STM32F446ZCHx +MCU_ST_STM32F4:STM32F446ZCJx +MCU_ST_STM32F4:STM32F446ZCTx +MCU_ST_STM32F4:STM32F446ZEHx +MCU_ST_STM32F4:STM32F446ZEJx +MCU_ST_STM32F4:STM32F446ZETx +MCU_ST_STM32F4:STM32F446Z_C-E_Hx +MCU_ST_STM32F4:STM32F446Z_C-E_Jx +MCU_ST_STM32F4:STM32F446Z_C-E_Tx +MCU_ST_STM32F4:STM32F469AEHx +MCU_ST_STM32F4:STM32F469AEYx +MCU_ST_STM32F4:STM32F469AGHx +MCU_ST_STM32F4:STM32F469AGYx +MCU_ST_STM32F4:STM32F469AIHx +MCU_ST_STM32F4:STM32F469AIYx +MCU_ST_STM32F4:STM32F469A_E-G-I_Hx +MCU_ST_STM32F4:STM32F469A_E-G-I_Yx +MCU_ST_STM32F4:STM32F469BETx +MCU_ST_STM32F4:STM32F469BGTx +MCU_ST_STM32F4:STM32F469BITx +MCU_ST_STM32F4:STM32F469B_E-G-I_Tx +MCU_ST_STM32F4:STM32F469IEHx +MCU_ST_STM32F4:STM32F469IETx +MCU_ST_STM32F4:STM32F469IGHx +MCU_ST_STM32F4:STM32F469IGTx +MCU_ST_STM32F4:STM32F469IIHx +MCU_ST_STM32F4:STM32F469IITx +MCU_ST_STM32F4:STM32F469I_E-G-I_Hx +MCU_ST_STM32F4:STM32F469I_E-G_Tx +MCU_ST_STM32F4:STM32F469NEHx +MCU_ST_STM32F4:STM32F469NGHx +MCU_ST_STM32F4:STM32F469NIHx +MCU_ST_STM32F4:STM32F469N_E-G_Hx +MCU_ST_STM32F4:STM32F469VETx +MCU_ST_STM32F4:STM32F469VGTx +MCU_ST_STM32F4:STM32F469VITx +MCU_ST_STM32F4:STM32F469V_E-G_Tx +MCU_ST_STM32F4:STM32F469ZETx +MCU_ST_STM32F4:STM32F469ZGTx +MCU_ST_STM32F4:STM32F469ZITx +MCU_ST_STM32F4:STM32F469Z_E-G_Tx +MCU_ST_STM32F4:STM32F479AGHx +MCU_ST_STM32F4:STM32F479AGYx +MCU_ST_STM32F4:STM32F479AIHx +MCU_ST_STM32F4:STM32F479AIYx +MCU_ST_STM32F4:STM32F479A_G-I_Hx +MCU_ST_STM32F4:STM32F479A_G-I_Yx +MCU_ST_STM32F4:STM32F479BGTx +MCU_ST_STM32F4:STM32F479BITx +MCU_ST_STM32F4:STM32F479B_G-I_Tx +MCU_ST_STM32F4:STM32F479IGHx +MCU_ST_STM32F4:STM32F479IGTx +MCU_ST_STM32F4:STM32F479IIHx +MCU_ST_STM32F4:STM32F479IITx +MCU_ST_STM32F4:STM32F479I_G-I_Hx +MCU_ST_STM32F4:STM32F479I_G-I_Tx +MCU_ST_STM32F4:STM32F479NGHx +MCU_ST_STM32F4:STM32F479NIHx +MCU_ST_STM32F4:STM32F479N_G-I_Hx +MCU_ST_STM32F4:STM32F479VGTx +MCU_ST_STM32F4:STM32F479VITx +MCU_ST_STM32F4:STM32F479V_G-I_Tx +MCU_ST_STM32F4:STM32F479ZGTx +MCU_ST_STM32F4:STM32F479ZITx +MCU_ST_STM32F4:STM32F479Z_G-I_Tx +MCU_ST_STM32F7:STM32F722ICKx +MCU_ST_STM32F7:STM32F722ICTx +MCU_ST_STM32F7:STM32F722IEKx +MCU_ST_STM32F7:STM32F722IETx +MCU_ST_STM32F7:STM32F722I_C-E_Kx +MCU_ST_STM32F7:STM32F722I_C-E_Tx +MCU_ST_STM32F7:STM32F722RCTx +MCU_ST_STM32F7:STM32F722RETx +MCU_ST_STM32F7:STM32F722R_C-E_Tx +MCU_ST_STM32F7:STM32F722VCTx +MCU_ST_STM32F7:STM32F722VETx +MCU_ST_STM32F7:STM32F722V_C-E_Tx +MCU_ST_STM32F7:STM32F722ZCTx +MCU_ST_STM32F7:STM32F722ZETx +MCU_ST_STM32F7:STM32F722Z_C-E_Tx +MCU_ST_STM32F7:STM32F723ICKx +MCU_ST_STM32F7:STM32F723ICTx +MCU_ST_STM32F7:STM32F723IEKx +MCU_ST_STM32F7:STM32F723IETx +MCU_ST_STM32F7:STM32F723I_C-E_Kx +MCU_ST_STM32F7:STM32F723I_C-E_Tx +MCU_ST_STM32F7:STM32F723VCTx +MCU_ST_STM32F7:STM32F723VCYx +MCU_ST_STM32F7:STM32F723VETx +MCU_ST_STM32F7:STM32F723VEYx +MCU_ST_STM32F7:STM32F723V_C-E_Tx +MCU_ST_STM32F7:STM32F723V_C-E_Yx +MCU_ST_STM32F7:STM32F723ZCIx +MCU_ST_STM32F7:STM32F723ZCTx +MCU_ST_STM32F7:STM32F723ZEIx +MCU_ST_STM32F7:STM32F723ZETx +MCU_ST_STM32F7:STM32F723Z_C-E_Ix +MCU_ST_STM32F7:STM32F723Z_C-E_Tx +MCU_ST_STM32F7:STM32F730I8Kx +MCU_ST_STM32F7:STM32F730R8Tx +MCU_ST_STM32F7:STM32F730V8Tx +MCU_ST_STM32F7:STM32F730Z8Tx +MCU_ST_STM32F7:STM32F732IEKx +MCU_ST_STM32F7:STM32F732IETx +MCU_ST_STM32F7:STM32F732RETx +MCU_ST_STM32F7:STM32F732VETx +MCU_ST_STM32F7:STM32F732ZETx +MCU_ST_STM32F7:STM32F733IEKx +MCU_ST_STM32F7:STM32F733IETx +MCU_ST_STM32F7:STM32F733VETx +MCU_ST_STM32F7:STM32F733VEYx +MCU_ST_STM32F7:STM32F733ZEIx +MCU_ST_STM32F7:STM32F733ZETx +MCU_ST_STM32F7:STM32F745IEKx +MCU_ST_STM32F7:STM32F745IETx +MCU_ST_STM32F7:STM32F745IGKx +MCU_ST_STM32F7:STM32F745IGTx +MCU_ST_STM32F7:STM32F745I_E-G_Kx +MCU_ST_STM32F7:STM32F745I_E-G_Tx +MCU_ST_STM32F7:STM32F745VEHx +MCU_ST_STM32F7:STM32F745VETx +MCU_ST_STM32F7:STM32F745VGHx +MCU_ST_STM32F7:STM32F745VGTx +MCU_ST_STM32F7:STM32F745V_E-G_Hx +MCU_ST_STM32F7:STM32F745V_E-G_Tx +MCU_ST_STM32F7:STM32F745ZETx +MCU_ST_STM32F7:STM32F745ZGTx +MCU_ST_STM32F7:STM32F745Z_E-G_Tx +MCU_ST_STM32F7:STM32F746BETx +MCU_ST_STM32F7:STM32F746BGTx +MCU_ST_STM32F7:STM32F746B_E-G_Tx +MCU_ST_STM32F7:STM32F746IEKx +MCU_ST_STM32F7:STM32F746IETx +MCU_ST_STM32F7:STM32F746IGKx +MCU_ST_STM32F7:STM32F746IGTx +MCU_ST_STM32F7:STM32F746I_E-G_Kx +MCU_ST_STM32F7:STM32F746NEHx +MCU_ST_STM32F7:STM32F746NGHx +MCU_ST_STM32F7:STM32F746VEHx +MCU_ST_STM32F7:STM32F746VETx +MCU_ST_STM32F7:STM32F746VGHx +MCU_ST_STM32F7:STM32F746VGTx +MCU_ST_STM32F7:STM32F746V_E-G_Hx +MCU_ST_STM32F7:STM32F746ZETx +MCU_ST_STM32F7:STM32F746ZEYx +MCU_ST_STM32F7:STM32F746ZGTx +MCU_ST_STM32F7:STM32F746ZGYx +MCU_ST_STM32F7:STM32F746Z_E-G_Yx +MCU_ST_STM32F7:STM32F750N8Hx +MCU_ST_STM32F7:STM32F750V8Tx +MCU_ST_STM32F7:STM32F750Z8Tx +MCU_ST_STM32F7:STM32F756BGTx +MCU_ST_STM32F7:STM32F756IGKx +MCU_ST_STM32F7:STM32F756IGTx +MCU_ST_STM32F7:STM32F756NGHx +MCU_ST_STM32F7:STM32F756VGHx +MCU_ST_STM32F7:STM32F756VGTx +MCU_ST_STM32F7:STM32F756ZGTx +MCU_ST_STM32F7:STM32F756ZGYx +MCU_ST_STM32F7:STM32F765BGTx +MCU_ST_STM32F7:STM32F765BITx +MCU_ST_STM32F7:STM32F765B_G-I_Tx +MCU_ST_STM32F7:STM32F765IGKx +MCU_ST_STM32F7:STM32F765IGTx +MCU_ST_STM32F7:STM32F765IIKx +MCU_ST_STM32F7:STM32F765IITx +MCU_ST_STM32F7:STM32F765I_G-I_Kx +MCU_ST_STM32F7:STM32F765I_G-I_Tx +MCU_ST_STM32F7:STM32F765NGHx +MCU_ST_STM32F7:STM32F765NIHx +MCU_ST_STM32F7:STM32F765N_G-I_Hx +MCU_ST_STM32F7:STM32F765VGHx +MCU_ST_STM32F7:STM32F765VGTx +MCU_ST_STM32F7:STM32F765VIHx +MCU_ST_STM32F7:STM32F765VITx +MCU_ST_STM32F7:STM32F765V_G-I_Hx +MCU_ST_STM32F7:STM32F765V_G-I_Tx +MCU_ST_STM32F7:STM32F765ZGTx +MCU_ST_STM32F7:STM32F765ZITx +MCU_ST_STM32F7:STM32F765Z_G-I_Tx +MCU_ST_STM32F7:STM32F767BGTx +MCU_ST_STM32F7:STM32F767BITx +MCU_ST_STM32F7:STM32F767B_G-I_Tx +MCU_ST_STM32F7:STM32F767IGKx +MCU_ST_STM32F7:STM32F767IGTx +MCU_ST_STM32F7:STM32F767IIKx +MCU_ST_STM32F7:STM32F767IITx +MCU_ST_STM32F7:STM32F767I_G-I_Kx +MCU_ST_STM32F7:STM32F767I_G-I_Tx +MCU_ST_STM32F7:STM32F767NGHx +MCU_ST_STM32F7:STM32F767NIHx +MCU_ST_STM32F7:STM32F767N_G-I_Hx +MCU_ST_STM32F7:STM32F767VGHx +MCU_ST_STM32F7:STM32F767VGTx +MCU_ST_STM32F7:STM32F767VIHx +MCU_ST_STM32F7:STM32F767VITx +MCU_ST_STM32F7:STM32F767ZGTx +MCU_ST_STM32F7:STM32F767ZITx +MCU_ST_STM32F7:STM32F768AIYx +MCU_ST_STM32F7:STM32F769AGYx +MCU_ST_STM32F7:STM32F769AIYx +MCU_ST_STM32F7:STM32F769A_G-I_Yx +MCU_ST_STM32F7:STM32F769BGTx +MCU_ST_STM32F7:STM32F769BITx +MCU_ST_STM32F7:STM32F769B_G-I_Tx +MCU_ST_STM32F7:STM32F769IGTx +MCU_ST_STM32F7:STM32F769IITx +MCU_ST_STM32F7:STM32F769NGHx +MCU_ST_STM32F7:STM32F769NIHx +MCU_ST_STM32F7:STM32F777BITx +MCU_ST_STM32F7:STM32F777IIKx +MCU_ST_STM32F7:STM32F777IITx +MCU_ST_STM32F7:STM32F777NIHx +MCU_ST_STM32F7:STM32F777VIHx +MCU_ST_STM32F7:STM32F777VITx +MCU_ST_STM32F7:STM32F777ZITx +MCU_ST_STM32F7:STM32F778AIYx +MCU_ST_STM32F7:STM32F779AIYx +MCU_ST_STM32F7:STM32F779BITx +MCU_ST_STM32F7:STM32F779IITx +MCU_ST_STM32F7:STM32F779NIHx +MCU_ST_STM32G0:STM32G030C6Tx +MCU_ST_STM32G0:STM32G030C8Tx +MCU_ST_STM32G0:STM32G030C_6-8_Tx +MCU_ST_STM32G0:STM32G030F6Px +MCU_ST_STM32G0:STM32G030J6Mx +MCU_ST_STM32G0:STM32G030K6Tx +MCU_ST_STM32G0:STM32G030K8Tx +MCU_ST_STM32G0:STM32G030K_6-8_Tx +MCU_ST_STM32G0:STM32G031C4Tx +MCU_ST_STM32G0:STM32G031C4Ux +MCU_ST_STM32G0:STM32G031C6Tx +MCU_ST_STM32G0:STM32G031C6Ux +MCU_ST_STM32G0:STM32G031C8Tx +MCU_ST_STM32G0:STM32G031C8Ux +MCU_ST_STM32G0:STM32G031C_4-6-8_Tx +MCU_ST_STM32G0:STM32G031C_4-6-8_Ux +MCU_ST_STM32G0:STM32G031F4Px +MCU_ST_STM32G0:STM32G031F6Px +MCU_ST_STM32G0:STM32G031F8Px +MCU_ST_STM32G0:STM32G031F_4-6-8_Px +MCU_ST_STM32G0:STM32G031G4Ux +MCU_ST_STM32G0:STM32G031G6Ux +MCU_ST_STM32G0:STM32G031G8Ux +MCU_ST_STM32G0:STM32G031G_4-6-8_Ux +MCU_ST_STM32G0:STM32G031J4Mx +MCU_ST_STM32G0:STM32G031J6Mx +MCU_ST_STM32G0:STM32G031J_4-6_Mx +MCU_ST_STM32G0:STM32G031K4Tx +MCU_ST_STM32G0:STM32G031K4Ux +MCU_ST_STM32G0:STM32G031K6Tx +MCU_ST_STM32G0:STM32G031K6Ux +MCU_ST_STM32G0:STM32G031K8Tx +MCU_ST_STM32G0:STM32G031K8Ux +MCU_ST_STM32G0:STM32G031K_4-6-8_Tx +MCU_ST_STM32G0:STM32G031K_4-6-8_Ux +MCU_ST_STM32G0:STM32G031Y8Yx +MCU_ST_STM32G0:STM32G041C6Tx +MCU_ST_STM32G0:STM32G041C6Ux +MCU_ST_STM32G0:STM32G041C8Tx +MCU_ST_STM32G0:STM32G041C8Ux +MCU_ST_STM32G0:STM32G041C_6-8_Tx +MCU_ST_STM32G0:STM32G041C_6-8_Ux +MCU_ST_STM32G0:STM32G041F6Px +MCU_ST_STM32G0:STM32G041F8Px +MCU_ST_STM32G0:STM32G041F_6-8_Px +MCU_ST_STM32G0:STM32G041G6Ux +MCU_ST_STM32G0:STM32G041G8Ux +MCU_ST_STM32G0:STM32G041G_6-8_Ux +MCU_ST_STM32G0:STM32G041J6Mx +MCU_ST_STM32G0:STM32G041K6Tx +MCU_ST_STM32G0:STM32G041K6Ux +MCU_ST_STM32G0:STM32G041K8Tx +MCU_ST_STM32G0:STM32G041K8Ux +MCU_ST_STM32G0:STM32G041K_6-8_Tx +MCU_ST_STM32G0:STM32G041K_6-8_Ux +MCU_ST_STM32G0:STM32G041Y8Yx +MCU_ST_STM32G0:STM32G050C6Tx +MCU_ST_STM32G0:STM32G050C8Tx +MCU_ST_STM32G0:STM32G050F6Px +MCU_ST_STM32G0:STM32G050K6Tx +MCU_ST_STM32G0:STM32G050K8Tx +MCU_ST_STM32G0:STM32G051C6Tx +MCU_ST_STM32G0:STM32G051C6Ux +MCU_ST_STM32G0:STM32G051C8Tx +MCU_ST_STM32G0:STM32G051C8Ux +MCU_ST_STM32G0:STM32G051C_6-8_Tx +MCU_ST_STM32G0:STM32G051C_6-8_Ux +MCU_ST_STM32G0:STM32G051F6Px +MCU_ST_STM32G0:STM32G051F8Px +MCU_ST_STM32G0:STM32G051F8Yx +MCU_ST_STM32G0:STM32G051F_6-8_Px +MCU_ST_STM32G0:STM32G051G6Ux +MCU_ST_STM32G0:STM32G051G8Ux +MCU_ST_STM32G0:STM32G051G_6-8_Ux +MCU_ST_STM32G0:STM32G051K6Tx +MCU_ST_STM32G0:STM32G051K6Ux +MCU_ST_STM32G0:STM32G051K8Tx +MCU_ST_STM32G0:STM32G051K8Ux +MCU_ST_STM32G0:STM32G051K_6-8_Tx +MCU_ST_STM32G0:STM32G051K_6-8_Ux +MCU_ST_STM32G0:STM32G061C6Tx +MCU_ST_STM32G0:STM32G061C6Ux +MCU_ST_STM32G0:STM32G061C8Tx +MCU_ST_STM32G0:STM32G061C8Ux +MCU_ST_STM32G0:STM32G061C_6-8_Tx +MCU_ST_STM32G0:STM32G061C_6-8_Ux +MCU_ST_STM32G0:STM32G061F6Px +MCU_ST_STM32G0:STM32G061F8Px +MCU_ST_STM32G0:STM32G061F8Yx +MCU_ST_STM32G0:STM32G061F_6-8_Px +MCU_ST_STM32G0:STM32G061G6Ux +MCU_ST_STM32G0:STM32G061G8Ux +MCU_ST_STM32G0:STM32G061G_6-8_Ux +MCU_ST_STM32G0:STM32G061K6Tx +MCU_ST_STM32G0:STM32G061K6Ux +MCU_ST_STM32G0:STM32G061K8Tx +MCU_ST_STM32G0:STM32G061K8Ux +MCU_ST_STM32G0:STM32G061K_6-8_Tx +MCU_ST_STM32G0:STM32G061K_6-8_Ux +MCU_ST_STM32G0:STM32G070CBTx +MCU_ST_STM32G0:STM32G070KBTx +MCU_ST_STM32G0:STM32G070RBTx +MCU_ST_STM32G0:STM32G071EBYx +MCU_ST_STM32G0:STM32G071G8UxN +MCU_ST_STM32G0:STM32G071GBUxN +MCU_ST_STM32G0:STM32G071G_8-B_UxN +MCU_ST_STM32G0:STM32G071K8TxN +MCU_ST_STM32G0:STM32G071K8UxN +MCU_ST_STM32G0:STM32G071KBTxN +MCU_ST_STM32G0:STM32G071KBUxN +MCU_ST_STM32G0:STM32G071K_8-B_TxN +MCU_ST_STM32G0:STM32G071K_8-B_UxN +MCU_ST_STM32G0:STM32G071RBIx +MCU_ST_STM32G0:STM32G081CBTx +MCU_ST_STM32G0:STM32G081CBUx +MCU_ST_STM32G0:STM32G081EBYx +MCU_ST_STM32G0:STM32G081GBUx +MCU_ST_STM32G0:STM32G081GBUxN +MCU_ST_STM32G0:STM32G081KBTx +MCU_ST_STM32G0:STM32G081KBTxN +MCU_ST_STM32G0:STM32G081KBUx +MCU_ST_STM32G0:STM32G081KBUxN +MCU_ST_STM32G0:STM32G081RBIx +MCU_ST_STM32G0:STM32G081RBTx +MCU_ST_STM32G0:STM32G0B0CETx +MCU_ST_STM32G0:STM32G0B0KETx +MCU_ST_STM32G0:STM32G0B0RETx +MCU_ST_STM32G0:STM32G0B0VETx +MCU_ST_STM32G0:STM32G0B1CBTx +MCU_ST_STM32G0:STM32G0B1CBTxN +MCU_ST_STM32G0:STM32G0B1CBUx +MCU_ST_STM32G0:STM32G0B1CBUxN +MCU_ST_STM32G0:STM32G0B1CCTx +MCU_ST_STM32G0:STM32G0B1CCTxN +MCU_ST_STM32G0:STM32G0B1CCUx +MCU_ST_STM32G0:STM32G0B1CCUxN +MCU_ST_STM32G0:STM32G0B1CETx +MCU_ST_STM32G0:STM32G0B1CETxN +MCU_ST_STM32G0:STM32G0B1CEUx +MCU_ST_STM32G0:STM32G0B1CEUxN +MCU_ST_STM32G0:STM32G0B1C_B-C-E_Tx +MCU_ST_STM32G0:STM32G0B1C_B-C-E_TxN +MCU_ST_STM32G0:STM32G0B1C_B-C-E_Ux +MCU_ST_STM32G0:STM32G0B1C_B-C-E_UxN +MCU_ST_STM32G0:STM32G0B1KBTx +MCU_ST_STM32G0:STM32G0B1KBTxN +MCU_ST_STM32G0:STM32G0B1KBUx +MCU_ST_STM32G0:STM32G0B1KBUxN +MCU_ST_STM32G0:STM32G0B1KCTx +MCU_ST_STM32G0:STM32G0B1KCTxN +MCU_ST_STM32G0:STM32G0B1KCUx +MCU_ST_STM32G0:STM32G0B1KCUxN +MCU_ST_STM32G0:STM32G0B1KETx +MCU_ST_STM32G0:STM32G0B1KETxN +MCU_ST_STM32G0:STM32G0B1KEUx +MCU_ST_STM32G0:STM32G0B1KEUxN +MCU_ST_STM32G0:STM32G0B1K_B-C-E_Tx +MCU_ST_STM32G0:STM32G0B1K_B-C-E_TxN +MCU_ST_STM32G0:STM32G0B1K_B-C-E_Ux +MCU_ST_STM32G0:STM32G0B1K_B-C-E_UxN +MCU_ST_STM32G0:STM32G0B1MBTx +MCU_ST_STM32G0:STM32G0B1MCTx +MCU_ST_STM32G0:STM32G0B1METx +MCU_ST_STM32G0:STM32G0B1M_B-C-E_Tx +MCU_ST_STM32G0:STM32G0B1NEYx +MCU_ST_STM32G0:STM32G0B1RBIxN +MCU_ST_STM32G0:STM32G0B1RBTx +MCU_ST_STM32G0:STM32G0B1RBTxN +MCU_ST_STM32G0:STM32G0B1RCIxN +MCU_ST_STM32G0:STM32G0B1RCTx +MCU_ST_STM32G0:STM32G0B1RCTxN +MCU_ST_STM32G0:STM32G0B1REIxN +MCU_ST_STM32G0:STM32G0B1RETx +MCU_ST_STM32G0:STM32G0B1RETxN +MCU_ST_STM32G0:STM32G0B1R_B-C-E_IxN +MCU_ST_STM32G0:STM32G0B1R_B-C-E_Tx +MCU_ST_STM32G0:STM32G0B1R_B-C-E_TxN +MCU_ST_STM32G0:STM32G0B1VBIx +MCU_ST_STM32G0:STM32G0B1VBTx +MCU_ST_STM32G0:STM32G0B1VCIx +MCU_ST_STM32G0:STM32G0B1VCTx +MCU_ST_STM32G0:STM32G0B1VEIx +MCU_ST_STM32G0:STM32G0B1VETx +MCU_ST_STM32G0:STM32G0B1V_B-C-E_Ix +MCU_ST_STM32G0:STM32G0B1V_B-C-E_Tx +MCU_ST_STM32G0:STM32G0C1CCTx +MCU_ST_STM32G0:STM32G0C1CCTxN +MCU_ST_STM32G0:STM32G0C1CCUx +MCU_ST_STM32G0:STM32G0C1CCUxN +MCU_ST_STM32G0:STM32G0C1CETx +MCU_ST_STM32G0:STM32G0C1CETxN +MCU_ST_STM32G0:STM32G0C1CEUx +MCU_ST_STM32G0:STM32G0C1CEUxN +MCU_ST_STM32G0:STM32G0C1C_C-E_Tx +MCU_ST_STM32G0:STM32G0C1C_C-E_TxN +MCU_ST_STM32G0:STM32G0C1C_C-E_Ux +MCU_ST_STM32G0:STM32G0C1C_C-E_UxN +MCU_ST_STM32G0:STM32G0C1KCTx +MCU_ST_STM32G0:STM32G0C1KCTxN +MCU_ST_STM32G0:STM32G0C1KCUx +MCU_ST_STM32G0:STM32G0C1KCUxN +MCU_ST_STM32G0:STM32G0C1KETx +MCU_ST_STM32G0:STM32G0C1KETxN +MCU_ST_STM32G0:STM32G0C1KEUx +MCU_ST_STM32G0:STM32G0C1KEUxN +MCU_ST_STM32G0:STM32G0C1K_C-E_Tx +MCU_ST_STM32G0:STM32G0C1K_C-E_TxN +MCU_ST_STM32G0:STM32G0C1K_C-E_Ux +MCU_ST_STM32G0:STM32G0C1K_C-E_UxN +MCU_ST_STM32G0:STM32G0C1MCTx +MCU_ST_STM32G0:STM32G0C1METx +MCU_ST_STM32G0:STM32G0C1M_C-E_Tx +MCU_ST_STM32G0:STM32G0C1NEYx +MCU_ST_STM32G0:STM32G0C1RCIxN +MCU_ST_STM32G0:STM32G0C1RCTx +MCU_ST_STM32G0:STM32G0C1RCTxN +MCU_ST_STM32G0:STM32G0C1REIxN +MCU_ST_STM32G0:STM32G0C1RETx +MCU_ST_STM32G0:STM32G0C1RETxN +MCU_ST_STM32G0:STM32G0C1R_C-E_IxN +MCU_ST_STM32G0:STM32G0C1R_C-E_Tx +MCU_ST_STM32G0:STM32G0C1R_C-E_TxN +MCU_ST_STM32G0:STM32G0C1VCIx +MCU_ST_STM32G0:STM32G0C1VCTx +MCU_ST_STM32G0:STM32G0C1VEIx +MCU_ST_STM32G0:STM32G0C1VETx +MCU_ST_STM32G0:STM32G0C1V_C-E_Ix +MCU_ST_STM32G0:STM32G0C1V_C-E_Tx +MCU_ST_STM32G4:STM32G431C6Tx +MCU_ST_STM32G4:STM32G431C6Ux +MCU_ST_STM32G4:STM32G431C8Tx +MCU_ST_STM32G4:STM32G431C8Ux +MCU_ST_STM32G4:STM32G431CBTx +MCU_ST_STM32G4:STM32G431CBTxZ +MCU_ST_STM32G4:STM32G431CBUx +MCU_ST_STM32G4:STM32G431CBYx +MCU_ST_STM32G4:STM32G431C_6-8-B_Tx +MCU_ST_STM32G4:STM32G431C_6-8-B_Ux +MCU_ST_STM32G4:STM32G431K6Tx +MCU_ST_STM32G4:STM32G431K6Ux +MCU_ST_STM32G4:STM32G431K8Tx +MCU_ST_STM32G4:STM32G431K8Ux +MCU_ST_STM32G4:STM32G431KBTx +MCU_ST_STM32G4:STM32G431KBUx +MCU_ST_STM32G4:STM32G431K_6-8-B_Tx +MCU_ST_STM32G4:STM32G431K_6-8-B_Ux +MCU_ST_STM32G4:STM32G431M6Tx +MCU_ST_STM32G4:STM32G431M8Tx +MCU_ST_STM32G4:STM32G431MBTx +MCU_ST_STM32G4:STM32G431M_6-8-B_Tx +MCU_ST_STM32G4:STM32G431R6Ix +MCU_ST_STM32G4:STM32G431R6Tx +MCU_ST_STM32G4:STM32G431R8Ix +MCU_ST_STM32G4:STM32G431R8Tx +MCU_ST_STM32G4:STM32G431RBIx +MCU_ST_STM32G4:STM32G431RBTx +MCU_ST_STM32G4:STM32G431RBTxZ +MCU_ST_STM32G4:STM32G431R_6-8-B_Ix +MCU_ST_STM32G4:STM32G431R_6-8-B_Tx +MCU_ST_STM32G4:STM32G431V6Tx +MCU_ST_STM32G4:STM32G431V8Tx +MCU_ST_STM32G4:STM32G431VBTx +MCU_ST_STM32G4:STM32G431V_6-8-B_Tx +MCU_ST_STM32G4:STM32G441CBTx +MCU_ST_STM32G4:STM32G441CBUx +MCU_ST_STM32G4:STM32G441CBYx +MCU_ST_STM32G4:STM32G441KBTx +MCU_ST_STM32G4:STM32G441KBUx +MCU_ST_STM32G4:STM32G441MBTx +MCU_ST_STM32G4:STM32G441RBIx +MCU_ST_STM32G4:STM32G441RBTx +MCU_ST_STM32G4:STM32G441VBTx +MCU_ST_STM32G4:STM32G473CBTx +MCU_ST_STM32G4:STM32G473CBUx +MCU_ST_STM32G4:STM32G473CCTx +MCU_ST_STM32G4:STM32G473CCUx +MCU_ST_STM32G4:STM32G473CETx +MCU_ST_STM32G4:STM32G473CEUx +MCU_ST_STM32G4:STM32G473C_B-C-E_Tx +MCU_ST_STM32G4:STM32G473C_B-C-E_Ux +MCU_ST_STM32G4:STM32G473MBTx +MCU_ST_STM32G4:STM32G473MCTx +MCU_ST_STM32G4:STM32G473METx +MCU_ST_STM32G4:STM32G473MEYx +MCU_ST_STM32G4:STM32G473M_B-C-E_Tx +MCU_ST_STM32G4:STM32G473PBIx +MCU_ST_STM32G4:STM32G473PCIx +MCU_ST_STM32G4:STM32G473PEIx +MCU_ST_STM32G4:STM32G473P_B-C-E_Ix +MCU_ST_STM32G4:STM32G473QBTx +MCU_ST_STM32G4:STM32G473QCTx +MCU_ST_STM32G4:STM32G473QETx +MCU_ST_STM32G4:STM32G473QETxZ +MCU_ST_STM32G4:STM32G473Q_B-C-E_Tx +MCU_ST_STM32G4:STM32G473RBTx +MCU_ST_STM32G4:STM32G473RCTx +MCU_ST_STM32G4:STM32G473RETx +MCU_ST_STM32G4:STM32G473RETxZ +MCU_ST_STM32G4:STM32G473R_B-C-E_Tx +MCU_ST_STM32G4:STM32G473VBHx +MCU_ST_STM32G4:STM32G473VBTx +MCU_ST_STM32G4:STM32G473VCHx +MCU_ST_STM32G4:STM32G473VCTx +MCU_ST_STM32G4:STM32G473VEHx +MCU_ST_STM32G4:STM32G473VETx +MCU_ST_STM32G4:STM32G473V_B-C-E_Hx +MCU_ST_STM32G4:STM32G473V_B-C-E_Tx +MCU_ST_STM32G4:STM32G474CBTx +MCU_ST_STM32G4:STM32G474CBUx +MCU_ST_STM32G4:STM32G474CCTx +MCU_ST_STM32G4:STM32G474CCUx +MCU_ST_STM32G4:STM32G474CETx +MCU_ST_STM32G4:STM32G474CEUx +MCU_ST_STM32G4:STM32G474C_B-C-E_Tx +MCU_ST_STM32G4:STM32G474C_B-C-E_Ux +MCU_ST_STM32G4:STM32G474MBTx +MCU_ST_STM32G4:STM32G474MCTx +MCU_ST_STM32G4:STM32G474METx +MCU_ST_STM32G4:STM32G474MEYx +MCU_ST_STM32G4:STM32G474M_B-C-E_Tx +MCU_ST_STM32G4:STM32G474PBIx +MCU_ST_STM32G4:STM32G474PCIx +MCU_ST_STM32G4:STM32G474PEIx +MCU_ST_STM32G4:STM32G474P_B-C-E_Ix +MCU_ST_STM32G4:STM32G474QBTx +MCU_ST_STM32G4:STM32G474QCTx +MCU_ST_STM32G4:STM32G474QETx +MCU_ST_STM32G4:STM32G474Q_B-C-E_Tx +MCU_ST_STM32G4:STM32G474RBTx +MCU_ST_STM32G4:STM32G474RCTx +MCU_ST_STM32G4:STM32G474RETx +MCU_ST_STM32G4:STM32G474R_B-C-E_Tx +MCU_ST_STM32G4:STM32G474VBHx +MCU_ST_STM32G4:STM32G474VBTx +MCU_ST_STM32G4:STM32G474VCHx +MCU_ST_STM32G4:STM32G474VCTx +MCU_ST_STM32G4:STM32G474VEHx +MCU_ST_STM32G4:STM32G474VETx +MCU_ST_STM32G4:STM32G474V_B-C-E_Hx +MCU_ST_STM32G4:STM32G474V_B-C-E_Tx +MCU_ST_STM32G4:STM32G483CETx +MCU_ST_STM32G4:STM32G483CEUx +MCU_ST_STM32G4:STM32G483METx +MCU_ST_STM32G4:STM32G483MEYx +MCU_ST_STM32G4:STM32G483PEIx +MCU_ST_STM32G4:STM32G483QETx +MCU_ST_STM32G4:STM32G483RETx +MCU_ST_STM32G4:STM32G483VEHx +MCU_ST_STM32G4:STM32G483VETx +MCU_ST_STM32G4:STM32G484CETx +MCU_ST_STM32G4:STM32G484CEUx +MCU_ST_STM32G4:STM32G484METx +MCU_ST_STM32G4:STM32G484MEYx +MCU_ST_STM32G4:STM32G484PEIx +MCU_ST_STM32G4:STM32G484QETx +MCU_ST_STM32G4:STM32G484RETx +MCU_ST_STM32G4:STM32G484VEHx +MCU_ST_STM32G4:STM32G484VETx +MCU_ST_STM32G4:STM32G491CCTx +MCU_ST_STM32G4:STM32G491CCUx +MCU_ST_STM32G4:STM32G491CETx +MCU_ST_STM32G4:STM32G491CEUx +MCU_ST_STM32G4:STM32G491C_C-E_Tx +MCU_ST_STM32G4:STM32G491C_C-E_Ux +MCU_ST_STM32G4:STM32G491KCUx +MCU_ST_STM32G4:STM32G491KEUx +MCU_ST_STM32G4:STM32G491K_C-E_Ux +MCU_ST_STM32G4:STM32G491MCSx +MCU_ST_STM32G4:STM32G491MCTx +MCU_ST_STM32G4:STM32G491MESx +MCU_ST_STM32G4:STM32G491METx +MCU_ST_STM32G4:STM32G491M_C-E_Sx +MCU_ST_STM32G4:STM32G491M_C-E_Tx +MCU_ST_STM32G4:STM32G491RCIx +MCU_ST_STM32G4:STM32G491RCTx +MCU_ST_STM32G4:STM32G491REIx +MCU_ST_STM32G4:STM32G491RETx +MCU_ST_STM32G4:STM32G491RETxZ +MCU_ST_STM32G4:STM32G491REYx +MCU_ST_STM32G4:STM32G491R_C-E_Ix +MCU_ST_STM32G4:STM32G491R_C-E_Tx +MCU_ST_STM32G4:STM32G491VCTx +MCU_ST_STM32G4:STM32G491VETx +MCU_ST_STM32G4:STM32G491V_C-E_Tx +MCU_ST_STM32G4:STM32G4A1CETx +MCU_ST_STM32G4:STM32G4A1CEUx +MCU_ST_STM32G4:STM32G4A1KEUx +MCU_ST_STM32G4:STM32G4A1MESx +MCU_ST_STM32G4:STM32G4A1METx +MCU_ST_STM32G4:STM32G4A1REIx +MCU_ST_STM32G4:STM32G4A1RETx +MCU_ST_STM32G4:STM32G4A1REYx +MCU_ST_STM32G4:STM32G4A1VETx +MCU_ST_STM32H5:STM32H503CBTx +MCU_ST_STM32H5:STM32H503CBUx +MCU_ST_STM32H5:STM32H503EBYx +MCU_ST_STM32H5:STM32H503KBUx +MCU_ST_STM32H5:STM32H503RBTx +MCU_ST_STM32H5:STM32H523CCTx +MCU_ST_STM32H5:STM32H523CCUx +MCU_ST_STM32H5:STM32H523CETx +MCU_ST_STM32H5:STM32H523CEUx +MCU_ST_STM32H5:STM32H523RCTx +MCU_ST_STM32H5:STM32H523RETx +MCU_ST_STM32H5:STM32H523VCIx +MCU_ST_STM32H5:STM32H523VCTx +MCU_ST_STM32H5:STM32H523VEIx +MCU_ST_STM32H5:STM32H523VETx +MCU_ST_STM32H5:STM32H523ZCJx +MCU_ST_STM32H5:STM32H523ZCTx +MCU_ST_STM32H5:STM32H523ZEJx +MCU_ST_STM32H5:STM32H523ZETx +MCU_ST_STM32H5:STM32H533CETx +MCU_ST_STM32H5:STM32H533CEUx +MCU_ST_STM32H5:STM32H533RETx +MCU_ST_STM32H5:STM32H533VEIx +MCU_ST_STM32H5:STM32H533VETx +MCU_ST_STM32H5:STM32H533ZEJx +MCU_ST_STM32H5:STM32H533ZETx +MCU_ST_STM32H5:STM32H562AGIx +MCU_ST_STM32H5:STM32H562AIIx +MCU_ST_STM32H5:STM32H562IGKx +MCU_ST_STM32H5:STM32H562IGTx +MCU_ST_STM32H5:STM32H562IIKx +MCU_ST_STM32H5:STM32H562IITx +MCU_ST_STM32H5:STM32H562RGTx +MCU_ST_STM32H5:STM32H562RGVx +MCU_ST_STM32H5:STM32H562RITx +MCU_ST_STM32H5:STM32H562RIVx +MCU_ST_STM32H5:STM32H562VGTx +MCU_ST_STM32H5:STM32H562VITx +MCU_ST_STM32H5:STM32H562ZGTx +MCU_ST_STM32H5:STM32H562ZITx +MCU_ST_STM32H5:STM32H563AGIx +MCU_ST_STM32H5:STM32H563AIIx +MCU_ST_STM32H5:STM32H563AIIxQ +MCU_ST_STM32H5:STM32H563IGKx +MCU_ST_STM32H5:STM32H563IGTx +MCU_ST_STM32H5:STM32H563IIKx +MCU_ST_STM32H5:STM32H563IIKxQ +MCU_ST_STM32H5:STM32H563IITx +MCU_ST_STM32H5:STM32H563IITxQ +MCU_ST_STM32H5:STM32H563MIYxQ +MCU_ST_STM32H5:STM32H563RGTx +MCU_ST_STM32H5:STM32H563RGVx +MCU_ST_STM32H5:STM32H563RITx +MCU_ST_STM32H5:STM32H563RIVx +MCU_ST_STM32H5:STM32H563VGTx +MCU_ST_STM32H5:STM32H563VITx +MCU_ST_STM32H5:STM32H563VITxQ +MCU_ST_STM32H5:STM32H563ZGTx +MCU_ST_STM32H5:STM32H563ZITx +MCU_ST_STM32H5:STM32H563ZITxQ +MCU_ST_STM32H5:STM32H573AIIx +MCU_ST_STM32H5:STM32H573AIIxQ +MCU_ST_STM32H5:STM32H573IIKx +MCU_ST_STM32H5:STM32H573IIKxQ +MCU_ST_STM32H5:STM32H573IITx +MCU_ST_STM32H5:STM32H573IITxQ +MCU_ST_STM32H5:STM32H573MIYxQ +MCU_ST_STM32H5:STM32H573RITx +MCU_ST_STM32H5:STM32H573RIVx +MCU_ST_STM32H5:STM32H573VITx +MCU_ST_STM32H5:STM32H573VITxQ +MCU_ST_STM32H5:STM32H573ZITx +MCU_ST_STM32H5:STM32H573ZITxQ +MCU_ST_STM32H7:STM32H723VEHx +MCU_ST_STM32H7:STM32H723VETx +MCU_ST_STM32H7:STM32H723VGHx +MCU_ST_STM32H7:STM32H723VGTx +MCU_ST_STM32H7:STM32H723ZEIx +MCU_ST_STM32H7:STM32H723ZETx +MCU_ST_STM32H7:STM32H723ZGIx +MCU_ST_STM32H7:STM32H723ZGTx +MCU_ST_STM32H7:STM32H725AEIx +MCU_ST_STM32H7:STM32H725AGIx +MCU_ST_STM32H7:STM32H725IEKx +MCU_ST_STM32H7:STM32H725IETx +MCU_ST_STM32H7:STM32H725IGKx +MCU_ST_STM32H7:STM32H725IGTx +MCU_ST_STM32H7:STM32H725REVx +MCU_ST_STM32H7:STM32H725RGVx +MCU_ST_STM32H7:STM32H725VEHx +MCU_ST_STM32H7:STM32H725VETx +MCU_ST_STM32H7:STM32H725VGHx +MCU_ST_STM32H7:STM32H725VGTx +MCU_ST_STM32H7:STM32H725VGYx +MCU_ST_STM32H7:STM32H725ZETx +MCU_ST_STM32H7:STM32H725ZGTx +MCU_ST_STM32H7:STM32H730ABIxQ +MCU_ST_STM32H7:STM32H730IBKxQ +MCU_ST_STM32H7:STM32H730IBTxQ +MCU_ST_STM32H7:STM32H730VBHx +MCU_ST_STM32H7:STM32H730VBTx +MCU_ST_STM32H7:STM32H730ZBIx +MCU_ST_STM32H7:STM32H730ZBTx +MCU_ST_STM32H7:STM32H733VGHx +MCU_ST_STM32H7:STM32H733VGTx +MCU_ST_STM32H7:STM32H733ZGIx +MCU_ST_STM32H7:STM32H733ZGTx +MCU_ST_STM32H7:STM32H735AGIx +MCU_ST_STM32H7:STM32H735IGKx +MCU_ST_STM32H7:STM32H735IGTx +MCU_ST_STM32H7:STM32H735RGVx +MCU_ST_STM32H7:STM32H735VGHx +MCU_ST_STM32H7:STM32H735VGTx +MCU_ST_STM32H7:STM32H735VGYx +MCU_ST_STM32H7:STM32H735ZGTx +MCU_ST_STM32H7:STM32H742AGIx +MCU_ST_STM32H7:STM32H742AIIx +MCU_ST_STM32H7:STM32H742A_G-I_Ix +MCU_ST_STM32H7:STM32H742BGTx +MCU_ST_STM32H7:STM32H742BITx +MCU_ST_STM32H7:STM32H742B_G-I_Tx +MCU_ST_STM32H7:STM32H742IGKx +MCU_ST_STM32H7:STM32H742IGTx +MCU_ST_STM32H7:STM32H742IIKx +MCU_ST_STM32H7:STM32H742IITx +MCU_ST_STM32H7:STM32H742I_G-I_Kx +MCU_ST_STM32H7:STM32H742I_G-I_Tx +MCU_ST_STM32H7:STM32H742VGHx +MCU_ST_STM32H7:STM32H742VGTx +MCU_ST_STM32H7:STM32H742VIHx +MCU_ST_STM32H7:STM32H742VITx +MCU_ST_STM32H7:STM32H742V_G-I_Hx +MCU_ST_STM32H7:STM32H742V_G-I_Tx +MCU_ST_STM32H7:STM32H742XGHx +MCU_ST_STM32H7:STM32H742XIHx +MCU_ST_STM32H7:STM32H742X_G-I_Hx +MCU_ST_STM32H7:STM32H742ZGTx +MCU_ST_STM32H7:STM32H742ZITx +MCU_ST_STM32H7:STM32H742Z_G-I_Tx +MCU_ST_STM32H7:STM32H743AGIx +MCU_ST_STM32H7:STM32H743AIIx +MCU_ST_STM32H7:STM32H743A_G-I_Ix +MCU_ST_STM32H7:STM32H743BGTx +MCU_ST_STM32H7:STM32H743BITx +MCU_ST_STM32H7:STM32H743IGKx +MCU_ST_STM32H7:STM32H743IGTx +MCU_ST_STM32H7:STM32H743IIKx +MCU_ST_STM32H7:STM32H743IITx +MCU_ST_STM32H7:STM32H743VGHx +MCU_ST_STM32H7:STM32H743VGTx +MCU_ST_STM32H7:STM32H743VIHx +MCU_ST_STM32H7:STM32H743VITx +MCU_ST_STM32H7:STM32H743V_G-I_Hx +MCU_ST_STM32H7:STM32H743XGHx +MCU_ST_STM32H7:STM32H743XIHx +MCU_ST_STM32H7:STM32H743ZGTx +MCU_ST_STM32H7:STM32H743ZITx +MCU_ST_STM32H7:STM32H745BGTx +MCU_ST_STM32H7:STM32H745BITx +MCU_ST_STM32H7:STM32H745IGKx +MCU_ST_STM32H7:STM32H745IGTx +MCU_ST_STM32H7:STM32H745IIKx +MCU_ST_STM32H7:STM32H745IITx +MCU_ST_STM32H7:STM32H745XGHx +MCU_ST_STM32H7:STM32H745XIHx +MCU_ST_STM32H7:STM32H745ZGTx +MCU_ST_STM32H7:STM32H745ZITx +MCU_ST_STM32H7:STM32H747AGIx +MCU_ST_STM32H7:STM32H747AIIx +MCU_ST_STM32H7:STM32H747A_G-I_Ix +MCU_ST_STM32H7:STM32H747BGTx +MCU_ST_STM32H7:STM32H747BITx +MCU_ST_STM32H7:STM32H747IGTx +MCU_ST_STM32H7:STM32H747IITx +MCU_ST_STM32H7:STM32H747XGHx +MCU_ST_STM32H7:STM32H747XIHx +MCU_ST_STM32H7:STM32H747ZIYx +MCU_ST_STM32H7:STM32H750IBKx +MCU_ST_STM32H7:STM32H750IBTx +MCU_ST_STM32H7:STM32H750VBTx +MCU_ST_STM32H7:STM32H750XBHx +MCU_ST_STM32H7:STM32H750ZBTx +MCU_ST_STM32H7:STM32H753AIIx +MCU_ST_STM32H7:STM32H753BITx +MCU_ST_STM32H7:STM32H753IIKx +MCU_ST_STM32H7:STM32H753IITx +MCU_ST_STM32H7:STM32H753VIHx +MCU_ST_STM32H7:STM32H753VITx +MCU_ST_STM32H7:STM32H753XIHx +MCU_ST_STM32H7:STM32H753ZITx +MCU_ST_STM32H7:STM32H755BITx +MCU_ST_STM32H7:STM32H755IIKx +MCU_ST_STM32H7:STM32H755IITx +MCU_ST_STM32H7:STM32H755XIHx +MCU_ST_STM32H7:STM32H755ZITx +MCU_ST_STM32H7:STM32H757AIIx +MCU_ST_STM32H7:STM32H757BITx +MCU_ST_STM32H7:STM32H757IITx +MCU_ST_STM32H7:STM32H757XIHx +MCU_ST_STM32H7:STM32H757ZIYx +MCU_ST_STM32H7:STM32H7A3AGIxQ +MCU_ST_STM32H7:STM32H7A3AIIxQ +MCU_ST_STM32H7:STM32H7A3A_G-I_IxQ +MCU_ST_STM32H7:STM32H7A3IGKx +MCU_ST_STM32H7:STM32H7A3IGKxQ +MCU_ST_STM32H7:STM32H7A3IGTx +MCU_ST_STM32H7:STM32H7A3IGTxQ +MCU_ST_STM32H7:STM32H7A3IIKx +MCU_ST_STM32H7:STM32H7A3IIKxQ +MCU_ST_STM32H7:STM32H7A3IITx +MCU_ST_STM32H7:STM32H7A3IITxQ +MCU_ST_STM32H7:STM32H7A3I_G-I_Kx +MCU_ST_STM32H7:STM32H7A3I_G-I_KxQ +MCU_ST_STM32H7:STM32H7A3I_G-I_Tx +MCU_ST_STM32H7:STM32H7A3I_G-I_TxQ +MCU_ST_STM32H7:STM32H7A3LGHxQ +MCU_ST_STM32H7:STM32H7A3LIHxQ +MCU_ST_STM32H7:STM32H7A3L_G-I_HxQ +MCU_ST_STM32H7:STM32H7A3NGHx +MCU_ST_STM32H7:STM32H7A3NIHx +MCU_ST_STM32H7:STM32H7A3N_G-I_Hx +MCU_ST_STM32H7:STM32H7A3QIYxQ +MCU_ST_STM32H7:STM32H7A3RGTx +MCU_ST_STM32H7:STM32H7A3RITx +MCU_ST_STM32H7:STM32H7A3R_G-I_Tx +MCU_ST_STM32H7:STM32H7A3VGHx +MCU_ST_STM32H7:STM32H7A3VGHxQ +MCU_ST_STM32H7:STM32H7A3VGTx +MCU_ST_STM32H7:STM32H7A3VGTxQ +MCU_ST_STM32H7:STM32H7A3VIHx +MCU_ST_STM32H7:STM32H7A3VIHxQ +MCU_ST_STM32H7:STM32H7A3VITx +MCU_ST_STM32H7:STM32H7A3VITxQ +MCU_ST_STM32H7:STM32H7A3V_G-I_Hx +MCU_ST_STM32H7:STM32H7A3V_G-I_HxQ +MCU_ST_STM32H7:STM32H7A3V_G-I_Tx +MCU_ST_STM32H7:STM32H7A3V_G-I_TxQ +MCU_ST_STM32H7:STM32H7A3ZGTx +MCU_ST_STM32H7:STM32H7A3ZGTxQ +MCU_ST_STM32H7:STM32H7A3ZITx +MCU_ST_STM32H7:STM32H7A3ZITxQ +MCU_ST_STM32H7:STM32H7A3Z_G-I_Tx +MCU_ST_STM32H7:STM32H7A3Z_G-I_TxQ +MCU_ST_STM32H7:STM32H7B0ABIxQ +MCU_ST_STM32H7:STM32H7B0IBKxQ +MCU_ST_STM32H7:STM32H7B0IBTx +MCU_ST_STM32H7:STM32H7B0RBTx +MCU_ST_STM32H7:STM32H7B0VBTx +MCU_ST_STM32H7:STM32H7B0ZBTx +MCU_ST_STM32H7:STM32H7B3AIIxQ +MCU_ST_STM32H7:STM32H7B3IIKx +MCU_ST_STM32H7:STM32H7B3IIKxQ +MCU_ST_STM32H7:STM32H7B3IITx +MCU_ST_STM32H7:STM32H7B3IITxQ +MCU_ST_STM32H7:STM32H7B3LIHxQ +MCU_ST_STM32H7:STM32H7B3NIHx +MCU_ST_STM32H7:STM32H7B3QIYxQ +MCU_ST_STM32H7:STM32H7B3RITx +MCU_ST_STM32H7:STM32H7B3VIHx +MCU_ST_STM32H7:STM32H7B3VIHxQ +MCU_ST_STM32H7:STM32H7B3VITx +MCU_ST_STM32H7:STM32H7B3VITxQ +MCU_ST_STM32H7:STM32H7B3ZITx +MCU_ST_STM32H7:STM32H7B3ZITxQ +MCU_ST_STM32H7:STM32H7R3A8Ix +MCU_ST_STM32H7:STM32H7R3I8Kx +MCU_ST_STM32H7:STM32H7R3I8Tx +MCU_ST_STM32H7:STM32H7R3L8Hx +MCU_ST_STM32H7:STM32H7R3L8HxH +MCU_ST_STM32H7:STM32H7R3R8Vx +MCU_ST_STM32H7:STM32H7R3V8Hx +MCU_ST_STM32H7:STM32H7R3V8Tx +MCU_ST_STM32H7:STM32H7R3V8Yx +MCU_ST_STM32H7:STM32H7R3Z8Jx +MCU_ST_STM32H7:STM32H7R3Z8Tx +MCU_ST_STM32H7:STM32H7R7A8Ix +MCU_ST_STM32H7:STM32H7R7I8Kx +MCU_ST_STM32H7:STM32H7R7I8Tx +MCU_ST_STM32H7:STM32H7R7L8Hx +MCU_ST_STM32H7:STM32H7R7L8HxH +MCU_ST_STM32H7:STM32H7R7Z8Jx +MCU_ST_STM32H7:STM32H7S3A8Ix +MCU_ST_STM32H7:STM32H7S3I8Kx +MCU_ST_STM32H7:STM32H7S3I8Tx +MCU_ST_STM32H7:STM32H7S3L8Hx +MCU_ST_STM32H7:STM32H7S3L8HxH +MCU_ST_STM32H7:STM32H7S3R8Vx +MCU_ST_STM32H7:STM32H7S3V8Hx +MCU_ST_STM32H7:STM32H7S3V8Tx +MCU_ST_STM32H7:STM32H7S3V8Yx +MCU_ST_STM32H7:STM32H7S3Z8Jx +MCU_ST_STM32H7:STM32H7S3Z8Tx +MCU_ST_STM32H7:STM32H7S7A8Ix +MCU_ST_STM32H7:STM32H7S7I8Kx +MCU_ST_STM32H7:STM32H7S7I8Tx +MCU_ST_STM32H7:STM32H7S7L8Hx +MCU_ST_STM32H7:STM32H7S7L8HxH +MCU_ST_STM32H7:STM32H7S7Z8Jx +MCU_ST_STM32L0:STM32L010C6Tx +MCU_ST_STM32L0:STM32L010F4Px +MCU_ST_STM32L0:STM32L010K4Tx +MCU_ST_STM32L0:STM32L010K8Tx +MCU_ST_STM32L0:STM32L010R8Tx +MCU_ST_STM32L0:STM32L010RBTx +MCU_ST_STM32L0:STM32L011D3Px +MCU_ST_STM32L0:STM32L011D4Px +MCU_ST_STM32L0:STM32L011D_3-4_Px +MCU_ST_STM32L0:STM32L011E3Yx +MCU_ST_STM32L0:STM32L011E4Yx +MCU_ST_STM32L0:STM32L011E_3-4_Yx +MCU_ST_STM32L0:STM32L011F3Px +MCU_ST_STM32L0:STM32L011F3Ux +MCU_ST_STM32L0:STM32L011F4Px +MCU_ST_STM32L0:STM32L011F4Ux +MCU_ST_STM32L0:STM32L011F_3-4_Px +MCU_ST_STM32L0:STM32L011F_3-4_Ux +MCU_ST_STM32L0:STM32L011G3Ux +MCU_ST_STM32L0:STM32L011G4Ux +MCU_ST_STM32L0:STM32L011G_3-4_Ux +MCU_ST_STM32L0:STM32L011K3Tx +MCU_ST_STM32L0:STM32L011K3Ux +MCU_ST_STM32L0:STM32L011K4Tx +MCU_ST_STM32L0:STM32L011K4Ux +MCU_ST_STM32L0:STM32L011K_3-4_Tx +MCU_ST_STM32L0:STM32L011K_3-4_Ux +MCU_ST_STM32L0:STM32L021D4Px +MCU_ST_STM32L0:STM32L021F4Px +MCU_ST_STM32L0:STM32L021F4Ux +MCU_ST_STM32L0:STM32L021G4Ux +MCU_ST_STM32L0:STM32L021K4Tx +MCU_ST_STM32L0:STM32L021K4Ux +MCU_ST_STM32L0:STM32L031C4Tx +MCU_ST_STM32L0:STM32L031C4Ux +MCU_ST_STM32L0:STM32L031C6Tx +MCU_ST_STM32L0:STM32L031C6Ux +MCU_ST_STM32L0:STM32L031C_4-6_Tx +MCU_ST_STM32L0:STM32L031C_4-6_Ux +MCU_ST_STM32L0:STM32L031E4Yx +MCU_ST_STM32L0:STM32L031E6Yx +MCU_ST_STM32L0:STM32L031E_4-6_Yx +MCU_ST_STM32L0:STM32L031F4Px +MCU_ST_STM32L0:STM32L031F6Px +MCU_ST_STM32L0:STM32L031F_4-6_Px +MCU_ST_STM32L0:STM32L031G4Ux +MCU_ST_STM32L0:STM32L031G6Ux +MCU_ST_STM32L0:STM32L031G6UxS +MCU_ST_STM32L0:STM32L031G_4-6_Ux +MCU_ST_STM32L0:STM32L031K4Tx +MCU_ST_STM32L0:STM32L031K4Ux +MCU_ST_STM32L0:STM32L031K6Tx +MCU_ST_STM32L0:STM32L031K6Ux +MCU_ST_STM32L0:STM32L031K_4-6_Tx +MCU_ST_STM32L0:STM32L031K_4-6_Ux +MCU_ST_STM32L0:STM32L041C4Tx +MCU_ST_STM32L0:STM32L041C6Tx +MCU_ST_STM32L0:STM32L041C6Ux +MCU_ST_STM32L0:STM32L041C_4-6_Tx +MCU_ST_STM32L0:STM32L041E6Yx +MCU_ST_STM32L0:STM32L041F6Px +MCU_ST_STM32L0:STM32L041G6Ux +MCU_ST_STM32L0:STM32L041G6UxS +MCU_ST_STM32L0:STM32L041K6Tx +MCU_ST_STM32L0:STM32L041K6Ux +MCU_ST_STM32L0:STM32L051C6Tx +MCU_ST_STM32L0:STM32L051C6Ux +MCU_ST_STM32L0:STM32L051C8Tx +MCU_ST_STM32L0:STM32L051C8Ux +MCU_ST_STM32L0:STM32L051C_6-8_Tx +MCU_ST_STM32L0:STM32L051C_6-8_Ux +MCU_ST_STM32L0:STM32L051K6Tx +MCU_ST_STM32L0:STM32L051K6Ux +MCU_ST_STM32L0:STM32L051K8Tx +MCU_ST_STM32L0:STM32L051K8Ux +MCU_ST_STM32L0:STM32L051K_6-8_Tx +MCU_ST_STM32L0:STM32L051K_6-8_Ux +MCU_ST_STM32L0:STM32L051R6Hx +MCU_ST_STM32L0:STM32L051R6Tx +MCU_ST_STM32L0:STM32L051R8Hx +MCU_ST_STM32L0:STM32L051R8Tx +MCU_ST_STM32L0:STM32L051R_6-8_Hx +MCU_ST_STM32L0:STM32L051R_6-8_Tx +MCU_ST_STM32L0:STM32L051T6Yx +MCU_ST_STM32L0:STM32L051T8Yx +MCU_ST_STM32L0:STM32L051T_6-8_Yx +MCU_ST_STM32L0:STM32L052C6Tx +MCU_ST_STM32L0:STM32L052C6Ux +MCU_ST_STM32L0:STM32L052C8Tx +MCU_ST_STM32L0:STM32L052C8Ux +MCU_ST_STM32L0:STM32L052C_6-8_Tx +MCU_ST_STM32L0:STM32L052C_6-8_Ux +MCU_ST_STM32L0:STM32L052K6Tx +MCU_ST_STM32L0:STM32L052K6Ux +MCU_ST_STM32L0:STM32L052K8Tx +MCU_ST_STM32L0:STM32L052K8Ux +MCU_ST_STM32L0:STM32L052K_6-8_Tx +MCU_ST_STM32L0:STM32L052K_6-8_Ux +MCU_ST_STM32L0:STM32L052R6Hx +MCU_ST_STM32L0:STM32L052R6Tx +MCU_ST_STM32L0:STM32L052R8Hx +MCU_ST_STM32L0:STM32L052R8Tx +MCU_ST_STM32L0:STM32L052R_6-8_Hx +MCU_ST_STM32L0:STM32L052R_6-8_Tx +MCU_ST_STM32L0:STM32L052T6Yx +MCU_ST_STM32L0:STM32L052T8Fx +MCU_ST_STM32L0:STM32L052T8Yx +MCU_ST_STM32L0:STM32L052T_6-8_Yx +MCU_ST_STM32L0:STM32L053C6Tx +MCU_ST_STM32L0:STM32L053C6Ux +MCU_ST_STM32L0:STM32L053C8Tx +MCU_ST_STM32L0:STM32L053C8Ux +MCU_ST_STM32L0:STM32L053C_6-8_Tx +MCU_ST_STM32L0:STM32L053C_6-8_Ux +MCU_ST_STM32L0:STM32L053R6Hx +MCU_ST_STM32L0:STM32L053R6Tx +MCU_ST_STM32L0:STM32L053R8Hx +MCU_ST_STM32L0:STM32L053R8Tx +MCU_ST_STM32L0:STM32L053R_6-8_Hx +MCU_ST_STM32L0:STM32L053R_6-8_Tx +MCU_ST_STM32L0:STM32L062C8Ux +MCU_ST_STM32L0:STM32L062K8Tx +MCU_ST_STM32L0:STM32L062K8Ux +MCU_ST_STM32L0:STM32L063C8Tx +MCU_ST_STM32L0:STM32L063C8Ux +MCU_ST_STM32L0:STM32L063R8Tx +MCU_ST_STM32L0:STM32L071C8Tx +MCU_ST_STM32L0:STM32L071C8Ux +MCU_ST_STM32L0:STM32L071CBTx +MCU_ST_STM32L0:STM32L071CBUx +MCU_ST_STM32L0:STM32L071CBYx +MCU_ST_STM32L0:STM32L071CZTx +MCU_ST_STM32L0:STM32L071CZUx +MCU_ST_STM32L0:STM32L071CZYx +MCU_ST_STM32L0:STM32L071C_B-Z_Tx +MCU_ST_STM32L0:STM32L071C_B-Z_Ux +MCU_ST_STM32L0:STM32L071C_B-Z_Yx +MCU_ST_STM32L0:STM32L071K8Ux +MCU_ST_STM32L0:STM32L071KBTx +MCU_ST_STM32L0:STM32L071KBUx +MCU_ST_STM32L0:STM32L071KZTx +MCU_ST_STM32L0:STM32L071KZUx +MCU_ST_STM32L0:STM32L071K_B-Z_Tx +MCU_ST_STM32L0:STM32L071K_B-Z_Ux +MCU_ST_STM32L0:STM32L071RBHx +MCU_ST_STM32L0:STM32L071RBTx +MCU_ST_STM32L0:STM32L071RZHx +MCU_ST_STM32L0:STM32L071RZTx +MCU_ST_STM32L0:STM32L071R_B-Z_Hx +MCU_ST_STM32L0:STM32L071R_B-Z_Tx +MCU_ST_STM32L0:STM32L071V8Ix +MCU_ST_STM32L0:STM32L071V8Tx +MCU_ST_STM32L0:STM32L071VBIx +MCU_ST_STM32L0:STM32L071VBTx +MCU_ST_STM32L0:STM32L071VZIx +MCU_ST_STM32L0:STM32L071VZTx +MCU_ST_STM32L0:STM32L071V_B-Z_Ix +MCU_ST_STM32L0:STM32L071V_B-Z_Tx +MCU_ST_STM32L0:STM32L072CBTx +MCU_ST_STM32L0:STM32L072CBUx +MCU_ST_STM32L0:STM32L072CBYx +MCU_ST_STM32L0:STM32L072CZEx +MCU_ST_STM32L0:STM32L072CZTx +MCU_ST_STM32L0:STM32L072CZUx +MCU_ST_STM32L0:STM32L072CZYx +MCU_ST_STM32L0:STM32L072C_B-Z_Tx +MCU_ST_STM32L0:STM32L072C_B-Z_Ux +MCU_ST_STM32L0:STM32L072C_B-Z_Yx +MCU_ST_STM32L0:STM32L072KBTx +MCU_ST_STM32L0:STM32L072KBUx +MCU_ST_STM32L0:STM32L072KZTx +MCU_ST_STM32L0:STM32L072KZUx +MCU_ST_STM32L0:STM32L072K_B-Z_Tx +MCU_ST_STM32L0:STM32L072K_B-Z_Ux +MCU_ST_STM32L0:STM32L072RBHx +MCU_ST_STM32L0:STM32L072RBIx +MCU_ST_STM32L0:STM32L072RBTx +MCU_ST_STM32L0:STM32L072RZHx +MCU_ST_STM32L0:STM32L072RZIx +MCU_ST_STM32L0:STM32L072RZTx +MCU_ST_STM32L0:STM32L072R_B-Z_Hx +MCU_ST_STM32L0:STM32L072R_B-Z_Ix +MCU_ST_STM32L0:STM32L072R_B-Z_Tx +MCU_ST_STM32L0:STM32L072V8Ix +MCU_ST_STM32L0:STM32L072V8Tx +MCU_ST_STM32L0:STM32L072VBIx +MCU_ST_STM32L0:STM32L072VBTx +MCU_ST_STM32L0:STM32L072VZIx +MCU_ST_STM32L0:STM32L072VZTx +MCU_ST_STM32L0:STM32L072V_B-Z_Ix +MCU_ST_STM32L0:STM32L072V_B-Z_Tx +MCU_ST_STM32L0:STM32L073CBTx +MCU_ST_STM32L0:STM32L073CBUx +MCU_ST_STM32L0:STM32L073CZTx +MCU_ST_STM32L0:STM32L073CZUx +MCU_ST_STM32L0:STM32L073CZYx +MCU_ST_STM32L0:STM32L073C_B-Z_Tx +MCU_ST_STM32L0:STM32L073C_B-Z_Ux +MCU_ST_STM32L0:STM32L073RBHx +MCU_ST_STM32L0:STM32L073RBTx +MCU_ST_STM32L0:STM32L073RZHx +MCU_ST_STM32L0:STM32L073RZIx +MCU_ST_STM32L0:STM32L073RZTx +MCU_ST_STM32L0:STM32L073R_B-Z_Hx +MCU_ST_STM32L0:STM32L073R_B-Z_Tx +MCU_ST_STM32L0:STM32L073V8Ix +MCU_ST_STM32L0:STM32L073V8Tx +MCU_ST_STM32L0:STM32L073VBIx +MCU_ST_STM32L0:STM32L073VBTx +MCU_ST_STM32L0:STM32L073VZIx +MCU_ST_STM32L0:STM32L073VZTx +MCU_ST_STM32L0:STM32L073V_B-Z_Ix +MCU_ST_STM32L0:STM32L073V_B-Z_Tx +MCU_ST_STM32L0:STM32L081CBTx +MCU_ST_STM32L0:STM32L081CZTx +MCU_ST_STM32L0:STM32L081CZUx +MCU_ST_STM32L0:STM32L081C_B-Z_Tx +MCU_ST_STM32L0:STM32L081KZTx +MCU_ST_STM32L0:STM32L081KZUx +MCU_ST_STM32L0:STM32L082CZUx +MCU_ST_STM32L0:STM32L082CZYx +MCU_ST_STM32L0:STM32L082KBTx +MCU_ST_STM32L0:STM32L082KBUx +MCU_ST_STM32L0:STM32L082KZTx +MCU_ST_STM32L0:STM32L082KZUx +MCU_ST_STM32L0:STM32L082K_B-Z_Tx +MCU_ST_STM32L0:STM32L082K_B-Z_Ux +MCU_ST_STM32L0:STM32L083CBTx +MCU_ST_STM32L0:STM32L083CZTx +MCU_ST_STM32L0:STM32L083CZUx +MCU_ST_STM32L0:STM32L083C_B-Z_Tx +MCU_ST_STM32L0:STM32L083RBHx +MCU_ST_STM32L0:STM32L083RBTx +MCU_ST_STM32L0:STM32L083RZHx +MCU_ST_STM32L0:STM32L083RZTx +MCU_ST_STM32L0:STM32L083R_B-Z_Hx +MCU_ST_STM32L0:STM32L083R_B-Z_Tx +MCU_ST_STM32L0:STM32L083V8Ix +MCU_ST_STM32L0:STM32L083V8Tx +MCU_ST_STM32L0:STM32L083VBIx +MCU_ST_STM32L0:STM32L083VBTx +MCU_ST_STM32L0:STM32L083VZIx +MCU_ST_STM32L0:STM32L083VZTx +MCU_ST_STM32L0:STM32L083V_B-Z_Ix +MCU_ST_STM32L0:STM32L083V_B-Z_Tx +MCU_ST_STM32L1:STM32L100C6Ux +MCU_ST_STM32L1:STM32L100C6UxA +MCU_ST_STM32L1:STM32L100R8Tx +MCU_ST_STM32L1:STM32L100R8TxA +MCU_ST_STM32L1:STM32L100RBTx +MCU_ST_STM32L1:STM32L100RBTxA +MCU_ST_STM32L1:STM32L100RCTx +MCU_ST_STM32L1:STM32L100R_8-B_Tx +MCU_ST_STM32L1:STM32L100R_8-B_TxA +MCU_ST_STM32L1:STM32L151C6Tx +MCU_ST_STM32L1:STM32L151C6TxA +MCU_ST_STM32L1:STM32L151C6Ux +MCU_ST_STM32L1:STM32L151C6UxA +MCU_ST_STM32L1:STM32L151C8Tx +MCU_ST_STM32L1:STM32L151C8TxA +MCU_ST_STM32L1:STM32L151C8Ux +MCU_ST_STM32L1:STM32L151C8UxA +MCU_ST_STM32L1:STM32L151CBTx +MCU_ST_STM32L1:STM32L151CBTxA +MCU_ST_STM32L1:STM32L151CBUx +MCU_ST_STM32L1:STM32L151CBUxA +MCU_ST_STM32L1:STM32L151CCTx +MCU_ST_STM32L1:STM32L151CCUx +MCU_ST_STM32L1:STM32L151C_6-8-B_Tx +MCU_ST_STM32L1:STM32L151C_6-8-B_TxA +MCU_ST_STM32L1:STM32L151C_6-8-B_Ux +MCU_ST_STM32L1:STM32L151C_6-8-B_UxA +MCU_ST_STM32L1:STM32L151QCHx +MCU_ST_STM32L1:STM32L151QDHx +MCU_ST_STM32L1:STM32L151QEHx +MCU_ST_STM32L1:STM32L151R6Hx +MCU_ST_STM32L1:STM32L151R6HxA +MCU_ST_STM32L1:STM32L151R6Tx +MCU_ST_STM32L1:STM32L151R6TxA +MCU_ST_STM32L1:STM32L151R8Hx +MCU_ST_STM32L1:STM32L151R8HxA +MCU_ST_STM32L1:STM32L151R8Tx +MCU_ST_STM32L1:STM32L151R8TxA +MCU_ST_STM32L1:STM32L151RBHx +MCU_ST_STM32L1:STM32L151RBHxA +MCU_ST_STM32L1:STM32L151RBTx +MCU_ST_STM32L1:STM32L151RBTxA +MCU_ST_STM32L1:STM32L151RCTx +MCU_ST_STM32L1:STM32L151RCTxA +MCU_ST_STM32L1:STM32L151RCYx +MCU_ST_STM32L1:STM32L151RDTx +MCU_ST_STM32L1:STM32L151RDYx +MCU_ST_STM32L1:STM32L151RETx +MCU_ST_STM32L1:STM32L151R_6-8-B_Hx +MCU_ST_STM32L1:STM32L151R_6-8-B_HxA +MCU_ST_STM32L1:STM32L151R_6-8-B_Tx +MCU_ST_STM32L1:STM32L151R_6-8-B_TxA +MCU_ST_STM32L1:STM32L151UCYx +MCU_ST_STM32L1:STM32L151V8Hx +MCU_ST_STM32L1:STM32L151V8HxA +MCU_ST_STM32L1:STM32L151V8Tx +MCU_ST_STM32L1:STM32L151V8TxA +MCU_ST_STM32L1:STM32L151VBHx +MCU_ST_STM32L1:STM32L151VBHxA +MCU_ST_STM32L1:STM32L151VBTx +MCU_ST_STM32L1:STM32L151VBTxA +MCU_ST_STM32L1:STM32L151VCHx +MCU_ST_STM32L1:STM32L151VCTx +MCU_ST_STM32L1:STM32L151VCTxA +MCU_ST_STM32L1:STM32L151VDTx +MCU_ST_STM32L1:STM32L151VDTxX +MCU_ST_STM32L1:STM32L151VDYxX +MCU_ST_STM32L1:STM32L151VETx +MCU_ST_STM32L1:STM32L151VEYx +MCU_ST_STM32L1:STM32L151V_8-B_Hx +MCU_ST_STM32L1:STM32L151V_8-B_HxA +MCU_ST_STM32L1:STM32L151V_8-B_Tx +MCU_ST_STM32L1:STM32L151V_8-B_TxA +MCU_ST_STM32L1:STM32L151ZCTx +MCU_ST_STM32L1:STM32L151ZDTx +MCU_ST_STM32L1:STM32L151ZETx +MCU_ST_STM32L1:STM32L152C6Tx +MCU_ST_STM32L1:STM32L152C6TxA +MCU_ST_STM32L1:STM32L152C6Ux +MCU_ST_STM32L1:STM32L152C6UxA +MCU_ST_STM32L1:STM32L152C8Tx +MCU_ST_STM32L1:STM32L152C8TxA +MCU_ST_STM32L1:STM32L152C8Ux +MCU_ST_STM32L1:STM32L152C8UxA +MCU_ST_STM32L1:STM32L152CBTx +MCU_ST_STM32L1:STM32L152CBTxA +MCU_ST_STM32L1:STM32L152CBUx +MCU_ST_STM32L1:STM32L152CBUxA +MCU_ST_STM32L1:STM32L152CCTx +MCU_ST_STM32L1:STM32L152CCUx +MCU_ST_STM32L1:STM32L152C_6-8-B_Tx +MCU_ST_STM32L1:STM32L152C_6-8-B_TxA +MCU_ST_STM32L1:STM32L152C_6-8-B_Ux +MCU_ST_STM32L1:STM32L152C_6-8-B_UxA +MCU_ST_STM32L1:STM32L152QCHx +MCU_ST_STM32L1:STM32L152QDHx +MCU_ST_STM32L1:STM32L152QEHx +MCU_ST_STM32L1:STM32L152R6Hx +MCU_ST_STM32L1:STM32L152R6HxA +MCU_ST_STM32L1:STM32L152R6Tx +MCU_ST_STM32L1:STM32L152R6TxA +MCU_ST_STM32L1:STM32L152R8Hx +MCU_ST_STM32L1:STM32L152R8HxA +MCU_ST_STM32L1:STM32L152R8Tx +MCU_ST_STM32L1:STM32L152R8TxA +MCU_ST_STM32L1:STM32L152RBHx +MCU_ST_STM32L1:STM32L152RBHxA +MCU_ST_STM32L1:STM32L152RBTx +MCU_ST_STM32L1:STM32L152RBTxA +MCU_ST_STM32L1:STM32L152RCTx +MCU_ST_STM32L1:STM32L152RCTxA +MCU_ST_STM32L1:STM32L152RDTx +MCU_ST_STM32L1:STM32L152RDYx +MCU_ST_STM32L1:STM32L152RETx +MCU_ST_STM32L1:STM32L152R_6-8-B_Hx +MCU_ST_STM32L1:STM32L152R_6-8-B_HxA +MCU_ST_STM32L1:STM32L152R_6-8-B_Tx +MCU_ST_STM32L1:STM32L152R_6-8-B_TxA +MCU_ST_STM32L1:STM32L152UCYx +MCU_ST_STM32L1:STM32L152V8Hx +MCU_ST_STM32L1:STM32L152V8HxA +MCU_ST_STM32L1:STM32L152V8Tx +MCU_ST_STM32L1:STM32L152V8TxA +MCU_ST_STM32L1:STM32L152VBHx +MCU_ST_STM32L1:STM32L152VBHxA +MCU_ST_STM32L1:STM32L152VBTx +MCU_ST_STM32L1:STM32L152VBTxA +MCU_ST_STM32L1:STM32L152VCHx +MCU_ST_STM32L1:STM32L152VCTx +MCU_ST_STM32L1:STM32L152VCTxA +MCU_ST_STM32L1:STM32L152VDTx +MCU_ST_STM32L1:STM32L152VDTxX +MCU_ST_STM32L1:STM32L152VETx +MCU_ST_STM32L1:STM32L152VEYx +MCU_ST_STM32L1:STM32L152V_8-B_Hx +MCU_ST_STM32L1:STM32L152V_8-B_HxA +MCU_ST_STM32L1:STM32L152V_8-B_Tx +MCU_ST_STM32L1:STM32L152V_8-B_TxA +MCU_ST_STM32L1:STM32L152ZCTx +MCU_ST_STM32L1:STM32L152ZDTx +MCU_ST_STM32L1:STM32L152ZETx +MCU_ST_STM32L1:STM32L162QCHx +MCU_ST_STM32L1:STM32L162QDHx +MCU_ST_STM32L1:STM32L162RCTx +MCU_ST_STM32L1:STM32L162RCTxA +MCU_ST_STM32L1:STM32L162RDTx +MCU_ST_STM32L1:STM32L162RDYx +MCU_ST_STM32L1:STM32L162RETx +MCU_ST_STM32L1:STM32L162VCHx +MCU_ST_STM32L1:STM32L162VCTx +MCU_ST_STM32L1:STM32L162VCTxA +MCU_ST_STM32L1:STM32L162VDTx +MCU_ST_STM32L1:STM32L162VDYxX +MCU_ST_STM32L1:STM32L162VETx +MCU_ST_STM32L1:STM32L162VEYx +MCU_ST_STM32L1:STM32L162ZCTx +MCU_ST_STM32L1:STM32L162ZDTx +MCU_ST_STM32L1:STM32L162ZETx +MCU_ST_STM32L4:STM32L412C8Tx +MCU_ST_STM32L4:STM32L412C8Ux +MCU_ST_STM32L4:STM32L412CBTx +MCU_ST_STM32L4:STM32L412CBTxP +MCU_ST_STM32L4:STM32L412CBUx +MCU_ST_STM32L4:STM32L412CBUxP +MCU_ST_STM32L4:STM32L412K8Tx +MCU_ST_STM32L4:STM32L412K8Ux +MCU_ST_STM32L4:STM32L412KBTx +MCU_ST_STM32L4:STM32L412KBUx +MCU_ST_STM32L4:STM32L412R8Ix +MCU_ST_STM32L4:STM32L412R8Tx +MCU_ST_STM32L4:STM32L412RBIx +MCU_ST_STM32L4:STM32L412RBIxP +MCU_ST_STM32L4:STM32L412RBTx +MCU_ST_STM32L4:STM32L412RBTxP +MCU_ST_STM32L4:STM32L412T8Yx +MCU_ST_STM32L4:STM32L412TBYx +MCU_ST_STM32L4:STM32L412TBYxP +MCU_ST_STM32L4:STM32L422CBTx +MCU_ST_STM32L4:STM32L422CBUx +MCU_ST_STM32L4:STM32L422KBTx +MCU_ST_STM32L4:STM32L422KBUx +MCU_ST_STM32L4:STM32L422RBIx +MCU_ST_STM32L4:STM32L422RBTx +MCU_ST_STM32L4:STM32L422TBYx +MCU_ST_STM32L4:STM32L431CBTx +MCU_ST_STM32L4:STM32L431CBUx +MCU_ST_STM32L4:STM32L431CBYx +MCU_ST_STM32L4:STM32L431CCTx +MCU_ST_STM32L4:STM32L431CCUx +MCU_ST_STM32L4:STM32L431CCYx +MCU_ST_STM32L4:STM32L431C_B-C_Tx +MCU_ST_STM32L4:STM32L431C_B-C_Ux +MCU_ST_STM32L4:STM32L431C_B-C_Yx +MCU_ST_STM32L4:STM32L431KBUx +MCU_ST_STM32L4:STM32L431KCUx +MCU_ST_STM32L4:STM32L431K_B-C_Ux +MCU_ST_STM32L4:STM32L431RBIx +MCU_ST_STM32L4:STM32L431RBTx +MCU_ST_STM32L4:STM32L431RBYx +MCU_ST_STM32L4:STM32L431RCIx +MCU_ST_STM32L4:STM32L431RCTx +MCU_ST_STM32L4:STM32L431RCYx +MCU_ST_STM32L4:STM32L431R_B-C_Ix +MCU_ST_STM32L4:STM32L431R_B-C_Tx +MCU_ST_STM32L4:STM32L431R_B-C_Yx +MCU_ST_STM32L4:STM32L431VCIx +MCU_ST_STM32L4:STM32L431VCTx +MCU_ST_STM32L4:STM32L432KBUx +MCU_ST_STM32L4:STM32L432KCUx +MCU_ST_STM32L4:STM32L432K_B-C_Ux +MCU_ST_STM32L4:STM32L433CBTx +MCU_ST_STM32L4:STM32L433CBUx +MCU_ST_STM32L4:STM32L433CBYx +MCU_ST_STM32L4:STM32L433CCTx +MCU_ST_STM32L4:STM32L433CCUx +MCU_ST_STM32L4:STM32L433CCYx +MCU_ST_STM32L4:STM32L433C_B-C_Tx +MCU_ST_STM32L4:STM32L433C_B-C_Ux +MCU_ST_STM32L4:STM32L433C_B-C_Yx +MCU_ST_STM32L4:STM32L433RBIx +MCU_ST_STM32L4:STM32L433RBTx +MCU_ST_STM32L4:STM32L433RBYx +MCU_ST_STM32L4:STM32L433RCIx +MCU_ST_STM32L4:STM32L433RCTx +MCU_ST_STM32L4:STM32L433RCTxP +MCU_ST_STM32L4:STM32L433RCYx +MCU_ST_STM32L4:STM32L433R_B-C_Ix +MCU_ST_STM32L4:STM32L433R_B-C_Tx +MCU_ST_STM32L4:STM32L433R_B-C_Yx +MCU_ST_STM32L4:STM32L433VCIx +MCU_ST_STM32L4:STM32L433VCTx +MCU_ST_STM32L4:STM32L442KCUx +MCU_ST_STM32L4:STM32L443CCFx +MCU_ST_STM32L4:STM32L443CCTx +MCU_ST_STM32L4:STM32L443CCUx +MCU_ST_STM32L4:STM32L443CCYx +MCU_ST_STM32L4:STM32L443RCIx +MCU_ST_STM32L4:STM32L443RCTx +MCU_ST_STM32L4:STM32L443RCYx +MCU_ST_STM32L4:STM32L443VCIx +MCU_ST_STM32L4:STM32L443VCTx +MCU_ST_STM32L4:STM32L451CCUx +MCU_ST_STM32L4:STM32L451CETx +MCU_ST_STM32L4:STM32L451CEUx +MCU_ST_STM32L4:STM32L451C_C-E_Ux +MCU_ST_STM32L4:STM32L451RCIx +MCU_ST_STM32L4:STM32L451RCTx +MCU_ST_STM32L4:STM32L451RCYx +MCU_ST_STM32L4:STM32L451REIx +MCU_ST_STM32L4:STM32L451RETx +MCU_ST_STM32L4:STM32L451REYx +MCU_ST_STM32L4:STM32L451R_C-E_Ix +MCU_ST_STM32L4:STM32L451R_C-E_Tx +MCU_ST_STM32L4:STM32L451R_C-E_Yx +MCU_ST_STM32L4:STM32L451VCIx +MCU_ST_STM32L4:STM32L451VCTx +MCU_ST_STM32L4:STM32L451VEIx +MCU_ST_STM32L4:STM32L451VETx +MCU_ST_STM32L4:STM32L451V_C-E_Ix +MCU_ST_STM32L4:STM32L451V_C-E_Tx +MCU_ST_STM32L4:STM32L452CCUx +MCU_ST_STM32L4:STM32L452CETx +MCU_ST_STM32L4:STM32L452CETxP +MCU_ST_STM32L4:STM32L452CEUx +MCU_ST_STM32L4:STM32L452C_C-E_Ux +MCU_ST_STM32L4:STM32L452RCIx +MCU_ST_STM32L4:STM32L452RCTx +MCU_ST_STM32L4:STM32L452RCYx +MCU_ST_STM32L4:STM32L452REIx +MCU_ST_STM32L4:STM32L452RETx +MCU_ST_STM32L4:STM32L452RETxP +MCU_ST_STM32L4:STM32L452REYx +MCU_ST_STM32L4:STM32L452REYxP +MCU_ST_STM32L4:STM32L452R_C-E_Ix +MCU_ST_STM32L4:STM32L452R_C-E_Tx +MCU_ST_STM32L4:STM32L452R_C-E_Yx +MCU_ST_STM32L4:STM32L452VCIx +MCU_ST_STM32L4:STM32L452VCTx +MCU_ST_STM32L4:STM32L452VEIx +MCU_ST_STM32L4:STM32L452VETx +MCU_ST_STM32L4:STM32L452V_C-E_Ix +MCU_ST_STM32L4:STM32L452V_C-E_Tx +MCU_ST_STM32L4:STM32L462CETx +MCU_ST_STM32L4:STM32L462CEUx +MCU_ST_STM32L4:STM32L462REIx +MCU_ST_STM32L4:STM32L462RETx +MCU_ST_STM32L4:STM32L462REYx +MCU_ST_STM32L4:STM32L462VEIx +MCU_ST_STM32L4:STM32L462VETx +MCU_ST_STM32L4:STM32L471QEIx +MCU_ST_STM32L4:STM32L471QGIx +MCU_ST_STM32L4:STM32L471Q_E-G_Ix +MCU_ST_STM32L4:STM32L471RETx +MCU_ST_STM32L4:STM32L471RGTx +MCU_ST_STM32L4:STM32L471R_E-G_Tx +MCU_ST_STM32L4:STM32L471VETx +MCU_ST_STM32L4:STM32L471VGTx +MCU_ST_STM32L4:STM32L471V_E-G_Tx +MCU_ST_STM32L4:STM32L471ZEJx +MCU_ST_STM32L4:STM32L471ZETx +MCU_ST_STM32L4:STM32L471ZGJx +MCU_ST_STM32L4:STM32L471ZGTx +MCU_ST_STM32L4:STM32L471Z_E-G_Jx +MCU_ST_STM32L4:STM32L471Z_E-G_Tx +MCU_ST_STM32L4:STM32L475RCTx +MCU_ST_STM32L4:STM32L475RETx +MCU_ST_STM32L4:STM32L475RGTx +MCU_ST_STM32L4:STM32L475R_C-E-G_Tx +MCU_ST_STM32L4:STM32L475VCTx +MCU_ST_STM32L4:STM32L475VETx +MCU_ST_STM32L4:STM32L475VGTx +MCU_ST_STM32L4:STM32L475V_C-E-G_Tx +MCU_ST_STM32L4:STM32L476JEYx +MCU_ST_STM32L4:STM32L476JGYx +MCU_ST_STM32L4:STM32L476JGYxP +MCU_ST_STM32L4:STM32L476J_E-G_Yx +MCU_ST_STM32L4:STM32L476MEYx +MCU_ST_STM32L4:STM32L476MGYx +MCU_ST_STM32L4:STM32L476M_E-G_Yx +MCU_ST_STM32L4:STM32L476QEIx +MCU_ST_STM32L4:STM32L476QGIx +MCU_ST_STM32L4:STM32L476QGIxP +MCU_ST_STM32L4:STM32L476Q_E-G_Ix +MCU_ST_STM32L4:STM32L476RCTx +MCU_ST_STM32L4:STM32L476RETx +MCU_ST_STM32L4:STM32L476RGTx +MCU_ST_STM32L4:STM32L476R_C-E-G_Tx +MCU_ST_STM32L4:STM32L476VCTx +MCU_ST_STM32L4:STM32L476VETx +MCU_ST_STM32L4:STM32L476VGTx +MCU_ST_STM32L4:STM32L476VGYxP +MCU_ST_STM32L4:STM32L476V_C-E-G_Tx +MCU_ST_STM32L4:STM32L476ZETx +MCU_ST_STM32L4:STM32L476ZGJx +MCU_ST_STM32L4:STM32L476ZGTx +MCU_ST_STM32L4:STM32L476ZGTxP +MCU_ST_STM32L4:STM32L476Z_E-G_Tx +MCU_ST_STM32L4:STM32L486JGYx +MCU_ST_STM32L4:STM32L486QGIx +MCU_ST_STM32L4:STM32L486RGTx +MCU_ST_STM32L4:STM32L486VGTx +MCU_ST_STM32L4:STM32L486ZGTx +MCU_ST_STM32L4:STM32L496AEIx +MCU_ST_STM32L4:STM32L496AGIx +MCU_ST_STM32L4:STM32L496AGIxP +MCU_ST_STM32L4:STM32L496A_E-G_Ix +MCU_ST_STM32L4:STM32L496QEIx +MCU_ST_STM32L4:STM32L496QGIx +MCU_ST_STM32L4:STM32L496QGIxP +MCU_ST_STM32L4:STM32L496QGIxS +MCU_ST_STM32L4:STM32L496Q_E-G_Ix +MCU_ST_STM32L4:STM32L496RETx +MCU_ST_STM32L4:STM32L496RGTx +MCU_ST_STM32L4:STM32L496RGTxP +MCU_ST_STM32L4:STM32L496R_E-G_Tx +MCU_ST_STM32L4:STM32L496VETx +MCU_ST_STM32L4:STM32L496VGTx +MCU_ST_STM32L4:STM32L496VGTxP +MCU_ST_STM32L4:STM32L496VGYx +MCU_ST_STM32L4:STM32L496VGYxP +MCU_ST_STM32L4:STM32L496V_E-G_Tx +MCU_ST_STM32L4:STM32L496WGYxP +MCU_ST_STM32L4:STM32L496ZETx +MCU_ST_STM32L4:STM32L496ZGTx +MCU_ST_STM32L4:STM32L496ZGTxP +MCU_ST_STM32L4:STM32L496Z_E-G_Tx +MCU_ST_STM32L4:STM32L4A6AGIx +MCU_ST_STM32L4:STM32L4A6AGIxP +MCU_ST_STM32L4:STM32L4A6QGIx +MCU_ST_STM32L4:STM32L4A6QGIxP +MCU_ST_STM32L4:STM32L4A6RGTx +MCU_ST_STM32L4:STM32L4A6RGTxP +MCU_ST_STM32L4:STM32L4A6VGTx +MCU_ST_STM32L4:STM32L4A6VGTxP +MCU_ST_STM32L4:STM32L4A6VGYx +MCU_ST_STM32L4:STM32L4A6VGYxP +MCU_ST_STM32L4:STM32L4A6ZGTx +MCU_ST_STM32L4:STM32L4A6ZGTxP +MCU_ST_STM32L4:STM32L4P5AEIx +MCU_ST_STM32L4:STM32L4P5AGIx +MCU_ST_STM32L4:STM32L4P5AGIxP +MCU_ST_STM32L4:STM32L4P5A_G-E_Ix +MCU_ST_STM32L4:STM32L4P5CETx +MCU_ST_STM32L4:STM32L4P5CEUx +MCU_ST_STM32L4:STM32L4P5CGTx +MCU_ST_STM32L4:STM32L4P5CGTxP +MCU_ST_STM32L4:STM32L4P5CGUx +MCU_ST_STM32L4:STM32L4P5CGUxP +MCU_ST_STM32L4:STM32L4P5C_G-E_Tx +MCU_ST_STM32L4:STM32L4P5C_G-E_Ux +MCU_ST_STM32L4:STM32L4P5QEIx +MCU_ST_STM32L4:STM32L4P5QGIx +MCU_ST_STM32L4:STM32L4P5QGIxP +MCU_ST_STM32L4:STM32L4P5QGIxS +MCU_ST_STM32L4:STM32L4P5Q_G-E_Ix +MCU_ST_STM32L4:STM32L4P5RETx +MCU_ST_STM32L4:STM32L4P5RGTx +MCU_ST_STM32L4:STM32L4P5RGTxP +MCU_ST_STM32L4:STM32L4P5R_G-E_Tx +MCU_ST_STM32L4:STM32L4P5VETx +MCU_ST_STM32L4:STM32L4P5VEYx +MCU_ST_STM32L4:STM32L4P5VGTx +MCU_ST_STM32L4:STM32L4P5VGTxP +MCU_ST_STM32L4:STM32L4P5VGYx +MCU_ST_STM32L4:STM32L4P5VGYxP +MCU_ST_STM32L4:STM32L4P5V_G-E_Tx +MCU_ST_STM32L4:STM32L4P5V_G-E_Yx +MCU_ST_STM32L4:STM32L4P5ZETx +MCU_ST_STM32L4:STM32L4P5ZGTx +MCU_ST_STM32L4:STM32L4P5ZGTxP +MCU_ST_STM32L4:STM32L4P5Z_G-E_Tx +MCU_ST_STM32L4:STM32L4Q5AGIx +MCU_ST_STM32L4:STM32L4Q5AGIxP +MCU_ST_STM32L4:STM32L4Q5CGTx +MCU_ST_STM32L4:STM32L4Q5CGTxP +MCU_ST_STM32L4:STM32L4Q5CGUx +MCU_ST_STM32L4:STM32L4Q5CGUxP +MCU_ST_STM32L4:STM32L4Q5QGIx +MCU_ST_STM32L4:STM32L4Q5QGIxP +MCU_ST_STM32L4:STM32L4Q5RGTx +MCU_ST_STM32L4:STM32L4Q5RGTxP +MCU_ST_STM32L4:STM32L4Q5VGTx +MCU_ST_STM32L4:STM32L4Q5VGTxP +MCU_ST_STM32L4:STM32L4Q5VGYx +MCU_ST_STM32L4:STM32L4Q5VGYxP +MCU_ST_STM32L4:STM32L4Q5ZGTx +MCU_ST_STM32L4:STM32L4Q5ZGTxP +MCU_ST_STM32L4:STM32L4R5AGIx +MCU_ST_STM32L4:STM32L4R5AIIx +MCU_ST_STM32L4:STM32L4R5AIIxP +MCU_ST_STM32L4:STM32L4R5A_G-I_Ix +MCU_ST_STM32L4:STM32L4R5QGIx +MCU_ST_STM32L4:STM32L4R5QGIxS +MCU_ST_STM32L4:STM32L4R5QIIx +MCU_ST_STM32L4:STM32L4R5QIIxP +MCU_ST_STM32L4:STM32L4R5Q_G-I_Ix +MCU_ST_STM32L4:STM32L4R5VGTx +MCU_ST_STM32L4:STM32L4R5VITx +MCU_ST_STM32L4:STM32L4R5V_G-I_Tx +MCU_ST_STM32L4:STM32L4R5ZGTx +MCU_ST_STM32L4:STM32L4R5ZGYx +MCU_ST_STM32L4:STM32L4R5ZITx +MCU_ST_STM32L4:STM32L4R5ZITxP +MCU_ST_STM32L4:STM32L4R5ZIYx +MCU_ST_STM32L4:STM32L4R5Z_G-I_Tx +MCU_ST_STM32L4:STM32L4R5Z_G-I_Yx +MCU_ST_STM32L4:STM32L4R7AIIx +MCU_ST_STM32L4:STM32L4R7VITx +MCU_ST_STM32L4:STM32L4R7ZITx +MCU_ST_STM32L4:STM32L4R9AGIx +MCU_ST_STM32L4:STM32L4R9AIIx +MCU_ST_STM32L4:STM32L4R9A_G-I_Ix +MCU_ST_STM32L4:STM32L4R9VGTx +MCU_ST_STM32L4:STM32L4R9VITx +MCU_ST_STM32L4:STM32L4R9V_G-I_Tx +MCU_ST_STM32L4:STM32L4R9ZGJx +MCU_ST_STM32L4:STM32L4R9ZGTx +MCU_ST_STM32L4:STM32L4R9ZGYx +MCU_ST_STM32L4:STM32L4R9ZIJx +MCU_ST_STM32L4:STM32L4R9ZITx +MCU_ST_STM32L4:STM32L4R9ZIYx +MCU_ST_STM32L4:STM32L4R9ZIYxP +MCU_ST_STM32L4:STM32L4R9Z_G-I_Jx +MCU_ST_STM32L4:STM32L4R9Z_G-I_Tx +MCU_ST_STM32L4:STM32L4R9Z_G-I_Yx +MCU_ST_STM32L4:STM32L4S5AIIx +MCU_ST_STM32L4:STM32L4S5QIIx +MCU_ST_STM32L4:STM32L4S5VITx +MCU_ST_STM32L4:STM32L4S5ZITx +MCU_ST_STM32L4:STM32L4S5ZIYx +MCU_ST_STM32L4:STM32L4S7AIIx +MCU_ST_STM32L4:STM32L4S7VITx +MCU_ST_STM32L4:STM32L4S7ZITx +MCU_ST_STM32L4:STM32L4S9AIIx +MCU_ST_STM32L4:STM32L4S9VITx +MCU_ST_STM32L4:STM32L4S9ZIJx +MCU_ST_STM32L4:STM32L4S9ZITx +MCU_ST_STM32L4:STM32L4S9ZIYx +MCU_ST_STM32L5:STM32L552CCTx +MCU_ST_STM32L5:STM32L552CCUx +MCU_ST_STM32L5:STM32L552CETx +MCU_ST_STM32L5:STM32L552CETxP +MCU_ST_STM32L5:STM32L552CEUx +MCU_ST_STM32L5:STM32L552CEUxP +MCU_ST_STM32L5:STM32L552C_C-E_Tx +MCU_ST_STM32L5:STM32L552C_C-E_Ux +MCU_ST_STM32L5:STM32L552MEYxP +MCU_ST_STM32L5:STM32L552MEYxQ +MCU_ST_STM32L5:STM32L552QCIxQ +MCU_ST_STM32L5:STM32L552QEIx +MCU_ST_STM32L5:STM32L552QEIxP +MCU_ST_STM32L5:STM32L552QEIxQ +MCU_ST_STM32L5:STM32L552Q_C-E_IxQ +MCU_ST_STM32L5:STM32L552RCTx +MCU_ST_STM32L5:STM32L552RETx +MCU_ST_STM32L5:STM32L552RETxP +MCU_ST_STM32L5:STM32L552RETxQ +MCU_ST_STM32L5:STM32L552R_C-E_Tx +MCU_ST_STM32L5:STM32L552VCTxQ +MCU_ST_STM32L5:STM32L552VETx +MCU_ST_STM32L5:STM32L552VETxQ +MCU_ST_STM32L5:STM32L552V_C-E_TxQ +MCU_ST_STM32L5:STM32L552ZCTxQ +MCU_ST_STM32L5:STM32L552ZETx +MCU_ST_STM32L5:STM32L552ZETxQ +MCU_ST_STM32L5:STM32L552Z_C-E_TxQ +MCU_ST_STM32L5:STM32L562CETx +MCU_ST_STM32L5:STM32L562CETxP +MCU_ST_STM32L5:STM32L562CEUx +MCU_ST_STM32L5:STM32L562CEUxP +MCU_ST_STM32L5:STM32L562MEYxP +MCU_ST_STM32L5:STM32L562MEYxQ +MCU_ST_STM32L5:STM32L562QEIx +MCU_ST_STM32L5:STM32L562QEIxP +MCU_ST_STM32L5:STM32L562QEIxQ +MCU_ST_STM32L5:STM32L562RETx +MCU_ST_STM32L5:STM32L562RETxP +MCU_ST_STM32L5:STM32L562RETxQ +MCU_ST_STM32L5:STM32L562VETx +MCU_ST_STM32L5:STM32L562VETxQ +MCU_ST_STM32L5:STM32L562ZETx +MCU_ST_STM32L5:STM32L562ZETxQ +MCU_ST_STM32MP1:STM32MP131AAEx +MCU_ST_STM32MP1:STM32MP131AAFx +MCU_ST_STM32MP1:STM32MP131AAGx +MCU_ST_STM32MP1:STM32MP131CAEx +MCU_ST_STM32MP1:STM32MP131CAFx +MCU_ST_STM32MP1:STM32MP131CAGx +MCU_ST_STM32MP1:STM32MP131DAEx +MCU_ST_STM32MP1:STM32MP131DAFx +MCU_ST_STM32MP1:STM32MP131DAGx +MCU_ST_STM32MP1:STM32MP131FAEx +MCU_ST_STM32MP1:STM32MP131FAFx +MCU_ST_STM32MP1:STM32MP131FAGx +MCU_ST_STM32MP1:STM32MP133AAEx +MCU_ST_STM32MP1:STM32MP133AAFx +MCU_ST_STM32MP1:STM32MP133AAGx +MCU_ST_STM32MP1:STM32MP133CAEx +MCU_ST_STM32MP1:STM32MP133CAFx +MCU_ST_STM32MP1:STM32MP133CAGx +MCU_ST_STM32MP1:STM32MP133DAEx +MCU_ST_STM32MP1:STM32MP133DAFx +MCU_ST_STM32MP1:STM32MP133DAGx +MCU_ST_STM32MP1:STM32MP133FAEx +MCU_ST_STM32MP1:STM32MP133FAFx +MCU_ST_STM32MP1:STM32MP133FAGx +MCU_ST_STM32MP1:STM32MP135AAEx +MCU_ST_STM32MP1:STM32MP135AAFx +MCU_ST_STM32MP1:STM32MP135AAGx +MCU_ST_STM32MP1:STM32MP135CAEx +MCU_ST_STM32MP1:STM32MP135CAFx +MCU_ST_STM32MP1:STM32MP135CAGx +MCU_ST_STM32MP1:STM32MP135DAEx +MCU_ST_STM32MP1:STM32MP135DAFx +MCU_ST_STM32MP1:STM32MP135DAGx +MCU_ST_STM32MP1:STM32MP135FAEx +MCU_ST_STM32MP1:STM32MP135FAFx +MCU_ST_STM32MP1:STM32MP135FAGx +MCU_ST_STM32MP1:STM32MP151AAAx +MCU_ST_STM32MP1:STM32MP151AABx +MCU_ST_STM32MP1:STM32MP151AACx +MCU_ST_STM32MP1:STM32MP151AADx +MCU_ST_STM32MP1:STM32MP151CAAx +MCU_ST_STM32MP1:STM32MP151CABx +MCU_ST_STM32MP1:STM32MP151CACx +MCU_ST_STM32MP1:STM32MP151CADx +MCU_ST_STM32MP1:STM32MP151DAAx +MCU_ST_STM32MP1:STM32MP151DABx +MCU_ST_STM32MP1:STM32MP151DACx +MCU_ST_STM32MP1:STM32MP151DADx +MCU_ST_STM32MP1:STM32MP151FAAx +MCU_ST_STM32MP1:STM32MP151FABx +MCU_ST_STM32MP1:STM32MP151FACx +MCU_ST_STM32MP1:STM32MP151FADx +MCU_ST_STM32MP1:STM32MP153AAAx +MCU_ST_STM32MP1:STM32MP153AABx +MCU_ST_STM32MP1:STM32MP153AACx +MCU_ST_STM32MP1:STM32MP153AADx +MCU_ST_STM32MP1:STM32MP153CAAx +MCU_ST_STM32MP1:STM32MP153CABx +MCU_ST_STM32MP1:STM32MP153CACx +MCU_ST_STM32MP1:STM32MP153CADx +MCU_ST_STM32MP1:STM32MP153DAAx +MCU_ST_STM32MP1:STM32MP153DABx +MCU_ST_STM32MP1:STM32MP153DACx +MCU_ST_STM32MP1:STM32MP153DADx +MCU_ST_STM32MP1:STM32MP153FAAx +MCU_ST_STM32MP1:STM32MP153FABx +MCU_ST_STM32MP1:STM32MP153FACx +MCU_ST_STM32MP1:STM32MP153FADx +MCU_ST_STM32MP1:STM32MP157AAAx +MCU_ST_STM32MP1:STM32MP157AABx +MCU_ST_STM32MP1:STM32MP157AACx +MCU_ST_STM32MP1:STM32MP157AADx +MCU_ST_STM32MP1:STM32MP157CAAx +MCU_ST_STM32MP1:STM32MP157CABx +MCU_ST_STM32MP1:STM32MP157CACx +MCU_ST_STM32MP1:STM32MP157CADx +MCU_ST_STM32MP1:STM32MP157DAAx +MCU_ST_STM32MP1:STM32MP157DABx +MCU_ST_STM32MP1:STM32MP157DACx +MCU_ST_STM32MP1:STM32MP157DADx +MCU_ST_STM32MP1:STM32MP157FAAx +MCU_ST_STM32MP1:STM32MP157FABx +MCU_ST_STM32MP1:STM32MP157FACx +MCU_ST_STM32MP1:STM32MP157FADx +MCU_ST_STM32U0:STM32U031C6Tx +MCU_ST_STM32U0:STM32U031C6Ux +MCU_ST_STM32U0:STM32U031C8Tx +MCU_ST_STM32U0:STM32U031C8Ux +MCU_ST_STM32U0:STM32U031F4Px +MCU_ST_STM32U0:STM32U031F6Px +MCU_ST_STM32U0:STM32U031F8Px +MCU_ST_STM32U0:STM32U031G6Yx +MCU_ST_STM32U0:STM32U031G8Yx +MCU_ST_STM32U0:STM32U031K4Ux +MCU_ST_STM32U0:STM32U031K6Ux +MCU_ST_STM32U0:STM32U031K8Ux +MCU_ST_STM32U0:STM32U031R6Ix +MCU_ST_STM32U0:STM32U031R6Tx +MCU_ST_STM32U0:STM32U031R8Ix +MCU_ST_STM32U0:STM32U031R8Tx +MCU_ST_STM32U0:STM32U073C8Tx +MCU_ST_STM32U0:STM32U073C8Ux +MCU_ST_STM32U0:STM32U073CBTx +MCU_ST_STM32U0:STM32U073CBUx +MCU_ST_STM32U0:STM32U073CCTx +MCU_ST_STM32U0:STM32U073CCUx +MCU_ST_STM32U0:STM32U073H8Yx +MCU_ST_STM32U0:STM32U073HBYx +MCU_ST_STM32U0:STM32U073HCYx +MCU_ST_STM32U0:STM32U073K8Ux +MCU_ST_STM32U0:STM32U073KBUx +MCU_ST_STM32U0:STM32U073KCUx +MCU_ST_STM32U0:STM32U073M8Ix +MCU_ST_STM32U0:STM32U073M8Tx +MCU_ST_STM32U0:STM32U073MBIx +MCU_ST_STM32U0:STM32U073MBTx +MCU_ST_STM32U0:STM32U073MCIx +MCU_ST_STM32U0:STM32U073MCTx +MCU_ST_STM32U0:STM32U073R8Ix +MCU_ST_STM32U0:STM32U073R8Tx +MCU_ST_STM32U0:STM32U073RBIx +MCU_ST_STM32U0:STM32U073RBTx +MCU_ST_STM32U0:STM32U073RCIx +MCU_ST_STM32U0:STM32U073RCTx +MCU_ST_STM32U0:STM32U083CCTx +MCU_ST_STM32U0:STM32U083CCUx +MCU_ST_STM32U0:STM32U083HCYx +MCU_ST_STM32U0:STM32U083KCUx +MCU_ST_STM32U0:STM32U083MCIx +MCU_ST_STM32U0:STM32U083MCTx +MCU_ST_STM32U0:STM32U083RCIx +MCU_ST_STM32U0:STM32U083RCTx +MCU_ST_STM32U5:STM32U535CBTx +MCU_ST_STM32U5:STM32U535CBTxQ +MCU_ST_STM32U5:STM32U535CBUx +MCU_ST_STM32U5:STM32U535CBUxQ +MCU_ST_STM32U5:STM32U535CCTx +MCU_ST_STM32U5:STM32U535CCTxQ +MCU_ST_STM32U5:STM32U535CCUx +MCU_ST_STM32U5:STM32U535CCUxQ +MCU_ST_STM32U5:STM32U535CETx +MCU_ST_STM32U5:STM32U535CETxQ +MCU_ST_STM32U5:STM32U535CEUx +MCU_ST_STM32U5:STM32U535CEUxQ +MCU_ST_STM32U5:STM32U535JEYxQ +MCU_ST_STM32U5:STM32U535NCYxQ +MCU_ST_STM32U5:STM32U535NEYxQ +MCU_ST_STM32U5:STM32U535RBIx +MCU_ST_STM32U5:STM32U535RBIxQ +MCU_ST_STM32U5:STM32U535RBTx +MCU_ST_STM32U5:STM32U535RBTxQ +MCU_ST_STM32U5:STM32U535RCIx +MCU_ST_STM32U5:STM32U535RCIxQ +MCU_ST_STM32U5:STM32U535RCTx +MCU_ST_STM32U5:STM32U535RCTxQ +MCU_ST_STM32U5:STM32U535REIx +MCU_ST_STM32U5:STM32U535REIxQ +MCU_ST_STM32U5:STM32U535RETx +MCU_ST_STM32U5:STM32U535RETxQ +MCU_ST_STM32U5:STM32U535VCIx +MCU_ST_STM32U5:STM32U535VCIxQ +MCU_ST_STM32U5:STM32U535VCTx +MCU_ST_STM32U5:STM32U535VCTxQ +MCU_ST_STM32U5:STM32U535VEIx +MCU_ST_STM32U5:STM32U535VEIxQ +MCU_ST_STM32U5:STM32U535VETx +MCU_ST_STM32U5:STM32U535VETxQ +MCU_ST_STM32U5:STM32U545CETx +MCU_ST_STM32U5:STM32U545CETxQ +MCU_ST_STM32U5:STM32U545CEUx +MCU_ST_STM32U5:STM32U545CEUxQ +MCU_ST_STM32U5:STM32U545JEYxQ +MCU_ST_STM32U5:STM32U545NEYxQ +MCU_ST_STM32U5:STM32U545REIx +MCU_ST_STM32U5:STM32U545REIxQ +MCU_ST_STM32U5:STM32U545RETx +MCU_ST_STM32U5:STM32U545RETxQ +MCU_ST_STM32U5:STM32U545VEIx +MCU_ST_STM32U5:STM32U545VEIxQ +MCU_ST_STM32U5:STM32U545VETx +MCU_ST_STM32U5:STM32U545VETxQ +MCU_ST_STM32U5:STM32U575AGIx +MCU_ST_STM32U5:STM32U575AGIxQ +MCU_ST_STM32U5:STM32U575AIIx +MCU_ST_STM32U5:STM32U575AIIxQ +MCU_ST_STM32U5:STM32U575CGTx +MCU_ST_STM32U5:STM32U575CGTxQ +MCU_ST_STM32U5:STM32U575CGUx +MCU_ST_STM32U5:STM32U575CGUxQ +MCU_ST_STM32U5:STM32U575CITx +MCU_ST_STM32U5:STM32U575CITxQ +MCU_ST_STM32U5:STM32U575CIUx +MCU_ST_STM32U5:STM32U575CIUxQ +MCU_ST_STM32U5:STM32U575OGYxQ +MCU_ST_STM32U5:STM32U575OIYxQ +MCU_ST_STM32U5:STM32U575QGIx +MCU_ST_STM32U5:STM32U575QGIxQ +MCU_ST_STM32U5:STM32U575QIIx +MCU_ST_STM32U5:STM32U575QIIxQ +MCU_ST_STM32U5:STM32U575RGTx +MCU_ST_STM32U5:STM32U575RGTxQ +MCU_ST_STM32U5:STM32U575RITx +MCU_ST_STM32U5:STM32U575RITxQ +MCU_ST_STM32U5:STM32U575VGTx +MCU_ST_STM32U5:STM32U575VGTxQ +MCU_ST_STM32U5:STM32U575VITx +MCU_ST_STM32U5:STM32U575VITxQ +MCU_ST_STM32U5:STM32U575ZGTx +MCU_ST_STM32U5:STM32U575ZGTxQ +MCU_ST_STM32U5:STM32U575ZITx +MCU_ST_STM32U5:STM32U575ZITxQ +MCU_ST_STM32U5:STM32U585AIIx +MCU_ST_STM32U5:STM32U585AIIxQ +MCU_ST_STM32U5:STM32U585CITx +MCU_ST_STM32U5:STM32U585CITxQ +MCU_ST_STM32U5:STM32U585CIUx +MCU_ST_STM32U5:STM32U585CIUxQ +MCU_ST_STM32U5:STM32U585OIYxQ +MCU_ST_STM32U5:STM32U585QIIx +MCU_ST_STM32U5:STM32U585QIIxQ +MCU_ST_STM32U5:STM32U585RITx +MCU_ST_STM32U5:STM32U585RITxQ +MCU_ST_STM32U5:STM32U585VITx +MCU_ST_STM32U5:STM32U585VITxQ +MCU_ST_STM32U5:STM32U585ZITx +MCU_ST_STM32U5:STM32U585ZITxQ +MCU_ST_STM32U5:STM32U595AIHx +MCU_ST_STM32U5:STM32U595AIHxQ +MCU_ST_STM32U5:STM32U595AJHx +MCU_ST_STM32U5:STM32U595AJHxQ +MCU_ST_STM32U5:STM32U595QIIx +MCU_ST_STM32U5:STM32U595QIIxQ +MCU_ST_STM32U5:STM32U595QJIx +MCU_ST_STM32U5:STM32U595QJIxQ +MCU_ST_STM32U5:STM32U595RITx +MCU_ST_STM32U5:STM32U595RITxQ +MCU_ST_STM32U5:STM32U595RJTx +MCU_ST_STM32U5:STM32U595RJTxQ +MCU_ST_STM32U5:STM32U595VITx +MCU_ST_STM32U5:STM32U595VITxQ +MCU_ST_STM32U5:STM32U595VJTx +MCU_ST_STM32U5:STM32U595VJTxQ +MCU_ST_STM32U5:STM32U595ZITx +MCU_ST_STM32U5:STM32U595ZITxQ +MCU_ST_STM32U5:STM32U595ZIYxQ +MCU_ST_STM32U5:STM32U595ZJTx +MCU_ST_STM32U5:STM32U595ZJTxQ +MCU_ST_STM32U5:STM32U595ZJYxQ +MCU_ST_STM32U5:STM32U599BJYxQ +MCU_ST_STM32U5:STM32U599NIHxQ +MCU_ST_STM32U5:STM32U599NJHxQ +MCU_ST_STM32U5:STM32U599VITxQ +MCU_ST_STM32U5:STM32U599VJTx +MCU_ST_STM32U5:STM32U599VJTxQ +MCU_ST_STM32U5:STM32U599ZITxQ +MCU_ST_STM32U5:STM32U599ZIYxQ +MCU_ST_STM32U5:STM32U599ZJTxQ +MCU_ST_STM32U5:STM32U599ZJYxQ +MCU_ST_STM32U5:STM32U5A5AJHx +MCU_ST_STM32U5:STM32U5A5AJHxQ +MCU_ST_STM32U5:STM32U5A5QIIxQ +MCU_ST_STM32U5:STM32U5A5QJIx +MCU_ST_STM32U5:STM32U5A5QJIxQ +MCU_ST_STM32U5:STM32U5A5RJTx +MCU_ST_STM32U5:STM32U5A5RJTxQ +MCU_ST_STM32U5:STM32U5A5VJTx +MCU_ST_STM32U5:STM32U5A5VJTxQ +MCU_ST_STM32U5:STM32U5A5ZJTx +MCU_ST_STM32U5:STM32U5A5ZJTxQ +MCU_ST_STM32U5:STM32U5A5ZJYxQ +MCU_ST_STM32U5:STM32U5A9BJYxQ +MCU_ST_STM32U5:STM32U5A9NJHxQ +MCU_ST_STM32U5:STM32U5A9VJTxQ +MCU_ST_STM32U5:STM32U5A9ZJTxQ +MCU_ST_STM32U5:STM32U5A9ZJYxQ +MCU_ST_STM32U5:STM32U5F7VITx +MCU_ST_STM32U5:STM32U5F7VITxQ +MCU_ST_STM32U5:STM32U5F7VJTx +MCU_ST_STM32U5:STM32U5F7VJTxQ +MCU_ST_STM32U5:STM32U5F9BJYxQ +MCU_ST_STM32U5:STM32U5F9NJHxQ +MCU_ST_STM32U5:STM32U5F9VITxQ +MCU_ST_STM32U5:STM32U5F9VJTxQ +MCU_ST_STM32U5:STM32U5F9ZIJxQ +MCU_ST_STM32U5:STM32U5F9ZITxQ +MCU_ST_STM32U5:STM32U5F9ZJJxQ +MCU_ST_STM32U5:STM32U5F9ZJTxQ +MCU_ST_STM32U5:STM32U5G7VJTx +MCU_ST_STM32U5:STM32U5G7VJTxQ +MCU_ST_STM32U5:STM32U5G9BJYxQ +MCU_ST_STM32U5:STM32U5G9NJHxQ +MCU_ST_STM32U5:STM32U5G9VJTxQ +MCU_ST_STM32U5:STM32U5G9ZJJxQ +MCU_ST_STM32U5:STM32U5G9ZJTxQ +MCU_ST_STM32WB:STM32WB05KZVx +MCU_ST_STM32WB:STM32WB06KCVx +MCU_ST_STM32WB:STM32WB07KCVx +MCU_ST_STM32WB:STM32WB09KEVx +MCU_ST_STM32WB:STM32WB10CCUx +MCU_ST_STM32WB:STM32WB15CCUx +MCU_ST_STM32WB:STM32WB15CCUxE +MCU_ST_STM32WB:STM32WB15CCYx +MCU_ST_STM32WB:STM32WB30CEUxA +MCU_ST_STM32WB:STM32WB35CCUxA +MCU_ST_STM32WB:STM32WB35CEUxA +MCU_ST_STM32WB:STM32WB35C_C-E_UxA +MCU_ST_STM32WB:STM32WB50CGUx +MCU_ST_STM32WB:STM32WB55CCUx +MCU_ST_STM32WB:STM32WB55CEUx +MCU_ST_STM32WB:STM32WB55CGUx +MCU_ST_STM32WB:STM32WB55RCVx +MCU_ST_STM32WB:STM32WB55REVx +MCU_ST_STM32WB:STM32WB55RGVx +MCU_ST_STM32WB:STM32WB55VCQx +MCU_ST_STM32WB:STM32WB55VCYx +MCU_ST_STM32WB:STM32WB55VEQx +MCU_ST_STM32WB:STM32WB55VEYx +MCU_ST_STM32WB:STM32WB55VGQx +MCU_ST_STM32WB:STM32WB55VGYx +MCU_ST_STM32WB:STM32WB55VYYx +MCU_ST_STM32WB:STM32WBA52CEUx +MCU_ST_STM32WB:STM32WBA52CGUx +MCU_ST_STM32WB:STM32WBA52KEUx +MCU_ST_STM32WB:STM32WBA52KGUx +MCU_ST_STM32WB:STM32WBA54CEUx +MCU_ST_STM32WB:STM32WBA54CGUx +MCU_ST_STM32WB:STM32WBA54KEUx +MCU_ST_STM32WB:STM32WBA54KGUx +MCU_ST_STM32WB:STM32WBA55CEUx +MCU_ST_STM32WB:STM32WBA55CGUx +MCU_ST_STM32WB:STM32WBA55HEFx +MCU_ST_STM32WB:STM32WBA55HGFx +MCU_ST_STM32WB:STM32WBA55UEIx +MCU_ST_STM32WB:STM32WBA55UGIx +MCU_ST_STM32WL:STM32WL54CCUx +MCU_ST_STM32WL:STM32WL54JCIx +MCU_ST_STM32WL:STM32WL55CCUx +MCU_ST_STM32WL:STM32WL55JCIx +MCU_ST_STM32WL:STM32WLE4C8Ux +MCU_ST_STM32WL:STM32WLE4CBUx +MCU_ST_STM32WL:STM32WLE4CCUx +MCU_ST_STM32WL:STM32WLE4J8Ix +MCU_ST_STM32WL:STM32WLE4JBIx +MCU_ST_STM32WL:STM32WLE4JCIx +MCU_ST_STM32WL:STM32WLE5C8Ux +MCU_ST_STM32WL:STM32WLE5CBUx +MCU_ST_STM32WL:STM32WLE5CCUx +MCU_ST_STM32WL:STM32WLE5J8Ix +MCU_ST_STM32WL:STM32WLE5JBIx +MCU_ST_STM32WL:STM32WLE5JCIx +MCU_ST_STM8:STM8AF6223 +MCU_ST_STM8:STM8AF6223A +MCU_ST_STM8:STM8AL3188T +MCU_ST_STM8:STM8AL3189T +MCU_ST_STM8:STM8AL318AT +MCU_ST_STM8:STM8AL3L88T +MCU_ST_STM8:STM8AL3L89T +MCU_ST_STM8:STM8AL3L8AT +MCU_ST_STM8:STM8L051F3P +MCU_ST_STM8:STM8L101F1U +MCU_ST_STM8:STM8L101F2P +MCU_ST_STM8:STM8L101F2U +MCU_ST_STM8:STM8L101F3P +MCU_ST_STM8:STM8L101F3U +MCU_ST_STM8:STM8L151C2T +MCU_ST_STM8:STM8L151C3T +MCU_ST_STM8:STM8L152R6T +MCU_ST_STM8:STM8L152R8T +MCU_ST_STM8:STM8S001J3M +MCU_ST_STM8:STM8S003F3P +MCU_ST_STM8:STM8S003F3U +MCU_ST_STM8:STM8S003K3T +MCU_ST_STM8:STM8S207C6 +MCU_ST_STM8:STM8S207C8 +MCU_ST_STM8:STM8S207CB +MCU_ST_STM8:STM8S207MB +MCU_ST_STM8:STM8S207R6 +MCU_ST_STM8:STM8S207R8 +MCU_ST_STM8:STM8S207RB +MCU_ST_STM8:STM8S208C6 +MCU_ST_STM8:STM8S208C8 +MCU_ST_STM8:STM8S208CB +MCU_ST_STM8:STM8S208MB +MCU_ST_STM8:STM8S208R6 +MCU_ST_STM8:STM8S208R8 +MCU_ST_STM8:STM8S208RB +MCU_Texas:LM3S6911-EQC50 +MCU_Texas:LM3S6911-IQC50 +MCU_Texas:LM4F110B2QR +MCU_Texas:LM4F110C4QR +MCU_Texas:LM4F110E5QR +MCU_Texas:LM4F110H5QR +MCU_Texas:LM4F111B2QR +MCU_Texas:LM4F111C4QR +MCU_Texas:LM4F111E5QR +MCU_Texas:LM4F111H5QR +MCU_Texas:MSP432E401Y +MCU_Texas:TM4C1230C3PM +MCU_Texas:TM4C1230D5PM +MCU_Texas:TM4C1230E6PM +MCU_Texas:TM4C1230H6PM +MCU_Texas:TM4C1231C3PM +MCU_Texas:TM4C1231D5PM +MCU_Texas:TM4C1231E6PM +MCU_Texas:TM4C1231H6PM +MCU_Texas:TMS320LF2406 +MCU_Texas:TMS470R1B768 +MCU_Texas_MSP430:CC430F5133xRGZ +MCU_Texas_MSP430:CC430F5135xRGZ +MCU_Texas_MSP430:CC430F5137xRGZ +MCU_Texas_MSP430:MSP430AFE221IPW +MCU_Texas_MSP430:MSP430AFE222IPW +MCU_Texas_MSP430:MSP430AFE223IPW +MCU_Texas_MSP430:MSP430AFE231IPW +MCU_Texas_MSP430:MSP430AFE232IPW +MCU_Texas_MSP430:MSP430AFE233IPW +MCU_Texas_MSP430:MSP430AFE251IPW +MCU_Texas_MSP430:MSP430AFE252IPW +MCU_Texas_MSP430:MSP430AFE253IPW +MCU_Texas_MSP430:MSP430F1101AIDGV +MCU_Texas_MSP430:MSP430F1101AIDW +MCU_Texas_MSP430:MSP430F1101AIPW +MCU_Texas_MSP430:MSP430F1101AIRGE +MCU_Texas_MSP430:MSP430F1111AIDGV +MCU_Texas_MSP430:MSP430F1111AIDW +MCU_Texas_MSP430:MSP430F1111AIPW +MCU_Texas_MSP430:MSP430F1111AIRGE +MCU_Texas_MSP430:MSP430F1121AIDGV +MCU_Texas_MSP430:MSP430F1121AIDW +MCU_Texas_MSP430:MSP430F1121AIPW +MCU_Texas_MSP430:MSP430F1121AIRGE +MCU_Texas_MSP430:MSP430F1122IDW +MCU_Texas_MSP430:MSP430F1122IPW +MCU_Texas_MSP430:MSP430F1122IRHB +MCU_Texas_MSP430:MSP430F1132IDW +MCU_Texas_MSP430:MSP430F1132IPW +MCU_Texas_MSP430:MSP430F1132IRHB +MCU_Texas_MSP430:MSP430F1222IDW +MCU_Texas_MSP430:MSP430F1222IPW +MCU_Texas_MSP430:MSP430F1222IRHB +MCU_Texas_MSP430:MSP430F122IDW +MCU_Texas_MSP430:MSP430F122IPW +MCU_Texas_MSP430:MSP430F122IRHB +MCU_Texas_MSP430:MSP430F1232IDW +MCU_Texas_MSP430:MSP430F1232IPW +MCU_Texas_MSP430:MSP430F1232IRHB +MCU_Texas_MSP430:MSP430F123IDW +MCU_Texas_MSP430:MSP430F123IPW +MCU_Texas_MSP430:MSP430F123IRHB +MCU_Texas_MSP430:MSP430F2001IN +MCU_Texas_MSP430:MSP430F2001IPW +MCU_Texas_MSP430:MSP430F2001IRSA +MCU_Texas_MSP430:MSP430F2002IN +MCU_Texas_MSP430:MSP430F2002IPW +MCU_Texas_MSP430:MSP430F2002IRSA +MCU_Texas_MSP430:MSP430F2003IN +MCU_Texas_MSP430:MSP430F2003IPW +MCU_Texas_MSP430:MSP430F2003IRSA +MCU_Texas_MSP430:MSP430F2011IN +MCU_Texas_MSP430:MSP430F2011IPW +MCU_Texas_MSP430:MSP430F2011IRSA +MCU_Texas_MSP430:MSP430F2012IN +MCU_Texas_MSP430:MSP430F2012IPW +MCU_Texas_MSP430:MSP430F2012IRSA +MCU_Texas_MSP430:MSP430F2013IN +MCU_Texas_MSP430:MSP430F2013IPW +MCU_Texas_MSP430:MSP430F2013IRSA +MCU_Texas_MSP430:MSP430F2101IDGV +MCU_Texas_MSP430:MSP430F2101IDW +MCU_Texas_MSP430:MSP430F2101IPW +MCU_Texas_MSP430:MSP430F2101IRGE +MCU_Texas_MSP430:MSP430F2111IDGV +MCU_Texas_MSP430:MSP430F2111IDW +MCU_Texas_MSP430:MSP430F2111IPW +MCU_Texas_MSP430:MSP430F2111IRGE +MCU_Texas_MSP430:MSP430F2112IPW +MCU_Texas_MSP430:MSP430F2112IRHB +MCU_Texas_MSP430:MSP430F2112IRTV +MCU_Texas_MSP430:MSP430F2121IDGV +MCU_Texas_MSP430:MSP430F2121IDW +MCU_Texas_MSP430:MSP430F2121IPW +MCU_Texas_MSP430:MSP430F2121IRGE +MCU_Texas_MSP430:MSP430F2122IPW +MCU_Texas_MSP430:MSP430F2122IRHB +MCU_Texas_MSP430:MSP430F2122IRTV +MCU_Texas_MSP430:MSP430F2131IDGV +MCU_Texas_MSP430:MSP430F2131IDW +MCU_Texas_MSP430:MSP430F2131IPW +MCU_Texas_MSP430:MSP430F2131IRGE +MCU_Texas_MSP430:MSP430F2132IPW +MCU_Texas_MSP430:MSP430F2132IRHB +MCU_Texas_MSP430:MSP430F2132IRTV +MCU_Texas_MSP430:MSP430F2232IDA +MCU_Texas_MSP430:MSP430F2232IRHA +MCU_Texas_MSP430:MSP430F2232IYFF +MCU_Texas_MSP430:MSP430F2234IDA +MCU_Texas_MSP430:MSP430F2234IRHA +MCU_Texas_MSP430:MSP430F2234IYFF +MCU_Texas_MSP430:MSP430F2252IDA +MCU_Texas_MSP430:MSP430F2252IRHA +MCU_Texas_MSP430:MSP430F2252IYFF +MCU_Texas_MSP430:MSP430F2254IDA +MCU_Texas_MSP430:MSP430F2254IRHA +MCU_Texas_MSP430:MSP430F2254IYFF +MCU_Texas_MSP430:MSP430F2272IDA +MCU_Texas_MSP430:MSP430F2272IRHA +MCU_Texas_MSP430:MSP430F2272IYFF +MCU_Texas_MSP430:MSP430F2274IDA +MCU_Texas_MSP430:MSP430F2274IRHA +MCU_Texas_MSP430:MSP430F2274IYFF +MCU_Texas_MSP430:MSP430F2330IRHA +MCU_Texas_MSP430:MSP430F2330IYFF +MCU_Texas_MSP430:MSP430F2350IRHA +MCU_Texas_MSP430:MSP430F2350IYFF +MCU_Texas_MSP430:MSP430F2370IRHA +MCU_Texas_MSP430:MSP430F2370IYFF +MCU_Texas_MSP430:MSP430F2618-EP +MCU_Texas_MSP430:MSP430F5217IRGC +MCU_Texas_MSP430:MSP430F5217IYFF +MCU_Texas_MSP430:MSP430F5219IRGC +MCU_Texas_MSP430:MSP430F5219IYFF +MCU_Texas_MSP430:MSP430F5227IRGC +MCU_Texas_MSP430:MSP430F5227IYFF +MCU_Texas_MSP430:MSP430F5229IRGC +MCU_Texas_MSP430:MSP430F5229IYFF +MCU_Texas_MSP430:MSP430F5232IRGZ +MCU_Texas_MSP430:MSP430F5234IRGZ +MCU_Texas_MSP430:MSP430F5237IRGC +MCU_Texas_MSP430:MSP430F5239IRGC +MCU_Texas_MSP430:MSP430F5242IRGZ +MCU_Texas_MSP430:MSP430F5244IRGZ +MCU_Texas_MSP430:MSP430F5247IRGC +MCU_Texas_MSP430:MSP430F5249IRGC +MCU_Texas_MSP430:MSP430F5304IPT +MCU_Texas_MSP430:MSP430F5304IRGZ +MCU_Texas_MSP430:MSP430F5308IPT +MCU_Texas_MSP430:MSP430F5308IRGC +MCU_Texas_MSP430:MSP430F5308IRGZ +MCU_Texas_MSP430:MSP430F5308IZQE +MCU_Texas_MSP430:MSP430F5309IPT +MCU_Texas_MSP430:MSP430F5309IRGC +MCU_Texas_MSP430:MSP430F5309IRGZ +MCU_Texas_MSP430:MSP430F5309IZQE +MCU_Texas_MSP430:MSP430F5310IPT +MCU_Texas_MSP430:MSP430F5310IRGC +MCU_Texas_MSP430:MSP430F5310IRGZ +MCU_Texas_MSP430:MSP430F5310IZQE +MCU_Texas_MSP430:MSP430F5333IPZ +MCU_Texas_MSP430:MSP430F5333IZQW +MCU_Texas_MSP430:MSP430F5335IPZ +MCU_Texas_MSP430:MSP430F5335IZQW +MCU_Texas_MSP430:MSP430F5336IPZ +MCU_Texas_MSP430:MSP430F5336IZQW +MCU_Texas_MSP430:MSP430F5338IPZ +MCU_Texas_MSP430:MSP430F5338IZQW +MCU_Texas_MSP430:MSP430F5340IRGZ +MCU_Texas_MSP430:MSP430F5341IRGZ +MCU_Texas_MSP430:MSP430F5342IRGZ +MCU_Texas_MSP430:MSP430F5358IZQW +MCU_Texas_MSP430:MSP430F5359IZQW +MCU_Texas_MSP430:MSP430F5500IRGZ +MCU_Texas_MSP430:MSP430F5501IRGZ +MCU_Texas_MSP430:MSP430F5502IRGZ +MCU_Texas_MSP430:MSP430F5503IRGZ +MCU_Texas_MSP430:MSP430F5504IRGZ +MCU_Texas_MSP430:MSP430F5505IRGZ +MCU_Texas_MSP430:MSP430F5506IRGZ +MCU_Texas_MSP430:MSP430F5507IRGZ +MCU_Texas_MSP430:MSP430F5508IRGZ +MCU_Texas_MSP430:MSP430F5509IRGZ +MCU_Texas_MSP430:MSP430F5510IRGZ +MCU_Texas_MSP430:MSP430F5524IYFF +MCU_Texas_MSP430:MSP430F5526IYFF +MCU_Texas_MSP430:MSP430F5528IYFF +MCU_Texas_MSP430:MSP430F5630IZQW +MCU_Texas_MSP430:MSP430F5631IZQW +MCU_Texas_MSP430:MSP430F5632IZQW +MCU_Texas_MSP430:MSP430F5633IZQW +MCU_Texas_MSP430:MSP430F5634IZQW +MCU_Texas_MSP430:MSP430F5635IZQW +MCU_Texas_MSP430:MSP430F5636IZQW +MCU_Texas_MSP430:MSP430F5637IZQW +MCU_Texas_MSP430:MSP430F5638IZQW +MCU_Texas_MSP430:MSP430F5658IZQW +MCU_Texas_MSP430:MSP430F5659IZQW +MCU_Texas_MSP430:MSP430FR5720IRGE +MCU_Texas_MSP430:MSP430FR5722IRGE +MCU_Texas_MSP430:MSP430FR5724IRGE +MCU_Texas_MSP430:MSP430FR5726IRGE +MCU_Texas_MSP430:MSP430FR5728IRGE +MCU_Texas_MSP430:MSP430FR5730IRGE +MCU_Texas_MSP430:MSP430FR5732IRGE +MCU_Texas_MSP430:MSP430FR5734IRGE +MCU_Texas_MSP430:MSP430FR5736IRGE +MCU_Texas_MSP430:MSP430FR5738IRGE +MCU_Texas_MSP430:MSP430G2001IN14 +MCU_Texas_MSP430:MSP430G2001IPW14 +MCU_Texas_MSP430:MSP430G2001IRSA16 +MCU_Texas_MSP430:MSP430G2101IN14 +MCU_Texas_MSP430:MSP430G2101IPW14 +MCU_Texas_MSP430:MSP430G2101IRSA16 +MCU_Texas_MSP430:MSP430G2102IN20 +MCU_Texas_MSP430:MSP430G2102IPW14 +MCU_Texas_MSP430:MSP430G2102IPW20 +MCU_Texas_MSP430:MSP430G2102IRSA16 +MCU_Texas_MSP430:MSP430G2111IN14 +MCU_Texas_MSP430:MSP430G2111IPW14 +MCU_Texas_MSP430:MSP430G2111IRSA16 +MCU_Texas_MSP430:MSP430G2112IN20 +MCU_Texas_MSP430:MSP430G2112IPW14 +MCU_Texas_MSP430:MSP430G2112IPW20 +MCU_Texas_MSP430:MSP430G2112IRSA16 +MCU_Texas_MSP430:MSP430G2113IPW20 +MCU_Texas_MSP430:MSP430G2121IN14 +MCU_Texas_MSP430:MSP430G2121IPW14 +MCU_Texas_MSP430:MSP430G2121IRSA16 +MCU_Texas_MSP430:MSP430G2131IN14 +MCU_Texas_MSP430:MSP430G2131IPW14 +MCU_Texas_MSP430:MSP430G2131IRSA16 +MCU_Texas_MSP430:MSP430G2132IN20 +MCU_Texas_MSP430:MSP430G2132IPW14 +MCU_Texas_MSP430:MSP430G2132IPW20 +MCU_Texas_MSP430:MSP430G2132IRSA16 +MCU_Texas_MSP430:MSP430G2152IN20 +MCU_Texas_MSP430:MSP430G2152IPW14 +MCU_Texas_MSP430:MSP430G2152IPW20 +MCU_Texas_MSP430:MSP430G2152IRSA16 +MCU_Texas_MSP430:MSP430G2153IN20 +MCU_Texas_MSP430:MSP430G2153IPW20 +MCU_Texas_MSP430:MSP430G2153IPW28 +MCU_Texas_MSP430:MSP430G2153IRHB32 +MCU_Texas_MSP430:MSP430G2201IN14 +MCU_Texas_MSP430:MSP430G2201IPW14 +MCU_Texas_MSP430:MSP430G2201IRSA16 +MCU_Texas_MSP430:MSP430G2202IN20 +MCU_Texas_MSP430:MSP430G2202IPW14 +MCU_Texas_MSP430:MSP430G2202IPW20 +MCU_Texas_MSP430:MSP430G2202IRSA16 +MCU_Texas_MSP430:MSP430G2203IN20 +MCU_Texas_MSP430:MSP430G2203IPW20 +MCU_Texas_MSP430:MSP430G2203IPW28 +MCU_Texas_MSP430:MSP430G2203IRHB32 +MCU_Texas_MSP430:MSP430G2210ID +MCU_Texas_MSP430:MSP430G2211IN14 +MCU_Texas_MSP430:MSP430G2211IPW14 +MCU_Texas_MSP430:MSP430G2211IRSA16 +MCU_Texas_MSP430:MSP430G2212IN20 +MCU_Texas_MSP430:MSP430G2212IPW14 +MCU_Texas_MSP430:MSP430G2212IPW20 +MCU_Texas_MSP430:MSP430G2212IRSA16 +MCU_Texas_MSP430:MSP430G2213IN20 +MCU_Texas_MSP430:MSP430G2213IPW20 +MCU_Texas_MSP430:MSP430G2213IPW28 +MCU_Texas_MSP430:MSP430G2213IRHB32 +MCU_Texas_MSP430:MSP430G2221IN14 +MCU_Texas_MSP430:MSP430G2221IPW14 +MCU_Texas_MSP430:MSP430G2221IRSA16 +MCU_Texas_MSP430:MSP430G2230ID +MCU_Texas_MSP430:MSP430G2231IN14 +MCU_Texas_MSP430:MSP430G2231IPW14 +MCU_Texas_MSP430:MSP430G2231IRSA16 +MCU_Texas_MSP430:MSP430G2232IN20 +MCU_Texas_MSP430:MSP430G2232IPW14 +MCU_Texas_MSP430:MSP430G2232IPW20 +MCU_Texas_MSP430:MSP430G2232IRSA16 +MCU_Texas_MSP430:MSP430G2233IN20 +MCU_Texas_MSP430:MSP430G2233IPW20 +MCU_Texas_MSP430:MSP430G2233IPW28 +MCU_Texas_MSP430:MSP430G2233IRHB32 +MCU_Texas_MSP430:MSP430G2252IN20 +MCU_Texas_MSP430:MSP430G2252IPW14 +MCU_Texas_MSP430:MSP430G2252IPW20 +MCU_Texas_MSP430:MSP430G2252IRSA16 +MCU_Texas_MSP430:MSP430G2253IN20 +MCU_Texas_MSP430:MSP430G2253IPW20 +MCU_Texas_MSP430:MSP430G2253IPW28 +MCU_Texas_MSP430:MSP430G2253IRHB32 +MCU_Texas_MSP430:MSP430G2302IN20 +MCU_Texas_MSP430:MSP430G2302IPW14 +MCU_Texas_MSP430:MSP430G2302IPW20 +MCU_Texas_MSP430:MSP430G2302IRSA16 +MCU_Texas_MSP430:MSP430G2303IN20 +MCU_Texas_MSP430:MSP430G2303IPW20 +MCU_Texas_MSP430:MSP430G2303IPW28 +MCU_Texas_MSP430:MSP430G2303IRHB32 +MCU_Texas_MSP430:MSP430G2312IN20 +MCU_Texas_MSP430:MSP430G2312IPW14 +MCU_Texas_MSP430:MSP430G2312IPW20 +MCU_Texas_MSP430:MSP430G2312IRSA16 +MCU_Texas_MSP430:MSP430G2313IN20 +MCU_Texas_MSP430:MSP430G2313IPW20 +MCU_Texas_MSP430:MSP430G2313IPW28 +MCU_Texas_MSP430:MSP430G2313IRHB32 +MCU_Texas_MSP430:MSP430G2332IN20 +MCU_Texas_MSP430:MSP430G2332IPW14 +MCU_Texas_MSP430:MSP430G2332IPW20 +MCU_Texas_MSP430:MSP430G2332IRSA16 +MCU_Texas_MSP430:MSP430G2333IN20 +MCU_Texas_MSP430:MSP430G2333IPW20 +MCU_Texas_MSP430:MSP430G2333IPW28 +MCU_Texas_MSP430:MSP430G2333IRHB32 +MCU_Texas_MSP430:MSP430G2352IN20 +MCU_Texas_MSP430:MSP430G2352IPW14 +MCU_Texas_MSP430:MSP430G2352IPW20 +MCU_Texas_MSP430:MSP430G2352IRSA16 +MCU_Texas_MSP430:MSP430G2353IN20 +MCU_Texas_MSP430:MSP430G2353IPW20 +MCU_Texas_MSP430:MSP430G2353IPW28 +MCU_Texas_MSP430:MSP430G2353IRHB32 +MCU_Texas_MSP430:MSP430G2402IN20 +MCU_Texas_MSP430:MSP430G2402IPW14 +MCU_Texas_MSP430:MSP430G2402IPW20 +MCU_Texas_MSP430:MSP430G2402IRSA16 +MCU_Texas_MSP430:MSP430G2403IN20 +MCU_Texas_MSP430:MSP430G2403IPW20 +MCU_Texas_MSP430:MSP430G2403IPW28 +MCU_Texas_MSP430:MSP430G2403IRHB32 +MCU_Texas_MSP430:MSP430G2412IN20 +MCU_Texas_MSP430:MSP430G2412IPW14 +MCU_Texas_MSP430:MSP430G2412IPW20 +MCU_Texas_MSP430:MSP430G2412IRSA16 +MCU_Texas_MSP430:MSP430G2413IN20 +MCU_Texas_MSP430:MSP430G2413IPW20 +MCU_Texas_MSP430:MSP430G2413IPW28 +MCU_Texas_MSP430:MSP430G2413IRHB32 +MCU_Texas_MSP430:MSP430G2432IN20 +MCU_Texas_MSP430:MSP430G2432IPW14 +MCU_Texas_MSP430:MSP430G2432IPW20 +MCU_Texas_MSP430:MSP430G2432IRSA16 +MCU_Texas_MSP430:MSP430G2433IN20 +MCU_Texas_MSP430:MSP430G2433IPW20 +MCU_Texas_MSP430:MSP430G2433IPW28 +MCU_Texas_MSP430:MSP430G2433IRHB32 +MCU_Texas_MSP430:MSP430G2444IDA38 +MCU_Texas_MSP430:MSP430G2444IRHA40 +MCU_Texas_MSP430:MSP430G2444IYFF +MCU_Texas_MSP430:MSP430G2452IN20 +MCU_Texas_MSP430:MSP430G2452IPW14 +MCU_Texas_MSP430:MSP430G2452IPW20 +MCU_Texas_MSP430:MSP430G2452IRSA16 +MCU_Texas_MSP430:MSP430G2453IN20 +MCU_Texas_MSP430:MSP430G2453IPW20 +MCU_Texas_MSP430:MSP430G2453IPW28 +MCU_Texas_MSP430:MSP430G2453IRHB32 +MCU_Texas_MSP430:MSP430G2513IN20 +MCU_Texas_MSP430:MSP430G2513IPW20 +MCU_Texas_MSP430:MSP430G2513IPW28 +MCU_Texas_MSP430:MSP430G2513IRHB32 +MCU_Texas_MSP430:MSP430G2533IN20 +MCU_Texas_MSP430:MSP430G2533IPW20 +MCU_Texas_MSP430:MSP430G2533IPW28 +MCU_Texas_MSP430:MSP430G2533IRHB32 +MCU_Texas_MSP430:MSP430G2544IDA38 +MCU_Texas_MSP430:MSP430G2544IRHA40 +MCU_Texas_MSP430:MSP430G2544IYFF +MCU_Texas_MSP430:MSP430G2553IN20 +MCU_Texas_MSP430:MSP430G2553IPW20 +MCU_Texas_MSP430:MSP430G2553IPW28 +MCU_Texas_MSP430:MSP430G2553IRHB32 +MCU_Texas_MSP430:MSP430G2744IDA38 +MCU_Texas_MSP430:MSP430G2744IRHA40 +MCU_Texas_MSP430:MSP430G2744IYFF +MCU_Texas_MSP430:MSP430G2755IDA38 +MCU_Texas_MSP430:MSP430G2755IRHA40 +MCU_Texas_MSP430:MSP430G2855IDA38 +MCU_Texas_MSP430:MSP430G2855IRHA40 +MCU_Texas_MSP430:MSP430G2955IDA38 +MCU_Texas_MSP430:MSP430G2955IRHA40 +MCU_Texas_SimpleLink:CC1312R1F3RGZ +MCU_WCH_CH32V0:CH32V003AxMx +MCU_WCH_CH32V0:CH32V003FxPx +MCU_WCH_CH32V0:CH32V003FxUx +MCU_WCH_CH32V0:CH32V003JxMx +MCU_WCH_CH32V2:CH32V203CxTx +MCU_WCH_CH32V2:CH32V203F6P6 +MCU_WCH_CH32V2:CH32V203GxUx +MCU_WCH_CH32V3:CH32V30xCxTx +MCU_WCH_CH32V3:CH32V30xFxPx +MCU_WCH_CH32V3:CH32V30xRxTx +MCU_WCH_CH32V3:CH32V30xVxTx +MCU_WCH_CH32V3:CH32V30xWxUx +MCU_WCH_CH32X0:CH32X035G8U6 +Mechanical:DIN_Rail_Adapter +Mechanical:Fiducial +Mechanical:Heatsink +Mechanical:Heatsink_Pad +Mechanical:Heatsink_Pad_2Pin +Mechanical:Heatsink_Pad_3Pin +Mechanical:Housing +Mechanical:Housing_Pad +Mechanical:MountingHole +Mechanical:MountingHole_Pad +Mechanical:MountingHole_Pad_MP +Memory_EEPROM:24AA02-OT +Memory_EEPROM:24AA025E-OT +Memory_EEPROM:24AA025E-SN +Memory_EEPROM:24AA02E-OT +Memory_EEPROM:24AA02E-SN +Memory_EEPROM:24LC00 +Memory_EEPROM:24LC01 +Memory_EEPROM:24LC02 +Memory_EEPROM:24LC04 +Memory_EEPROM:24LC08 +Memory_EEPROM:24LC1025 +Memory_EEPROM:24LC128 +Memory_EEPROM:24LC16 +Memory_EEPROM:24LC256 +Memory_EEPROM:24LC32 +Memory_EEPROM:24LC512 +Memory_EEPROM:24LC64 +Memory_EEPROM:25CSM04xxMF +Memory_EEPROM:25CSM04xxSN +Memory_EEPROM:25LCxxx +Memory_EEPROM:25LCxxx-MC +Memory_EEPROM:25LCxxx-MF +Memory_EEPROM:28C256 +Memory_EEPROM:93AAxxA +Memory_EEPROM:93AAxxAT-xOT +Memory_EEPROM:93AAxxB +Memory_EEPROM:93AAxxBT-xOT +Memory_EEPROM:93AAxxC +Memory_EEPROM:93CxxA +Memory_EEPROM:93CxxB +Memory_EEPROM:93CxxC +Memory_EEPROM:93LCxxA +Memory_EEPROM:93LCxxAxxOT +Memory_EEPROM:93LCxxB +Memory_EEPROM:93LCxxBxxOT +Memory_EEPROM:93LCxxC +Memory_EEPROM:AT24CS01-MAHM +Memory_EEPROM:AT24CS01-SSHM +Memory_EEPROM:AT24CS01-STUM +Memory_EEPROM:AT24CS01-XHM +Memory_EEPROM:AT24CS02-MAHM +Memory_EEPROM:AT24CS02-SSHM +Memory_EEPROM:AT24CS02-STUM +Memory_EEPROM:AT24CS02-XHM +Memory_EEPROM:AT24CS04-MAHM +Memory_EEPROM:AT24CS04-SSHM +Memory_EEPROM:AT24CS04-STUM +Memory_EEPROM:AT24CS04-XHM +Memory_EEPROM:AT24CS08-MAHM +Memory_EEPROM:AT24CS08-SSHM +Memory_EEPROM:AT24CS08-STUM +Memory_EEPROM:AT24CS08-XHM +Memory_EEPROM:AT24CS16-MAHM +Memory_EEPROM:AT24CS16-SSHM +Memory_EEPROM:AT24CS16-STUM +Memory_EEPROM:AT24CS16-XHM +Memory_EEPROM:AT24CS32-MAHM +Memory_EEPROM:AT24CS32-SSHM +Memory_EEPROM:AT24CS32-STUM +Memory_EEPROM:AT24CS32-XHM +Memory_EEPROM:AT24CS64-MAHM +Memory_EEPROM:AT24CS64-SSHM +Memory_EEPROM:AT24CS64-XHM +Memory_EEPROM:AT25xxx +Memory_EEPROM:AT25xxx-MA +Memory_EEPROM:BR25Sxxx +Memory_EEPROM:BR25xxx-NUX +Memory_EEPROM:CAT24C128 +Memory_EEPROM:CAT24C256 +Memory_EEPROM:CAT24M01L +Memory_EEPROM:CAT24M01W +Memory_EEPROM:CAT24M01X +Memory_EEPROM:CAT24M01Y +Memory_EEPROM:CAT250xxx +Memory_EEPROM:CAT250xxx-HU4 +Memory_EEPROM:DS2431 +Memory_EEPROM:DS2431P +Memory_EEPROM:DS2431Q +Memory_EEPROM:DS28E07 +Memory_EEPROM:DS28E07P +Memory_EEPROM:DS28E07Q +Memory_EEPROM:KM28C64A +Memory_EEPROM:KM28C65A +Memory_EEPROM:M24C01-FDW +Memory_EEPROM:M24C01-FMN +Memory_EEPROM:M24C01-RDW +Memory_EEPROM:M24C01-RMN +Memory_EEPROM:M24C01-WDW +Memory_EEPROM:M24C01-WMN +Memory_EEPROM:M24C02-FDW +Memory_EEPROM:M24C02-FMN +Memory_EEPROM:M24C02-RDW +Memory_EEPROM:M24C02-RMN +Memory_EEPROM:M24C02-WDW +Memory_EEPROM:M24C02-WMN +Memory_EEPROM:M95256-WMN6P +Memory_EEPROM:M95512-Axxx-MF +Memory_EEPROM:TMS4C1050N +Memory_EPROM:27128 +Memory_EPROM:27256 +Memory_EPROM:27512 +Memory_EPROM:2764 +Memory_EPROM:27C010 +Memory_EPROM:27C020 +Memory_EPROM:27C040 +Memory_EPROM:27C080 +Memory_EPROM:27C128 +Memory_EPROM:27C256 +Memory_EPROM:27C512 +Memory_EPROM:27C512PLCC +Memory_EPROM:27C64 +Memory_Flash:28F400 +Memory_Flash:29F010-TSOP-SP +Memory_Flash:29W040 +Memory_Flash:AM29F400BB-90SC +Memory_Flash:AM29F400Bx-xxEx +Memory_Flash:AM29F400Bx-xxSx +Memory_Flash:AM29PDL128G +Memory_Flash:AT25DF041x-UxN-x +Memory_Flash:AT25SF081-SSHD-X +Memory_Flash:AT25SF081-SSHF-X +Memory_Flash:AT25SF081-XMHD-X +Memory_Flash:AT25SF081-XMHF-X +Memory_Flash:AT25SL321-U +Memory_Flash:AT45DB161-JC +Memory_Flash:AT45DB161-RC +Memory_Flash:AT45DB161-TC +Memory_Flash:AT45DB161B-RC +Memory_Flash:AT45DB161B-RC-2.5 +Memory_Flash:AT45DB161B-TC +Memory_Flash:AT45DB161B-TC-2.5 +Memory_Flash:AT45DB161D-SU +Memory_Flash:GD25D05CT +Memory_Flash:GD25D10CT +Memory_Flash:GD25QxxxEY +Memory_Flash:IS25WP256D-xM +Memory_Flash:M25PX32-VMP +Memory_Flash:M25PX32-VMW +Memory_Flash:M29W004 +Memory_Flash:M29W008 +Memory_Flash:MT25QUxxxxxx1xW7 +Memory_Flash:MX25L3233FM +Memory_Flash:MX25L3233FM1 +Memory_Flash:MX25L3233FM2 +Memory_Flash:MX25L3233FZN +Memory_Flash:MX25R3235FM1xx0 +Memory_Flash:MX25R3235FM1xx1 +Memory_Flash:MX25R3235FM2xx0 +Memory_Flash:MX25R3235FM2xx1 +Memory_Flash:MX25R3235FZNxx0 +Memory_Flash:MX25R3235FZNxx1 +Memory_Flash:SST25VF080B-50-4x-S2Ax +Memory_Flash:SST39SF010 +Memory_Flash:SST39SF020 +Memory_Flash:SST39SF040 +Memory_Flash:W25Q128JVE +Memory_Flash:W25Q128JVP +Memory_Flash:W25Q128JVS +Memory_Flash:W25Q16JVSS +Memory_Flash:W25Q32JVSS +Memory_Flash:W25Q32JVZP +Memory_Flash:W25X20CLSN +Memory_Flash:W25X20CLZP +Memory_Flash:W25X40CLSN +Memory_Flash:W25X40CLSS +Memory_Flash:W25X40CLSV +Memory_Flash:XTSD01G +Memory_Flash:XTSD02G +Memory_Flash:XTSD04G +Memory_Flash:XTSD08G +Memory_NVRAM:47C04 +Memory_NVRAM:47C16 +Memory_NVRAM:47L04 +Memory_NVRAM:47L16 +Memory_NVRAM:CY14B256LA-SP +Memory_NVRAM:CY14B256LA-SZ +Memory_NVRAM:CY14B256LA-ZS +Memory_NVRAM:CY14E256LA-SZ +Memory_NVRAM:CY14E256LA-ZS +Memory_NVRAM:CY14U256LA-BA +Memory_NVRAM:CY14V256LA-BA +Memory_NVRAM:FM1608B-SG +Memory_NVRAM:FM16W08-SG +Memory_NVRAM:FM1808B-SG +Memory_NVRAM:FM18W08-SG +Memory_NVRAM:FM24C64B +Memory_NVRAM:FM24C64C +Memory_NVRAM:FM24CL16B +Memory_NVRAM:MB85RS128B +Memory_NVRAM:MB85RS16 +Memory_NVRAM:MB85RS1MT +Memory_NVRAM:MB85RS256B +Memory_NVRAM:MB85RS2MT +Memory_NVRAM:MB85RS512T +Memory_NVRAM:MB85RS64 +Memory_NVRAM:MR20H40 +Memory_NVRAM:MR25H40 +Memory_NVRAM:STK14C88 +Memory_NVRAM:STK14C88-3 +Memory_NVRAM:STK14C88C +Memory_NVRAM:STK14C88C-3 +Memory_RAM:AS4C256M16D3 +Memory_RAM:AS4C4M16SA +Memory_RAM:AS6C1008-xxB +Memory_RAM:AS6C1008-xxP +Memory_RAM:AS6C1008-xxS +Memory_RAM:AS6C1008-xxST +Memory_RAM:AS6C1008-xxT +Memory_RAM:AS6C1616 +Memory_RAM:AS6C4008-55PCN +Memory_RAM:AS7C1024B-xxJ +Memory_RAM:AS7C1024B-xxT +Memory_RAM:AS7C1024B-xxTJ +Memory_RAM:AS7C31024B-xxJ +Memory_RAM:AS7C31024B-xxST +Memory_RAM:AS7C31024B-xxT +Memory_RAM:AS7C31024B-xxTJ +Memory_RAM:CY62128EV30xx-xxS +Memory_RAM:CY62128EV30xx-xxZ +Memory_RAM:CY62128Exx-xxS +Memory_RAM:CY62128Exx-xxZ +Memory_RAM:CY62256-70PC +Memory_RAM:CY7C199 +Memory_RAM:ESP-PSRAM32 +Memory_RAM:H5AN8G8NAFR-UHC +Memory_RAM:HM62256BLP +Memory_RAM:HM628128D_DIP32_SOP32 +Memory_RAM:HM628128D_TSOP32 +Memory_RAM:HM628128_DIP32_SOP32 +Memory_RAM:HM628128_TSOP32 +Memory_RAM:HY6264AxJ +Memory_RAM:HY6264AxP +Memory_RAM:IDT7006PF +Memory_RAM:IDT7027_TQ100 +Memory_RAM:IDT7132 +Memory_RAM:IDT71V65903S +Memory_RAM:IDT7201 +Memory_RAM:IDT7202 +Memory_RAM:IDT7203 +Memory_RAM:IDT7204 +Memory_RAM:IDT7205 +Memory_RAM:IDT7206 +Memory_RAM:IDT7207 +Memory_RAM:IDT7208 +Memory_RAM:IS42S16400J-xC +Memory_RAM:IS42S16400J-xT +Memory_RAM:IS43LQ32256A-062BLI +Memory_RAM:IS43LQ32256AL-062BLI +Memory_RAM:IS61C5128AL-10KLI +Memory_RAM:IS61C5128AL-10TLI +Memory_RAM:IS61C5128AS-25HLI +Memory_RAM:IS61C5128AS-25QLI +Memory_RAM:IS61C5128AS-25TLI +Memory_RAM:IS62C256AL +Memory_RAM:IS64C5128AL-12CTLA3 +Memory_RAM:IS64C5128AL-12KLA3 +Memory_RAM:IS65C256AL +Memory_RAM:IS6xC1024AL-xxH +Memory_RAM:IS6xC1024AL-xxJ +Memory_RAM:IS6xC1024AL-xxK +Memory_RAM:IS6xC1024AL-xxT +Memory_RAM:KM62256CLP +Memory_RAM:M48Tx2 +Memory_RAM:M48Zx2 +Memory_RAM:MK4116N +Memory_RAM:MK4164N +Memory_RAM:MT48LC16M16A2P +Memory_RAM:MT48LC16M16A2TG +Memory_RAM:MT48LC32M8A2P +Memory_RAM:MT48LC32M8A2TG +Memory_RAM:MT48LC64M4A2P +Memory_RAM:MT48LC64M4A2TG +Memory_RAM:R1LP0108ESF +Memory_RAM:R1LP0108ESN +Memory_RAM:W9812G6KH-5 +Memory_RAM:W9812G6KH-6 +Memory_RAM:W9812G6KH-6I +Memory_RAM:W9812G6KH-75 +Memory_ROM:XC18V01SO20 +Memory_ROM:XCF08P +Memory_UniqueID:DS2401P +Memory_UniqueID:DS2401Z +Motor:Fan +Motor:Fan_3pin +Motor:Fan_4pin +Motor:Fan_ALT +Motor:Fan_CPU_4pin +Motor:Fan_IEC-60617 +Motor:Fan_ISO-14617 +Motor:Fan_PC_Chassis +Motor:Fan_Tacho +Motor:Fan_Tacho_PWM +Motor:Motor_AC +Motor:Motor_DC +Motor:Motor_DC_ALT +Motor:Motor_Servo +Motor:Motor_Servo_AirTronics +Motor:Motor_Servo_Futaba_J +Motor:Motor_Servo_Grapner_JR +Motor:Motor_Servo_Hitec +Motor:Motor_Servo_JR +Motor:Motor_Servo_Robbe +Motor:Stepper_Motor_bipolar +Motor:Stepper_Motor_unipolar_5pin +Motor:Stepper_Motor_unipolar_6pin +Oscillator:5P49V6965 +Oscillator:ABLNO +Oscillator:ACO-xxxMHz +Oscillator:ACO-xxxMHz-A +Oscillator:ASCO +Oscillator:ASDMB-xxxMHz +Oscillator:ASE-xxxMHz +Oscillator:ASV-xxxMHz +Oscillator:CFPS-72 +Oscillator:CVCO55xx +Oscillator:CXO_DIP14 +Oscillator:CXO_DIP8 +Oscillator:DFA-S11 +Oscillator:DFA-S15 +Oscillator:DFA-S2 +Oscillator:DFA-S3 +Oscillator:DGOF5S3 +Oscillator:ECS-2520MV-xxx-xx +Oscillator:FT5HN +Oscillator:FT5HV +Oscillator:GTXO-14T +Oscillator:GTXO-14V +Oscillator:GTXO-S14T +Oscillator:GTXO-S14V +Oscillator:IQXO-70 +Oscillator:JTOS-25 +Oscillator:JTOS-50 +Oscillator:KC2520Z +Oscillator:KT2520K-T +Oscillator:LTC6905xS5-100 +Oscillator:LTC6905xS5-133 +Oscillator:LTC6905xS5-80 +Oscillator:LTC6905xS5-96 +Oscillator:MAX7375AXR105 +Oscillator:MAX7375AXR185 +Oscillator:MAX7375AXR365 +Oscillator:MAX7375AXR375 +Oscillator:MAX7375AXR405 +Oscillator:MAX7375AXR425 +Oscillator:MAX7375AXR805 +Oscillator:MV267 +Oscillator:MV317 +Oscillator:NB3N502 +Oscillator:NB3N511 +Oscillator:OCXO-14 +Oscillator:OH300 +Oscillator:SG-210SCD +Oscillator:SG-210SDD +Oscillator:SG-210SED +Oscillator:SG-210STF +Oscillator:SG-211 +Oscillator:SG-3030CM +Oscillator:SG-5032CAN +Oscillator:SG-5032CBN +Oscillator:SG-5032CCN +Oscillator:SG-51 +Oscillator:SG-531 +Oscillator:SG-615 +Oscillator:SG-7050CAN +Oscillator:SG-7050CBN +Oscillator:SG-7050CCN +Oscillator:SG-8002CA +Oscillator:SG-8002CE +Oscillator:SG-8002DB +Oscillator:SG-8002DC +Oscillator:SG-8002JA +Oscillator:SG-8002JC +Oscillator:SG-8002LB +Oscillator:Si512A_2.5x3.2mm +Oscillator:Si513A_2.5x3.2mm +Oscillator:Si5351A-B-GM +Oscillator:Si5351A-B-GT +Oscillator:Si5351B-B-GM +Oscillator:Si5351C-B-GM +Oscillator:Si570 +Oscillator:Si571 +Oscillator:SiT8008xx-1x-xxE +Oscillator:SiT8008xx-1x-xxN +Oscillator:SiT8008xx-1x-xxS +Oscillator:SiT8008xx-2x-xxE +Oscillator:SiT8008xx-2x-xxN +Oscillator:SiT8008xx-2x-xxS +Oscillator:SiT8008xx-3x-xxE +Oscillator:SiT8008xx-3x-xxN +Oscillator:SiT8008xx-3x-xxS +Oscillator:SiT8008xx-7x-xxE +Oscillator:SiT8008xx-7x-xxN +Oscillator:SiT8008xx-7x-xxS +Oscillator:SiT8008xx-8x-xxE +Oscillator:SiT8008xx-8x-xxN +Oscillator:SiT8008xx-8x-xxS +Oscillator:SiT9365xx-xBx-xxE +Oscillator:SiT9365xx-xBx-xxN +Oscillator:SiT9366xx-xBx-xxE +Oscillator:SiT9366xx-xBx-xxN +Oscillator:SiT9367xx-xBx-xxE +Oscillator:SiT9367xx-xBx-xxN +Oscillator:TCXO-14 +Oscillator:TCXO3 +Oscillator:TFT660 +Oscillator:TFT680 +Oscillator:TG2520SMN-xx.xxxxxxMhz-xxxxNM +Oscillator:TXC-7C +Oscillator:VC-81 +Oscillator:VC-83 +Oscillator:VTCXO-14 +Oscillator:XO32 +Oscillator:XO53 +Oscillator:XO91 +Oscillator:XUX51 +Oscillator:XUX52 +Oscillator:XUX53 +Oscillator:XUX71 +Oscillator:XUX72 +Oscillator:XUX73 +Oscillator:XUY51 +Oscillator:XUY52 +Oscillator:XUY53 +Oscillator:XUY71 +Oscillator:XUY72 +Oscillator:XUY73 +Potentiometer_Digital:AD5253 +Potentiometer_Digital:AD5254 +Potentiometer_Digital:AD5272BCP +Potentiometer_Digital:AD5272BRM +Potentiometer_Digital:AD5274BCP +Potentiometer_Digital:AD5274BRM +Potentiometer_Digital:AD5280 +Potentiometer_Digital:AD5282 +Potentiometer_Digital:AD5290 +Potentiometer_Digital:AD5293 +Potentiometer_Digital:DS1267_DIP +Potentiometer_Digital:DS1267_SOIC +Potentiometer_Digital:DS1267_TSSOP +Potentiometer_Digital:DS1882E +Potentiometer_Digital:MAX5436 +Potentiometer_Digital:MAX5438 +Potentiometer_Digital:MCP4011-xxxxMS +Potentiometer_Digital:MCP4011-xxxxSN +Potentiometer_Digital:MCP4012-xxxxCH +Potentiometer_Digital:MCP4013-xxxxCH +Potentiometer_Digital:MCP4014-xxxxOT +Potentiometer_Digital:MCP4017-xxxxLT +Potentiometer_Digital:MCP4018-xxxxLT +Potentiometer_Digital:MCP4019-xxxxLT +Potentiometer_Digital:MCP4021-xxxxMS +Potentiometer_Digital:MCP4021-xxxxSN +Potentiometer_Digital:MCP4022-xxxxCH +Potentiometer_Digital:MCP4023-xxxxCH +Potentiometer_Digital:MCP4024-xxxxOT +Potentiometer_Digital:MCP41010 +Potentiometer_Digital:MCP41050 +Potentiometer_Digital:MCP41100 +Potentiometer_Digital:MCP4131-xxxx-P +Potentiometer_Digital:MCP4132-xxxx-P +Potentiometer_Digital:MCP4141-xxxx-P +Potentiometer_Digital:MCP4142-xxxx-P +Potentiometer_Digital:MCP4151-xxxx-P +Potentiometer_Digital:MCP4152-xxxx-P +Potentiometer_Digital:MCP4161-xxxx-P +Potentiometer_Digital:MCP4162-xxxx-P +Potentiometer_Digital:MCP42010 +Potentiometer_Digital:MCP42050 +Potentiometer_Digital:MCP42100 +Potentiometer_Digital:MCP4251-xxxx-ML +Potentiometer_Digital:MCP4251-xxxx-P +Potentiometer_Digital:MCP4251-xxxx-SL +Potentiometer_Digital:MCP4251-xxxx-ST +Potentiometer_Digital:MCP4431-xxxx-ST +Potentiometer_Digital:MCP4441-xxxx-ST +Potentiometer_Digital:MCP4451-xxxx-ST +Potentiometer_Digital:MCP4461-xxxx-ST +Potentiometer_Digital:MCP45HV31-MQ +Potentiometer_Digital:MCP45HV31-ST +Potentiometer_Digital:MCP45HV51-MQ +Potentiometer_Digital:MCP45HV51-ST +Potentiometer_Digital:TPL0401A-10-Q1 +Potentiometer_Digital:TPL0401B-10-Q1 +Potentiometer_Digital:X9118 +Potentiometer_Digital:X9250 +Potentiometer_Digital:X9258 +power:+10V +power:+12C +power:+12L +power:+12LF +power:+12P +power:+12V +power:+12VA +power:+15V +power:+1V0 +power:+1V1 +power:+1V2 +power:+1V35 +power:+1V5 +power:+1V8 +power:+24V +power:+28V +power:+2V5 +power:+2V8 +power:+3.3V +power:+3.3VA +power:+3.3VADC +power:+3.3VDAC +power:+3.3VP +power:+36V +power:+3V0 +power:+3V3 +power:+3V8 +power:+48V +power:+4V +power:+5C +power:+5F +power:+5P +power:+5V +power:+5VA +power:+5VD +power:+5VL +power:+5VP +power:+6V +power:+7.5V +power:+8V +power:+9V +power:+9VA +power:+BATT +power:+VDC +power:+VSW +power:-10V +power:-12V +power:-12VA +power:-15V +power:-24V +power:-2V5 +power:-36V +power:-3V3 +power:-48V +power:-5V +power:-5VA +power:-6V +power:-8V +power:-9V +power:-9VA +power:-BATT +power:-VDC +power:-VSW +power:AC +power:Earth +power:Earth_Clean +power:Earth_Protective +power:GND +power:GND1 +power:GND2 +power:GND3 +power:GNDA +power:GNDD +power:GNDPWR +power:GNDREF +power:GNDS +power:HT +power:LINE +power:NEUT +power:PRI_HI +power:PRI_LO +power:PRI_MID +power:PWR_FLAG +power:VAA +power:VAC +power:VBUS +power:VCC +power:VCCQ +power:VCOM +power:VD +power:VDC +power:VDD +power:VDDA +power:VDDF +power:VEE +power:VMEM +power:VPP +power:VS +power:VSS +power:VSSA +power:Vdrive +Power_Management:AAT4610BIGV-1-T1 +Power_Management:AAT4610BIGV-T1 +Power_Management:AAT4616IGV-1-T1 +Power_Management:AAT4616IGV-T1 +Power_Management:ADM1270ACPZ +Power_Management:ADM1270ARQZ +Power_Management:AP2161W +Power_Management:AP2171W +Power_Management:AP22804AW5 +Power_Management:AP22804BW5 +Power_Management:AP22814AW5 +Power_Management:AP22814BW5 +Power_Management:AP22816AKEWT +Power_Management:AP22816BKEWT +Power_Management:AP22817AKEWT +Power_Management:AP22817BKEWT +Power_Management:AP22818AKEWT +Power_Management:AP22818BKEWT +Power_Management:AP22913CN4 +Power_Management:AUIPS1041R +Power_Management:AUIPS1042G +Power_Management:AUIPS1051L +Power_Management:AUIPS1052G +Power_Management:AUIPS2031R +Power_Management:AUIPS2041L +Power_Management:AUIPS2051L +Power_Management:AUIPS2052G +Power_Management:AUIPS6011R +Power_Management:AUIPS6031R +Power_Management:AUIPS6041G +Power_Management:AUIPS6044G +Power_Management:AUIPS7081R +Power_Management:AUIPS7081S +Power_Management:AUIPS7091G +Power_Management:AUIPS7111S +Power_Management:AUIPS7121R +Power_Management:AUIPS7125R +Power_Management:AUIPS7141R +Power_Management:AUIPS7142G +Power_Management:AUIPS71451G +Power_Management:AUIPS7145R +Power_Management:AUIPS72211R +Power_Management:AUIPS7221R +Power_Management:AUIR3313S +Power_Management:AUIR3314S +Power_Management:AUIR3315S +Power_Management:AUIR3316S +Power_Management:AUIR3320S +Power_Management:AUIR33402S +Power_Management:BD2222G +Power_Management:BD2242G +Power_Management:BD2243G +Power_Management:BD48ExxG +Power_Management:BD48KxxG +Power_Management:BD48LxxG +Power_Management:BD48xxFVE +Power_Management:BD49ExxG +Power_Management:BD49KxxG +Power_Management:BD49LxxG +Power_Management:BD49xxFVE +Power_Management:BQ24230RGT +Power_Management:BTN8982TA +Power_Management:BTS40K2-1EJC +Power_Management:BTS443P +Power_Management:BTS462TATMA1 +Power_Management:BTS50010-1TAD +Power_Management:BTS50055-1TMA +Power_Management:BTS50055-1TMC +Power_Management:BTS50080-1TEA +Power_Management:BTS50080-1TEB +Power_Management:BTS50080-1TMA +Power_Management:BTS50080-1TMC +Power_Management:BTS50085-1TMA +Power_Management:BTS5012SDA +Power_Management:BTS5014SDA +Power_Management:BTS5016SDA +Power_Management:BTS5030-1EJA +Power_Management:BTS5045-1EJA +Power_Management:BTS5090-1EJA +Power_Management:BTS5200-1EJA +Power_Management:BTS6133D +Power_Management:BTS6142D +Power_Management:BTS6143D +Power_Management:BTS6163D +Power_Management:BTS6200-1EJA +Power_Management:BTS7004-1EPP +Power_Management:BTS711L1 +Power_Management:BTS712N1 +Power_Management:BTS716G +Power_Management:BTS716GB +Power_Management:BTS721L1 +Power_Management:BTS724G +Power_Management:CAP002DG +Power_Management:CAP003DG +Power_Management:CAP004DG +Power_Management:CAP005DG +Power_Management:CAP006DG +Power_Management:CAP007DG +Power_Management:CAP008DG +Power_Management:CAP009DG +Power_Management:CAP012DG +Power_Management:CAP013DG +Power_Management:CAP014DG +Power_Management:CAP015DG +Power_Management:CAP016DG +Power_Management:CAP017DG +Power_Management:CAP018DG +Power_Management:CAP019DG +Power_Management:CAP200DG +Power_Management:CAP300DG +Power_Management:DS1210 +Power_Management:EPC23102 +Power_Management:EPC23103 +Power_Management:EPC23104 +Power_Management:FPF2000 +Power_Management:FPF2001 +Power_Management:FPF2002 +Power_Management:FPF2003 +Power_Management:FPF2004 +Power_Management:FPF2005 +Power_Management:FPF2006 +Power_Management:FPF2007 +Power_Management:HF81 +Power_Management:INA3221 +Power_Management:IPS6011PBF +Power_Management:IPS6011RPBF +Power_Management:IPS6011SPBF +Power_Management:IPS6021PBF +Power_Management:IPS6021RPBF +Power_Management:IPS6021SPBF +Power_Management:IPS6031PBF +Power_Management:IPS6031RPBF +Power_Management:IPS6031SPBF +Power_Management:IPS6041GPBF +Power_Management:IPS6041PBF +Power_Management:IPS6041RPBF +Power_Management:IPS6041SPBF +Power_Management:IPS7091GPBF +Power_Management:IPS7091PBF +Power_Management:IPS7091SPBF +Power_Management:IRS25751L +Power_Management:ITS5215 +Power_Management:LM5050-1 +Power_Management:LM5050-2 +Power_Management:LM5051 +Power_Management:LM5060 +Power_Management:LM50672NPAR +Power_Management:LM5067MM-1 +Power_Management:LM5067MM-2 +Power_Management:LM5067MMX-2 +Power_Management:LM5067MWX-1 +Power_Management:LM66100DCK +Power_Management:LM74700 +Power_Management:LMG3410 +Power_Management:LMG5200 +Power_Management:LT1641-1 +Power_Management:LT1641-2 +Power_Management:LT4230xDD +Power_Management:LT4231xUF +Power_Management:LT4320xDD-1 +Power_Management:LTC4242xUHF +Power_Management:LTC4357DCB +Power_Management:LTC4357MS8 +Power_Management:LTC4359-DCB +Power_Management:LTC4359-MS8 +Power_Management:LTC4364CDE +Power_Management:LTC4364CMS +Power_Management:LTC4364CS +Power_Management:LTC4364HDE +Power_Management:LTC4364HMS +Power_Management:LTC4364HS +Power_Management:LTC4364IDE +Power_Management:LTC4364IMS +Power_Management:LTC4364IS +Power_Management:LTC4365DDB +Power_Management:LTC4365DDB-1 +Power_Management:LTC4365TS8 +Power_Management:LTC4365TS8-1 +Power_Management:LTC4365xTS8 +Power_Management:LTC4370xDE +Power_Management:LTC4370xMS +Power_Management:LTC4412xS6 +Power_Management:LTC4417CGN +Power_Management:LTC4417CUF +Power_Management:LTC4417HGN +Power_Management:LTC4417HUF +Power_Management:LTC4417IGN +Power_Management:LTC4417IUF +Power_Management:MAX14919xUP +Power_Management:MAX8586 +Power_Management:MAX9611 +Power_Management:MAX9612 +Power_Management:MIC2007YM6 +Power_Management:MIC2008YM6 +Power_Management:MIC2017YM6 +Power_Management:MIC2018YM6 +Power_Management:MIC2025-1YM +Power_Management:MIC2025-1YMM +Power_Management:MIC2025-2YM +Power_Management:MIC2025-2YMM +Power_Management:MIC2026-1BN +Power_Management:MIC2026-1xM +Power_Management:MIC2026-2BN +Power_Management:MIC2026-2xM +Power_Management:MIC2090-1YM5 +Power_Management:MIC2090-2YM5 +Power_Management:MIC2091-1YM5 +Power_Management:MIC2091-2YM5 +Power_Management:MIC2544-2YM +Power_Management:MIC2587-1 +Power_Management:MIC2587R-1 +Power_Management:NIS5420MTxTXG +Power_Management:NPC45560-H +Power_Management:NPC45560-L +Power_Management:PD70224 +Power_Management:RT9701 +Power_Management:RT9742AGJ5F +Power_Management:RT9742ANGJ5F +Power_Management:RT9742BGJ5F +Power_Management:RT9742BNGJ5F +Power_Management:SN6505ADBV +Power_Management:SN6505BDBV +Power_Management:SN6507DGQ +Power_Management:STM6600 +Power_Management:STM6601 +Power_Management:SiP32431DR3 +Power_Management:SiP32432DR3 +Power_Management:TEA1708T +Power_Management:TLE8102SG +Power_Management:TLE8104E +Power_Management:TPS2041B +Power_Management:TPS2042D +Power_Management:TPS2044D +Power_Management:TPS2051CDBV +Power_Management:TPS2054D +Power_Management:TPS2065CDBV +Power_Management:TPS2065CDBVx-2 +Power_Management:TPS2069CDBV +Power_Management:TPS2116DRL +Power_Management:TPS22810DRV +Power_Management:TPS22917DBV +Power_Management:TPS22929D +Power_Management:TPS22993 +Power_Management:TPS2412D +Power_Management:TPS2412PW +Power_Management:TPS2419D +Power_Management:TPS2419PW +Power_Management:TPS2592xx +Power_Management:TPS26630RGE +Power_Management:TPS26631PWP +Power_Management:TPS26631RGE +Power_Management:TPS26632RGE +Power_Management:TPS26633PWP +Power_Management:TPS26633RGE +Power_Management:TPS26635RGE +Power_Management:TPS26636PWP +Power_Management:TSM102 +Power_Management:TSM102A +Power_Management:TSM103W +Power_Management:TSM103WA +Power_Management:UCC39002D +Power_Protection:CDNBS08-SLVU2.8-4 +Power_Protection:CDSOT236-0504C +Power_Protection:CM1213A-01SO +Power_Protection:CM1624 +Power_Protection:D3V3X8U9LP3810 +Power_Protection:D3V3XA4B10LP +Power_Protection:DT1240A-08LP3810 +Power_Protection:ECMF02-2AMX6 +Power_Protection:ECMF04-4HSWM10 +Power_Protection:EMI2121MTTAG +Power_Protection:EMI8132 +Power_Protection:ESD224DQA +Power_Protection:ESDA14V2SC5 +Power_Protection:ESDA5V3L +Power_Protection:ESDA5V3SC5 +Power_Protection:ESDA6V1-5SC6 +Power_Protection:ESDA6V1BC6 +Power_Protection:ESDA6V1SC5 +Power_Protection:ESDLC5V0PB8 +Power_Protection:IP3319CX6 +Power_Protection:IP4234CZ6 +Power_Protection:IP4251CZ8-4-TTL +Power_Protection:IP4252CZ12 +Power_Protection:IP4252CZ16 +Power_Protection:IP4252CZ8 +Power_Protection:IP4252CZ8-4-TTL +Power_Protection:IP4253CZ8-4-TTL +Power_Protection:IP4254CZ8-4-TTL +Power_Protection:NCP349MN +Power_Protection:NCP349MNAE +Power_Protection:NCP349MNAM +Power_Protection:NCP349MNBG +Power_Protection:NCP349MNBK +Power_Protection:NCP361MU +Power_Protection:NCP361SN +Power_Protection:NUF4401MN +Power_Protection:NUP2105L +Power_Protection:NUP2202 +Power_Protection:NUP4202 +Power_Protection:PCMF3USB3S +Power_Protection:PESD3V3L4UF +Power_Protection:PESD3V3L4UG +Power_Protection:PESD3V3L4UW +Power_Protection:PESD3V3L5UF +Power_Protection:PESD3V3L5UV +Power_Protection:PESD3V3L5UY +Power_Protection:PESD5V0L4UF +Power_Protection:PESD5V0L4UG +Power_Protection:PESD5V0L4UW +Power_Protection:PESD5V0L5UF +Power_Protection:PESD5V0L5UV +Power_Protection:PESD5V0L5UY +Power_Protection:PRTR5V0U2X +Power_Protection:RCLAMP0502B +Power_Protection:RCLAMP0502BA +Power_Protection:RCLAMP0582B +Power_Protection:RCLAMP3328P +Power_Protection:SN65220 +Power_Protection:SN65240 +Power_Protection:SN75240 +Power_Protection:SP0502BAHT +Power_Protection:SP0502BAJT +Power_Protection:SP0503BAHT +Power_Protection:SP0504BAHT +Power_Protection:SP0504BAJT +Power_Protection:SP0505BAHT +Power_Protection:SP0505BAJT +Power_Protection:SP7538P +Power_Protection:SRV05-4 +Power_Protection:SZNUP2105L +Power_Protection:TBU-CA-025-050-WH +Power_Protection:TBU-CA-025-100-WH +Power_Protection:TBU-CA-025-200-WH +Power_Protection:TBU-CA-025-300-WH +Power_Protection:TBU-CA-025-500-WH +Power_Protection:TBU-CA-040-050-WH +Power_Protection:TBU-CA-040-100-WH +Power_Protection:TBU-CA-040-200-WH +Power_Protection:TBU-CA-040-300-WH +Power_Protection:TBU-CA-040-500-WH +Power_Protection:TBU-CA-050-050-WH +Power_Protection:TBU-CA-050-100-WH +Power_Protection:TBU-CA-050-200-WH +Power_Protection:TBU-CA-050-300-WH +Power_Protection:TBU-CA-050-500-WH +Power_Protection:TBU-CA-065-050-WH +Power_Protection:TBU-CA-065-100-WH +Power_Protection:TBU-CA-065-200-WH +Power_Protection:TBU-CA-065-300-WH +Power_Protection:TBU-CA-065-500-WH +Power_Protection:TBU-CA-085-050-WH +Power_Protection:TBU-CA-085-100-WH +Power_Protection:TBU-CA-085-200-WH +Power_Protection:TBU-CA-085-300-WH +Power_Protection:TBU-CA-085-500-WH +Power_Protection:TPD1E05U06DPY +Power_Protection:TPD1E05U06DYA +Power_Protection:TPD2E2U06DCK +Power_Protection:TPD2E2U06DRL +Power_Protection:TPD2EUSB30 +Power_Protection:TPD2EUSB30A +Power_Protection:TPD2S017 +Power_Protection:TPD3E001DRLR +Power_Protection:TPD3F303DPV +Power_Protection:TPD3S014 +Power_Protection:TPD3S044 +Power_Protection:TPD4E02B04DQA +Power_Protection:TPD4E05U06DQA +Power_Protection:TPD4EUSB30 +Power_Protection:TPD4S014 +Power_Protection:TPD4S1394 +Power_Protection:TPD6E05U06RVZ +Power_Protection:TPD6F003 +Power_Protection:TPD6S300A +Power_Protection:TPD8F003 +Power_Protection:TVS0500DRV +Power_Protection:TVS1400DRV +Power_Protection:TVS1800DRV +Power_Protection:TVS2200DRV +Power_Protection:TVS2700DRV +Power_Protection:TVS3300DRV +Power_Protection:USB6B1 +Power_Protection:USBLC6-2P6 +Power_Protection:USBLC6-2SC6 +Power_Protection:USBLC6-4SC6 +Power_Protection:WE-TVS-82400102 +Power_Protection:WE-TVS-824014881 +Power_Protection:WE-TVS-824015043 +Power_Protection:ZEN056V075A48LS +Power_Protection:ZEN056V115A24LS +Power_Protection:ZEN056V130A24LS +Power_Protection:ZEN056V230A16LS +Power_Protection:ZEN059V130A24LS +Power_Protection:ZEN065V130A24LS +Power_Protection:ZEN065V230A16LS +Power_Protection:ZEN098V130A24LS +Power_Protection:ZEN098V230A16LS +Power_Protection:ZEN132V075A48LS +Power_Protection:ZEN132V130A24LS +Power_Protection:ZEN132V230A16LS +Power_Protection:ZEN164V130A24LS +Power_Supervisor:CAT811JTBI-GT3 +Power_Supervisor:CAT811LTBI-GT3 +Power_Supervisor:CAT811MTBI-GT3 +Power_Supervisor:CAT811RTBI-GT3 +Power_Supervisor:CAT811STBI-GT3 +Power_Supervisor:CAT811TTBI-GT3 +Power_Supervisor:CAT811ZTBI-GT3 +Power_Supervisor:DIO705 +Power_Supervisor:DIO706 +Power_Supervisor:DIO706J +Power_Supervisor:DIO706R +Power_Supervisor:DIO706S +Power_Supervisor:DIO706T +Power_Supervisor:LM3880 +Power_Supervisor:LM809 +Power_Supervisor:LM810 +Power_Supervisor:MAX16050xTI +Power_Supervisor:MAX16051xTI +Power_Supervisor:MAX6355 +Power_Supervisor:MAX6369 +Power_Supervisor:MAX6370 +Power_Supervisor:MAX6371 +Power_Supervisor:MAX6372 +Power_Supervisor:MAX6373 +Power_Supervisor:MAX6374 +Power_Supervisor:MAX690ACSA +Power_Supervisor:MAX690xPA +Power_Supervisor:MAX691xPE +Power_Supervisor:MAX691xWE +Power_Supervisor:MAX692ACSA +Power_Supervisor:MAX692xPA +Power_Supervisor:MAX694xPA +Power_Supervisor:MAX802LCSA +Power_Supervisor:MAX805LCSA +Power_Supervisor:MAX811LEUS-T +Power_Supervisor:MAX811MEUS-T +Power_Supervisor:MAX811REUS-T +Power_Supervisor:MAX811SEUS-T +Power_Supervisor:MAX811TEUS-T +Power_Supervisor:MC34064D +Power_Supervisor:MC34064DM +Power_Supervisor:MC34064P +Power_Supervisor:MC34064SN +Power_Supervisor:MCP100-270D +Power_Supervisor:MCP100-300D +Power_Supervisor:MCP100-315D +Power_Supervisor:MCP100-450D +Power_Supervisor:MCP100-460D +Power_Supervisor:MCP100-475D +Power_Supervisor:MCP100-485D +Power_Supervisor:MCP101-270D +Power_Supervisor:MCP101-300D +Power_Supervisor:MCP101-315D +Power_Supervisor:MCP101-450D +Power_Supervisor:MCP101-460D +Power_Supervisor:MCP101-475D +Power_Supervisor:MCP101-485D +Power_Supervisor:MCP120-xxxDxTO +Power_Supervisor:MCP120-xxxGxTO +Power_Supervisor:MCP120-xxxHxTO +Power_Supervisor:MCP120-xxxxSN +Power_Supervisor:MCP120-xxxxTT +Power_Supervisor:MCP130-xxxDxTO +Power_Supervisor:MCP130-xxxFxTO +Power_Supervisor:MCP130-xxxHxTO +Power_Supervisor:MCP130-xxxxSN +Power_Supervisor:MCP130-xxxxTT +Power_Supervisor:MIC811JUY +Power_Supervisor:MIC811LUY +Power_Supervisor:MIC811MUY +Power_Supervisor:MIC811RUY +Power_Supervisor:MIC811SUY +Power_Supervisor:MIC811TUY +Power_Supervisor:TCM809 +Power_Supervisor:TCM810 +Power_Supervisor:TL7702A +Power_Supervisor:TL7702B +Power_Supervisor:TL7705A +Power_Supervisor:TL7705AxPS +Power_Supervisor:TL7705B +Power_Supervisor:TL7709A +Power_Supervisor:TL7712A +Power_Supervisor:TL7715A +Power_Supervisor:TL7733B +Power_Supervisor:TLV810EA29DBZ +Power_Supervisor:TPS3430WDRC +Power_Supervisor:TPS3702 +Power_Supervisor:TPS3808DBV +Power_Supervisor:TPS3831 +Power_Supervisor:TPS3839DBZ +Power_Supervisor:TPS3839DQN +Reference_Current:LM134H +Reference_Current:LM234Z-3 +Reference_Current:LM234Z-6 +Reference_Current:LM334M +Reference_Current:LM334SM +Reference_Current:LM334Z +Reference_Current:LM334Z-LFT1 +Reference_Current:LT3092xDD +Reference_Current:LT3092xST +Reference_Current:LT3092xTS8 +Reference_Current:PSSI2021SAY +Reference_Current:REF200AU +Reference_Voltage:AD586 +Reference_Voltage:ADR1399KEZ +Reference_Voltage:ADR1399KHZ +Reference_Voltage:ADR420ARMZ +Reference_Voltage:ADR421ARMZ +Reference_Voltage:ADR423ARMZ +Reference_Voltage:ADR425ARMZ +Reference_Voltage:ADR440ARMZ +Reference_Voltage:ADR440xRZ +Reference_Voltage:ADR441ARMZ +Reference_Voltage:ADR441xRZ +Reference_Voltage:ADR443ARMZ +Reference_Voltage:ADR443xRZ +Reference_Voltage:ADR444ARMZ +Reference_Voltage:ADR444xRZ +Reference_Voltage:ADR445ARMZ +Reference_Voltage:ADR445xRZ +Reference_Voltage:ADR4520 +Reference_Voltage:ADR4525 +Reference_Voltage:ADR4530 +Reference_Voltage:ADR4533 +Reference_Voltage:ADR4540 +Reference_Voltage:ADR4550 +Reference_Voltage:CJ432 +Reference_Voltage:ISL21070CIH320Z-TK +Reference_Voltage:ISL21070CIH325Z-TK +Reference_Voltage:ISL21070DIH306Z-TK +Reference_Voltage:LM285D-1.2 +Reference_Voltage:LM285D-2.5 +Reference_Voltage:LM285M-ADJ +Reference_Voltage:LM285S-1.2 +Reference_Voltage:LM285S-2.5 +Reference_Voltage:LM285Z-1.2 +Reference_Voltage:LM285Z-2.5 +Reference_Voltage:LM285Z-ADJ +Reference_Voltage:LM329xZ +Reference_Voltage:LM385BZ-1.2 +Reference_Voltage:LM385BZ-2.5 +Reference_Voltage:LM385D-1.2 +Reference_Voltage:LM385D-2.5 +Reference_Voltage:LM385M-ADJ +Reference_Voltage:LM385S-1.2 +Reference_Voltage:LM385S-2.5 +Reference_Voltage:LM385Z-1.2 +Reference_Voltage:LM385Z-2.5 +Reference_Voltage:LM385Z-ADJ +Reference_Voltage:LM399 +Reference_Voltage:LM4030-2.5 +Reference_Voltage:LM4030-4.096 +Reference_Voltage:LM4040DBZ-10 +Reference_Voltage:LM4040DBZ-2.0 +Reference_Voltage:LM4040DBZ-2.5 +Reference_Voltage:LM4040DBZ-3 +Reference_Voltage:LM4040DBZ-4.1 +Reference_Voltage:LM4040DBZ-5 +Reference_Voltage:LM4040DBZ-8.2 +Reference_Voltage:LM4040DCK-10 +Reference_Voltage:LM4040DCK-2.0 +Reference_Voltage:LM4040DCK-2.5 +Reference_Voltage:LM4040DCK-3 +Reference_Voltage:LM4040DCK-4.1 +Reference_Voltage:LM4040DCK-5 +Reference_Voltage:LM4040DCK-8.2 +Reference_Voltage:LM4040LP-10 +Reference_Voltage:LM4040LP-2.0 +Reference_Voltage:LM4040LP-2.5 +Reference_Voltage:LM4040LP-3 +Reference_Voltage:LM4040LP-4.1 +Reference_Voltage:LM4040LP-5 +Reference_Voltage:LM4040LP-8.2 +Reference_Voltage:LM4041LP-ADJ +Reference_Voltage:LM4125AIM5-2.5 +Reference_Voltage:LM4125IM5-2.0 +Reference_Voltage:LM4125IM5-2.5 +Reference_Voltage:LM4128 +Reference_Voltage:LM4132xMF-1.8 +Reference_Voltage:LM4132xMF-2.0 +Reference_Voltage:LM4132xMF-2.5 +Reference_Voltage:LM4132xMF-3.0 +Reference_Voltage:LM4132xMF-3.3 +Reference_Voltage:LM4132xMF-4.1 +Reference_Voltage:LT1009CMS8 +Reference_Voltage:LT1009xS8 +Reference_Voltage:LT1009xZ +Reference_Voltage:LT1019xN8 +Reference_Voltage:LT1461AxS8-2.5 +Reference_Voltage:LT1461AxS8-3 +Reference_Voltage:LT1461AxS8-3.3 +Reference_Voltage:LT1461AxS8-4 +Reference_Voltage:LT1461AxS8-5 +Reference_Voltage:LT1461BxS8-2.5 +Reference_Voltage:LT1461BxS8-3 +Reference_Voltage:LT1461BxS8-3.3 +Reference_Voltage:LT1461BxS8-4 +Reference_Voltage:LT1461BxS8-5 +Reference_Voltage:LT1461CxS8-2.5 +Reference_Voltage:LT1461CxS8-3 +Reference_Voltage:LT1461CxS8-3.3 +Reference_Voltage:LT1461CxS8-4 +Reference_Voltage:LT1461CxS8-5 +Reference_Voltage:LT1461DxS8-2.5 +Reference_Voltage:LT1461DxS8-3 +Reference_Voltage:LT1461DxS8-3.3 +Reference_Voltage:LT1461DxS8-4 +Reference_Voltage:LT1461DxS8-5 +Reference_Voltage:LT1634BCMS8-1.25 +Reference_Voltage:LT1634BCMS8-2.5 +Reference_Voltage:LT1634CCZ-1.25 +Reference_Voltage:LT1634CCZ-2.5 +Reference_Voltage:LT1634CCZ-4.096 +Reference_Voltage:LT1634CCZ-5 +Reference_Voltage:LT1634xxS8-1.25 +Reference_Voltage:LT1634xxS8-2.5 +Reference_Voltage:LT1634xxS8-4.096 +Reference_Voltage:LT1634xxS8-5 +Reference_Voltage:LT1790-1.25 +Reference_Voltage:LT1790-2.048 +Reference_Voltage:LT1790-2.5 +Reference_Voltage:LT1790-3 +Reference_Voltage:LT1790-3.3 +Reference_Voltage:LT1790-4.096 +Reference_Voltage:LT1790-5 +Reference_Voltage:LT6657AHMS8-2.5 +Reference_Voltage:LT6657AHMS8-3 +Reference_Voltage:LT6657AHMS8-4.096 +Reference_Voltage:LT6657AHMS8-5 +Reference_Voltage:LT6657BHMS8-2.5 +Reference_Voltage:LT6657BHMS8-3 +Reference_Voltage:LT6657BHMS8-4.096 +Reference_Voltage:LT6657BHMS8-5 +Reference_Voltage:MAX6001 +Reference_Voltage:MAX6002 +Reference_Voltage:MAX6003 +Reference_Voltage:MAX6004 +Reference_Voltage:MAX6005 +Reference_Voltage:MAX6035xSA25 +Reference_Voltage:MAX6035xxUR25 +Reference_Voltage:MAX6035xxUR30 +Reference_Voltage:MAX6035xxUR50 +Reference_Voltage:MAX6070AAUT12+T +Reference_Voltage:MAX6070AAUT18+T +Reference_Voltage:MAX6070AAUT18V+T +Reference_Voltage:MAX6070AAUT21+T +Reference_Voltage:MAX6070AAUT25+T +Reference_Voltage:MAX6070AAUT30+T +Reference_Voltage:MAX6070AAUT33+T +Reference_Voltage:MAX6070AAUT33V+T +Reference_Voltage:MAX6070AAUT41+T +Reference_Voltage:MAX6070AAUT50+T +Reference_Voltage:MAX6070AAUT50V+T +Reference_Voltage:MAX6070BAUT12+T +Reference_Voltage:MAX6070BAUT12V+T +Reference_Voltage:MAX6070BAUT18+T +Reference_Voltage:MAX6070BAUT21+T +Reference_Voltage:MAX6070BAUT21V+T +Reference_Voltage:MAX6070BAUT25+T +Reference_Voltage:MAX6070BAUT25V+T +Reference_Voltage:MAX6070BAUT30+T +Reference_Voltage:MAX6070BAUT33+T +Reference_Voltage:MAX6070BAUT33V+T +Reference_Voltage:MAX6070BAUT41+T +Reference_Voltage:MAX6070BAUT41V+T +Reference_Voltage:MAX6070BAUT50+T +Reference_Voltage:MAX6070BAUT50V+T +Reference_Voltage:MAX6070DAUT12V+T +Reference_Voltage:MAX6070DAUT25V+T +Reference_Voltage:MAX6070DAUT30V+T +Reference_Voltage:MAX6070DAUT41V+T +Reference_Voltage:MAX6071AAUT12+T +Reference_Voltage:MAX6071AAUT18+T +Reference_Voltage:MAX6071AAUT21+T +Reference_Voltage:MAX6071AAUT25+T +Reference_Voltage:MAX6071AAUT30+T +Reference_Voltage:MAX6071AAUT30V+T +Reference_Voltage:MAX6071AAUT33+T +Reference_Voltage:MAX6071AAUT41+T +Reference_Voltage:MAX6071AAUT50+T +Reference_Voltage:MAX6071BAUT12+T +Reference_Voltage:MAX6071BAUT18+T +Reference_Voltage:MAX6071BAUT21+T +Reference_Voltage:MAX6071BAUT25+T +Reference_Voltage:MAX6071BAUT25V+T +Reference_Voltage:MAX6071BAUT30+T +Reference_Voltage:MAX6071BAUT33+T +Reference_Voltage:MAX6071BAUT41+T +Reference_Voltage:MAX6071BAUT50+T +Reference_Voltage:MAX6100 +Reference_Voltage:MAX6101 +Reference_Voltage:MAX6102 +Reference_Voltage:MAX6103 +Reference_Voltage:MAX6104 +Reference_Voltage:MAX6105 +Reference_Voltage:MAX6106 +Reference_Voltage:MAX6107 +Reference_Voltage:MAX6225 +Reference_Voltage:MAX6241 +Reference_Voltage:MAX6250 +Reference_Voltage:MAX6325 +Reference_Voltage:MAX6341 +Reference_Voltage:MAX6350 +Reference_Voltage:MAX872 +Reference_Voltage:MAX874 +Reference_Voltage:MCP1501-10xCH +Reference_Voltage:MCP1501-10xRW +Reference_Voltage:MCP1501-10xSN +Reference_Voltage:MCP1501-12xCH +Reference_Voltage:MCP1501-12xRW +Reference_Voltage:MCP1501-12xSN +Reference_Voltage:MCP1501-18xCH +Reference_Voltage:MCP1501-18xRW +Reference_Voltage:MCP1501-18xSN +Reference_Voltage:MCP1501-20xCH +Reference_Voltage:MCP1501-20xRW +Reference_Voltage:MCP1501-20xSN +Reference_Voltage:MCP1501-25xCH +Reference_Voltage:MCP1501-25xRW +Reference_Voltage:MCP1501-25xSN +Reference_Voltage:MCP1501-30xCH +Reference_Voltage:MCP1501-30xRW +Reference_Voltage:MCP1501-30xSN +Reference_Voltage:MCP1501-33xCH +Reference_Voltage:MCP1501-33xRW +Reference_Voltage:MCP1501-33xSN +Reference_Voltage:MCP1501-40xCH +Reference_Voltage:MCP1501-40xRW +Reference_Voltage:MCP1501-40xSN +Reference_Voltage:MCP1525-TO +Reference_Voltage:MCP1525-TT +Reference_Voltage:MCP1541-TO +Reference_Voltage:MCP1541-TT +Reference_Voltage:REF01CP +Reference_Voltage:REF01CS +Reference_Voltage:REF01EZ +Reference_Voltage:REF01HP +Reference_Voltage:REF01HZ +Reference_Voltage:REF02AP +Reference_Voltage:REF02AU +Reference_Voltage:REF02AZ +Reference_Voltage:REF02BP +Reference_Voltage:REF02BU +Reference_Voltage:REF02CP +Reference_Voltage:REF02CS +Reference_Voltage:REF02EZ +Reference_Voltage:REF02HP +Reference_Voltage:REF02HS +Reference_Voltage:REF02HZ +Reference_Voltage:REF02Z +Reference_Voltage:REF03GP +Reference_Voltage:REF03GS +Reference_Voltage:REF102AP +Reference_Voltage:REF102AU +Reference_Voltage:REF102BP +Reference_Voltage:REF102BU +Reference_Voltage:REF102CP +Reference_Voltage:REF102CU +Reference_Voltage:REF191 +Reference_Voltage:REF192 +Reference_Voltage:REF193 +Reference_Voltage:REF194 +Reference_Voltage:REF195 +Reference_Voltage:REF196 +Reference_Voltage:REF198 +Reference_Voltage:REF2025 +Reference_Voltage:REF2030 +Reference_Voltage:REF2033 +Reference_Voltage:REF2041 +Reference_Voltage:REF3012 +Reference_Voltage:REF3020 +Reference_Voltage:REF3025 +Reference_Voltage:REF3030 +Reference_Voltage:REF3033 +Reference_Voltage:REF3040 +Reference_Voltage:REF3212AMDBVREP +Reference_Voltage:REF3220AMDBVREP +Reference_Voltage:REF3225AMDBVREP +Reference_Voltage:REF3230AMDBVREP +Reference_Voltage:REF3233AMDBVREP +Reference_Voltage:REF3240AMDBVREP +Reference_Voltage:REF35102QDBVR +Reference_Voltage:REF35120QDBVR +Reference_Voltage:REF35125QDBVR +Reference_Voltage:REF35160QDBVR +Reference_Voltage:REF35170QDBVR +Reference_Voltage:REF35180QDBVR +Reference_Voltage:REF35205QDBVR +Reference_Voltage:REF35250QDBVR +Reference_Voltage:REF35300QDBVR +Reference_Voltage:REF35330QDBVR +Reference_Voltage:REF35360QDBVR +Reference_Voltage:REF35409QDBVR +Reference_Voltage:REF35500QDBVR +Reference_Voltage:REF5010AD +Reference_Voltage:REF5010ADGK +Reference_Voltage:REF5010ID +Reference_Voltage:REF5010IDGK +Reference_Voltage:REF5020AD +Reference_Voltage:REF5020ADGK +Reference_Voltage:REF5020ID +Reference_Voltage:REF5020IDGK +Reference_Voltage:REF5025AD +Reference_Voltage:REF5025ADGK +Reference_Voltage:REF5025ID +Reference_Voltage:REF5025IDGK +Reference_Voltage:REF5030AD +Reference_Voltage:REF5030ADGK +Reference_Voltage:REF5030ID +Reference_Voltage:REF5030IDGK +Reference_Voltage:REF5040AD +Reference_Voltage:REF5040ADGK +Reference_Voltage:REF5040ID +Reference_Voltage:REF5040IDGK +Reference_Voltage:REF5045AD +Reference_Voltage:REF5045ADGK +Reference_Voltage:REF5045ID +Reference_Voltage:REF5045IDGK +Reference_Voltage:REF5050AD +Reference_Voltage:REF5050ADGK +Reference_Voltage:REF5050ID +Reference_Voltage:REF5050IDGK +Reference_Voltage:REF6025xDGK +Reference_Voltage:REF6030xDGK +Reference_Voltage:REF6033xDGK +Reference_Voltage:REF6041xDGK +Reference_Voltage:REF6045xDGK +Reference_Voltage:REF6050xDGK +Reference_Voltage:TL431D +Reference_Voltage:TL431DBV +Reference_Voltage:TL431DBZ +Reference_Voltage:TL431DCK +Reference_Voltage:TL431KTP +Reference_Voltage:TL431LP +Reference_Voltage:TL431P +Reference_Voltage:TL431PK +Reference_Voltage:TL431PS +Reference_Voltage:TL431PW +Reference_Voltage:TL432DBV +Reference_Voltage:TL432DBZ +Reference_Voltage:TL432PK +Reference_Voltage:TLE2425xD +Reference_Voltage:TLE2425xLP +Reference_Voltage:TLE2426xD +Reference_Voltage:TLE2426xLP +Reference_Voltage:TLE2426xP +Regulator_Controller:ICE1PCS01 +Regulator_Controller:ICE1PCS02 +Regulator_Controller:ICE2PCS01 +Regulator_Controller:ICE2PCS02 +Regulator_Controller:ICE2PCS03 +Regulator_Controller:ICE2PCS04 +Regulator_Controller:ICE2PCS05 +Regulator_Controller:ICE2PCS06 +Regulator_Controller:ICE3PCS01 +Regulator_Controller:ICE3PCS02 +Regulator_Controller:ICE3PCS03 +Regulator_Controller:IR1153S +Regulator_Controller:IR1155S +Regulator_Controller:IR1161L +Regulator_Controller:IR11662S +Regulator_Controller:IR11672AS +Regulator_Controller:IR1167S +Regulator_Controller:IR11682S +Regulator_Controller:IR11688S +Regulator_Controller:IR1168S +Regulator_Controller:IR1169S +Regulator_Controller:IRS2505L +Regulator_Controller:ISL6551 +Regulator_Controller:L4981A +Regulator_Controller:L4981B +Regulator_Controller:L4984D +Regulator_Controller:L6561 +Regulator_Controller:L6562 +Regulator_Controller:L6562A +Regulator_Controller:L6562AT +Regulator_Controller:L6563 +Regulator_Controller:L6563A +Regulator_Controller:L6563H +Regulator_Controller:L6563S +Regulator_Controller:L6564 +Regulator_Controller:L6564H +Regulator_Controller:L6564T +Regulator_Controller:L6598 +Regulator_Controller:L6599 +Regulator_Controller:L6727 +Regulator_Controller:LM25119 +Regulator_Controller:LM3478MA +Regulator_Controller:LM3478MM +Regulator_Controller:LM3478QMM +Regulator_Controller:LM5023 +Regulator_Controller:LT1248 +Regulator_Controller:LT1249 +Regulator_Controller:LT1509 +Regulator_Controller:LT4295xUFD +Regulator_Controller:LTC1624CS8 +Regulator_Controller:LTC3805xMSE +Regulator_Controller:LTC3890 +Regulator_Controller:LTC3890-1 +Regulator_Controller:LTC3892 +Regulator_Controller:LTC3892-1 +Regulator_Controller:LTC3892-2 +Regulator_Controller:LTC7810 +Regulator_Controller:NCP1200D100 +Regulator_Controller:NCP1200D40 +Regulator_Controller:NCP1200D60 +Regulator_Controller:NCP1200P100 +Regulator_Controller:NCP1200P40 +Regulator_Controller:NCP1200P60 +Regulator_Controller:NCP1203D100 +Regulator_Controller:NCP1203D40 +Regulator_Controller:NCP1203D60 +Regulator_Controller:NCP1203P100 +Regulator_Controller:NCP1203P40 +Regulator_Controller:NCP1203P60 +Regulator_Controller:NCP1207A +Regulator_Controller:NCP1207B +Regulator_Controller:NCP1217AD100 +Regulator_Controller:NCP1217AD133 +Regulator_Controller:NCP1217AD65 +Regulator_Controller:NCP1217AP100 +Regulator_Controller:NCP1217AP133 +Regulator_Controller:NCP1217AP65 +Regulator_Controller:NCP1217D100 +Regulator_Controller:NCP1217D133 +Regulator_Controller:NCP1217D65 +Regulator_Controller:NCP1217P100 +Regulator_Controller:NCP1217P133 +Regulator_Controller:NCP1217P65 +Regulator_Controller:NCP1280 +Regulator_Controller:NCP1380A +Regulator_Controller:NCP1380B +Regulator_Controller:NCP1380C +Regulator_Controller:NCP1380D +Regulator_Controller:NCP1653 +Regulator_Controller:NCP1653A +Regulator_Controller:NCP4308AD +Regulator_Controller:NCP4308AMT +Regulator_Controller:NCP4308DD +Regulator_Controller:NCP4308DMN +Regulator_Controller:NCP4308DMT +Regulator_Controller:NCP4308QD +Regulator_Controller:SG3525 +Regulator_Controller:SG3527 +Regulator_Controller:TDA4862 +Regulator_Controller:TDA4863 +Regulator_Controller:TDA4863-2 +Regulator_Controller:TL494 +Regulator_Controller:TPS2375-1 +Regulator_Controller:UC3525 +Regulator_Controller:UC3527 +Regulator_Controller:UC3842_DIP8 +Regulator_Controller:UC3842_SOIC14 +Regulator_Controller:UC3842_SOIC16 +Regulator_Controller:UC3842_SOIC8 +Regulator_Controller:UC3843_DIP8 +Regulator_Controller:UC3843_SOIC14 +Regulator_Controller:UC3843_SOIC8 +Regulator_Controller:UC3844_DIP8 +Regulator_Controller:UC3844_SOIC14 +Regulator_Controller:UC3844_SOIC8 +Regulator_Controller:UC3845_DIP8 +Regulator_Controller:UC3845_SOIC14 +Regulator_Controller:UC3845_SOIC8 +Regulator_Controller:UC3854 +Regulator_Controller:UCC1895J +Regulator_Controller:UCC24610D +Regulator_Controller:UCC24610DRB +Regulator_Controller:UCC24612-1DBV +Regulator_Controller:UCC24612-2DBV +Regulator_Controller:UCC24630DBV +Regulator_Controller:UCC24636DBV +Regulator_Controller:UCC25600 +Regulator_Controller:UCC256301 +Regulator_Controller:UCC25800 +Regulator_Controller:UCC2891 +Regulator_Controller:UCC2892 +Regulator_Controller:UCC2893 +Regulator_Controller:UCC2894 +Regulator_Controller:UCC2895DW +Regulator_Controller:UCC2895N +Regulator_Controller:UCC2895PW +Regulator_Controller:UCC2897 +Regulator_Controller:UCC3800 +Regulator_Controller:UCC3801 +Regulator_Controller:UCC3802 +Regulator_Controller:UCC3803 +Regulator_Controller:UCC3804 +Regulator_Controller:UCC3805 +Regulator_Controller:UCC3808D +Regulator_Controller:UCC3808N +Regulator_Controller:UCC3813-0 +Regulator_Controller:UCC3813-1 +Regulator_Controller:UCC3813-2 +Regulator_Controller:UCC3813-3 +Regulator_Controller:UCC3813-4 +Regulator_Controller:UCC3813-5 +Regulator_Controller:UCC3895DW +Regulator_Controller:UCC3895N +Regulator_Controller:UCC3895PW +Regulator_Current:HV100K5-G +Regulator_Current:HV101K5-G +Regulator_Linear:ADP3336ARMZ +Regulator_Linear:ADP7118ACPZN +Regulator_Linear:ADP7142ARDZ +Regulator_Linear:ADP7142ARDZ-1.8 +Regulator_Linear:ADP7142ARDZ-2.5 +Regulator_Linear:ADP7142ARDZ-3.3 +Regulator_Linear:ADP7142ARDZ-5.0 +Regulator_Linear:ADP7142AUJZ +Regulator_Linear:ADP7142AUJZ-1.8 +Regulator_Linear:ADP7142AUJZ-2.5 +Regulator_Linear:ADP7142AUJZ-3.3 +Regulator_Linear:ADP7142AUJZ-5.0 +Regulator_Linear:ADP7182AUJZ +Regulator_Linear:ADP7182AUJZ-1.8 +Regulator_Linear:ADP7182AUJZ-2.5 +Regulator_Linear:ADP7182AUJZ-3.3 +Regulator_Linear:ADP7182AUJZ-5.0 +Regulator_Linear:AMS1117 +Regulator_Linear:AMS1117-1.5 +Regulator_Linear:AMS1117-1.8 +Regulator_Linear:AMS1117-2.5 +Regulator_Linear:AMS1117-2.85 +Regulator_Linear:AMS1117-3.3 +Regulator_Linear:AMS1117-5.0 +Regulator_Linear:AMS1117CD +Regulator_Linear:AMS1117CD-1.5 +Regulator_Linear:AMS1117CD-1.8 +Regulator_Linear:AMS1117CD-2.5 +Regulator_Linear:AMS1117CD-2.85 +Regulator_Linear:AMS1117CD-3.3 +Regulator_Linear:AMS1117CD-5.0 +Regulator_Linear:AMS1117CS +Regulator_Linear:AMS1117CS-1.5 +Regulator_Linear:AMS1117CS-1.8 +Regulator_Linear:AMS1117CS-2.5 +Regulator_Linear:AMS1117CS-2.85 +Regulator_Linear:AMS1117CS-3.3 +Regulator_Linear:AMS1117CS-5.0 +Regulator_Linear:AP1117-15 +Regulator_Linear:AP1117-18 +Regulator_Linear:AP1117-25 +Regulator_Linear:AP1117-33 +Regulator_Linear:AP1117-50 +Regulator_Linear:AP1117-ADJ +Regulator_Linear:AP130-15Y +Regulator_Linear:AP130-18Y +Regulator_Linear:AP130-20Y +Regulator_Linear:AP130-25Y +Regulator_Linear:AP130-28Y +Regulator_Linear:AP130-30Y +Regulator_Linear:AP130-33Y +Regulator_Linear:AP130-35Y +Regulator_Linear:AP131-15 +Regulator_Linear:AP131-18 +Regulator_Linear:AP131-20 +Regulator_Linear:AP131-25 +Regulator_Linear:AP131-28 +Regulator_Linear:AP131-29 +Regulator_Linear:AP131-30 +Regulator_Linear:AP131-33 +Regulator_Linear:AP131-35 +Regulator_Linear:AP2112K-1.2 +Regulator_Linear:AP2112K-1.8 +Regulator_Linear:AP2112K-2.5 +Regulator_Linear:AP2112K-2.6 +Regulator_Linear:AP2112K-3.3 +Regulator_Linear:AP2127K-1.0 +Regulator_Linear:AP2127K-1.2 +Regulator_Linear:AP2127K-1.5 +Regulator_Linear:AP2127K-1.8 +Regulator_Linear:AP2127K-2.5 +Regulator_Linear:AP2127K-2.8 +Regulator_Linear:AP2127K-3.0 +Regulator_Linear:AP2127K-3.3 +Regulator_Linear:AP2127K-4.2 +Regulator_Linear:AP2127K-4.75 +Regulator_Linear:AP2127K-ADJ +Regulator_Linear:AP2127N-1.0 +Regulator_Linear:AP2127N-1.2 +Regulator_Linear:AP2127N-1.5 +Regulator_Linear:AP2127N-1.8 +Regulator_Linear:AP2127N-2.5 +Regulator_Linear:AP2127N-2.8 +Regulator_Linear:AP2127N-3.0 +Regulator_Linear:AP2127N-3.3 +Regulator_Linear:AP2127N3-1.2 +Regulator_Linear:AP2127N3-1.5 +Regulator_Linear:AP2127R-3.3 +Regulator_Linear:AP2204K-1.5 +Regulator_Linear:AP2204K-1.8 +Regulator_Linear:AP2204K-2.5 +Regulator_Linear:AP2204K-2.8 +Regulator_Linear:AP2204K-3.0 +Regulator_Linear:AP2204K-3.3 +Regulator_Linear:AP2204K-5.0 +Regulator_Linear:AP2204K-ADJ +Regulator_Linear:AP2204MP-ADJ +Regulator_Linear:AP2204R-1.5 +Regulator_Linear:AP2204R-1.8 +Regulator_Linear:AP2204R-2.5 +Regulator_Linear:AP2204R-2.8 +Regulator_Linear:AP2204R-3.0 +Regulator_Linear:AP2204R-3.3 +Regulator_Linear:AP2204R-5.0 +Regulator_Linear:AP2204RA-3.3 +Regulator_Linear:AP2204RA-5.0 +Regulator_Linear:AP2204RB-3.3 +Regulator_Linear:AP2204RB-5.0 +Regulator_Linear:AP22615AWU +Regulator_Linear:AP22615BWU +Regulator_Linear:AP7361C-10E +Regulator_Linear:AP7361C-12E +Regulator_Linear:AP7361C-15E +Regulator_Linear:AP7361C-18E +Regulator_Linear:AP7361C-25E +Regulator_Linear:AP7361C-28E +Regulator_Linear:AP7361C-33E +Regulator_Linear:AP7370-12FDC +Regulator_Linear:AP7370-15FDC +Regulator_Linear:AP7370-18FDC +Regulator_Linear:AP7370-28FDC +Regulator_Linear:AP7370-30FDC +Regulator_Linear:AP7370-33FDC +Regulator_Linear:AP7370-36FDC +Regulator_Linear:AP7370-50FDC +Regulator_Linear:AP7381-28SA-7 +Regulator_Linear:AP7381-33SA-7 +Regulator_Linear:AP7381-50SA-7 +Regulator_Linear:AP7381-70SA-7 +Regulator_Linear:AP7384-28SA +Regulator_Linear:AP7384-28V +Regulator_Linear:AP7384-28Y +Regulator_Linear:AP7384-33SA +Regulator_Linear:AP7384-33V +Regulator_Linear:AP7384-33Y +Regulator_Linear:AP7384-50SA +Regulator_Linear:AP7384-50V +Regulator_Linear:AP7384-50Y +Regulator_Linear:AP7384-70SA +Regulator_Linear:AP7384-70V +Regulator_Linear:AP7384-70Y +Regulator_Linear:APE8865N-12-HF-3 +Regulator_Linear:APE8865N-15-HF-3 +Regulator_Linear:APE8865N-16-HF-3 +Regulator_Linear:APE8865N-17-HF-3 +Regulator_Linear:APE8865N-18-HF-3 +Regulator_Linear:APE8865N-19-HF-3 +Regulator_Linear:APE8865N-20-HF-3 +Regulator_Linear:APE8865N-21-HF-3 +Regulator_Linear:APE8865N-22-HF-3 +Regulator_Linear:APE8865N-23-HF-3 +Regulator_Linear:APE8865N-24-HF-3 +Regulator_Linear:APE8865N-25-HF-3 +Regulator_Linear:APE8865N-26-HF-3 +Regulator_Linear:APE8865N-27-HF-3 +Regulator_Linear:APE8865N-28-HF-3 +Regulator_Linear:APE8865N-29-HF-3 +Regulator_Linear:APE8865N-30-HF-3 +Regulator_Linear:APE8865N-31-HF-3 +Regulator_Linear:APE8865N-32-HF-3 +Regulator_Linear:APE8865N-33-HF-3 +Regulator_Linear:APE8865NL-12-HF-3 +Regulator_Linear:APE8865NL-15-HF-3 +Regulator_Linear:APE8865NL-16-HF-3 +Regulator_Linear:APE8865NL-17-HF-3 +Regulator_Linear:APE8865NL-18-HF-3 +Regulator_Linear:APE8865NL-19-HF-3 +Regulator_Linear:APE8865NL-20-HF-3 +Regulator_Linear:APE8865NL-21-HF-3 +Regulator_Linear:APE8865NL-22-HF-3 +Regulator_Linear:APE8865NL-23-HF-3 +Regulator_Linear:APE8865NL-24-HF-3 +Regulator_Linear:APE8865NL-25-HF-3 +Regulator_Linear:APE8865NL-26-HF-3 +Regulator_Linear:APE8865NL-27-HF-3 +Regulator_Linear:APE8865NL-28-HF-3 +Regulator_Linear:APE8865NL-29-HF-3 +Regulator_Linear:APE8865NL-30-HF-3 +Regulator_Linear:APE8865NL-31-HF-3 +Regulator_Linear:APE8865NL-32-HF-3 +Regulator_Linear:APE8865NL-33-HF-3 +Regulator_Linear:APE8865NR-12-HF-3 +Regulator_Linear:APE8865NR-15-HF-3 +Regulator_Linear:APE8865NR-16-HF-3 +Regulator_Linear:APE8865NR-17-HF-3 +Regulator_Linear:APE8865NR-18-HF-3 +Regulator_Linear:APE8865NR-19-HF-3 +Regulator_Linear:APE8865NR-20-HF-3 +Regulator_Linear:APE8865NR-21-HF-3 +Regulator_Linear:APE8865NR-22-HF-3 +Regulator_Linear:APE8865NR-23-HF-3 +Regulator_Linear:APE8865NR-24-HF-3 +Regulator_Linear:APE8865NR-25-HF-3 +Regulator_Linear:APE8865NR-26-HF-3 +Regulator_Linear:APE8865NR-27-HF-3 +Regulator_Linear:APE8865NR-28-HF-3 +Regulator_Linear:APE8865NR-29-HF-3 +Regulator_Linear:APE8865NR-30-HF-3 +Regulator_Linear:APE8865NR-31-HF-3 +Regulator_Linear:APE8865NR-32-HF-3 +Regulator_Linear:APE8865NR-33-HF-3 +Regulator_Linear:APE8865U4-12-HF-3 +Regulator_Linear:APE8865U4-15-HF-3 +Regulator_Linear:APE8865U4-16-HF-3 +Regulator_Linear:APE8865U4-17-HF-3 +Regulator_Linear:APE8865U4-18-HF-3 +Regulator_Linear:APE8865U4-19-HF-3 +Regulator_Linear:APE8865U4-20-HF-3 +Regulator_Linear:APE8865U4-21-HF-3 +Regulator_Linear:APE8865U4-22-HF-3 +Regulator_Linear:APE8865U4-23-HF-3 +Regulator_Linear:APE8865U4-24-HF-3 +Regulator_Linear:APE8865U4-25-HF-3 +Regulator_Linear:APE8865U4-26-HF-3 +Regulator_Linear:APE8865U4-27-HF-3 +Regulator_Linear:APE8865U4-28-HF-3 +Regulator_Linear:APE8865U4-29-HF-3 +Regulator_Linear:APE8865U4-30-HF-3 +Regulator_Linear:APE8865U4-31-HF-3 +Regulator_Linear:APE8865U4-32-HF-3 +Regulator_Linear:APE8865U4-33-HF-3 +Regulator_Linear:APE8865U5-12-HF-3 +Regulator_Linear:APE8865U5-15-HF-3 +Regulator_Linear:APE8865U5-16-HF-3 +Regulator_Linear:APE8865U5-17-HF-3 +Regulator_Linear:APE8865U5-18-HF-3 +Regulator_Linear:APE8865U5-19-HF-3 +Regulator_Linear:APE8865U5-20-HF-3 +Regulator_Linear:APE8865U5-21-HF-3 +Regulator_Linear:APE8865U5-22-HF-3 +Regulator_Linear:APE8865U5-23-HF-3 +Regulator_Linear:APE8865U5-24-HF-3 +Regulator_Linear:APE8865U5-25-HF-3 +Regulator_Linear:APE8865U5-26-HF-3 +Regulator_Linear:APE8865U5-27-HF-3 +Regulator_Linear:APE8865U5-28-HF-3 +Regulator_Linear:APE8865U5-29-HF-3 +Regulator_Linear:APE8865U5-30-HF-3 +Regulator_Linear:APE8865U5-31-HF-3 +Regulator_Linear:APE8865U5-32-HF-3 +Regulator_Linear:APE8865U5-33-HF-3 +Regulator_Linear:APE8865Y5-12-HF-3 +Regulator_Linear:APE8865Y5-15-HF-3 +Regulator_Linear:APE8865Y5-16-HF-3 +Regulator_Linear:APE8865Y5-17-HF-3 +Regulator_Linear:APE8865Y5-18-HF-3 +Regulator_Linear:APE8865Y5-19-HF-3 +Regulator_Linear:APE8865Y5-20-HF-3 +Regulator_Linear:APE8865Y5-21-HF-3 +Regulator_Linear:APE8865Y5-22-HF-3 +Regulator_Linear:APE8865Y5-23-HF-3 +Regulator_Linear:APE8865Y5-24-HF-3 +Regulator_Linear:APE8865Y5-25-HF-3 +Regulator_Linear:APE8865Y5-26-HF-3 +Regulator_Linear:APE8865Y5-27-HF-3 +Regulator_Linear:APE8865Y5-28-HF-3 +Regulator_Linear:APE8865Y5-29-HF-3 +Regulator_Linear:APE8865Y5-30-HF-3 +Regulator_Linear:APE8865Y5-31-HF-3 +Regulator_Linear:APE8865Y5-32-HF-3 +Regulator_Linear:APE8865Y5-33-HF-3 +Regulator_Linear:AZ1084-1.2 +Regulator_Linear:AZ1084-1.5 +Regulator_Linear:AZ1084-1.8 +Regulator_Linear:AZ1084-2.5 +Regulator_Linear:AZ1084-2.85 +Regulator_Linear:AZ1084-3.3 +Regulator_Linear:AZ1084-5.0 +Regulator_Linear:AZ1117-1.2 +Regulator_Linear:AZ1117-1.5 +Regulator_Linear:AZ1117-1.8 +Regulator_Linear:AZ1117-2.5 +Regulator_Linear:AZ1117-2.85 +Regulator_Linear:AZ1117-3.3 +Regulator_Linear:AZ1117-5.0 +Regulator_Linear:AZ1117-ADJ +Regulator_Linear:AZ1117D-ADJ +Regulator_Linear:AZ1117H-ADJ +Regulator_Linear:AZ1117R-ADJ +Regulator_Linear:AZ1117S-ADJ +Regulator_Linear:AZ1117T-ADJ +Regulator_Linear:BD00FC0WEFJ +Regulator_Linear:BD00FC0WFP +Regulator_Linear:BD15GA3WEFJ +Regulator_Linear:BD15GA5WEFJ +Regulator_Linear:BD18GA3WEFJ +Regulator_Linear:BD18GA5WEFJ +Regulator_Linear:BD25GA3WEFJ +Regulator_Linear:BD25GA5WEFJ +Regulator_Linear:BD30FC0WEFJ +Regulator_Linear:BD30FC0WFP +Regulator_Linear:BD30GA3WEFJ +Regulator_Linear:BD30GA5WEFJ +Regulator_Linear:BD33FC0FP +Regulator_Linear:BD33FC0WEFJ +Regulator_Linear:BD33FC0WFP +Regulator_Linear:BD33GA3WEFJ +Regulator_Linear:BD33GA5WEFJ +Regulator_Linear:BD50FC0FP +Regulator_Linear:BD50FC0WEFJ +Regulator_Linear:BD50FC0WFP +Regulator_Linear:BD50GA3WEFJ +Regulator_Linear:BD50GA5WEFJ +Regulator_Linear:BD60FC0WEFJ +Regulator_Linear:BD60FC0WFP +Regulator_Linear:BD60GA3WEFJ +Regulator_Linear:BD60GA5WEFJ +Regulator_Linear:BD70FC0WEFJ +Regulator_Linear:BD70FC0WFP +Regulator_Linear:BD70GA3WEFJ +Regulator_Linear:BD70GA5WEFJ +Regulator_Linear:BD80FC0WEFJ +Regulator_Linear:BD80FC0WFP +Regulator_Linear:BD80GA3WEFJ +Regulator_Linear:BD80GA5WEFJ +Regulator_Linear:BD90FC0WEFJ +Regulator_Linear:BD90FC0WFP +Regulator_Linear:BD90GA3WEFJ +Regulator_Linear:BD90GA5WEFJ +Regulator_Linear:BDJ0FC0WEFJ +Regulator_Linear:BDJ0FC0WFP +Regulator_Linear:BDJ0GA3WEFJ +Regulator_Linear:BDJ0GA5WEFJ +Regulator_Linear:BDJ2FC0WEFJ +Regulator_Linear:BDJ2FC0WFP +Regulator_Linear:BDJ2GA3WEFJ +Regulator_Linear:BDJ2GA5WEFJ +Regulator_Linear:BDJ5FC0WEFJ +Regulator_Linear:BDJ5FC0WFP +Regulator_Linear:HT75xx-1-SOT89 +Regulator_Linear:ICL7663 +Regulator_Linear:ICL7664 +Regulator_Linear:IFX25401TBV +Regulator_Linear:IFX25401TBV50 +Regulator_Linear:IFX25401TEV +Regulator_Linear:IFX25401TEV50 +Regulator_Linear:IFX27001TFV +Regulator_Linear:IFX27001TFV15 +Regulator_Linear:IFX27001TFV18 +Regulator_Linear:IFX27001TFV26 +Regulator_Linear:IFX27001TFV33 +Regulator_Linear:IFX27001TFV50 +Regulator_Linear:KA378R05 +Regulator_Linear:KA378R12C +Regulator_Linear:KA378R33 +Regulator_Linear:KA78M05_TO252 +Regulator_Linear:KF25BDT +Regulator_Linear:KF33BDT +Regulator_Linear:KF50BDT +Regulator_Linear:KF80BDT +Regulator_Linear:L200 +Regulator_Linear:L7805 +Regulator_Linear:L7806 +Regulator_Linear:L7808 +Regulator_Linear:L7809 +Regulator_Linear:L7812 +Regulator_Linear:L7815 +Regulator_Linear:L7818 +Regulator_Linear:L7824 +Regulator_Linear:L7885 +Regulator_Linear:L78L05_SO8 +Regulator_Linear:L78L05_SOT89 +Regulator_Linear:L78L05_TO92 +Regulator_Linear:L78L06_SO8 +Regulator_Linear:L78L06_SOT89 +Regulator_Linear:L78L06_TO92 +Regulator_Linear:L78L08_SO8 +Regulator_Linear:L78L08_SOT89 +Regulator_Linear:L78L08_TO92 +Regulator_Linear:L78L09_SO8 +Regulator_Linear:L78L09_SOT89 +Regulator_Linear:L78L09_TO92 +Regulator_Linear:L78L10_SO8 +Regulator_Linear:L78L10_SOT89 +Regulator_Linear:L78L10_TO92 +Regulator_Linear:L78L12_SO8 +Regulator_Linear:L78L12_SOT89 +Regulator_Linear:L78L12_TO92 +Regulator_Linear:L78L15_SO8 +Regulator_Linear:L78L15_SOT89 +Regulator_Linear:L78L15_TO92 +Regulator_Linear:L78L18_SO8 +Regulator_Linear:L78L18_SOT89 +Regulator_Linear:L78L18_TO92 +Regulator_Linear:L78L24_SO8 +Regulator_Linear:L78L24_SOT89 +Regulator_Linear:L78L24_TO92 +Regulator_Linear:L78L33_SO8 +Regulator_Linear:L78L33_SOT89 +Regulator_Linear:L78L33_TO92 +Regulator_Linear:L7905 +Regulator_Linear:L7908 +Regulator_Linear:L7912 +Regulator_Linear:L7915 +Regulator_Linear:L79L05_SO8 +Regulator_Linear:L79L05_SOT89 +Regulator_Linear:L79L05_TO92 +Regulator_Linear:L79L08_SO8 +Regulator_Linear:L79L08_SOT89 +Regulator_Linear:L79L08_TO92 +Regulator_Linear:L79L12_SO8 +Regulator_Linear:L79L12_SOT89 +Regulator_Linear:L79L12_TO92 +Regulator_Linear:L79L15_SO8 +Regulator_Linear:L79L15_SOT89 +Regulator_Linear:L79L15_TO92 +Regulator_Linear:LD0186D2T50TR +Regulator_Linear:LD1086D2M33TR +Regulator_Linear:LD1086D2MTR +Regulator_Linear:LD1086D2T12TR +Regulator_Linear:LD1086D2T18TR +Regulator_Linear:LD1086D2T33TR +Regulator_Linear:LD1086D2TTR +Regulator_Linear:LD1086DT18TR +Regulator_Linear:LD1086DT25TR +Regulator_Linear:LD1086DT33TR +Regulator_Linear:LD1086DT50TR +Regulator_Linear:LD1086DTTR +Regulator_Linear:LD1086PUR +Regulator_Linear:LD1086V-DG +Regulator_Linear:LD1086V18-DG +Regulator_Linear:LD1086V33-DG +Regulator_Linear:LD1117S12TR_SOT223 +Regulator_Linear:LD1117S18TR_SOT223 +Regulator_Linear:LD1117S25TR_SOT223 +Regulator_Linear:LD1117S33TR_SOT223 +Regulator_Linear:LD1117S50TR_SOT223 +Regulator_Linear:LD39015M08R +Regulator_Linear:LD39015M10R +Regulator_Linear:LD39015M125R +Regulator_Linear:LD39015M12R +Regulator_Linear:LD39015M15R +Regulator_Linear:LD39015M18R +Regulator_Linear:LD39015M25R +Regulator_Linear:LD39015M33R +Regulator_Linear:LD39150DT18 +Regulator_Linear:LD39150DT25 +Regulator_Linear:LD39150DT33 +Regulator_Linear:LD3985G122R_TSOT23 +Regulator_Linear:LD3985G18R_TSOT23 +Regulator_Linear:LD3985G25R_TSOT23 +Regulator_Linear:LD3985G26R_TSOT23 +Regulator_Linear:LD3985G27R_TSOT23 +Regulator_Linear:LD3985G28R_TSOT23 +Regulator_Linear:LD3985G30R_TSOT23 +Regulator_Linear:LD3985G33R_TSOT23 +Regulator_Linear:LD3985G47R_TSOT23 +Regulator_Linear:LD3985M122R_SOT23 +Regulator_Linear:LD3985M18R_SOT23 +Regulator_Linear:LD3985M25R_SOT23 +Regulator_Linear:LD3985M26R_SOT23 +Regulator_Linear:LD3985M27R_SOT23 +Regulator_Linear:LD3985M28R_SOT23 +Regulator_Linear:LD3985M29R_SOT23 +Regulator_Linear:LD3985M30R_SOT23 +Regulator_Linear:LD3985M33R_SOT23 +Regulator_Linear:LD3985M47R_SOT23 +Regulator_Linear:LDK130-08_SOT23_SOT353 +Regulator_Linear:LDK130-10_SOT23_SOT353 +Regulator_Linear:LDK130-12_SOT23_SOT353 +Regulator_Linear:LDK130-15_SOT23_SOT353 +Regulator_Linear:LDK130-18_SOT23_SOT353 +Regulator_Linear:LDK130-25_SOT23_SOT353 +Regulator_Linear:LDK130-29_SOT23_SOT353 +Regulator_Linear:LDK130-30_SOT23_SOT353 +Regulator_Linear:LDK130-32_SOT23_SOT353 +Regulator_Linear:LDK130-33_SOT23_SOT353 +Regulator_Linear:LDK130-ADJ_DFN6 +Regulator_Linear:LDK130-ADJ_SOT23_SOT353 +Regulator_Linear:LDK130PU08R_DFN6 +Regulator_Linear:LDK130PU10R_DFN6 +Regulator_Linear:LDK130PU12R_DFN6 +Regulator_Linear:LDK130PU15R_DFN6 +Regulator_Linear:LDK130PU18R_DFN6 +Regulator_Linear:LDK130PU25R_DFN6 +Regulator_Linear:LDK130PU29R_DFN6 +Regulator_Linear:LDK130PU30R_DFN6 +Regulator_Linear:LDK130PU32R_DFN6 +Regulator_Linear:LDK130PU33R_DFN6 +Regulator_Linear:LF120_TO220 +Regulator_Linear:LF120_TO252 +Regulator_Linear:LF15_TO220 +Regulator_Linear:LF15_TO252 +Regulator_Linear:LF18_TO220 +Regulator_Linear:LF18_TO252 +Regulator_Linear:LF25_TO220 +Regulator_Linear:LF25_TO252 +Regulator_Linear:LF33_TO220 +Regulator_Linear:LF33_TO252 +Regulator_Linear:LF47_TO220 +Regulator_Linear:LF47_TO252 +Regulator_Linear:LF50_TO220 +Regulator_Linear:LF50_TO252 +Regulator_Linear:LF60_TO220 +Regulator_Linear:LF60_TO252 +Regulator_Linear:LF80_TO220 +Regulator_Linear:LF80_TO252 +Regulator_Linear:LF85_TO220 +Regulator_Linear:LF85_TO252 +Regulator_Linear:LF90_TO220 +Regulator_Linear:LF90_TO252 +Regulator_Linear:LK112M15TR +Regulator_Linear:LK112M18TR +Regulator_Linear:LK112M25TR +Regulator_Linear:LK112M33TR +Regulator_Linear:LK112M50TR +Regulator_Linear:LK112M55TR +Regulator_Linear:LK112M60TR +Regulator_Linear:LK112M80TR +Regulator_Linear:LM1084-3.3 +Regulator_Linear:LM1084-5.0 +Regulator_Linear:LM1084-ADJ +Regulator_Linear:LM1085-12 +Regulator_Linear:LM1085-3.3 +Regulator_Linear:LM1085-5.0 +Regulator_Linear:LM1085-ADJ +Regulator_Linear:LM1117DT-1.8 +Regulator_Linear:LM1117DT-2.5 +Regulator_Linear:LM1117DT-3.3 +Regulator_Linear:LM1117DT-5.0 +Regulator_Linear:LM1117DT-ADJ +Regulator_Linear:LM1117LD-1.8 +Regulator_Linear:LM1117LD-2.5 +Regulator_Linear:LM1117LD-3.3 +Regulator_Linear:LM1117LD-5.0 +Regulator_Linear:LM1117LD-ADJ +Regulator_Linear:LM1117MP-1.8 +Regulator_Linear:LM1117MP-2.5 +Regulator_Linear:LM1117MP-3.3 +Regulator_Linear:LM1117MP-5.0 +Regulator_Linear:LM1117MP-ADJ +Regulator_Linear:LM1117S-3.3 +Regulator_Linear:LM1117S-5.0 +Regulator_Linear:LM1117S-ADJ +Regulator_Linear:LM1117T-2.5 +Regulator_Linear:LM1117T-3.3 +Regulator_Linear:LM1117T-5.0 +Regulator_Linear:LM1117T-ADJ +Regulator_Linear:LM117_TO3 +Regulator_Linear:LM117_TO39 +Regulator_Linear:LM137_TO3 +Regulator_Linear:LM150_TO3 +Regulator_Linear:LM2931-3.3_TO220 +Regulator_Linear:LM2931-5.0_SO8 +Regulator_Linear:LM2931-5.0_TO220 +Regulator_Linear:LM2931-ADJ_TO92 +Regulator_Linear:LM2931AZ-5.0_TO92 +Regulator_Linear:LM2936-3.0 +Regulator_Linear:LM2936-3.0_TO92 +Regulator_Linear:LM2936-3.3 +Regulator_Linear:LM2936-3.3_TO92 +Regulator_Linear:LM2936-5.0 +Regulator_Linear:LM2936-5.0_TO92 +Regulator_Linear:LM2937xMP +Regulator_Linear:LM2937xS +Regulator_Linear:LM2937xT +Regulator_Linear:LM2990SX-12 +Regulator_Linear:LM2990SX-15 +Regulator_Linear:LM2990SX-5.0 +Regulator_Linear:LM317L_SO8 +Regulator_Linear:LM317L_SOT-89 +Regulator_Linear:LM317L_TO92 +Regulator_Linear:LM317_SOT-223 +Regulator_Linear:LM317_TO-220 +Regulator_Linear:LM317_TO-252 +Regulator_Linear:LM317_TO-263 +Regulator_Linear:LM317_TO3 +Regulator_Linear:LM317_TO39 +Regulator_Linear:LM337L_SO8 +Regulator_Linear:LM337L_TO92 +Regulator_Linear:LM337_SOT223 +Regulator_Linear:LM337_TO220 +Regulator_Linear:LM337_TO252 +Regulator_Linear:LM337_TO263 +Regulator_Linear:LM337_TO3 +Regulator_Linear:LM337_TO39 +Regulator_Linear:LM341T-05_TO220 +Regulator_Linear:LM341T-12_TO220 +Regulator_Linear:LM341T-15_TO220 +Regulator_Linear:LM3480-12 +Regulator_Linear:LM3480-15 +Regulator_Linear:LM3480-3.3 +Regulator_Linear:LM3480-5.0 +Regulator_Linear:LM350_TO220 +Regulator_Linear:LM350_TO3 +Regulator_Linear:LM723_DIP14 +Regulator_Linear:LM723_TO100 +Regulator_Linear:LM7805_TO220 +Regulator_Linear:LM7806_TO220 +Regulator_Linear:LM7808_TO220 +Regulator_Linear:LM7809_TO220 +Regulator_Linear:LM7810_TO220 +Regulator_Linear:LM7812_TO220 +Regulator_Linear:LM7815_TO220 +Regulator_Linear:LM7818_TO220 +Regulator_Linear:LM7824_TO220 +Regulator_Linear:LM78L05_SO8 +Regulator_Linear:LM78L05_TO92 +Regulator_Linear:LM78L12_SO8 +Regulator_Linear:LM78L12_TO92 +Regulator_Linear:LM78M05_TO220 +Regulator_Linear:LM78M05_TO252 +Regulator_Linear:LM7905_TO220 +Regulator_Linear:LM7906_TO220 +Regulator_Linear:LM7908_TO220 +Regulator_Linear:LM7909_TO220 +Regulator_Linear:LM7910_TO220 +Regulator_Linear:LM7912_TO220 +Regulator_Linear:LM7915_TO220 +Regulator_Linear:LM7918_TO220 +Regulator_Linear:LM7924_TO220 +Regulator_Linear:LM79L05_TO92 +Regulator_Linear:LM79L12_TO92 +Regulator_Linear:LM79L15_TO92 +Regulator_Linear:LM79M05_TO220 +Regulator_Linear:LM79M12_TO220 +Regulator_Linear:LM79M15_TO220 +Regulator_Linear:LP2950-3.0_TO252 +Regulator_Linear:LP2950-3.0_TO92 +Regulator_Linear:LP2950-3.3_TO252 +Regulator_Linear:LP2950-3.3_TO92 +Regulator_Linear:LP2950-5.0_TO252 +Regulator_Linear:LP2950-5.0_TO92 +Regulator_Linear:LP2951-3.0_DIP +Regulator_Linear:LP2951-3.0_SOIC +Regulator_Linear:LP2951-3.0_VSSOP +Regulator_Linear:LP2951-3.0_WSON +Regulator_Linear:LP2951-3.3_DIP +Regulator_Linear:LP2951-3.3_SOIC +Regulator_Linear:LP2951-3.3_VSSOP +Regulator_Linear:LP2951-3.3_WSON +Regulator_Linear:LP2951-5.0_DIP +Regulator_Linear:LP2951-5.0_SOIC +Regulator_Linear:LP2951-5.0_VSSOP +Regulator_Linear:LP2951-5.0_WSON +Regulator_Linear:LP2980-ADJ +Regulator_Linear:LP2985-1.8 +Regulator_Linear:LP2985-10.0 +Regulator_Linear:LP2985-2.5 +Regulator_Linear:LP2985-2.8 +Regulator_Linear:LP2985-2.9 +Regulator_Linear:LP2985-3.0 +Regulator_Linear:LP2985-3.1 +Regulator_Linear:LP2985-3.3 +Regulator_Linear:LP2985-5.0 +Regulator_Linear:LP2987-3.0_SOIC8_VSSOP8 +Regulator_Linear:LP2987-3.0_WSON8 +Regulator_Linear:LP2987-3.3_SOIC8_VSSOP8 +Regulator_Linear:LP2987-3.3_WSON8 +Regulator_Linear:LP2987-5.0_SOIC8_VSSOP8 +Regulator_Linear:LP2987-5.0_WSON8 +Regulator_Linear:LP2988-2.8_SOIC8_VSSOP8 +Regulator_Linear:LP2988-2.8_WSON8 +Regulator_Linear:LP2988-3.0_SOIC8_VSSOP8 +Regulator_Linear:LP2988-3.0_WSON8 +Regulator_Linear:LP2988-3.3_SOIC8_VSSOP8 +Regulator_Linear:LP2988-3.3_WSON8 +Regulator_Linear:LP2988-3.8_SOIC8_VSSOP8 +Regulator_Linear:LP2988-3.8_WSON8 +Regulator_Linear:LP2988-5.0_SOIC8_VSSOP8 +Regulator_Linear:LP2988-5.0_WSON8 +Regulator_Linear:LP38512MR-ADJ +Regulator_Linear:LP38512TJ-1.8 +Regulator_Linear:LP38512TJ-ADJ +Regulator_Linear:LP38512TS-1.8 +Regulator_Linear:LP38691SD-1.8 +Regulator_Linear:LP38691SD-2.5 +Regulator_Linear:LP38691SD-3.3 +Regulator_Linear:LP38691SD-5.0 +Regulator_Linear:LP38693DT-1.8 +Regulator_Linear:LP38693DT-2.5 +Regulator_Linear:LP38693DT-3.3 +Regulator_Linear:LP38693DT-5.0 +Regulator_Linear:LP38693MP-1.8 +Regulator_Linear:LP38693MP-2.5 +Regulator_Linear:LP38693MP-3.3 +Regulator_Linear:LP38693MP-5.0 +Regulator_Linear:LP38693SD-1.8 +Regulator_Linear:LP38693SD-2.5 +Regulator_Linear:LP38693SD-3.3 +Regulator_Linear:LP38693SD-5.0 +Regulator_Linear:LP3963-1.8 +Regulator_Linear:LP3963-2.5 +Regulator_Linear:LP3963-3.3 +Regulator_Linear:LP3966-1.8 +Regulator_Linear:LP3966-2.5 +Regulator_Linear:LP3966-3.3 +Regulator_Linear:LP3966-ADJ +Regulator_Linear:LP3982ILD-1.8 +Regulator_Linear:LP3982ILD-2.5 +Regulator_Linear:LP3982ILD-3.0 +Regulator_Linear:LP3982ILD-3.3 +Regulator_Linear:LP3982ILD-ADJ +Regulator_Linear:LP3982IMM-1.8 +Regulator_Linear:LP3982IMM-2.5 +Regulator_Linear:LP3982IMM-3.0 +Regulator_Linear:LP3982IMM-3.3 +Regulator_Linear:LP3982IMM-ADJ +Regulator_Linear:LP3982IMMX-2.82 +Regulator_Linear:LP3987H-33B5 +Regulator_Linear:LP3992-18B5 +Regulator_Linear:LP5907MFX-1.2 +Regulator_Linear:LP5907MFX-1.5 +Regulator_Linear:LP5907MFX-1.8 +Regulator_Linear:LP5907MFX-2.5 +Regulator_Linear:LP5907MFX-2.8 +Regulator_Linear:LP5907MFX-2.85 +Regulator_Linear:LP5907MFX-2.9 +Regulator_Linear:LP5907MFX-3.0 +Regulator_Linear:LP5907MFX-3.1 +Regulator_Linear:LP5907MFX-3.2 +Regulator_Linear:LP5907MFX-3.3 +Regulator_Linear:LP5907MFX-4.5 +Regulator_Linear:LP5912-0.9DRV +Regulator_Linear:LP5912-1.0DRV +Regulator_Linear:LP5912-1.1DRV +Regulator_Linear:LP5912-1.2DRV +Regulator_Linear:LP5912-1.5DRV +Regulator_Linear:LP5912-1.8DRV +Regulator_Linear:LP5912-2.5DRV +Regulator_Linear:LP5912-2.8DRV +Regulator_Linear:LP5912-3.0DRV +Regulator_Linear:LP5912-3.3DRV +Regulator_Linear:LP5912-5.0DRV +Regulator_Linear:LR8K4-G +Regulator_Linear:LR8N3-G +Regulator_Linear:LR8N8-G +Regulator_Linear:LT1033C +Regulator_Linear:LT1083-12 +Regulator_Linear:LT1083-3.3 +Regulator_Linear:LT1083-3.6 +Regulator_Linear:LT1083-5.0 +Regulator_Linear:LT1083-ADJ +Regulator_Linear:LT1084-12 +Regulator_Linear:LT1084-3.3 +Regulator_Linear:LT1084-3.6 +Regulator_Linear:LT1084-5.0 +Regulator_Linear:LT1084-ADJ +Regulator_Linear:LT1085-12 +Regulator_Linear:LT1085-3.3 +Regulator_Linear:LT1085-3.6 +Regulator_Linear:LT1085-5.0 +Regulator_Linear:LT1085-ADJ +Regulator_Linear:LT1086-12 +Regulator_Linear:LT1086-2.85 +Regulator_Linear:LT1086-3.3 +Regulator_Linear:LT1086-3.6 +Regulator_Linear:LT1086-5.0 +Regulator_Linear:LT1086-ADJ +Regulator_Linear:LT1117-2.85 +Regulator_Linear:LT1117-3.3 +Regulator_Linear:LT1117-5.0 +Regulator_Linear:LT1117-ADJ +Regulator_Linear:LT1129-3.3_SO8 +Regulator_Linear:LT1129-3.3_SOT223 +Regulator_Linear:LT1129-3.3_TO220_TO263 +Regulator_Linear:LT1129-5.0_SO8 +Regulator_Linear:LT1129-5.0_SOT223 +Regulator_Linear:LT1129-5.0_TO220_TO263 +Regulator_Linear:LT1129-ADJ_SO8 +Regulator_Linear:LT1129-ADJ_TO220_TO263 +Regulator_Linear:LT1175-5_SO8_DIP8 +Regulator_Linear:LT1175-5_SOT223 +Regulator_Linear:LT1175-5_TO263_TO220 +Regulator_Linear:LT1175-ADJ_SO8_DIP8 +Regulator_Linear:LT1175-ADJ_SOT223 +Regulator_Linear:LT1175-ADJ_TO263_TO220 +Regulator_Linear:LT1584-3.3 +Regulator_Linear:LT1584-3.38 +Regulator_Linear:LT1584-3.45 +Regulator_Linear:LT1584-3.6 +Regulator_Linear:LT1584-ADJ +Regulator_Linear:LT1585-3.3 +Regulator_Linear:LT1585-3.38 +Regulator_Linear:LT1585-3.45 +Regulator_Linear:LT1585-3.6 +Regulator_Linear:LT1585-ADJ +Regulator_Linear:LT1587-3.3 +Regulator_Linear:LT1587-3.45 +Regulator_Linear:LT1587-3.6 +Regulator_Linear:LT1587-ADJ +Regulator_Linear:LT1761-1.2 +Regulator_Linear:LT1761-1.5 +Regulator_Linear:LT1761-1.8 +Regulator_Linear:LT1761-2 +Regulator_Linear:LT1761-2.5 +Regulator_Linear:LT1761-2.8 +Regulator_Linear:LT1761-3 +Regulator_Linear:LT1761-3.3 +Regulator_Linear:LT1761-5 +Regulator_Linear:LT1761-BYP +Regulator_Linear:LT1761-SD +Regulator_Linear:LT1762 +Regulator_Linear:LT1762-2.5 +Regulator_Linear:LT1762-3 +Regulator_Linear:LT1762-3.3 +Regulator_Linear:LT1762-5 +Regulator_Linear:LT1962 +Regulator_Linear:LT1962-1.5 +Regulator_Linear:LT1962-1.8 +Regulator_Linear:LT1962-2.5 +Regulator_Linear:LT1962-3 +Regulator_Linear:LT1962-3.3 +Regulator_Linear:LT1962-5 +Regulator_Linear:LT1963AEQ +Regulator_Linear:LT1963AxQ-1.5 +Regulator_Linear:LT1963AxQ-1.8 +Regulator_Linear:LT1963AxQ-2.5 +Regulator_Linear:LT1963AxQ-3.3 +Regulator_Linear:LT1963AxST-1.5 +Regulator_Linear:LT1963AxST-1.8 +Regulator_Linear:LT1963AxST-2.5 +Regulator_Linear:LT1963AxST-3.3 +Regulator_Linear:LT1963EQ +Regulator_Linear:LT1964-5 +Regulator_Linear:LT1964-BYP +Regulator_Linear:LT1964-SD +Regulator_Linear:LT3010 +Regulator_Linear:LT3010-5 +Regulator_Linear:LT3011xDD +Regulator_Linear:LT3011xMSE +Regulator_Linear:LT3014xDD +Regulator_Linear:LT3014xS5 +Regulator_Linear:LT3015Q +Regulator_Linear:LT3015xQ-12 +Regulator_Linear:LT3015xQ-15 +Regulator_Linear:LT3015xQ-2.5 +Regulator_Linear:LT3015xQ-3 +Regulator_Linear:LT3015xQ-3.3 +Regulator_Linear:LT3015xQ-5 +Regulator_Linear:LT3032 +Regulator_Linear:LT3032-12 +Regulator_Linear:LT3032-15 +Regulator_Linear:LT3032-3.3 +Regulator_Linear:LT3032-5 +Regulator_Linear:LT3033xUDC +Regulator_Linear:LT3042xMSE +Regulator_Linear:LT3045xDD +Regulator_Linear:LT3045xMSE +Regulator_Linear:LT3080xDD +Regulator_Linear:LT3080xMS8E +Regulator_Linear:LT3080xQ +Regulator_Linear:LT3080xST +Regulator_Linear:LT3080xT +Regulator_Linear:LT3091xT7 +Regulator_Linear:LT3093xMSE +Regulator_Linear:LT3094xDD +Regulator_Linear:LT3094xMSE +Regulator_Linear:LTC3026-1 +Regulator_Linear:MAX1615xUK +Regulator_Linear:MAX1616xUK +Regulator_Linear:MAX1658ESA +Regulator_Linear:MAX1659ESA +Regulator_Linear:MAX16910 +Regulator_Linear:MAX38908xTD +Regulator_Linear:MAX38909xTD +Regulator_Linear:MAX38911ATA+ +Regulator_Linear:MAX38912ATA+ +Regulator_Linear:MAX5092AATE +Regulator_Linear:MAX5092BATE +Regulator_Linear:MAX5093AATE +Regulator_Linear:MAX5093BATE +Regulator_Linear:MAX603 +Regulator_Linear:MAX604 +Regulator_Linear:MAX663 +Regulator_Linear:MAX664 +Regulator_Linear:MAX666 +Regulator_Linear:MAX882 +Regulator_Linear:MAX883 +Regulator_Linear:MAX884 +Regulator_Linear:MC78L05_SO8 +Regulator_Linear:MC78L05_SOT89 +Regulator_Linear:MC78L05_TO92 +Regulator_Linear:MC78L06_SO8 +Regulator_Linear:MC78L06_TO92 +Regulator_Linear:MC78L08_SO8 +Regulator_Linear:MC78L08_SOT89 +Regulator_Linear:MC78L08_TO92 +Regulator_Linear:MC78L12_SO8 +Regulator_Linear:MC78L12_SOT89 +Regulator_Linear:MC78L12_TO92 +Regulator_Linear:MC78L15_SO8 +Regulator_Linear:MC78L15_TO92 +Regulator_Linear:MC78L18_SO8 +Regulator_Linear:MC78L18_TO92 +Regulator_Linear:MC78L24_SO8 +Regulator_Linear:MC78L24_TO92 +Regulator_Linear:MC78LC18NTR +Regulator_Linear:MC78LC25NTR +Regulator_Linear:MC78LC30NTR +Regulator_Linear:MC78LC33NTR +Regulator_Linear:MC78LC50NTR +Regulator_Linear:MC78M05_TO252 +Regulator_Linear:MC7905 +Regulator_Linear:MC7905.2 +Regulator_Linear:MC7906 +Regulator_Linear:MC7908 +Regulator_Linear:MC7912 +Regulator_Linear:MC7915 +Regulator_Linear:MC7918 +Regulator_Linear:MC7924 +Regulator_Linear:MC79L05_SO8 +Regulator_Linear:MC79L12_SO8 +Regulator_Linear:MC79L15_SO8 +Regulator_Linear:MC79M05_TO220 +Regulator_Linear:MC79M05_TO252 +Regulator_Linear:MC79M08_TO220 +Regulator_Linear:MC79M08_TO252 +Regulator_Linear:MC79M12_TO220 +Regulator_Linear:MC79M12_TO252 +Regulator_Linear:MC79M15_TO220 +Regulator_Linear:MC79M15_TO252 +Regulator_Linear:MCP1700x-120xxMB +Regulator_Linear:MCP1700x-120xxTO +Regulator_Linear:MCP1700x-120xxTT +Regulator_Linear:MCP1700x-180xxMB +Regulator_Linear:MCP1700x-180xxTO +Regulator_Linear:MCP1700x-180xxTT +Regulator_Linear:MCP1700x-250xxMB +Regulator_Linear:MCP1700x-250xxTO +Regulator_Linear:MCP1700x-250xxTT +Regulator_Linear:MCP1700x-280xxMB +Regulator_Linear:MCP1700x-280xxTO +Regulator_Linear:MCP1700x-280xxTT +Regulator_Linear:MCP1700x-300xxMB +Regulator_Linear:MCP1700x-300xxTO +Regulator_Linear:MCP1700x-300xxTT +Regulator_Linear:MCP1700x-330xxMB +Regulator_Linear:MCP1700x-330xxTO +Regulator_Linear:MCP1700x-330xxTT +Regulator_Linear:MCP1700x-500xxMB +Regulator_Linear:MCP1700x-500xxTO +Regulator_Linear:MCP1700x-500xxTT +Regulator_Linear:MCP1703Ax-120xxDB +Regulator_Linear:MCP1703Ax-120xxMB +Regulator_Linear:MCP1703Ax-120xxTT +Regulator_Linear:MCP1703Ax-150xxDB +Regulator_Linear:MCP1703Ax-150xxMB +Regulator_Linear:MCP1703Ax-150xxTT +Regulator_Linear:MCP1703Ax-180xxDB +Regulator_Linear:MCP1703Ax-180xxMB +Regulator_Linear:MCP1703Ax-180xxTT +Regulator_Linear:MCP1703Ax-250xxDB +Regulator_Linear:MCP1703Ax-250xxMB +Regulator_Linear:MCP1703Ax-250xxTT +Regulator_Linear:MCP1703Ax-280xxDB +Regulator_Linear:MCP1703Ax-280xxMB +Regulator_Linear:MCP1703Ax-280xxTT +Regulator_Linear:MCP1703Ax-300xxDB +Regulator_Linear:MCP1703Ax-300xxMB +Regulator_Linear:MCP1703Ax-300xxTT +Regulator_Linear:MCP1703Ax-330xxDB +Regulator_Linear:MCP1703Ax-330xxMB +Regulator_Linear:MCP1703Ax-330xxTT +Regulator_Linear:MCP1703Ax-400xxDB +Regulator_Linear:MCP1703Ax-400xxMB +Regulator_Linear:MCP1703Ax-400xxTT +Regulator_Linear:MCP1703Ax-500xxDB +Regulator_Linear:MCP1703Ax-500xxMB +Regulator_Linear:MCP1703Ax-500xxTT +Regulator_Linear:MCP1727-0802xMF +Regulator_Linear:MCP1727-0802xSN +Regulator_Linear:MCP1727-1202xMF +Regulator_Linear:MCP1727-1202xSN +Regulator_Linear:MCP1727-1802xMF +Regulator_Linear:MCP1727-1802xSN +Regulator_Linear:MCP1727-2502xMF +Regulator_Linear:MCP1727-2502xSN +Regulator_Linear:MCP1727-3002xMF +Regulator_Linear:MCP1727-3002xSN +Regulator_Linear:MCP1727-3302xMF +Regulator_Linear:MCP1727-3302xSN +Regulator_Linear:MCP1727-5002xMF +Regulator_Linear:MCP1727-5002xSN +Regulator_Linear:MCP1727-ADJxMF +Regulator_Linear:MCP1727-ADJxSN +Regulator_Linear:MCP1754S-1802xCB +Regulator_Linear:MCP1754S-1802xMB +Regulator_Linear:MCP1754S-3302xCB +Regulator_Linear:MCP1754S-3302xMB +Regulator_Linear:MCP1754S-5002xCB +Regulator_Linear:MCP1754S-5002xMB +Regulator_Linear:MCP1792x-3302xCB +Regulator_Linear:MCP1792x-3302xDB +Regulator_Linear:MCP1792x-4102xCB +Regulator_Linear:MCP1792x-4102xDB +Regulator_Linear:MCP1792x-5002xCB +Regulator_Linear:MCP1792x-5002xDB +Regulator_Linear:MCP1799x-330xxTT +Regulator_Linear:MCP1799x-500xxTT +Regulator_Linear:MCP1802x-xx02xOT +Regulator_Linear:MCP1804x-1802xDB +Regulator_Linear:MCP1804x-1802xMB +Regulator_Linear:MCP1804x-1802xMT +Regulator_Linear:MCP1804x-1802xOT +Regulator_Linear:MCP1804x-2502xDB +Regulator_Linear:MCP1804x-2502xMB +Regulator_Linear:MCP1804x-2502xMT +Regulator_Linear:MCP1804x-2502xOT +Regulator_Linear:MCP1804x-3002xDB +Regulator_Linear:MCP1804x-3002xMB +Regulator_Linear:MCP1804x-3002xMT +Regulator_Linear:MCP1804x-3002xOT +Regulator_Linear:MCP1804x-3302xDB +Regulator_Linear:MCP1804x-3302xMB +Regulator_Linear:MCP1804x-3302xMT +Regulator_Linear:MCP1804x-3302xOT +Regulator_Linear:MCP1804x-5002xDB +Regulator_Linear:MCP1804x-5002xMB +Regulator_Linear:MCP1804x-5002xMT +Regulator_Linear:MCP1804x-5002xOT +Regulator_Linear:MCP1804x-A002xDB +Regulator_Linear:MCP1804x-A002xMB +Regulator_Linear:MCP1804x-A002xMT +Regulator_Linear:MCP1804x-A002xOT +Regulator_Linear:MCP1804x-C002xDB +Regulator_Linear:MCP1804x-C002xMB +Regulator_Linear:MCP1804x-C002xMT +Regulator_Linear:MCP1804x-C002xOT +Regulator_Linear:MCP1825S +Regulator_Linear:MCP1826S +Regulator_Linear:ME6211C10M5 +Regulator_Linear:ME6211C12M5 +Regulator_Linear:ME6211C15M5 +Regulator_Linear:ME6211C18M5 +Regulator_Linear:ME6211C21M5 +Regulator_Linear:ME6211C25M5 +Regulator_Linear:ME6211C27M5 +Regulator_Linear:ME6211C28M5 +Regulator_Linear:ME6211C29M5 +Regulator_Linear:ME6211C30M5 +Regulator_Linear:ME6211C33M5 +Regulator_Linear:ME6211C50M5 +Regulator_Linear:MIC29152WT +Regulator_Linear:MIC29152WU +Regulator_Linear:MIC29153WT +Regulator_Linear:MIC29153WU +Regulator_Linear:MIC29302AWD +Regulator_Linear:MIC29302AWU +Regulator_Linear:MIC29302WT +Regulator_Linear:MIC29302WU +Regulator_Linear:MIC29303WT +Regulator_Linear:MIC29303WU +Regulator_Linear:MIC29502WT +Regulator_Linear:MIC29502WU +Regulator_Linear:MIC29503WT +Regulator_Linear:MIC29503WU +Regulator_Linear:MIC29752WT +Regulator_Linear:MIC5205-2.5YM5 +Regulator_Linear:MIC5205-2.7YM5 +Regulator_Linear:MIC5205-2.85YM5 +Regulator_Linear:MIC5205-2.8YM5 +Regulator_Linear:MIC5205-2.9YM5 +Regulator_Linear:MIC5205-3.0YM5 +Regulator_Linear:MIC5205-3.1YM5 +Regulator_Linear:MIC5205-3.2YM5 +Regulator_Linear:MIC5205-3.3YM5 +Regulator_Linear:MIC5205-3.6YM5 +Regulator_Linear:MIC5205-3.8YM5 +Regulator_Linear:MIC5205-4.0YM5 +Regulator_Linear:MIC5205-5.0YM5 +Regulator_Linear:MIC5205YM5 +Regulator_Linear:MIC5219-2.5YM5 +Regulator_Linear:MIC5219-2.5YMM +Regulator_Linear:MIC5219-2.6YM5 +Regulator_Linear:MIC5219-2.7YM5 +Regulator_Linear:MIC5219-2.85YM5 +Regulator_Linear:MIC5219-2.85YMM +Regulator_Linear:MIC5219-2.8YM5 +Regulator_Linear:MIC5219-2.9YM5 +Regulator_Linear:MIC5219-3.0YM5 +Regulator_Linear:MIC5219-3.0YMM +Regulator_Linear:MIC5219-3.1YM5 +Regulator_Linear:MIC5219-3.3YM5 +Regulator_Linear:MIC5219-3.3YMM +Regulator_Linear:MIC5219-3.6YM5 +Regulator_Linear:MIC5219-3.6YMM +Regulator_Linear:MIC5219-5.0YM5 +Regulator_Linear:MIC5219-5.0YMM +Regulator_Linear:MIC5219YM5 +Regulator_Linear:MIC5219YMM +Regulator_Linear:MIC5317-1.0xM5 +Regulator_Linear:MIC5317-1.2xM5 +Regulator_Linear:MIC5317-1.5xM5 +Regulator_Linear:MIC5317-1.8xM5 +Regulator_Linear:MIC5317-2.5xM5 +Regulator_Linear:MIC5317-2.8xM5 +Regulator_Linear:MIC5317-3.0xM5 +Regulator_Linear:MIC5317-3.3xM5 +Regulator_Linear:MIC5350 +Regulator_Linear:MIC5353-1.8YMT +Regulator_Linear:MIC5353-2.5YMT +Regulator_Linear:MIC5353-2.6YMT +Regulator_Linear:MIC5353-2.8YMT +Regulator_Linear:MIC5353-3.0YMT +Regulator_Linear:MIC5353-3.3YMT +Regulator_Linear:MIC5353YMT +Regulator_Linear:MIC5355-G4YMME +Regulator_Linear:MIC5355-JGYMME +Regulator_Linear:MIC5355-S4YMME +Regulator_Linear:MIC5355-SCYMME +Regulator_Linear:MIC5355-SGYMME +Regulator_Linear:MIC5356-G4YMME +Regulator_Linear:MIC5356-JGYMME +Regulator_Linear:MIC5356-MGYML +Regulator_Linear:MIC5356-MMYML +Regulator_Linear:MIC5356-S4YMME +Regulator_Linear:MIC5356-SCYMME +Regulator_Linear:MIC5356-SGYMME +Regulator_Linear:MIC5365-3.3YC5 +Regulator_Linear:MIC5365-3.3YD5 +Regulator_Linear:MIC5366-3.3YC5 +Regulator_Linear:MIC5501-3.0YM5 +Regulator_Linear:MIC5504-1.2YM5 +Regulator_Linear:MIC5504-1.8YM5 +Regulator_Linear:MIC5504-2.5YM5 +Regulator_Linear:MIC5504-2.8YM5 +Regulator_Linear:MIC5504-3.3YM5 +Regulator_Linear:NCP1117-1.5_SOT223 +Regulator_Linear:NCP1117-1.5_TO252 +Regulator_Linear:NCP1117-1.8_SOT223 +Regulator_Linear:NCP1117-1.8_TO252 +Regulator_Linear:NCP1117-1.9_TO252 +Regulator_Linear:NCP1117-12_SOT223 +Regulator_Linear:NCP1117-12_TO252 +Regulator_Linear:NCP1117-2.0_SOT223 +Regulator_Linear:NCP1117-2.0_TO252 +Regulator_Linear:NCP1117-2.5_SOT223 +Regulator_Linear:NCP1117-2.5_TO252 +Regulator_Linear:NCP1117-2.85_SOT223 +Regulator_Linear:NCP1117-2.85_TO252 +Regulator_Linear:NCP1117-3.3_SOT223 +Regulator_Linear:NCP1117-3.3_TO252 +Regulator_Linear:NCP1117-5.0_SOT223 +Regulator_Linear:NCP1117-5.0_TO252 +Regulator_Linear:NCP1117-ADJ_SOT223 +Regulator_Linear:NCP1117-ADJ_TO252 +Regulator_Linear:NCP115AMX120TCG +Regulator_Linear:NCP115AMX250TCG +Regulator_Linear:NCP133AMX100TCG +Regulator_Linear:NCP163AFCS120T2G +Regulator_Linear:NCP163AFCS180T2G +Regulator_Linear:NCP163AFCS250T2G +Regulator_Linear:NCP163AFCS260T2G +Regulator_Linear:NCP163AFCS270T2G +Regulator_Linear:NCP163AFCS280T2G +Regulator_Linear:NCP163AFCS285T2G +Regulator_Linear:NCP163AFCS290T2G +Regulator_Linear:NCP163AFCS2925T2G +Regulator_Linear:NCP163AFCS514T2G +Regulator_Linear:NCP163AFCT120T2G +Regulator_Linear:NCP163AFCT180T2G +Regulator_Linear:NCP163AFCT250T2G +Regulator_Linear:NCP163AFCT260T2G +Regulator_Linear:NCP163AFCT270T2G +Regulator_Linear:NCP163AFCT280T2G +Regulator_Linear:NCP163AFCT285T2G +Regulator_Linear:NCP163AFCT290T2G +Regulator_Linear:NCP163AFCT2925T2G +Regulator_Linear:NCP163AFCT300T2G +Regulator_Linear:NCP163AFCT330T2G +Regulator_Linear:NCP163AFCT514T2G +Regulator_Linear:NCP163ASN150T1G +Regulator_Linear:NCP163ASN180T1G +Regulator_Linear:NCP163ASN250T1G +Regulator_Linear:NCP163ASN270T1G +Regulator_Linear:NCP163ASN280T1G +Regulator_Linear:NCP163ASN300T1G +Regulator_Linear:NCP163ASN330T1G +Regulator_Linear:NCP163ASN350T1G +Regulator_Linear:NCP163ASN500T1G +Regulator_Linear:NCP163BFCS180T2G +Regulator_Linear:NCP163BFCS2925T2G +Regulator_Linear:NCP163BFCT180T2G +Regulator_Linear:NCP163CFCS285T2G +Regulator_Linear:NCP662SQ15 +Regulator_Linear:NCP662SQ18 +Regulator_Linear:NCP662SQ33 +Regulator_Linear:NCP662SQ50 +Regulator_Linear:NCP718xSN120 +Regulator_Linear:NCP718xSN150 +Regulator_Linear:NCP718xSN180 +Regulator_Linear:NCP718xSN250 +Regulator_Linear:NCP718xSN300 +Regulator_Linear:NCP718xSN330 +Regulator_Linear:NCP718xSN500 +Regulator_Linear:NCV8114ASN120T1G +Regulator_Linear:NCV8114ASN150T1G +Regulator_Linear:NCV8114ASN165T1G +Regulator_Linear:NCV8114ASN180T1G +Regulator_Linear:NCV8114ASN250T1G +Regulator_Linear:NCV8114ASN280T1G +Regulator_Linear:NCV8114ASN300T1G +Regulator_Linear:NCV8114ASN330T1G +Regulator_Linear:NCV8114BSN120T1G +Regulator_Linear:NCV8114BSN150T1G +Regulator_Linear:NCV8114BSN180T1G +Regulator_Linear:NCV8114BSN280T1G +Regulator_Linear:NCV8114BSN300T1G +Regulator_Linear:NCV8114BSN330T1G +Regulator_Linear:NDP6802SF-33 +Regulator_Linear:NDP6802SF-50 +Regulator_Linear:NDP6802SF-A2 +Regulator_Linear:NVC8674DS120 +Regulator_Linear:NVC8674DS50 +Regulator_Linear:OM1323_TO220 +Regulator_Linear:SPX2920M3-3.3_SOT223 +Regulator_Linear:SPX2920M3-5.0_SOT223 +Regulator_Linear:SPX2920S-3.3_SO8 +Regulator_Linear:SPX2920S-5.0_SO8 +Regulator_Linear:SPX2920T-3.3_TO263 +Regulator_Linear:SPX2920T-5.0_TO263 +Regulator_Linear:SPX2920U-3.3_TO220 +Regulator_Linear:SPX2920U-5.0_TO220 +Regulator_Linear:SPX3819M5-L +Regulator_Linear:SPX3819M5-L-1-2 +Regulator_Linear:SPX3819M5-L-1-5 +Regulator_Linear:SPX3819M5-L-1-8 +Regulator_Linear:SPX3819M5-L-2-5 +Regulator_Linear:SPX3819M5-L-3-0 +Regulator_Linear:SPX3819M5-L-3-3 +Regulator_Linear:SPX3819M5-L-5-0 +Regulator_Linear:TC1014-xCT +Regulator_Linear:TC1015-xCT +Regulator_Linear:TC1017-xCT +Regulator_Linear:TC1017-xLT +Regulator_Linear:TC1017R-xLT +Regulator_Linear:TC1054 +Regulator_Linear:TC1055 +Regulator_Linear:TC1185-xCT +Regulator_Linear:TC1186 +Regulator_Linear:TC1262-25 +Regulator_Linear:TC1262-28 +Regulator_Linear:TC1262-30 +Regulator_Linear:TC1262-33 +Regulator_Linear:TC1262-50 +Regulator_Linear:TC2014-1.8VxCTTR +Regulator_Linear:TC2014-2.5VxCTTR +Regulator_Linear:TC2014-2.6VxCTTR +Regulator_Linear:TC2014-2.7VxCTTR +Regulator_Linear:TC2014-2.85VxCTTR +Regulator_Linear:TC2014-2.8VxCTTR +Regulator_Linear:TC2014-3.0VxCTTR +Regulator_Linear:TC2014-3.3VxCTTR +Regulator_Linear:TC2014-5.0VxCTTR +Regulator_Linear:TC2015-1.8VxCTTR +Regulator_Linear:TC2015-2.5VxCTTR +Regulator_Linear:TC2015-2.6VxCTTR +Regulator_Linear:TC2015-2.7VxCTTR +Regulator_Linear:TC2015-2.85VxCTTR +Regulator_Linear:TC2015-2.8VxCTTR +Regulator_Linear:TC2015-3.0VxCTTR +Regulator_Linear:TC2015-3.3VxCTTR +Regulator_Linear:TC2015-5.0VxCTTR +Regulator_Linear:TC2185-1.8VxCTTR +Regulator_Linear:TC2185-2.5VxCTTR +Regulator_Linear:TC2185-2.6VxCTTR +Regulator_Linear:TC2185-2.7VxCTTR +Regulator_Linear:TC2185-2.85VxCTTR +Regulator_Linear:TC2185-2.8VxCTTR +Regulator_Linear:TC2185-3.0VxCTTR +Regulator_Linear:TC2185-3.3VxCTTR +Regulator_Linear:TC2185-5.0VxCTTR +Regulator_Linear:TCR2EE10 +Regulator_Linear:TCR2EE105 +Regulator_Linear:TCR2EE11 +Regulator_Linear:TCR2EE115 +Regulator_Linear:TCR2EE12 +Regulator_Linear:TCR2EE125 +Regulator_Linear:TCR2EE13 +Regulator_Linear:TCR2EE135 +Regulator_Linear:TCR2EE14 +Regulator_Linear:TCR2EE145 +Regulator_Linear:TCR2EE15 +Regulator_Linear:TCR2EE17 +Regulator_Linear:TCR2EE18 +Regulator_Linear:TCR2EE185 +Regulator_Linear:TCR2EE19 +Regulator_Linear:TCR2EE20 +Regulator_Linear:TCR2EE24 +Regulator_Linear:TCR2EE25 +Regulator_Linear:TCR2EE27 +Regulator_Linear:TCR2EE275 +Regulator_Linear:TLV1117-15 +Regulator_Linear:TLV1117-18 +Regulator_Linear:TLV1117-25 +Regulator_Linear:TLV1117-33 +Regulator_Linear:TLV1117-50 +Regulator_Linear:TLV1117-ADJ +Regulator_Linear:TLV70012_SOT23-5 +Regulator_Linear:TLV70012_SOT353 +Regulator_Linear:TLV70012_WSON6 +Regulator_Linear:TLV70013_SOT23-5 +Regulator_Linear:TLV70015_SOT23-5 +Regulator_Linear:TLV70015_SOT353 +Regulator_Linear:TLV70015_WSON6 +Regulator_Linear:TLV70018_SOT23-5 +Regulator_Linear:TLV70018_SOT353 +Regulator_Linear:TLV70018_WSON6 +Regulator_Linear:TLV70019_SOT23-5 +Regulator_Linear:TLV70025_SOT23-5 +Regulator_Linear:TLV70025_SOT353 +Regulator_Linear:TLV70025_WSON6 +Regulator_Linear:TLV70028_SOT353 +Regulator_Linear:TLV70028_WSON6 +Regulator_Linear:TLV70029_WSON6 +Regulator_Linear:TLV70030_SOT23-5 +Regulator_Linear:TLV70030_SOT353 +Regulator_Linear:TLV70030_WSON6 +Regulator_Linear:TLV70031_WSON6 +Regulator_Linear:TLV70032_SOT23-5 +Regulator_Linear:TLV70033_SOT23-5 +Regulator_Linear:TLV70033_SOT353 +Regulator_Linear:TLV70033_WSON6 +Regulator_Linear:TLV70036_SOT23-5 +Regulator_Linear:TLV70036_WSON6 +Regulator_Linear:TLV70212_SOT23-5 +Regulator_Linear:TLV70215_SOT23-5 +Regulator_Linear:TLV70218_SOT23-5 +Regulator_Linear:TLV70225_SOT23-5 +Regulator_Linear:TLV70225_WSON6 +Regulator_Linear:TLV70228_SOT23-5 +Regulator_Linear:TLV70228_WSON6 +Regulator_Linear:TLV70229_WSON6 +Regulator_Linear:TLV70230_SOT23-5 +Regulator_Linear:TLV70231_SOT23-5 +Regulator_Linear:TLV70233_SOT23-5 +Regulator_Linear:TLV70233_WSON6 +Regulator_Linear:TLV70235_SOT23-5 +Regulator_Linear:TLV70236_WSON6 +Regulator_Linear:TLV70237_SOT23-5 +Regulator_Linear:TLV70237_WSON6 +Regulator_Linear:TLV70242PDSE +Regulator_Linear:TLV70245_SOT23-5 +Regulator_Linear:TLV702475_SOT23-5 +Regulator_Linear:TLV7113318DDSE +Regulator_Linear:TLV7113333DDSE +Regulator_Linear:TLV71209_SOT23-5 +Regulator_Linear:TLV71210_SOT23-5 +Regulator_Linear:TLV71211_SOT23-5 +Regulator_Linear:TLV71310PDBV +Regulator_Linear:TLV71311PDBV +Regulator_Linear:TLV71312PDBV +Regulator_Linear:TLV71315PDBV +Regulator_Linear:TLV713185PDBV +Regulator_Linear:TLV71318PDBV +Regulator_Linear:TLV71325PDBV +Regulator_Linear:TLV713285PDBV +Regulator_Linear:TLV71328PDBV +Regulator_Linear:TLV71330PDBV +Regulator_Linear:TLV71333PDBV +Regulator_Linear:TLV71x_WSON-6 +Regulator_Linear:TLV73310PDBV +Regulator_Linear:TLV73311PDBV +Regulator_Linear:TLV73312PDBV +Regulator_Linear:TLV73315PDBV +Regulator_Linear:TLV73318PDBV +Regulator_Linear:TLV73325PDBV +Regulator_Linear:TLV733285PDBV +Regulator_Linear:TLV73328PDBV +Regulator_Linear:TLV73330PDBV +Regulator_Linear:TLV73333PDBV +Regulator_Linear:TLV75509PDBV +Regulator_Linear:TLV75509PDRV +Regulator_Linear:TLV75510PDBV +Regulator_Linear:TLV75510PDRV +Regulator_Linear:TLV75512PDBV +Regulator_Linear:TLV75512PDRV +Regulator_Linear:TLV75515PDBV +Regulator_Linear:TLV75515PDRV +Regulator_Linear:TLV75518PDBV +Regulator_Linear:TLV75518PDRV +Regulator_Linear:TLV75519PDBV +Regulator_Linear:TLV75519PDRV +Regulator_Linear:TLV75525PDBV +Regulator_Linear:TLV75525PDRV +Regulator_Linear:TLV75528PDBV +Regulator_Linear:TLV75528PDRV +Regulator_Linear:TLV75529PDBV +Regulator_Linear:TLV75529PDRV +Regulator_Linear:TLV75530PDBV +Regulator_Linear:TLV75530PDRV +Regulator_Linear:TLV75533PDBV +Regulator_Linear:TLV75533PDRV +Regulator_Linear:TLV75709PDBV +Regulator_Linear:TLV75709PDRV +Regulator_Linear:TLV75710PDBV +Regulator_Linear:TLV75710PDRV +Regulator_Linear:TLV75712PDBV +Regulator_Linear:TLV75712PDRV +Regulator_Linear:TLV75715PDBV +Regulator_Linear:TLV75715PDRV +Regulator_Linear:TLV75718PDBV +Regulator_Linear:TLV75718PDRV +Regulator_Linear:TLV75719PDBV +Regulator_Linear:TLV75719PDRV +Regulator_Linear:TLV75725PDBV +Regulator_Linear:TLV75725PDRV +Regulator_Linear:TLV75728PDBV +Regulator_Linear:TLV75728PDRV +Regulator_Linear:TLV75729PDBV +Regulator_Linear:TLV75730PDBV +Regulator_Linear:TLV75730PDRV +Regulator_Linear:TLV75733PDBV +Regulator_Linear:TLV75733PDRV +Regulator_Linear:TLV75740PDRV +Regulator_Linear:TLV75801PDBV +Regulator_Linear:TLV75801PDRV +Regulator_Linear:TLV76133DCY +Regulator_Linear:TLV76150DCY +Regulator_Linear:TLV76701DRVx +Regulator_Linear:TLV76701QWDRBxQ1 +Regulator_Linear:TLV76708DRVx +Regulator_Linear:TLV76718DRVx +Regulator_Linear:TLV76728DRVx +Regulator_Linear:TLV76733DRVx +Regulator_Linear:TLV76733QWDRBxQ1 +Regulator_Linear:TLV76750DRVx +Regulator_Linear:TLV76750QWDRBxQ1 +Regulator_Linear:TLV76760QWDRBxQ1 +Regulator_Linear:TLV76780QWDRBxQ1 +Regulator_Linear:TLV76790QWDRBxQ1 +Regulator_Linear:TPS51200DRC +Regulator_Linear:TPS70202_HTSSOP20 +Regulator_Linear:TPS70245_HTSSOP20 +Regulator_Linear:TPS70248_HTSSOP20 +Regulator_Linear:TPS70251_HTSSOP20 +Regulator_Linear:TPS70258_HTSSOP20 +Regulator_Linear:TPS70302 +Regulator_Linear:TPS70345 +Regulator_Linear:TPS70348 +Regulator_Linear:TPS70351 +Regulator_Linear:TPS70358 +Regulator_Linear:TPS70402 +Regulator_Linear:TPS70445 +Regulator_Linear:TPS70448 +Regulator_Linear:TPS70451 +Regulator_Linear:TPS70458 +Regulator_Linear:TPS7101 +Regulator_Linear:TPS7133 +Regulator_Linear:TPS7148 +Regulator_Linear:TPS7150 +Regulator_Linear:TPS71518__SC70 +Regulator_Linear:TPS71519__SC70 +Regulator_Linear:TPS71523__SC70 +Regulator_Linear:TPS71525__SC70 +Regulator_Linear:TPS71530__SC70 +Regulator_Linear:TPS71533__SC70 +Regulator_Linear:TPS715345__SC70 +Regulator_Linear:TPS71550__SC70 +Regulator_Linear:TPS72201 +Regulator_Linear:TPS72215 +Regulator_Linear:TPS72216 +Regulator_Linear:TPS72218 +Regulator_Linear:TPS72301DBV +Regulator_Linear:TPS72301DDC +Regulator_Linear:TPS72325DBV +Regulator_Linear:TPS73018DBV +Regulator_Linear:TPS730285DBV +Regulator_Linear:TPS73101DBV +Regulator_Linear:TPS731125DBV +Regulator_Linear:TPS73115DBV +Regulator_Linear:TPS73118DBV +Regulator_Linear:TPS73125DBV +Regulator_Linear:TPS73130DBV +Regulator_Linear:TPS73131DBV +Regulator_Linear:TPS73132DBV +Regulator_Linear:TPS73133DBV +Regulator_Linear:TPS73150DBV +Regulator_Linear:TPS73601DBV +Regulator_Linear:TPS736125DBV +Regulator_Linear:TPS73615DBV +Regulator_Linear:TPS73616DBV +Regulator_Linear:TPS73618DBV +Regulator_Linear:TPS73619DBV +Regulator_Linear:TPS73625DBV +Regulator_Linear:TPS73630DBV +Regulator_Linear:TPS73632DBV +Regulator_Linear:TPS73633DBV +Regulator_Linear:TPS73643DBV +Regulator_Linear:TPS74401_VQFN +Regulator_Linear:TPS74801AWDRC +Regulator_Linear:TPS74801DRC +Regulator_Linear:TPS75005RGW +Regulator_Linear:TPS76301 +Regulator_Linear:TPS76316 +Regulator_Linear:TPS76318 +Regulator_Linear:TPS76325 +Regulator_Linear:TPS76327 +Regulator_Linear:TPS76329 +Regulator_Linear:TPS76330 +Regulator_Linear:TPS76333 +Regulator_Linear:TPS76338 +Regulator_Linear:TPS76350 +Regulator_Linear:TPS76901 +Regulator_Linear:TPS76912 +Regulator_Linear:TPS76915 +Regulator_Linear:TPS76918 +Regulator_Linear:TPS76925 +Regulator_Linear:TPS76927 +Regulator_Linear:TPS76928 +Regulator_Linear:TPS76930 +Regulator_Linear:TPS76933 +Regulator_Linear:TPS76950 +Regulator_Linear:TPS77701_HTSSOP20 +Regulator_Linear:TPS77701_SO8 +Regulator_Linear:TPS77715_HTSSOP20 +Regulator_Linear:TPS77715_SO8 +Regulator_Linear:TPS77718_HTSSOP20 +Regulator_Linear:TPS77718_SO8 +Regulator_Linear:TPS77725_HTSSOP20 +Regulator_Linear:TPS77725_SO8 +Regulator_Linear:TPS77733_HTSSOP20 +Regulator_Linear:TPS77733_SO8 +Regulator_Linear:TPS77801_HTSSOP20 +Regulator_Linear:TPS77801_SO8 +Regulator_Linear:TPS77815_HTSSOP20 +Regulator_Linear:TPS77815_SO8 +Regulator_Linear:TPS77818_HTSSOP20 +Regulator_Linear:TPS77818_SO8 +Regulator_Linear:TPS77825_HTSSOP20 +Regulator_Linear:TPS77825_SO8 +Regulator_Linear:TPS77833_HTSSOP20 +Regulator_Linear:TPS77833_SO8 +Regulator_Linear:TPS78218DDC +Regulator_Linear:TPS78223DDC +Regulator_Linear:TPS78225DDC +Regulator_Linear:TPS78227DDC +Regulator_Linear:TPS78228DDC +Regulator_Linear:TPS78230DDC +Regulator_Linear:TPS78233DDC +Regulator_Linear:TPS78236DDC +Regulator_Linear:TPS79301-EP +Regulator_Linear:TPS79318-EP +Regulator_Linear:TPS79325-EP +Regulator_Linear:TPS79328-EP +Regulator_Linear:TPS793285-EP +Regulator_Linear:TPS79330-EP +Regulator_Linear:TPS79333-EP +Regulator_Linear:TPS793475-EP +Regulator_Linear:TPS7A0508PDBV +Regulator_Linear:TPS7A0508PDBZ +Regulator_Linear:TPS7A0510PDBV +Regulator_Linear:TPS7A0512PDBV +Regulator_Linear:TPS7A0512PDBZ +Regulator_Linear:TPS7A0515PDBV +Regulator_Linear:TPS7A0518PDBV +Regulator_Linear:TPS7A0518PDBZ +Regulator_Linear:TPS7A0520PDBZ +Regulator_Linear:TPS7A0522PDBV +Regulator_Linear:TPS7A0522PDBZ +Regulator_Linear:TPS7A0525PDBV +Regulator_Linear:TPS7A0527PDBZ +Regulator_Linear:TPS7A05285PDBV +Regulator_Linear:TPS7A0528PDBZ +Regulator_Linear:TPS7A0530PDBV +Regulator_Linear:TPS7A0530PDBZ +Regulator_Linear:TPS7A0531PDBV +Regulator_Linear:TPS7A0533PDBV +Regulator_Linear:TPS7A0533PDBZ +Regulator_Linear:TPS7A20xxxDQN +Regulator_Linear:TPS7A3301RGW +Regulator_Linear:TPS7A39 +Regulator_Linear:TPS7A4101DGN +Regulator_Linear:TPS7A4701xRGW +Regulator_Linear:TPS7A7001DDA +Regulator_Linear:TPS7A7200RGW +Regulator_Linear:TPS7A90 +Regulator_Linear:TPS7A91 +Regulator_Linear:UA78M05QDCYRQ1 +Regulator_Linear:UA78M08QDCYRQ1 +Regulator_Linear:UA78M10QDCYRQ1 +Regulator_Linear:UA78M33QDCYRQ1 +Regulator_Linear:XC6206PxxxMR +Regulator_Linear:XC6210B332MR +Regulator_Linear:XC6220B331MR +Regulator_Linear:uA7805 +Regulator_Linear:uA7808 +Regulator_Linear:uA7810 +Regulator_Linear:uA7812 +Regulator_Linear:uA7815 +Regulator_Linear:uA7824 +Regulator_SwitchedCapacitor:CAT3200 +Regulator_SwitchedCapacitor:CAT3200-5 +Regulator_SwitchedCapacitor:ICL7660 +Regulator_SwitchedCapacitor:LM2665M6 +Regulator_SwitchedCapacitor:LM2775DSG +Regulator_SwitchedCapacitor:LM2776 +Regulator_SwitchedCapacitor:LM27761 +Regulator_SwitchedCapacitor:LM27762 +Regulator_SwitchedCapacitor:LM7705 +Regulator_SwitchedCapacitor:LMC7660 +Regulator_SwitchedCapacitor:LT1054 +Regulator_SwitchedCapacitor:LT1054L +Regulator_SwitchedCapacitor:LT1054xSW +Regulator_SwitchedCapacitor:LTC1044 +Regulator_SwitchedCapacitor:LTC1502xMS8-3.3 +Regulator_SwitchedCapacitor:LTC1502xS8-3.3 +Regulator_SwitchedCapacitor:LTC1503CMS8-1.8 +Regulator_SwitchedCapacitor:LTC1503CMS8-2 +Regulator_SwitchedCapacitor:LTC1503xS8-1.8 +Regulator_SwitchedCapacitor:LTC1503xS8-2 +Regulator_SwitchedCapacitor:LTC1751 +Regulator_SwitchedCapacitor:LTC1754 +Regulator_SwitchedCapacitor:LTC3260xDE +Regulator_SwitchedCapacitor:LTC3260xMSE +Regulator_SwitchedCapacitor:LTC660 +Regulator_SwitchedCapacitor:MAX1044 +Regulator_SwitchedCapacitor:RT9361AxE +Regulator_SwitchedCapacitor:RT9361BxE +Regulator_SwitchedCapacitor:TPS60151DRV +Regulator_SwitchedCapacitor:TPS60400DBV +Regulator_SwitchedCapacitor:TPS60401DBV +Regulator_SwitchedCapacitor:TPS60402DBV +Regulator_SwitchedCapacitor:TPS60403DBV +Regulator_SwitchedCapacitor:TPS60500DGS +Regulator_SwitchedCapacitor:TPS60501DGS +Regulator_SwitchedCapacitor:TPS60502DGS +Regulator_SwitchedCapacitor:TPS60503DGS +Regulator_Switching:171050601 +Regulator_Switching:A4403GEU +Regulator_Switching:AAT1217ICA-1.2 +Regulator_Switching:AAT1217ICA-3.3 +Regulator_Switching:AAT1217ICA-5.0 +Regulator_Switching:AAT1217IGU-3.3 +Regulator_Switching:ADP1108AN +Regulator_Switching:ADP1108AN-12 +Regulator_Switching:ADP1108AN-3.3 +Regulator_Switching:ADP1108AN-5 +Regulator_Switching:ADP1108AR +Regulator_Switching:ADP1108AR-12 +Regulator_Switching:ADP1108AR-3.3 +Regulator_Switching:ADP1108AR-5 +Regulator_Switching:ADP2108AUJ-1.0 +Regulator_Switching:ADP2108AUJ-1.1 +Regulator_Switching:ADP2108AUJ-1.2 +Regulator_Switching:ADP2108AUJ-1.3 +Regulator_Switching:ADP2108AUJ-1.5 +Regulator_Switching:ADP2108AUJ-1.8 +Regulator_Switching:ADP2108AUJ-1.82 +Regulator_Switching:ADP2108AUJ-2.3 +Regulator_Switching:ADP2108AUJ-2.5 +Regulator_Switching:ADP2108AUJ-3.0 +Regulator_Switching:ADP2108AUJ-3.3 +Regulator_Switching:ADP2302ARDZ +Regulator_Switching:ADP2302ARDZ-2.5 +Regulator_Switching:ADP2302ARDZ-3.3 +Regulator_Switching:ADP2302ARDZ-5.0 +Regulator_Switching:ADP2303ARDZ +Regulator_Switching:ADP2303ARDZ-2.5 +Regulator_Switching:ADP2303ARDZ-3.3 +Regulator_Switching:ADP2303ARDZ-5.0 +Regulator_Switching:ADP2360xCP +Regulator_Switching:ADP2360xCP-3.3 +Regulator_Switching:ADP2360xCP-5.0 +Regulator_Switching:ADP5054 +Regulator_Switching:ADP5070AREZ +Regulator_Switching:ADP5071AREZ +Regulator_Switching:ADuM6000 +Regulator_Switching:AOZ1280CI +Regulator_Switching:AOZ1282CI +Regulator_Switching:AOZ1282CI-1 +Regulator_Switching:AOZ6663DI +Regulator_Switching:AOZ6663DI-01 +Regulator_Switching:AP3012 +Regulator_Switching:AP3211K +Regulator_Switching:AP3402 +Regulator_Switching:AP3441SHE +Regulator_Switching:AP62150WU +Regulator_Switching:AP62150Z6 +Regulator_Switching:AP62250WU +Regulator_Switching:AP62250Z6 +Regulator_Switching:AP62300TWU +Regulator_Switching:AP62300WU +Regulator_Switching:AP62300Z6 +Regulator_Switching:AP62301WU +Regulator_Switching:AP63200WU +Regulator_Switching:AP63201WU +Regulator_Switching:AP63203WU +Regulator_Switching:AP63205WU +Regulator_Switching:AP6502 +Regulator_Switching:AP6503 +Regulator_Switching:AP65111AWU +Regulator_Switching:APE1707H-12-HF +Regulator_Switching:APE1707H-33-HF +Regulator_Switching:APE1707H-50-HF +Regulator_Switching:APE1707H-HF +Regulator_Switching:APE1707M-12-HF +Regulator_Switching:APE1707M-33-HF +Regulator_Switching:APE1707M-50-HF +Regulator_Switching:APE1707M-HF +Regulator_Switching:APE1707S-12-HF +Regulator_Switching:APE1707S-33-HF +Regulator_Switching:APE1707S-50-HF +Regulator_Switching:APE1707S-HF +Regulator_Switching:BD9001F +Regulator_Switching:BD9778F +Regulator_Switching:BD9778HFP +Regulator_Switching:BD9781HFP +Regulator_Switching:BD9G341EFJ +Regulator_Switching:CRE1S0305S3C +Regulator_Switching:CRE1S0505DC +Regulator_Switching:CRE1S0505S3C +Regulator_Switching:CRE1S0505SC +Regulator_Switching:CRE1S0515SC +Regulator_Switching:CRE1S1205SC +Regulator_Switching:CRE1S1212SC +Regulator_Switching:CRE1S2405SC +Regulator_Switching:CRE1S2412SC +Regulator_Switching:DIO6970 +Regulator_Switching:FSBH0170 +Regulator_Switching:FSBH0170A +Regulator_Switching:FSBH0170W +Regulator_Switching:FSBH0270 +Regulator_Switching:FSBH0270A +Regulator_Switching:FSBH0270W +Regulator_Switching:FSBH0370 +Regulator_Switching:FSBH0F70A +Regulator_Switching:FSBH0F70WA +Regulator_Switching:FSDH321 +Regulator_Switching:FSDH321L +Regulator_Switching:FSDL321 +Regulator_Switching:FSDL321L +Regulator_Switching:FSL136MRT +Regulator_Switching:FSQ0565RQLDTU +Regulator_Switching:FSQ0565RQWDTU +Regulator_Switching:FSQ0565RSLDTU +Regulator_Switching:FSQ0565RSWDTU +Regulator_Switching:GL2576-12SF8DR +Regulator_Switching:GL2576-12TA5R +Regulator_Switching:GL2576-12TB5T +Regulator_Switching:GL2576-15SF8DR +Regulator_Switching:GL2576-15TA5R +Regulator_Switching:GL2576-15TB5T +Regulator_Switching:GL2576-3.3SF8DR +Regulator_Switching:GL2576-3.3TA5R +Regulator_Switching:GL2576-3.3TB5T +Regulator_Switching:GL2576-5.0SF8DR +Regulator_Switching:GL2576-5.0TA5R +Regulator_Switching:GL2576-5.0TB5T +Regulator_Switching:GL2576-ASF8DR +Regulator_Switching:GL2576-ATA5R +Regulator_Switching:GL2576-ATB5T +Regulator_Switching:HT7463A +Regulator_Switching:HT7463B +Regulator_Switching:ISL8117FRZ +Regulator_Switching:ISL8117FVEZ +Regulator_Switching:KA5H02659RN +Regulator_Switching:KA5H0265RCTU +Regulator_Switching:KA5H0265RCYDTU +Regulator_Switching:KA5H0280RTU +Regulator_Switching:KA5H0280RYDTU +Regulator_Switching:KA5L0265RTU +Regulator_Switching:KA5L0265RYDTU +Regulator_Switching:KA5M02659RN +Regulator_Switching:KA5M0265RTU +Regulator_Switching:KA5M0265RYDTU +Regulator_Switching:KA5M0280RTU +Regulator_Switching:KA5M0280RYDTU +Regulator_Switching:L4962-A +Regulator_Switching:L4962E-A +Regulator_Switching:L4962EH-A +Regulator_Switching:L5973D +Regulator_Switching:L7980A +Regulator_Switching:LD7575 +Regulator_Switching:LGS5116B +Regulator_Switching:LGS5145 +Regulator_Switching:LGS6302B5 +Regulator_Switching:LM22676MR-5 +Regulator_Switching:LM22676MR-ADJ +Regulator_Switching:LM22678TJ-5 +Regulator_Switching:LM22678TJ-ADJ +Regulator_Switching:LM25085MM +Regulator_Switching:LM25085MY +Regulator_Switching:LM25085SD +Regulator_Switching:LM2574HVM-12 +Regulator_Switching:LM2574HVM-15 +Regulator_Switching:LM2574HVM-3.3 +Regulator_Switching:LM2574HVM-5 +Regulator_Switching:LM2574HVM-ADJ +Regulator_Switching:LM2574HVN-12 +Regulator_Switching:LM2574HVN-15 +Regulator_Switching:LM2574HVN-3.3 +Regulator_Switching:LM2574HVN-5 +Regulator_Switching:LM2574HVN-ADJ +Regulator_Switching:LM2574M-12 +Regulator_Switching:LM2574M-15 +Regulator_Switching:LM2574M-3.3 +Regulator_Switching:LM2574M-5 +Regulator_Switching:LM2574M-ADJ +Regulator_Switching:LM2574N-12 +Regulator_Switching:LM2574N-15 +Regulator_Switching:LM2574N-3.3 +Regulator_Switching:LM2574N-5 +Regulator_Switching:LM2574N-ADJ +Regulator_Switching:LM2575-12BT +Regulator_Switching:LM2575-12BU +Regulator_Switching:LM2575-3.3BT +Regulator_Switching:LM2575-3.3BU +Regulator_Switching:LM2575-5.0BT +Regulator_Switching:LM2575-5.0BU +Regulator_Switching:LM2575BT-ADJ +Regulator_Switching:LM2575BU-ADJ +Regulator_Switching:LM2576HVS-12 +Regulator_Switching:LM2576HVS-15 +Regulator_Switching:LM2576HVS-3.3 +Regulator_Switching:LM2576HVS-5 +Regulator_Switching:LM2576HVS-ADJ +Regulator_Switching:LM2576HVT-12 +Regulator_Switching:LM2576HVT-15 +Regulator_Switching:LM2576HVT-3.3 +Regulator_Switching:LM2576HVT-5 +Regulator_Switching:LM2576HVT-ADJ +Regulator_Switching:LM2576S-12 +Regulator_Switching:LM2576S-15 +Regulator_Switching:LM2576S-3.3 +Regulator_Switching:LM2576S-5 +Regulator_Switching:LM2576S-ADJ +Regulator_Switching:LM2576T-12 +Regulator_Switching:LM2576T-15 +Regulator_Switching:LM2576T-3.3 +Regulator_Switching:LM2576T-5 +Regulator_Switching:LM2576T-ADJ +Regulator_Switching:LM2578 +Regulator_Switching:LM2594HVM-12 +Regulator_Switching:LM2594HVM-3.3 +Regulator_Switching:LM2594HVM-5.0 +Regulator_Switching:LM2594HVM-ADJ +Regulator_Switching:LM2594HVN-12 +Regulator_Switching:LM2594HVN-3.3 +Regulator_Switching:LM2594HVN-5.0 +Regulator_Switching:LM2594HVN-ADJ +Regulator_Switching:LM2594M-12 +Regulator_Switching:LM2594M-3.3 +Regulator_Switching:LM2594M-5.0 +Regulator_Switching:LM2594M-ADJ +Regulator_Switching:LM2594N-12 +Regulator_Switching:LM2594N-3.3 +Regulator_Switching:LM2594N-5.0 +Regulator_Switching:LM2594N-ADJ +Regulator_Switching:LM2595S-12 +Regulator_Switching:LM2595S-3.3 +Regulator_Switching:LM2595S-5 +Regulator_Switching:LM2595S-ADJ +Regulator_Switching:LM2595T-12 +Regulator_Switching:LM2595T-3.3 +Regulator_Switching:LM2595T-5 +Regulator_Switching:LM2595T-ADJ +Regulator_Switching:LM2596S-12 +Regulator_Switching:LM2596S-3.3 +Regulator_Switching:LM2596S-5 +Regulator_Switching:LM2596S-ADJ +Regulator_Switching:LM2596T-12 +Regulator_Switching:LM2596T-3.3 +Regulator_Switching:LM2596T-5 +Regulator_Switching:LM2596T-ADJ +Regulator_Switching:LM2611xMF +Regulator_Switching:LM26480SQ +Regulator_Switching:LM2672M-12 +Regulator_Switching:LM2672M-3.3 +Regulator_Switching:LM2672M-5.0 +Regulator_Switching:LM2672M-ADJ +Regulator_Switching:LM2672N-12 +Regulator_Switching:LM2672N-3.3 +Regulator_Switching:LM2672N-5.0 +Regulator_Switching:LM2672N-ADJ +Regulator_Switching:LM2674M-12 +Regulator_Switching:LM2674M-3.3 +Regulator_Switching:LM2674M-5.0 +Regulator_Switching:LM2674M-ADJ +Regulator_Switching:LM2674N-12 +Regulator_Switching:LM2674N-3.3 +Regulator_Switching:LM2674N-5.0 +Regulator_Switching:LM2674N-ADJ +Regulator_Switching:LM2675M-12 +Regulator_Switching:LM2675M-3.3 +Regulator_Switching:LM2675M-5 +Regulator_Switching:LM2675M-ADJ +Regulator_Switching:LM2675N-12 +Regulator_Switching:LM2675N-3.3 +Regulator_Switching:LM2675N-5 +Regulator_Switching:LM2675N-ADJ +Regulator_Switching:LM27313XMF +Regulator_Switching:LM2731XMF +Regulator_Switching:LM2731YMF +Regulator_Switching:LM2733XMF +Regulator_Switching:LM2733YMF +Regulator_Switching:LM2734X +Regulator_Switching:LM2734Y +Regulator_Switching:LM2735XMF +Regulator_Switching:LM2840X +Regulator_Switching:LM2840Y +Regulator_Switching:LM2841X +Regulator_Switching:LM2841Y +Regulator_Switching:LM2842X +Regulator_Switching:LM2842Y +Regulator_Switching:LM3150MH +Regulator_Switching:LM3407MY +Regulator_Switching:LM3578 +Regulator_Switching:LM3670MF +Regulator_Switching:LM5001MA +Regulator_Switching:LM5006MM +Regulator_Switching:LM5007MM +Regulator_Switching:LM5007SD +Regulator_Switching:LM5008MM +Regulator_Switching:LM5008SD +Regulator_Switching:LM5009MM +Regulator_Switching:LM5009SD +Regulator_Switching:LM5017MR +Regulator_Switching:LM5017SD +Regulator_Switching:LM5022MM +Regulator_Switching:LM5088-1 +Regulator_Switching:LM5088-2 +Regulator_Switching:LM5118MH +Regulator_Switching:LM5161PWP +Regulator_Switching:LM5164DDA +Regulator_Switching:LM5165 +Regulator_Switching:LM5165X +Regulator_Switching:LM5165Y +Regulator_Switching:LM5166 +Regulator_Switching:LM5166X +Regulator_Switching:LM5166Y +Regulator_Switching:LM5175PWP +Regulator_Switching:LM5175RHF +Regulator_Switching:LM5176PWP +Regulator_Switching:LM5176RHF +Regulator_Switching:LMR10510XMF +Regulator_Switching:LMR10510YMF +Regulator_Switching:LMR10510YSD +Regulator_Switching:LMR14206 +Regulator_Switching:LMR16006YQ +Regulator_Switching:LMR16006YQ3 +Regulator_Switching:LMR16006YQ5 +Regulator_Switching:LMR33610ADDAR +Regulator_Switching:LMR33610BDDAR +Regulator_Switching:LMR33620ADDA +Regulator_Switching:LMR33620BDDA +Regulator_Switching:LMR33620CDDA +Regulator_Switching:LMR33630ADDA +Regulator_Switching:LMR33630BDDA +Regulator_Switching:LMR33630CDDA +Regulator_Switching:LMR33640ADDA +Regulator_Switching:LMR33640DDDA +Regulator_Switching:LMR36510ADDA +Regulator_Switching:LMR50410 +Regulator_Switching:LMR51430 +Regulator_Switching:LMR62014XMF +Regulator_Switching:LMR62421XMF +Regulator_Switching:LMR62421XSD +Regulator_Switching:LMR64010XMF +Regulator_Switching:LMZ13608 +Regulator_Switching:LMZ22003TZ +Regulator_Switching:LMZ22005TZ +Regulator_Switching:LMZ23603TZ +Regulator_Switching:LMZ23605TZ +Regulator_Switching:LMZM23600 +Regulator_Switching:LMZM23600V3 +Regulator_Switching:LMZM23600V5 +Regulator_Switching:LMZM23601 +Regulator_Switching:LMZM23601V3 +Regulator_Switching:LMZM23601V5 +Regulator_Switching:LNK302D +Regulator_Switching:LNK302G +Regulator_Switching:LNK302P +Regulator_Switching:LNK304D +Regulator_Switching:LNK304G +Regulator_Switching:LNK304P +Regulator_Switching:LNK305D +Regulator_Switching:LNK305G +Regulator_Switching:LNK305P +Regulator_Switching:LNK306D +Regulator_Switching:LNK306G +Regulator_Switching:LNK306P +Regulator_Switching:LNK3202D +Regulator_Switching:LNK3202G +Regulator_Switching:LNK3202P +Regulator_Switching:LNK3204D +Regulator_Switching:LNK3204G +Regulator_Switching:LNK3204P +Regulator_Switching:LNK3205D +Regulator_Switching:LNK3205G +Regulator_Switching:LNK3205P +Regulator_Switching:LNK3206D +Regulator_Switching:LNK3206G +Regulator_Switching:LNK3206P +Regulator_Switching:LNK362D +Regulator_Switching:LNK362G +Regulator_Switching:LNK362P +Regulator_Switching:LNK363D +Regulator_Switching:LNK363G +Regulator_Switching:LNK363P +Regulator_Switching:LNK364D +Regulator_Switching:LNK364G +Regulator_Switching:LNK364P +Regulator_Switching:LNK403EG +Regulator_Switching:LNK403LG +Regulator_Switching:LNK404EG +Regulator_Switching:LNK404LG +Regulator_Switching:LNK405EG +Regulator_Switching:LNK405LG +Regulator_Switching:LNK406EG +Regulator_Switching:LNK406LG +Regulator_Switching:LNK407EG +Regulator_Switching:LNK407LG +Regulator_Switching:LNK408EG +Regulator_Switching:LNK408LG +Regulator_Switching:LNK409EG +Regulator_Switching:LNK409LG +Regulator_Switching:LNK410EG +Regulator_Switching:LNK410LG +Regulator_Switching:LNK413EG +Regulator_Switching:LNK413LG +Regulator_Switching:LNK414EG +Regulator_Switching:LNK414LG +Regulator_Switching:LNK415EG +Regulator_Switching:LNK415LG +Regulator_Switching:LNK416EG +Regulator_Switching:LNK416LG +Regulator_Switching:LNK417EG +Regulator_Switching:LNK417LG +Regulator_Switching:LNK418EG +Regulator_Switching:LNK418LG +Regulator_Switching:LNK419EG +Regulator_Switching:LNK419LG +Regulator_Switching:LNK420EG +Regulator_Switching:LNK420LG +Regulator_Switching:LNK454D +Regulator_Switching:LNK456D +Regulator_Switching:LNK457D +Regulator_Switching:LNK457K +Regulator_Switching:LNK457V +Regulator_Switching:LNK458K +Regulator_Switching:LNK458V +Regulator_Switching:LNK460K +Regulator_Switching:LNK460V +Regulator_Switching:LNK562D +Regulator_Switching:LNK562G +Regulator_Switching:LNK562P +Regulator_Switching:LNK563D +Regulator_Switching:LNK563G +Regulator_Switching:LNK563P +Regulator_Switching:LNK564D +Regulator_Switching:LNK564G +Regulator_Switching:LNK564P +Regulator_Switching:LNK603DG +Regulator_Switching:LNK603PG +Regulator_Switching:LNK604DG +Regulator_Switching:LNK604PG +Regulator_Switching:LNK605DG +Regulator_Switching:LNK605PG +Regulator_Switching:LNK606DG +Regulator_Switching:LNK606GG +Regulator_Switching:LNK606PG +Regulator_Switching:LNK613DG +Regulator_Switching:LNK613PG +Regulator_Switching:LNK614DG +Regulator_Switching:LNK614PG +Regulator_Switching:LNK615DG +Regulator_Switching:LNK615PG +Regulator_Switching:LNK616DG +Regulator_Switching:LNK616GG +Regulator_Switching:LNK616PG +Regulator_Switching:LNK623DG +Regulator_Switching:LNK623PG +Regulator_Switching:LNK624DG +Regulator_Switching:LNK624PG +Regulator_Switching:LNK625DG +Regulator_Switching:LNK625PG +Regulator_Switching:LNK626DG +Regulator_Switching:LNK626PG +Regulator_Switching:LNK632DG +Regulator_Switching:LT1073CN +Regulator_Switching:LT1073CN-12 +Regulator_Switching:LT1073CN-5 +Regulator_Switching:LT1073CS +Regulator_Switching:LT1073CS-12 +Regulator_Switching:LT1073CS-5 +Regulator_Switching:LT1108CN +Regulator_Switching:LT1108CN-12 +Regulator_Switching:LT1108CN-5 +Regulator_Switching:LT1108CS +Regulator_Switching:LT1108CS-12 +Regulator_Switching:LT1108CS-5 +Regulator_Switching:LT1301 +Regulator_Switching:LT1307BCMS8 +Regulator_Switching:LT1307BCS8 +Regulator_Switching:LT1307CMS8 +Regulator_Switching:LT1307CN8 +Regulator_Switching:LT1307CS8 +Regulator_Switching:LT1372CN8 +Regulator_Switching:LT1372CS8 +Regulator_Switching:LT1372HVCN8 +Regulator_Switching:LT1372HVCS8 +Regulator_Switching:LT1373CN8 +Regulator_Switching:LT1373CS8 +Regulator_Switching:LT1373HVCN8 +Regulator_Switching:LT1373HVCS8 +Regulator_Switching:LT1377CN8 +Regulator_Switching:LT1377CS8 +Regulator_Switching:LT1945 +Regulator_Switching:LT3430 +Regulator_Switching:LT3430-1 +Regulator_Switching:LT3439 +Regulator_Switching:LT3471 +Regulator_Switching:LT3472 +Regulator_Switching:LT3483AxS6 +Regulator_Switching:LT3483xS6 +Regulator_Switching:LT3514xUFD +Regulator_Switching:LT3580xDD +Regulator_Switching:LT3580xMS8E +Regulator_Switching:LT3748xMS +Regulator_Switching:LT3757AEDD +Regulator_Switching:LT3757AEMSE +Regulator_Switching:LT3757EDD +Regulator_Switching:LT3757EMSE +Regulator_Switching:LT3988 +Regulator_Switching:LT8303 +Regulator_Switching:LT8306 +Regulator_Switching:LT8610 +Regulator_Switching:LT8610AC +Regulator_Switching:LT8610AC-1 +Regulator_Switching:LT8705AxFE +Regulator_Switching:LTC1436A +Regulator_Switching:LTC1436A-PLL +Regulator_Switching:LTC1437A +Regulator_Switching:LTC1878EMS8 +Regulator_Switching:LTC3105xDD +Regulator_Switching:LTC3105xMS +Regulator_Switching:LTC3245xDE +Regulator_Switching:LTC3245xMSE +Regulator_Switching:LTC3406AES5 +Regulator_Switching:LTC3406B-2ES5 +Regulator_Switching:LTC3406BES5-1.2 +Regulator_Switching:LTC3406ES5 +Regulator_Switching:LTC3406ES5-1.2 +Regulator_Switching:LTC3406ES5-1.5 +Regulator_Switching:LTC3406ES5-1.8 +Regulator_Switching:LTC3429 +Regulator_Switching:LTC3429B +Regulator_Switching:LTC3442 +Regulator_Switching:LTC3525 +Regulator_Switching:LTC3525-3 +Regulator_Switching:LTC3525-3.3 +Regulator_Switching:LTC3525-5 +Regulator_Switching:LTC3525D-3.3 +Regulator_Switching:LTC3525L-3 +Regulator_Switching:LTC3561EDD +Regulator_Switching:LTC3630AxDHC +Regulator_Switching:LTC3630AxMSE +Regulator_Switching:LTC3630xDHC +Regulator_Switching:LTC3630xMSE +Regulator_Switching:LTC3638xMSE +Regulator_Switching:LTC3639xMSE +Regulator_Switching:LTC3886 +Regulator_Switching:LTC7138xMSE +Regulator_Switching:LTM4626 +Regulator_Switching:LTM4637xV +Regulator_Switching:LTM4637xY +Regulator_Switching:LTM4638 +Regulator_Switching:LTM4657 +Regulator_Switching:LTM4668 +Regulator_Switching:LTM4668A +Regulator_Switching:LTM4671 +Regulator_Switching:LTM8049 +Regulator_Switching:LTM8063 +Regulator_Switching:LV2862XDDC +Regulator_Switching:LV2862YDDC +Regulator_Switching:MAX15062A +Regulator_Switching:MAX15062B +Regulator_Switching:MAX15062C +Regulator_Switching:MAX1522 +Regulator_Switching:MAX1523 +Regulator_Switching:MAX1524 +Regulator_Switching:MAX17501AxTB +Regulator_Switching:MAX17501BxTB +Regulator_Switching:MAX17501ExTB +Regulator_Switching:MAX17501FxTB +Regulator_Switching:MAX17501GxTB +Regulator_Switching:MAX17501HxTB +Regulator_Switching:MAX17572 +Regulator_Switching:MAX17574 +Regulator_Switching:MAX17620ATA +Regulator_Switching:MAX1771xSA +Regulator_Switching:MAX5035AUPA +Regulator_Switching:MAX5035AUSA +Regulator_Switching:MAX5035BUPA +Regulator_Switching:MAX5035BUSA +Regulator_Switching:MAX5035CUPA +Regulator_Switching:MAX5035CUSA +Regulator_Switching:MAX5035DUPA +Regulator_Switching:MAX5035DUSA +Regulator_Switching:MAX5035EUSA +Regulator_Switching:MAX777L +Regulator_Switching:MAX77827AEFD +Regulator_Switching:MAX778L +Regulator_Switching:MAX779L +Regulator_Switching:MC33063AD +Regulator_Switching:MC33063AP +Regulator_Switching:MC33063MNTXG +Regulator_Switching:MC34063AD +Regulator_Switching:MC34063AP +Regulator_Switching:MCP1623x-xCHY +Regulator_Switching:MCP1623x-xMC +Regulator_Switching:MCP16301Hx-xCH +Regulator_Switching:MCP16301x-xCH +Regulator_Switching:MCP16311x-xMNY +Regulator_Switching:MCP16311x-xMS +Regulator_Switching:MCP16312x-xMNY +Regulator_Switching:MCP16312x-xMS +Regulator_Switching:MCP16331x-xCH +Regulator_Switching:MCP16331x-xMNY +Regulator_Switching:MCP1640Bx-xCHY +Regulator_Switching:MCP1640Bx-xMC +Regulator_Switching:MCP1640Cx-xCHY +Regulator_Switching:MCP1640Cx-xMC +Regulator_Switching:MCP1640Dx-xCHY +Regulator_Switching:MCP1640Dx-xMC +Regulator_Switching:MCP1640x-xCHY +Regulator_Switching:MCP1640x-xMC +Regulator_Switching:MCP1650x-xMC +Regulator_Switching:MCP1651x-xMC +Regulator_Switching:MCP1652x-xMC +Regulator_Switching:MCP1653x-xUN +Regulator_Switching:MIC2177 +Regulator_Switching:MIC2177-3.3 +Regulator_Switching:MIC2177-5.0 +Regulator_Switching:MIC2178 +Regulator_Switching:MIC2178-3.3 +Regulator_Switching:MIC2178-5.0 +Regulator_Switching:MIC2207 +Regulator_Switching:MIC2253 +Regulator_Switching:MIC2290 +Regulator_Switching:MIC23050-4YML +Regulator_Switching:MIC23050-CYML +Regulator_Switching:MIC23050-GYML +Regulator_Switching:MIC23050-SYML +Regulator_Switching:MIC4684 +Regulator_Switching:MIC4690 +Regulator_Switching:MP1470 +Regulator_Switching:MP171GJ +Regulator_Switching:MP171GS +Regulator_Switching:MP2303ADN +Regulator_Switching:MP2303ADP +Regulator_Switching:MPM3550EGLE +Regulator_Switching:MT3608 +Regulator_Switching:MUN12AD01-SH +Regulator_Switching:MUN12AD03-SH +Regulator_Switching:NBM5100A +Regulator_Switching:NBM7100A +Regulator_Switching:NCP1070P065 +Regulator_Switching:NCP1070P100 +Regulator_Switching:NCP1070P130 +Regulator_Switching:NCP1070STAT +Regulator_Switching:NCP1070STBT +Regulator_Switching:NCP1070STCT +Regulator_Switching:NCP1071P065 +Regulator_Switching:NCP1071P100 +Regulator_Switching:NCP1071P130 +Regulator_Switching:NCP1071STAT +Regulator_Switching:NCP1071STBT +Regulator_Switching:NCP1071STCT +Regulator_Switching:NCP1072P065 +Regulator_Switching:NCP1072P100 +Regulator_Switching:NCP1072P130 +Regulator_Switching:NCP1072STAT +Regulator_Switching:NCP1072STBT +Regulator_Switching:NCP1072STCT +Regulator_Switching:NCP1075P065 +Regulator_Switching:NCP1075P100 +Regulator_Switching:NCP1075P130 +Regulator_Switching:NCP1075STAT +Regulator_Switching:NCP1075STBT +Regulator_Switching:NCP1075STCT +Regulator_Switching:NCP1076P065 +Regulator_Switching:NCP1076P100 +Regulator_Switching:NCP1076P130 +Regulator_Switching:NCP1076STAT +Regulator_Switching:NCP1076STBT +Regulator_Switching:NCP1076STCT +Regulator_Switching:NCP1077P065 +Regulator_Switching:NCP1077P100 +Regulator_Switching:NCP1077P130 +Regulator_Switching:NCP1077STAT +Regulator_Switching:NCP1077STBT +Regulator_Switching:NCP1077STCT +Regulator_Switching:NCP1529A +Regulator_Switching:NCV33063AVD +Regulator_Switching:NID30S24-05 +Regulator_Switching:NID30S24-12 +Regulator_Switching:NID30S24-15 +Regulator_Switching:NID30S48-24 +Regulator_Switching:NID60S24-05 +Regulator_Switching:NID60S24-12 +Regulator_Switching:NID60S24-15 +Regulator_Switching:NID60S48-24 +Regulator_Switching:NMA0505DC +Regulator_Switching:NMA0505SC +Regulator_Switching:NMA0509DC +Regulator_Switching:NMA0509SC +Regulator_Switching:NMA0512DC +Regulator_Switching:NMA0512SC +Regulator_Switching:NMA0515DC +Regulator_Switching:NMA0515SC +Regulator_Switching:NMA1205DC +Regulator_Switching:NMA1205SC +Regulator_Switching:NMA1209DC +Regulator_Switching:NMA1209SC +Regulator_Switching:NMA1212DC +Regulator_Switching:NMA1212SC +Regulator_Switching:NMA1215DC +Regulator_Switching:NMA1215SC +Regulator_Switching:NMA1505DC +Regulator_Switching:NMA1505SC +Regulator_Switching:NMA1512DC +Regulator_Switching:NMA1512SC +Regulator_Switching:NMA1515DC +Regulator_Switching:NMA1515SC +Regulator_Switching:NXE1S0303MC +Regulator_Switching:NXE1S0305MC +Regulator_Switching:NXE1S0505MC +Regulator_Switching:NXE2S0505MC +Regulator_Switching:NXE2S1205MC +Regulator_Switching:NXE2S1212MC +Regulator_Switching:NXE2S1215MC +Regulator_Switching:PAM2301CAAB120 +Regulator_Switching:PAM2301CAAB330 +Regulator_Switching:PAM2301CAABADJ +Regulator_Switching:PAM2305AAB120 +Regulator_Switching:PAM2305AAB150 +Regulator_Switching:PAM2305AAB180 +Regulator_Switching:PAM2305AAB250 +Regulator_Switching:PAM2305AAB280 +Regulator_Switching:PAM2305AAB330 +Regulator_Switching:PAM2305AABADJ +Regulator_Switching:PAM2305BJE120 +Regulator_Switching:PAM2305BJE150 +Regulator_Switching:PAM2305BJE180 +Regulator_Switching:PAM2305BJE250 +Regulator_Switching:PAM2305BJE280 +Regulator_Switching:PAM2305BJE330 +Regulator_Switching:PAM2305BJEADJ +Regulator_Switching:PAM2305CGF120 +Regulator_Switching:PAM2305CGF150 +Regulator_Switching:PAM2305CGF180 +Regulator_Switching:PAM2305CGF250 +Regulator_Switching:PAM2305CGF280 +Regulator_Switching:PAM2305CGF330 +Regulator_Switching:PAM2305CGFADJ +Regulator_Switching:PAM2306AYPAA +Regulator_Switching:PAM2306AYPBB +Regulator_Switching:PAM2306AYPBK +Regulator_Switching:PAM2306AYPCB +Regulator_Switching:PAM2306AYPKB +Regulator_Switching:PAM2306AYPKE +Regulator_Switching:PAM2306DYPAA +Regulator_Switching:R-781.5-0.5 +Regulator_Switching:R-781.8-0.5 +Regulator_Switching:R-781.8-1.0 +Regulator_Switching:R-7812-0.5 +Regulator_Switching:R-7815-0.5 +Regulator_Switching:R-782.5-0.5 +Regulator_Switching:R-782.5-1.0 +Regulator_Switching:R-783.3-0.5 +Regulator_Switching:R-783.3-1.0 +Regulator_Switching:R-785.0-0.5 +Regulator_Switching:R-785.0-1.0 +Regulator_Switching:R-786.5-0.5 +Regulator_Switching:R-78B1.2-2.0 +Regulator_Switching:R-78B1.5-2.0 +Regulator_Switching:R-78B1.8-2.0 +Regulator_Switching:R-78B12-2.0 +Regulator_Switching:R-78B15-2.0 +Regulator_Switching:R-78B2.5-2.0 +Regulator_Switching:R-78B3.3-2.0 +Regulator_Switching:R-78B5.0-2.0 +Regulator_Switching:R-78B9.0-2.0 +Regulator_Switching:R-78C1.8-1.0 +Regulator_Switching:R-78C12-1.0 +Regulator_Switching:R-78C15-1.0 +Regulator_Switching:R-78C3.3-1.0 +Regulator_Switching:R-78C5.0-1.0 +Regulator_Switching:R-78C9.0-1.0 +Regulator_Switching:R-78E12-0.5 +Regulator_Switching:R-78E15-0.5 +Regulator_Switching:R-78E3.3-0.5 +Regulator_Switching:R-78E3.3-1.0 +Regulator_Switching:R-78E5.0-0.5 +Regulator_Switching:R-78E5.0-1.0 +Regulator_Switching:R-78E9.0-0.5 +Regulator_Switching:R-78HB12-0.5 +Regulator_Switching:R-78HB15-0.5 +Regulator_Switching:R-78HB24-0.3 +Regulator_Switching:R-78HB3.3-0.5 +Regulator_Switching:R-78HB5.0-0.5 +Regulator_Switching:R-78HB6.5-0.5 +Regulator_Switching:R-78HB9.0-0.5 +Regulator_Switching:R-78S3.3-0.1 +Regulator_Switching:SC33063AD +Regulator_Switching:SC34063AP +Regulator_Switching:SC4503TSK +Regulator_Switching:SIC431A +Regulator_Switching:SIC431B +Regulator_Switching:SIC431C +Regulator_Switching:SIC431D +Regulator_Switching:SIC437A +Regulator_Switching:SIC437B +Regulator_Switching:SIC437C +Regulator_Switching:SIC437D +Regulator_Switching:SIC438A +Regulator_Switching:SIC438B +Regulator_Switching:SIC438C +Regulator_Switching:SIC438D +Regulator_Switching:ST1S10PHR +Regulator_Switching:ST1S10PUR +Regulator_Switching:ST1S12XX +Regulator_Switching:ST1S14PHR +Regulator_Switching:TDN_5-0910WISM +Regulator_Switching:TDN_5-0911WISM +Regulator_Switching:TDN_5-0912WISM +Regulator_Switching:TDN_5-0913WISM +Regulator_Switching:TDN_5-0915WISM +Regulator_Switching:TDN_5-0919WISM +Regulator_Switching:TDN_5-2410WISM +Regulator_Switching:TDN_5-2411WISM +Regulator_Switching:TDN_5-2412WISM +Regulator_Switching:TDN_5-2413WISM +Regulator_Switching:TDN_5-2415WISM +Regulator_Switching:TDN_5-2419WISM +Regulator_Switching:TDN_5-4810WISM +Regulator_Switching:TDN_5-4811WISM +Regulator_Switching:TDN_5-4812WISM +Regulator_Switching:TDN_5-4813WISM +Regulator_Switching:TDN_5-4815WISM +Regulator_Switching:TDN_5-4819WISM +Regulator_Switching:TL497 +Regulator_Switching:TL497A +Regulator_Switching:TL5001 +Regulator_Switching:TL5001A +Regulator_Switching:TLV61046ADB +Regulator_Switching:TLV61070ADBV +Regulator_Switching:TLV61225DC +Regulator_Switching:TLV62080DSGx +Regulator_Switching:TLV62084ADSGx +Regulator_Switching:TLV62084DSGx +Regulator_Switching:TLV62095RGTx +Regulator_Switching:TLV62565DBVx +Regulator_Switching:TLV62566DBVx +Regulator_Switching:TLV62568ADRL +Regulator_Switching:TLV62568DBV +Regulator_Switching:TLV62568DDC +Regulator_Switching:TLV62568DRL +Regulator_Switching:TLV62569ADRL +Regulator_Switching:TLV62569DBV +Regulator_Switching:TLV62569DDC +Regulator_Switching:TLV62569DRL +Regulator_Switching:TMR_1-0511 +Regulator_Switching:TMR_1-0511SM +Regulator_Switching:TMR_1-0512 +Regulator_Switching:TMR_1-0512SM +Regulator_Switching:TMR_1-0513 +Regulator_Switching:TMR_1-0513SM +Regulator_Switching:TMR_1-0515 +Regulator_Switching:TMR_1-0522 +Regulator_Switching:TMR_1-0522SM +Regulator_Switching:TMR_1-0523 +Regulator_Switching:TMR_1-0523SM +Regulator_Switching:TMR_1-1211 +Regulator_Switching:TMR_1-1211SM +Regulator_Switching:TMR_1-1212 +Regulator_Switching:TMR_1-1212SM +Regulator_Switching:TMR_1-1213 +Regulator_Switching:TMR_1-1213SM +Regulator_Switching:TMR_1-1215 +Regulator_Switching:TMR_1-1222 +Regulator_Switching:TMR_1-1222SM +Regulator_Switching:TMR_1-1223 +Regulator_Switching:TMR_1-1223SM +Regulator_Switching:TMR_1-2411 +Regulator_Switching:TMR_1-2411SM +Regulator_Switching:TMR_1-2412 +Regulator_Switching:TMR_1-2412SM +Regulator_Switching:TMR_1-2413 +Regulator_Switching:TMR_1-2413SM +Regulator_Switching:TMR_1-2415 +Regulator_Switching:TMR_1-2422 +Regulator_Switching:TMR_1-2422SM +Regulator_Switching:TMR_1-2423 +Regulator_Switching:TMR_1-2423SM +Regulator_Switching:TMR_1-4811 +Regulator_Switching:TMR_1-4811SM +Regulator_Switching:TMR_1-4812 +Regulator_Switching:TMR_1-4812SM +Regulator_Switching:TMR_1-4813 +Regulator_Switching:TMR_1-4813SM +Regulator_Switching:TMR_1-4815 +Regulator_Switching:TMR_1-4822 +Regulator_Switching:TMR_1-4822SM +Regulator_Switching:TMR_1-4823 +Regulator_Switching:TMR_1-4823SM +Regulator_Switching:TNY263G +Regulator_Switching:TNY263P +Regulator_Switching:TNY264G +Regulator_Switching:TNY264P +Regulator_Switching:TNY265G +Regulator_Switching:TNY265P +Regulator_Switching:TNY266G +Regulator_Switching:TNY266P +Regulator_Switching:TNY267G +Regulator_Switching:TNY267P +Regulator_Switching:TNY268G +Regulator_Switching:TNY268P +Regulator_Switching:TNY274G +Regulator_Switching:TNY274P +Regulator_Switching:TNY275G +Regulator_Switching:TNY275P +Regulator_Switching:TNY276G +Regulator_Switching:TNY276P +Regulator_Switching:TNY277G +Regulator_Switching:TNY277P +Regulator_Switching:TNY278G +Regulator_Switching:TNY278P +Regulator_Switching:TNY279G +Regulator_Switching:TNY279P +Regulator_Switching:TNY280G +Regulator_Switching:TNY280P +Regulator_Switching:TNY284D +Regulator_Switching:TNY284K +Regulator_Switching:TNY284P +Regulator_Switching:TNY285D +Regulator_Switching:TNY285K +Regulator_Switching:TNY285P +Regulator_Switching:TNY286D +Regulator_Switching:TNY286K +Regulator_Switching:TNY286P +Regulator_Switching:TNY287D +Regulator_Switching:TNY287K +Regulator_Switching:TNY287P +Regulator_Switching:TNY288D +Regulator_Switching:TNY288K +Regulator_Switching:TNY288P +Regulator_Switching:TNY289K +Regulator_Switching:TNY289P +Regulator_Switching:TNY290K +Regulator_Switching:TNY290P +Regulator_Switching:TOP100YN +Regulator_Switching:TOP101YN +Regulator_Switching:TOP102YN +Regulator_Switching:TOP103YN +Regulator_Switching:TOP104YN +Regulator_Switching:TOP200YAI +Regulator_Switching:TOP201YAI +Regulator_Switching:TOP202YAI +Regulator_Switching:TOP203YAI +Regulator_Switching:TOP204YAI +Regulator_Switching:TOP209G +Regulator_Switching:TOP209P +Regulator_Switching:TOP210G +Regulator_Switching:TOP210PFI +Regulator_Switching:TOP214YAI +Regulator_Switching:TOP252EG +Regulator_Switching:TOP252EN +Regulator_Switching:TOP252GN +Regulator_Switching:TOP252MN +Regulator_Switching:TOP252PN +Regulator_Switching:TOP253EG +Regulator_Switching:TOP253EN +Regulator_Switching:TOP253GN +Regulator_Switching:TOP253MN +Regulator_Switching:TOP253PN +Regulator_Switching:TOP254EG +Regulator_Switching:TOP254EN +Regulator_Switching:TOP254GN +Regulator_Switching:TOP254MN +Regulator_Switching:TOP254PN +Regulator_Switching:TOP254YN +Regulator_Switching:TOP255EG +Regulator_Switching:TOP255EN +Regulator_Switching:TOP255GN +Regulator_Switching:TOP255LN +Regulator_Switching:TOP255MN +Regulator_Switching:TOP255PN +Regulator_Switching:TOP255YN +Regulator_Switching:TOP256EG +Regulator_Switching:TOP256EN +Regulator_Switching:TOP256GN +Regulator_Switching:TOP256LN +Regulator_Switching:TOP256MN +Regulator_Switching:TOP256PN +Regulator_Switching:TOP256YN +Regulator_Switching:TOP257EG +Regulator_Switching:TOP257EN +Regulator_Switching:TOP257GN +Regulator_Switching:TOP257LN +Regulator_Switching:TOP257MN +Regulator_Switching:TOP257PN +Regulator_Switching:TOP257YN +Regulator_Switching:TOP258EG +Regulator_Switching:TOP258EN +Regulator_Switching:TOP258GN +Regulator_Switching:TOP258LN +Regulator_Switching:TOP258MN +Regulator_Switching:TOP258PN +Regulator_Switching:TOP258YN +Regulator_Switching:TOP259EG +Regulator_Switching:TOP259EN +Regulator_Switching:TOP259LN +Regulator_Switching:TOP259YN +Regulator_Switching:TOP260EG +Regulator_Switching:TOP260EN +Regulator_Switching:TOP260LN +Regulator_Switching:TOP260YN +Regulator_Switching:TOP261EG +Regulator_Switching:TOP261EN +Regulator_Switching:TOP261LN +Regulator_Switching:TOP261YN +Regulator_Switching:TOP262EN +Regulator_Switching:TOP262LN +Regulator_Switching:TOP264EG +Regulator_Switching:TOP264KG +Regulator_Switching:TOP264VG +Regulator_Switching:TOP265EG +Regulator_Switching:TOP265KG +Regulator_Switching:TOP265VG +Regulator_Switching:TOP266EG +Regulator_Switching:TOP266KG +Regulator_Switching:TOP266VG +Regulator_Switching:TOP267EG +Regulator_Switching:TOP267KG +Regulator_Switching:TOP267VG +Regulator_Switching:TOP268EG +Regulator_Switching:TOP268KG +Regulator_Switching:TOP268VG +Regulator_Switching:TOP269EG +Regulator_Switching:TOP269KG +Regulator_Switching:TOP269VG +Regulator_Switching:TOP270EG +Regulator_Switching:TOP270KG +Regulator_Switching:TOP270VG +Regulator_Switching:TOP271EG +Regulator_Switching:TOP271KG +Regulator_Switching:TOP271VG +Regulator_Switching:TOS06-05SIL +Regulator_Switching:TOS06-12SIL +Regulator_Switching:TPS51363 +Regulator_Switching:TPS5403 +Regulator_Switching:TPS54061DRB +Regulator_Switching:TPS54202DDC +Regulator_Switching:TPS5420D +Regulator_Switching:TPS54233 +Regulator_Switching:TPS54260DGQ +Regulator_Switching:TPS54260DRC +Regulator_Switching:TPS54302 +Regulator_Switching:TPS54308 +Regulator_Switching:TPS5430DDA +Regulator_Switching:TPS5431DDA +Regulator_Switching:TPS54336ADDA +Regulator_Switching:TPS54340DDA +Regulator_Switching:TPS54360DDA +Regulator_Switching:TPS54560BDDA +Regulator_Switching:TPS560200 +Regulator_Switching:TPS562200 +Regulator_Switching:TPS562202 +Regulator_Switching:TPS562202S +Regulator_Switching:TPS562203 +Regulator_Switching:TPS562206 +Regulator_Switching:TPS563200 +Regulator_Switching:TPS563202S +Regulator_Switching:TPS563203 +Regulator_Switching:TPS563206 +Regulator_Switching:TPS563240DDC +Regulator_Switching:TPS563300 +Regulator_Switching:TPS56339DDC +Regulator_Switching:TPS565208 +Regulator_Switching:TPS56528DDA +Regulator_Switching:TPS568215RNN +Regulator_Switching:TPS61040DBV +Regulator_Switching:TPS61040DDC +Regulator_Switching:TPS61040DRV +Regulator_Switching:TPS61041DBV +Regulator_Switching:TPS61041DDC +Regulator_Switching:TPS61041DRV +Regulator_Switching:TPS61085DGK +Regulator_Switching:TPS61085PW +Regulator_Switching:TPS61089 +Regulator_Switching:TPS610891 +Regulator_Switching:TPS61090 +Regulator_Switching:TPS61091 +Regulator_Switching:TPS61092 +Regulator_Switching:TPS610991DRV +Regulator_Switching:TPS610992DRV +Regulator_Switching:TPS610993DRV +Regulator_Switching:TPS610994DRV +Regulator_Switching:TPS610995DRV +Regulator_Switching:TPS610996DRV +Regulator_Switching:TPS610997DRV +Regulator_Switching:TPS61099DRV +Regulator_Switching:TPS61200DRC +Regulator_Switching:TPS61201DRC +Regulator_Switching:TPS61202DRC +Regulator_Switching:TPS61202DSC +Regulator_Switching:TPS61220DCK +Regulator_Switching:TPS61221DCK +Regulator_Switching:TPS61222DCK +Regulator_Switching:TPS61230DRC +Regulator_Switching:TPS61252DSG +Regulator_Switching:TPS613221ADBV +Regulator_Switching:TPS613221ADBZ +Regulator_Switching:TPS613222ADBV +Regulator_Switching:TPS613222ADBZ +Regulator_Switching:TPS613223ADBV +Regulator_Switching:TPS613223ADBZ +Regulator_Switching:TPS613224ADBV +Regulator_Switching:TPS613224ADBZ +Regulator_Switching:TPS613225ADBV +Regulator_Switching:TPS613225ADBZ +Regulator_Switching:TPS613226ADBV +Regulator_Switching:TPS613226ADBZ +Regulator_Switching:TPS61322DBZ +Regulator_Switching:TPS62056DGS +Regulator_Switching:TPS62125DSG +Regulator_Switching:TPS62130 +Regulator_Switching:TPS62130A +Regulator_Switching:TPS62131 +Regulator_Switching:TPS62132 +Regulator_Switching:TPS62133 +Regulator_Switching:TPS62140 +Regulator_Switching:TPS62140A +Regulator_Switching:TPS62141 +Regulator_Switching:TPS62142 +Regulator_Switching:TPS62143 +Regulator_Switching:TPS62150 +Regulator_Switching:TPS62150A +Regulator_Switching:TPS62151 +Regulator_Switching:TPS62152 +Regulator_Switching:TPS62153 +Regulator_Switching:TPS62160DGK +Regulator_Switching:TPS62160DSG +Regulator_Switching:TPS62161DSG +Regulator_Switching:TPS62162DSG +Regulator_Switching:TPS62163DSG +Regulator_Switching:TPS62170DSG +Regulator_Switching:TPS62171DSG +Regulator_Switching:TPS62172DSG +Regulator_Switching:TPS62173DSG +Regulator_Switching:TPS62175DQC +Regulator_Switching:TPS62177DQC +Regulator_Switching:TPS62200DBV +Regulator_Switching:TPS62201DBV +Regulator_Switching:TPS62202DBV +Regulator_Switching:TPS62203DBV +Regulator_Switching:TPS62204DBV +Regulator_Switching:TPS62205DBV +Regulator_Switching:TPS62207DBV +Regulator_Switching:TPS62208DBV +Regulator_Switching:TPS62821DLC +Regulator_Switching:TPS62822DLC +Regulator_Switching:TPS62823DLC +Regulator_Switching:TPS628436DRL +Regulator_Switching:TPS628436YKA +Regulator_Switching:TPS628437DRL +Regulator_Switching:TPS628437YKA +Regulator_Switching:TPS628438DRL +Regulator_Switching:TPS628438YKA +Regulator_Switching:TPS62912 +Regulator_Switching:TPS62913 +Regulator_Switching:TPS62932 +Regulator_Switching:TPS62933 +Regulator_Switching:TPS62933F +Regulator_Switching:TPS62933O +Regulator_Switching:TPS62933P +Regulator_Switching:TPS62A01ADRL +Regulator_Switching:TPS62A01APDDC +Regulator_Switching:TPS62A01DRL +Regulator_Switching:TPS62A01PDDC +Regulator_Switching:TPS62A02ADRL +Regulator_Switching:TPS62A02APDDC +Regulator_Switching:TPS62A02DRL +Regulator_Switching:TPS62A02NADRL +Regulator_Switching:TPS62A02NDRL +Regulator_Switching:TPS62A02PDDC +Regulator_Switching:TPS63000 +Regulator_Switching:TPS63000-Q1 +Regulator_Switching:TPS63001 +Regulator_Switching:TPS63002 +Regulator_Switching:TPS63030DSK +Regulator_Switching:TPS63031DSK +Regulator_Switching:TPS63060 +Regulator_Switching:TPS63061 +Regulator_Switching:TPS63900 +Regulator_Switching:TPS65130RGE +Regulator_Switching:TPS65131RGE +Regulator_Switching:TPS82130 +Regulator_Switching:TPS82140 +Regulator_Switching:TPS82150 +Regulator_Switching:TSR0.6-48120WI +Regulator_Switching:TSR0.6-48150WI +Regulator_Switching:TSR0.6-48240WI +Regulator_Switching:TSR0.6-4833WI +Regulator_Switching:TSR0.6-4850WI +Regulator_Switching:TSR0.6-4865WI +Regulator_Switching:TSR0.6-4890WI +Regulator_Switching:TSR1-2433E +Regulator_Switching:TSR1-2450E +Regulator_Switching:TSR2-24120N +Regulator_Switching:TSR2-2412N +Regulator_Switching:TSR2-24150N +Regulator_Switching:TSR2-2415N +Regulator_Switching:TSR2-2418N +Regulator_Switching:TSR2-2425N +Regulator_Switching:TSR2-2433N +Regulator_Switching:TSR2-2450N +Regulator_Switching:TSR2-2465N +Regulator_Switching:TSR2-2490N +Regulator_Switching:TSR_1-2412 +Regulator_Switching:TSR_1-24120 +Regulator_Switching:TSR_1-2415 +Regulator_Switching:TSR_1-24150 +Regulator_Switching:TSR_1-2418 +Regulator_Switching:TSR_1-2425 +Regulator_Switching:TSR_1-2433 +Regulator_Switching:TSR_1-2450 +Regulator_Switching:TSR_1-2465 +Regulator_Switching:TSR_1-2490 +Regulator_Switching:VIPer22ADIP-E +Regulator_Switching:VIPer22AS +Regulator_Switching:VIPer25HN +Regulator_Switching:VIPer25LN +Regulator_Switching:VIPer26HD +Regulator_Switching:VIPer26HN +Regulator_Switching:VIPer26LD +Regulator_Switching:VIPer26LN +Regulator_Switching:XL1509-12 +Regulator_Switching:XL1509-3.3 +Regulator_Switching:XL1509-5.0 +Regulator_Switching:XL1509-ADJ +Regulator_Switching:XL4015 +Relay:ADW11 +Relay:AZ850-x +Relay:AZ850P1-x +Relay:AZ850P2-x +Relay:AZSR131-1AE-12D +Relay:COTO_3602_Split +Relay:COTO_3650_Split +Relay:COTO_3660_Split +Relay:DIPxx-1Axx-11x +Relay:DIPxx-1Axx-12x +Relay:DIPxx-1Axx-12xD +Relay:DIPxx-1Axx-13x +Relay:DIPxx-1Cxx-51x +Relay:DIPxx-2Axx-21x +Relay:DR-24V +Relay:DR-3V +Relay:DR-5V +Relay:DR-L-3V +Relay:DR-L2-3V_Form1 +Relay:DR-L2-3V_Form2 +Relay:EC2-12NU +Relay:EC2-12SNU +Relay:EC2-12TNU +Relay:EC2-24NU +Relay:EC2-24SNU +Relay:EC2-24TNU +Relay:EC2-3NU +Relay:EC2-3SNU +Relay:EC2-3TNU +Relay:EC2-4.5NU +Relay:EC2-4.5SNU +Relay:EC2-4.5TNU +Relay:EC2-5NU +Relay:EC2-5SNU +Relay:EC2-5TNU +Relay:EE2-12NKX +Relay:EE2-12NU +Relay:EE2-12NUH +Relay:EE2-12NUX +Relay:EE2-12SNU +Relay:EE2-12SNUH +Relay:EE2-12SNUX +Relay:EE2-12TNU +Relay:EE2-12TNUH +Relay:EE2-12TNUX +Relay:EE2-24NU +Relay:EE2-24NUH +Relay:EE2-24NUX +Relay:EE2-24SNU +Relay:EE2-24SNUH +Relay:EE2-24SNUX +Relay:EE2-24TNU +Relay:EE2-24TNUH +Relay:EE2-24TNUX +Relay:EE2-3NKX +Relay:EE2-3NU +Relay:EE2-3NUH +Relay:EE2-3NUX +Relay:EE2-3SNU +Relay:EE2-3SNUH +Relay:EE2-3SNUX +Relay:EE2-3TNU +Relay:EE2-3TNUH +Relay:EE2-3TNUX +Relay:EE2-4.5NKX +Relay:EE2-4.5NU +Relay:EE2-4.5NUH +Relay:EE2-4.5NUX +Relay:EE2-4.5SNU +Relay:EE2-4.5SNUH +Relay:EE2-4.5SNUX +Relay:EE2-4.5TNU +Relay:EE2-4.5TNUH +Relay:EE2-4.5TNUX +Relay:EE2-5NU +Relay:EE2-5NUH +Relay:EE2-5NUX +Relay:EE2-5SNU +Relay:EE2-5SNUH +Relay:EE2-5SNUX +Relay:EE2-5TNU +Relay:EE2-5TNUH +Relay:EE2-5TNUX +Relay:FINDER-30.22 +Relay:FINDER-32.21-x000 +Relay:FINDER-32.21-x300 +Relay:FINDER-34.51 +Relay:FINDER-34.51.7xxx.x019 +Relay:FINDER-36.11 +Relay:FINDER-40.11 +Relay:FINDER-40.11-2016 +Relay:FINDER-40.31 +Relay:FINDER-40.41 +Relay:FINDER-40.51 +Relay:FINDER-40.52 +Relay:FINDER-41.52 +Relay:FINDER-44.52 +Relay:FINDER-44.62 +Relay:FRT5 +Relay:FRT5_separated +Relay:Fujitsu_FTR-F1A +Relay:Fujitsu_FTR-F1C +Relay:Fujitsu_FTR-LYAA005x +Relay:Fujitsu_FTR-LYCA005x +Relay:G2RL-1 +Relay:G2RL-1-E +Relay:G2RL-1-H +Relay:G2RL-1A +Relay:G2RL-1A-E +Relay:G2RL-1A-H +Relay:G2RL-2 +Relay:G2RL-2A +Relay:G5LE-1 +Relay:G5NB +Relay:G5Q-1 +Relay:G5Q-1A +Relay:G5V-1 +Relay:G5V-2 +Relay:G5V-2_Split +Relay:G6A +Relay:G6AK +Relay:G6AU +Relay:G6E +Relay:G6EU +Relay:G6H-2 +Relay:G6HU-2 +Relay:G6K-2 +Relay:G6KU-2 +Relay:G6S-2 +Relay:G6SK-2 +Relay:G6SU-2 +Relay:HF115F-2Z-x4 +Relay:HF3-01 +Relay:HF3-02 +Relay:HF3-03 +Relay:HF3-04 +Relay:HF3-05 +Relay:HF3-06 +Relay:HF3-07 +Relay:HF3-51 +Relay:HF3-52 +Relay:HF3-53 +Relay:HF3-54 +Relay:HF3-55 +Relay:HF3-56 +Relay:HF3-57 +Relay:HK19F-DCxxV-SHC +Relay:HONGFA_HFD2-0xx-x-L2-x +Relay:IM00 +Relay:IM01 +Relay:IM02 +Relay:IM03 +Relay:IM04 +Relay:IM05 +Relay:IM06 +Relay:IM07 +Relay:IM08 +Relay:IM11 +Relay:IM12 +Relay:IM13 +Relay:IM16 +Relay:IM17 +Relay:IM21 +Relay:IM22 +Relay:IM23 +Relay:IM26 +Relay:IM40 +Relay:IM41 +Relay:IM42 +Relay:IM43 +Relay:IM44 +Relay:IM45 +Relay:IM46 +Relay:IM47 +Relay:IM48 +Relay:JQC-3FF-005-1H +Relay:JQC-3FF-005-1Z +Relay:JQC-3FF-006-1H +Relay:JQC-3FF-006-1Z +Relay:JQC-3FF-009-1H +Relay:JQC-3FF-009-1Z +Relay:JQC-3FF-012-1H +Relay:JQC-3FF-012-1Z +Relay:JQC-3FF-018-1H +Relay:JQC-3FF-018-1Z +Relay:JQC-3FF-024-1H +Relay:JQC-3FF-024-1Z +Relay:JQC-3FF-048-1H +Relay:JQC-3FF-048-1Z +Relay:JW2 +Relay:MSxx-1Axx-75 +Relay:MSxx-1Bxx-75 +Relay:Panasonic_ALFG1PF09 +Relay:Panasonic_ALFG1PF091 +Relay:Panasonic_ALFG1PF12 +Relay:Panasonic_ALFG1PF121 +Relay:Panasonic_ALFG1PF18 +Relay:Panasonic_ALFG1PF181 +Relay:Panasonic_ALFG1PF24 +Relay:Panasonic_ALFG1PF241 +Relay:Panasonic_ALFG2PF09 +Relay:Panasonic_ALFG2PF091 +Relay:Panasonic_ALFG2PF12 +Relay:Panasonic_ALFG2PF121 +Relay:Panasonic_ALFG2PF18 +Relay:Panasonic_ALFG2PF181 +Relay:Panasonic_ALFG2PF24 +Relay:Panasonic_ALFG2PF241 +Relay:RAYEX-L90 +Relay:RAYEX-L90A +Relay:RAYEX-L90AS +Relay:RAYEX-L90B +Relay:RAYEX-L90BS +Relay:RAYEX-L90S +Relay:RM50-xx21 +Relay:RM84 +Relay:RSM822 +Relay:RT314A03 +Relay:RT314A05 +Relay:RT314A06 +Relay:RT314A12 +Relay:RT314A24 +Relay:RT42xAxx +Relay:RT42xFxx +Relay:RT42xxxx +Relay:RT44xxxx +Relay:RTE2xAxx +Relay:RTE2xFxx +Relay:RTE2xxxx +Relay:RTE4xxxx +Relay:Relay_DPDT +Relay:Relay_DPDT_Latching_1coil +Relay:Relay_DPDT_Latching_2coil +Relay:Relay_DPST-NC +Relay:Relay_DPST-NO +Relay:Relay_DPST_Latching_1coil +Relay:Relay_DPST_Latching_2coil +Relay:Relay_SPDT +Relay:Relay_SPDT_Latching_1coil +Relay:Relay_SPDT_Latching_2coil +Relay:Relay_SPST-NC +Relay:Relay_SPST-NO +Relay:Relay_SPST_Latching_1coil +Relay:Relay_SPST_Latching_2coil +Relay:SANYOU_SRD_Form_A +Relay:SANYOU_SRD_Form_B +Relay:SANYOU_SRD_Form_C +Relay:SILxx-1Axx-71x +Relay:SILxx-1Bxx-71x +Relay:SILxx-1Cxx-51x +Relay:TE_PCH-1xxx2M +Relay:TIANBO-HJR-4102-L +Relay:UMS05-1A80-75D +Relay:UMS05-1A80-75L +Relay:V23072-Cx061-xxx8 +Relay:V23072-Cx062-xxx8 +Relay:Y14x-1C-xxDS +Relay_SolidState:34.81-7048 +Relay_SolidState:34.81-8240 +Relay_SolidState:34.81-9024 +Relay_SolidState:AQH0213 +Relay_SolidState:AQH0213A +Relay_SolidState:AQH0223 +Relay_SolidState:AQH0223A +Relay_SolidState:AQH1213 +Relay_SolidState:AQH1213A +Relay_SolidState:AQH1223 +Relay_SolidState:AQH1223A +Relay_SolidState:AQH2213 +Relay_SolidState:AQH2213A +Relay_SolidState:AQH2223 +Relay_SolidState:AQH2223A +Relay_SolidState:AQH3213 +Relay_SolidState:AQH3213A +Relay_SolidState:AQH3223 +Relay_SolidState:AQH3223A +Relay_SolidState:ASSR-1218 +Relay_SolidState:BC2213A +Relay_SolidState:CPC1002N +Relay_SolidState:CPC1017N +Relay_SolidState:CPC1117N +Relay_SolidState:FOD420 +Relay_SolidState:FOD4208 +Relay_SolidState:FOD4216 +Relay_SolidState:FOD4218 +Relay_SolidState:FODM3011 +Relay_SolidState:FODM3012 +Relay_SolidState:FODM3022 +Relay_SolidState:FODM3023 +Relay_SolidState:FODM3052 +Relay_SolidState:FODM3053 +Relay_SolidState:HHG1D-1 +Relay_SolidState:LAA110 +Relay_SolidState:LBB110 +Relay_SolidState:LCC110 +Relay_SolidState:MOC3010M +Relay_SolidState:MOC3011M +Relay_SolidState:MOC3012M +Relay_SolidState:MOC3020M +Relay_SolidState:MOC3021M +Relay_SolidState:MOC3022M +Relay_SolidState:MOC3023M +Relay_SolidState:MOC3031M +Relay_SolidState:MOC3032M +Relay_SolidState:MOC3033M +Relay_SolidState:MOC3041M +Relay_SolidState:MOC3042M +Relay_SolidState:MOC3043M +Relay_SolidState:MOC3051M +Relay_SolidState:MOC3052M +Relay_SolidState:MOC3061M +Relay_SolidState:MOC3062M +Relay_SolidState:MOC3063M +Relay_SolidState:MOC3081M +Relay_SolidState:MOC3082M +Relay_SolidState:MOC3083M +Relay_SolidState:MOC3162M +Relay_SolidState:MOC3163M +Relay_SolidState:S102S01 +Relay_SolidState:S102S02 +Relay_SolidState:S112S01 +Relay_SolidState:S116S01 +Relay_SolidState:S116S02 +Relay_SolidState:S202S01 +Relay_SolidState:S202S02 +Relay_SolidState:S212S01 +Relay_SolidState:S216S01 +Relay_SolidState:S216S02 +Relay_SolidState:TLP141G +Relay_SolidState:TLP148G +Relay_SolidState:TLP160G +Relay_SolidState:TLP160J +Relay_SolidState:TLP161G +Relay_SolidState:TLP161J +Relay_SolidState:TLP175A +Relay_SolidState:TLP222A +Relay_SolidState:TLP222A-2 +Relay_SolidState:TLP3123 +Relay_SolidState:TLP3542 +Relay_SolidState:TLP3543 +Relay_SolidState:TLP3544 +Relay_SolidState:TLP3545 +Relay_SolidState:TLP3546 +RF:0900PC15J0013 +RF:ADC-10-1R +RF:ADCH-80 +RF:ADCH-80A +RF:ADL5904 +RF:ADP-2-1W +RF:AMK-2-13 +RF:AX5043 +RF:CC1000 +RF:CC1200 +RF:CC2500 +RF:DC4759J5020AHF-1 +RF:DC4759J5020AHF-2 +RF:DW1000 +RF:F113 +RF:F115 +RF:F117 +RF:HMC394LP4 +RF:HMC431 +RF:LAT-3 +RF:LRPS-2-1 +RF:LTC5507ES6 +RF:MAADSS0008 +RF:MAAVSS0004 +RF:MC12080 +RF:MC12093D +RF:MICRF112YMM +RF:MICRF220AYQS +RF:MRF89XA +RF:NRF24L01 +RF:NRF24L01_Breakout +RF:PAT1220-C-0DB +RF:PAT1220-C-10DB +RF:PAT1220-C-1DB +RF:PAT1220-C-2DB +RF:PAT1220-C-3DB +RF:PAT1220-C-4DB +RF:PAT1220-C-5DB +RF:PAT1220-C-6DB +RF:PAT1220-C-7DB +RF:PAT1220-C-8DB +RF:PAT1220-C-9DB +RF:PD4859J5050S2HF +RF:RMK-3-451 +RF:RMK-5-51 +RF:SE5004L +RF:SX1231IMLTRT +RF:SX1261IMLTRT +RF:SX1262IMLTRT +RF:SX1272 +RF:SX1273 +RF:SX1276 +RF:SX1277 +RF:SX1278 +RF:SX1279 +RF:SYPD-1 +RF:SYPD-2 +RF:SYPD-52 +RF:Si4460 +RF:Si4461 +RF:Si4463 +RF:Si4464 +RF:TCP-2-10X +RF:nRF24L01P +RF_Amplifier:AD8313xRM +RF_Amplifier:ADL5541 +RF_Amplifier:ADL5542 +RF_Amplifier:ADL5610 +RF_Amplifier:BGA2800 +RF_Amplifier:BGA2801 +RF_Amplifier:BGA2803 +RF_Amplifier:BGA2815 +RF_Amplifier:BGA2817 +RF_Amplifier:BGA2818 +RF_Amplifier:BGA2850 +RF_Amplifier:BGA2851 +RF_Amplifier:BGA2865 +RF_Amplifier:BGA2866 +RF_Amplifier:BGA2867 +RF_Amplifier:BGA2869 +RF_Amplifier:BGA2870 +RF_Amplifier:BGA2874 +RF_Amplifier:CMX901 +RF_Amplifier:GALI-1 +RF_Amplifier:GALI-19 +RF_Amplifier:GALI-2 +RF_Amplifier:GALI-21 +RF_Amplifier:GALI-24 +RF_Amplifier:GALI-29 +RF_Amplifier:GALI-3 +RF_Amplifier:GALI-33 +RF_Amplifier:GALI-39 +RF_Amplifier:GALI-4 +RF_Amplifier:GALI-49 +RF_Amplifier:GALI-4F +RF_Amplifier:GALI-5 +RF_Amplifier:GALI-51 +RF_Amplifier:GALI-51F +RF_Amplifier:GALI-52 +RF_Amplifier:GALI-55 +RF_Amplifier:GALI-59 +RF_Amplifier:GALI-5F +RF_Amplifier:GALI-6 +RF_Amplifier:GALI-6F +RF_Amplifier:GALI-74 +RF_Amplifier:GALI-84 +RF_Amplifier:GALI-S66 +RF_Amplifier:GVA-123 +RF_Amplifier:GVA-60 +RF_Amplifier:GVA-62 +RF_Amplifier:GVA-63 +RF_Amplifier:GVA-81 +RF_Amplifier:GVA-82 +RF_Amplifier:GVA-83 +RF_Amplifier:GVA-84 +RF_Amplifier:GVA-93 +RF_Amplifier:HMC1099PM5E +RF_Amplifier:HMC8500PM5E +RF_Amplifier:MAX2679 +RF_Amplifier:MAX2679B +RF_Amplifier:MMZ09332BT1 +RF_Amplifier:PGA-102 +RF_Amplifier:PGA-1021 +RF_Amplifier:PGA-103 +RF_Amplifier:PGA-105 +RF_Amplifier:PGA-106-75 +RF_Amplifier:PGA-106R-75 +RF_Amplifier:PGA-122-75 +RF_Amplifier:PGA-32-75 +RF_Amplifier:PHA-1 +RF_Amplifier:PHA-101 +RF_Amplifier:PHA-13HLN +RF_Amplifier:PHA-13LN +RF_Amplifier:PHA-1H +RF_Amplifier:PHA-23HLN +RF_Amplifier:PHA-23LN +RF_Amplifier:QPL9547 +RF_Amplifier:SGL0622Z +RF_Amplifier:SKY65404 +RF_Amplifier:SPF5189Z +RF_Amplifier:TRF37A73 +RF_AM_FM:LA1185 +RF_AM_FM:MCS3142 +RF_AM_FM:SA605D +RF_AM_FM:SA605DK +RF_AM_FM:SA636DK +RF_AM_FM:Si4362 +RF_AM_FM:Si4730-D60-GU +RF_AM_FM:Si4731-D60-GU +RF_AM_FM:Si4734-D60-GU +RF_AM_FM:Si4735-D60-GU +RF_AM_FM:ZETA-433-SO +RF_AM_FM:ZETA-868-SO +RF_AM_FM:ZETA-915-SO +RF_Bluetooth:BL652 +RF_Bluetooth:BM78SPPS5MC2 +RF_Bluetooth:BM78SPPS5NC2 +RF_Bluetooth:BTM112 +RF_Bluetooth:BTM222 +RF_Bluetooth:MOD-nRF8001 +RF_Bluetooth:Microchip_BM83 +RF_Bluetooth:RFD77101 +RF_Bluetooth:RN42 +RF_Bluetooth:RN42N +RF_Bluetooth:RN4871 +RF_Bluetooth:SPBTLE-RF +RF_Bluetooth:SPBTLE-RF0 +RF_Bluetooth:nRF8001 +RF_Filter:B3715 +RF_Filter:BFCN-1445 +RF_Filter:BFCN-1525 +RF_Filter:BFCN-152W-75 +RF_Filter:BFCN-1560 +RF_Filter:BFCN-1575 +RF_Filter:BFCN-1690 +RF_Filter:BFCN-1840 +RF_Filter:BFCN-1855 +RF_Filter:BFCN-1860 +RF_Filter:BFCN-1900 +RF_Filter:BFCN-1945 +RF_Filter:BFCN-2275 +RF_Filter:BFCN-2360 +RF_Filter:BFCN-2435 +RF_Filter:BFCN-2450 +RF_Filter:BFCN-2500 +RF_Filter:BFCN-2555 +RF_Filter:BFCN-2700 +RF_Filter:BFCN-2840 +RF_Filter:BFCN-2850 +RF_Filter:BFCN-2900 +RF_Filter:BFCN-2910 +RF_Filter:BFCN-2975 +RF_Filter:BFCN-3010 +RF_Filter:BFCN-3085 +RF_Filter:BFCN-3085A +RF_Filter:BFCN-3115 +RF_Filter:BFCN-3600 +RF_Filter:BFCN-3700 +RF_Filter:BFCN-4100 +RF_Filter:BFCN-4440 +RF_Filter:BFCN-4800 +RF_Filter:BFCN-5100 +RF_Filter:BFCN-5200 +RF_Filter:BFCN-5540 +RF_Filter:BFCN-5750 +RF_Filter:BFCN-7200 +RF_Filter:BFCN-7331 +RF_Filter:BFCN-7350 +RF_Filter:BFCN-7500 +RF_Filter:BFCN-7700 +RF_Filter:BFCN-7900 +RF_Filter:BFCN-8000 +RF_Filter:BFCN-8350 +RF_Filter:BFCN-8450 +RF_Filter:BFCN-8650 +RF_Filter:BPF-A355 +RF_Filter:HFCN-1000 +RF_Filter:HFCN-1080 +RF_Filter:HFCN-1100 +RF_Filter:HFCN-1150 +RF_Filter:HFCN-1200 +RF_Filter:HFCN-1200D +RF_Filter:HFCN-1300 +RF_Filter:HFCN-1300D +RF_Filter:HFCN-1320 +RF_Filter:HFCN-1320D +RF_Filter:HFCN-1322 +RF_Filter:HFCN-1500 +RF_Filter:HFCN-1500D +RF_Filter:HFCN-1600 +RF_Filter:HFCN-1600D +RF_Filter:HFCN-1760 +RF_Filter:HFCN-1810 +RF_Filter:HFCN-1810D +RF_Filter:HFCN-1910 +RF_Filter:HFCN-1910D +RF_Filter:HFCN-2000 +RF_Filter:HFCN-2100 +RF_Filter:HFCN-2100D +RF_Filter:HFCN-2275 +RF_Filter:HFCN-2700 +RF_Filter:HFCN-2700A +RF_Filter:HFCN-2700AD +RF_Filter:HFCN-3100 +RF_Filter:HFCN-3100D +RF_Filter:HFCN-3500 +RF_Filter:HFCN-3500D +RF_Filter:HFCN-3800 +RF_Filter:HFCN-3800D +RF_Filter:HFCN-440 +RF_Filter:HFCN-4400 +RF_Filter:HFCN-4400D +RF_Filter:HFCN-4600 +RF_Filter:HFCN-5050 +RF_Filter:HFCN-5500 +RF_Filter:HFCN-5500D +RF_Filter:HFCN-6010 +RF_Filter:HFCN-650 +RF_Filter:HFCN-650D +RF_Filter:HFCN-672 +RF_Filter:HFCN-7150 +RF_Filter:HFCN-740 +RF_Filter:HFCN-740D +RF_Filter:HFCN-7971 +RF_Filter:HFCN-8400 +RF_Filter:HFCN-8400D +RF_Filter:HFCN-880 +RF_Filter:HFCN-880D +RF_Filter:HFCN-9700 +RF_Filter:LFCN-1000 +RF_Filter:LFCN-1000D +RF_Filter:LFCN-105 +RF_Filter:LFCN-113 +RF_Filter:LFCN-120 +RF_Filter:LFCN-1200 +RF_Filter:LFCN-1200D +RF_Filter:LFCN-123 +RF_Filter:LFCN-1282 +RF_Filter:LFCN-1325 +RF_Filter:LFCN-1400 +RF_Filter:LFCN-1400D +RF_Filter:LFCN-1450 +RF_Filter:LFCN-1500 +RF_Filter:LFCN-1500D +RF_Filter:LFCN-1525 +RF_Filter:LFCN-1525D +RF_Filter:LFCN-1575 +RF_Filter:LFCN-1575D +RF_Filter:LFCN-160 +RF_Filter:LFCN-1700 +RF_Filter:LFCN-1700D +RF_Filter:LFCN-180 +RF_Filter:LFCN-1800 +RF_Filter:LFCN-1800D +RF_Filter:LFCN-190 +RF_Filter:LFCN-2000 +RF_Filter:LFCN-2000D +RF_Filter:LFCN-225 +RF_Filter:LFCN-2250 +RF_Filter:LFCN-2250D +RF_Filter:LFCN-225D +RF_Filter:LFCN-2290 +RF_Filter:LFCN-2400 +RF_Filter:LFCN-2400D +RF_Filter:LFCN-2500 +RF_Filter:LFCN-2500D +RF_Filter:LFCN-2600 +RF_Filter:LFCN-2600D +RF_Filter:LFCN-2750 +RF_Filter:LFCN-2750D +RF_Filter:LFCN-2850 +RF_Filter:LFCN-2850D +RF_Filter:LFCN-3000 +RF_Filter:LFCN-3000D +RF_Filter:LFCN-320 +RF_Filter:LFCN-320D +RF_Filter:LFCN-3400 +RF_Filter:LFCN-3400D +RF_Filter:LFCN-3800 +RF_Filter:LFCN-3800D +RF_Filter:LFCN-400 +RF_Filter:LFCN-400D +RF_Filter:LFCN-4400 +RF_Filter:LFCN-4400D +RF_Filter:LFCN-490 +RF_Filter:LFCN-490D +RF_Filter:LFCN-5000 +RF_Filter:LFCN-5000D +RF_Filter:LFCN-530 +RF_Filter:LFCN-530D +RF_Filter:LFCN-5500 +RF_Filter:LFCN-5500D +RF_Filter:LFCN-575 +RF_Filter:LFCN-575D +RF_Filter:LFCN-5850 +RF_Filter:LFCN-5850D +RF_Filter:LFCN-6000 +RF_Filter:LFCN-6000D +RF_Filter:LFCN-630 +RF_Filter:LFCN-630D +RF_Filter:LFCN-6400 +RF_Filter:LFCN-6400D +RF_Filter:LFCN-6700 +RF_Filter:LFCN-6700D +RF_Filter:LFCN-7200 +RF_Filter:LFCN-7200D +RF_Filter:LFCN-722 +RF_Filter:LFCN-80 +RF_Filter:LFCN-800 +RF_Filter:LFCN-800D +RF_Filter:LFCN-8400 +RF_Filter:LFCN-8440 +RF_Filter:LFCN-900 +RF_Filter:LFCN-900D +RF_Filter:LFCN-9170 +RF_Filter:LFCN-95 +RF_Filter:LPF-B0R3 +RF_Filter:RBP-280 +RF_Filter:RBPF-246 +RF_Filter:RLP-30 +RF_Filter:SCHF-31 +RF_Filter:STA0232A +RF_Filter:STA1090EC +RF_Filter:SXBP-100 +RF_Filter:SXBP-140 +RF_Filter:SXBP-202 +RF_Filter:SXBP-27R5 +RF_Filter:TA0232A +RF_Filter:TA0970B +RF_GPS:L70-R +RF_GPS:L80-R +RF_GPS:LEA-M8F +RF_GPS:LEA-M8S +RF_GPS:LEA-M8T +RF_GPS:MAX-8C +RF_GPS:MAX-8Q +RF_GPS:MAX-M10S +RF_GPS:MAX-M8C +RF_GPS:MAX-M8Q +RF_GPS:MAX-M8W +RF_GPS:NEO-8Q +RF_GPS:NEO-M8M +RF_GPS:NEO-M8N +RF_GPS:NEO-M8P +RF_GPS:NEO-M8Q +RF_GPS:NEO-M8T +RF_GPS:NEO-M9N +RF_GPS:RXM-GPS-FM +RF_GPS:RXM-GPS-RM +RF_GPS:SAM-M8Q +RF_GPS:SIM28ML +RF_GPS:ZED-F9P +RF_GPS:ZOE-M8G +RF_GPS:ZOE-M8Q +RF_GSM:BC66 +RF_GSM:BC95 +RF_GSM:BG95-M1 +RF_GSM:BG95-M2 +RF_GSM:BG95-M3 +RF_GSM:BG95-M4 +RF_GSM:BG95-M5 +RF_GSM:BG95-M6 +RF_GSM:BG95-M8 +RF_GSM:BG95-MF +RF_GSM:LENA-R8001 +RF_GSM:M95 +RF_GSM:SARA-U201 +RF_GSM:SARA-U260 +RF_GSM:SARA-U270 +RF_GSM:SARA-U280 +RF_GSM:SE150A4 +RF_GSM:SIM7020C +RF_GSM:SIM7020E +RF_GSM:SIM800C +RF_GSM:SIM900 +RF_GSM:UL865 +RF_Mixer:AD831AP +RF_Mixer:ADE-6 +RF_Mixer:ADEX-10 +RF_Mixer:ADL5801 +RF_Mixer:ADL5802 +RF_Mixer:HMC213A +RF_Mixer:HMC213B +RF_Mixer:LT5560 +RF_Module:AST50147-xx +RF_Module:ATSAMR21G18-MR210UA_NoRFPads +RF_Module:AX-SIP-SFEU +RF_Module:AX-SIP-SFEU-API +RF_Module:Ai-Thinker-Ra-01 +RF_Module:Ai-Thinker-Ra-02 +RF_Module:CMWX1ZZABZ-078 +RF_Module:CMWX1ZZABZ-091 +RF_Module:D52MxxM8 +RF_Module:DCTR-52DA +RF_Module:DCTR-52DAT +RF_Module:DWM1000 +RF_Module:DWM1001 +RF_Module:DWM3000 +RF_Module:E18-MS1-PCB +RF_Module:E73-2G4M04S-52810 +RF_Module:E73-2G4M04S-52832 +RF_Module:ESP-07 +RF_Module:ESP-12E +RF_Module:ESP-12F +RF_Module:ESP-WROOM-02 +RF_Module:ESP32-C3-DevKitM-1 +RF_Module:ESP32-C3-WROOM-02 +RF_Module:ESP32-C3-WROOM-02U +RF_Module:ESP32-C6-MINI-1 +RF_Module:ESP32-S2-WROVER +RF_Module:ESP32-S2-WROVER-I +RF_Module:ESP32-S3-MINI-1 +RF_Module:ESP32-S3-MINI-1U +RF_Module:ESP32-S3-WROOM-1 +RF_Module:ESP32-S3-WROOM-2 +RF_Module:ESP32-WROOM-32 +RF_Module:ESP32-WROOM-32D +RF_Module:ESP32-WROOM-32E +RF_Module:ESP32-WROOM-32E-R2 +RF_Module:ESP32-WROOM-32U +RF_Module:ESP32-WROOM-32UE +RF_Module:ESP32-WROOM-32UE-R2 +RF_Module:HT-CT62 +RF_Module:Jadak_Thingmagic_M6e-Nano +RF_Module:MDBT42Q-512K +RF_Module:MDBT50Q-1MV2 +RF_Module:MDBT50Q-512K +RF_Module:MDBT50Q-P1MV2 +RF_Module:MDBT50Q-P512K +RF_Module:MDBT50Q-U1MV2 +RF_Module:MDBT50Q-U512K +RF_Module:MM002 +RF_Module:Particle_P1 +RF_Module:RAK4200 +RF_Module:RAK811-HF-AS923 +RF_Module:RAK811-HF-AU915 +RF_Module:RAK811-HF-EU868 +RF_Module:RAK811-HF-IN865 +RF_Module:RAK811-HF-KR920 +RF_Module:RAK811-HF-US915 +RF_Module:RAK811-LF-CN470 +RF_Module:RAK811-LF-EU433 +RF_Module:RFM69HCW +RF_Module:RFM69HW +RF_Module:RFM69W +RF_Module:RFM95W-868S2 +RF_Module:RFM95W-915S2 +RF_Module:RFM96W-315S2 +RF_Module:RFM96W-433S2 +RF_Module:RFM97W-868S2 +RF_Module:RFM97W-915S2 +RF_Module:RFM98W-315S2 +RF_Module:RFM98W-433S2 +RF_Module:STM32WB5MMG +RF_Module:TD1205 +RF_Module:TD1208 +RF_Module:TR-52DA +RF_Module:TR-52DAT +RF_Module:TR-72DA +RF_Module:TR-72DAT +RF_Module:WEMOS_C3_mini +RF_Module:WEMOS_D1_mini +RF_Module:iM880A +RF_Module:iM880B +RF_NFC:PN5321A3HN_C1xx +RF_NFC:ST25DV04K-IER8C3 +RF_NFC:ST25DV04K-JFR6D3 +RF_NFC:ST25DV16K-IER8C3 +RF_NFC:ST25DV16K-JFR6D3 +RF_NFC:ST25DV64K-IER8C3 +RF_NFC:ST25DV64K-JFR6D3 +RF_NFC:ST25R3911B-AQF +RF_NFC:ST25R3911B-AQW +RF_RFID:HTRC11001T +RF_RFID:PN5120A0HN1 +RF_Switch:ADG901BCPZ +RF_Switch:ADG901BRMZ +RF_Switch:ADG902BRMZ +RF_Switch:ADG918BCPZ +RF_Switch:ADG918BRM +RF_Switch:ADG919BCPZ +RF_Switch:ADG919BRMZ +RF_Switch:AS179-92LF +RF_Switch:BGS12WN6E6327 +RF_Switch:HMC7992 +RF_Switch:HMC849A +RF_Switch:KSW-2-46 +RF_Switch:KSWA-2-46 +RF_Switch:KSWHA-1-20 +RF_Switch:MASW-007221 +RF_Switch:MASWSS0115 +RF_Switch:MASWSS0136 +RF_Switch:MASWSS0143 +RF_Switch:MASWSS0151 +RF_Switch:MASWSS0166 +RF_Switch:MASWSS0176 +RF_Switch:MASWSS0178 +RF_Switch:MASWSS0179 +RF_Switch:MASWSS0192 +RF_Switch:MSW-2-20 +RF_Switch:MSW2-50 +RF_Switch:MSWA-2-20 +RF_Switch:MSWA2-50 +RF_Switch:SKY13380-350LF +RF_Switch:SKY13575-639LF +RF_WiFi:HF-A11-SMT +RF_WiFi:USR-C322 +RF_ZigBee:CC2520 +RF_ZigBee:MC13192 +RF_ZigBee:MW-R-DP-W +RF_ZigBee:MW-R-WX +RF_ZigBee:TWE-L-DP-W +RF_ZigBee:TWE-L-WX +RF_ZigBee:XBee_SMT +Security:ATAES132A-SH +Security:ATECC508A-MAHDA +Security:ATECC508A-SSHDA +Security:ATECC608A-MAHDA +Security:ATECC608A-SSHDA +Security:ATECC608B-MAHDA +Security:ATECC608B-SSHDA +Sensor:ADE7758 +Sensor:ADE7763xRS +Sensor:ADE7953xCP +Sensor:AM2302 +Sensor:APDS-9960 +Sensor:AS3935 +Sensor:BL0937 +Sensor:BME280 +Sensor:BME680 +Sensor:CHT11 +Sensor:DHT11 +Sensor:INA260 +Sensor:LTC2990 +Sensor:MAX30102 +Sensor:Nuclear-Radiation_Detector +Sensor:RPR-0521RS +Sensor:SHT1x +Sensor_Audio:ICS-43434 +Sensor_Audio:IM69D120 +Sensor_Audio:IM69D130 +Sensor_Audio:IM73A135V01 +Sensor_Audio:MP45DT02 +Sensor_Audio:SPH0641LU4H-1 +Sensor_Audio:SPH0645LM4H +Sensor_Audio:SPM0687LR5H-1 +Sensor_Current:A1363xKTTN-1 +Sensor_Current:A1363xKTTN-10 +Sensor_Current:A1363xKTTN-2 +Sensor_Current:A1363xKTTN-5 +Sensor_Current:A1365xKTTN-1 +Sensor_Current:A1365xKTTN-10 +Sensor_Current:A1365xKTTN-2 +Sensor_Current:A1365xKTTN-5 +Sensor_Current:A1366xKTTN-1 +Sensor_Current:A1366xKTTN-10 +Sensor_Current:A1366xKTTN-2 +Sensor_Current:A1366xKTTN-5 +Sensor_Current:A1367xKTTN-1 +Sensor_Current:A1367xKTTN-10 +Sensor_Current:A1367xKTTN-2 +Sensor_Current:A1367xKTTN-5 +Sensor_Current:A1369xUA-10 +Sensor_Current:A1369xUA-24 +Sensor_Current:ACS706xLC-05C +Sensor_Current:ACS709xLFTR-10BB +Sensor_Current:ACS709xLFTR-20BB +Sensor_Current:ACS709xLFTR-35BB +Sensor_Current:ACS709xLFTR-6BB +Sensor_Current:ACS710xLATR-10BB +Sensor_Current:ACS710xLATR-10BB-NL +Sensor_Current:ACS710xLATR-12BB +Sensor_Current:ACS710xLATR-12BB-NL +Sensor_Current:ACS710xLATR-25BB +Sensor_Current:ACS710xLATR-25BB-NL +Sensor_Current:ACS710xLATR-6BB +Sensor_Current:ACS710xLATR-6BB-NL +Sensor_Current:ACS711xEXLT-15AB +Sensor_Current:ACS711xEXLT-31AB +Sensor_Current:ACS711xLCTR-12AB +Sensor_Current:ACS711xLCTR-25AB +Sensor_Current:ACS712xLCTR-05B +Sensor_Current:ACS712xLCTR-20A +Sensor_Current:ACS712xLCTR-30A +Sensor_Current:ACS713xLCTR-20A +Sensor_Current:ACS713xLCTR-30A +Sensor_Current:ACS714xLCTR-05B +Sensor_Current:ACS714xLCTR-20A +Sensor_Current:ACS714xLCTR-30A +Sensor_Current:ACS714xLCTR-50A +Sensor_Current:ACS715xLCTR-20A +Sensor_Current:ACS715xLCTR-30A +Sensor_Current:ACS716xLATR-12BB +Sensor_Current:ACS716xLATR-12BB-NL +Sensor_Current:ACS716xLATR-25BB +Sensor_Current:ACS716xLATR-25BB-NL +Sensor_Current:ACS716xLATR-6BB +Sensor_Current:ACS716xLATR-6BB-NL +Sensor_Current:ACS717xMATR-10B +Sensor_Current:ACS717xMATR-20B +Sensor_Current:ACS718xMATR-10B +Sensor_Current:ACS718xMATR-20B +Sensor_Current:ACS720xMATR-15B +Sensor_Current:ACS720xMATR-35B +Sensor_Current:ACS720xMATR-65B +Sensor_Current:ACS722xLCTR-05AB +Sensor_Current:ACS722xLCTR-10AB +Sensor_Current:ACS722xLCTR-10AU +Sensor_Current:ACS722xLCTR-20AB +Sensor_Current:ACS722xLCTR-20AU +Sensor_Current:ACS722xLCTR-40AB +Sensor_Current:ACS722xLCTR-40AU +Sensor_Current:ACS722xMATR-10AB +Sensor_Current:ACS722xMATR-20AB +Sensor_Current:ACS722xMATR-40AB +Sensor_Current:ACS723xLCTR-05AB +Sensor_Current:ACS723xLCTR-10AB +Sensor_Current:ACS723xLCTR-10AU +Sensor_Current:ACS723xLCTR-20AB +Sensor_Current:ACS723xLCTR-20AU +Sensor_Current:ACS723xLCTR-40AB +Sensor_Current:ACS723xLCTR-40AU +Sensor_Current:ACS723xMATR-10AB +Sensor_Current:ACS723xMATR-20AB +Sensor_Current:ACS723xMATR-40AB +Sensor_Current:ACS724xLCTR-05AB +Sensor_Current:ACS724xLCTR-10AB +Sensor_Current:ACS724xLCTR-10AU +Sensor_Current:ACS724xLCTR-20AB +Sensor_Current:ACS724xLCTR-20AU +Sensor_Current:ACS724xLCTR-30AB +Sensor_Current:ACS724xLCTR-30AU +Sensor_Current:ACS724xLCTR-50AB +Sensor_Current:ACS724xMATR-12AB +Sensor_Current:ACS724xMATR-20AB +Sensor_Current:ACS724xMATR-30AB +Sensor_Current:ACS724xMATR-30AU +Sensor_Current:ACS724xMATR-65AB +Sensor_Current:ACS725xLCTR-10AU +Sensor_Current:ACS725xLCTR-20AB +Sensor_Current:ACS725xLCTR-20AU +Sensor_Current:ACS725xLCTR-30AB +Sensor_Current:ACS725xLCTR-30AU +Sensor_Current:ACS725xLCTR-40AB +Sensor_Current:ACS725xLCTR-50AB +Sensor_Current:ACS725xMATR-20AB +Sensor_Current:ACS725xMATR-30AB +Sensor_Current:ACS725xMATR-30AU +Sensor_Current:ACS726xLFTR-20B +Sensor_Current:ACS726xLFTR-40B +Sensor_Current:ACS730xLCTR-20AB +Sensor_Current:ACS730xLCTR-40AB +Sensor_Current:ACS730xLCTR-40AU +Sensor_Current:ACS730xLCTR-50AB +Sensor_Current:ACS730xLCTR-80AU +Sensor_Current:ACS732xLATR-40AB +Sensor_Current:ACS73369xUAA-010B5 +Sensor_Current:ACS733xLATR-20AB +Sensor_Current:ACS733xLATR-40AB +Sensor_Current:ACS733xLATR-40AU +Sensor_Current:ACS733xLATR-65AB +Sensor_Current:ACS756xCB-050B-PFF +Sensor_Current:ACS756xCB-100B-PFF +Sensor_Current:ACS758xCB-050B-PFF +Sensor_Current:ACS758xCB-050U-PFF +Sensor_Current:ACS758xCB-100B-PFF +Sensor_Current:ACS758xCB-100B-PSF +Sensor_Current:ACS758xCB-100U-PFF +Sensor_Current:ACS758xCB-150B-PFF +Sensor_Current:ACS758xCB-150B-PSS +Sensor_Current:ACS758xCB-150U-PFF +Sensor_Current:ACS758xCB-150U-PSF +Sensor_Current:ACS758xCB-200B-PFF +Sensor_Current:ACS758xCB-200B-PSF +Sensor_Current:ACS758xCB-200B-PSS +Sensor_Current:ACS758xCB-200U-PFF +Sensor_Current:ACS758xCB-200U-PSF +Sensor_Current:ACS759xCB-050B-PFF +Sensor_Current:ACS759xCB-100B-PFF +Sensor_Current:ACS759xCB-150B-PFF +Sensor_Current:ACS759xCB-150B-PSS +Sensor_Current:ACS759xCB-200B-PFF +Sensor_Current:ACS759xCB-200B-PSS +Sensor_Current:ACS770xCB-050B-PFF +Sensor_Current:ACS770xCB-050U-PFF +Sensor_Current:ACS770xCB-100B-PFF +Sensor_Current:ACS770xCB-100U-PFF +Sensor_Current:ACS770xCB-100U-PSF +Sensor_Current:ACS770xCB-150B-PFF +Sensor_Current:ACS770xCB-150B-PSF +Sensor_Current:ACS770xCB-150U-PFF +Sensor_Current:ACS770xCB-150U-PSF +Sensor_Current:ACS770xCB-200B-PFF +Sensor_Current:ACS770xCB-200B-PSF +Sensor_Current:ACS770xCB-200U-PFF +Sensor_Current:ACS770xCB-200U-PSF +Sensor_Current:ACS780xLRTR-050B +Sensor_Current:ACS780xLRTR-050U +Sensor_Current:ACS780xLRTR-100B +Sensor_Current:ACS780xLRTR-100U +Sensor_Current:ACS780xLRTR-150B +Sensor_Current:ACS780xLRTR-150U +Sensor_Current:ACS781xLRTR-050B +Sensor_Current:ACS781xLRTR-050U +Sensor_Current:ACS781xLRTR-100B +Sensor_Current:ACS781xLRTR-100U +Sensor_Current:ACS781xLRTR-150B +Sensor_Current:ACS781xLRTR-150U +Sensor_Current:CKSR_15-NP +Sensor_Current:CKSR_25-NP +Sensor_Current:CKSR_50-NP +Sensor_Current:CKSR_50-NP-SP1 +Sensor_Current:CKSR_6-NP +Sensor_Current:CKSR_75-NP +Sensor_Current:CQ-2063 +Sensor_Current:CQ-2064 +Sensor_Current:CQ-2065 +Sensor_Current:CQ-206A +Sensor_Current:CQ-206B +Sensor_Current:CQ-2092 +Sensor_Current:CQ-2093 +Sensor_Current:CQ-209A +Sensor_Current:CQ-209B +Sensor_Current:CQ-209D +Sensor_Current:CQ-2232 +Sensor_Current:CQ-2233 +Sensor_Current:CQ-2234 +Sensor_Current:CQ-2235 +Sensor_Current:CQ-2332 +Sensor_Current:CQ-2333 +Sensor_Current:CQ-2334 +Sensor_Current:CQ-2335 +Sensor_Current:CQ-2336 +Sensor_Current:CQ-236B +Sensor_Current:CQ-3200 +Sensor_Current:CQ-3201 +Sensor_Current:CQ-3202 +Sensor_Current:CQ-3203 +Sensor_Current:CQ-3204 +Sensor_Current:CQ-320A +Sensor_Current:CQ-320B +Sensor_Current:CQ-3300 +Sensor_Current:CQ-3301 +Sensor_Current:CQ-3302 +Sensor_Current:CQ-3303 +Sensor_Current:CQ-330A +Sensor_Current:CQ-330B +Sensor_Current:CQ-330E +Sensor_Current:CQ-330F +Sensor_Current:CQ-330G +Sensor_Current:CQ-330H +Sensor_Current:CQ-330J +Sensor_Current:CSLW6B1 +Sensor_Current:CSLW6B200M +Sensor_Current:CSLW6B40M +Sensor_Current:CSLW6B5 +Sensor_Current:CZ-3813 +Sensor_Current:CZ-3814 +Sensor_Current:CZ-3815 +Sensor_Current:HO120-NP +Sensor_Current:HO128-NP +Sensor_Current:HO15-NP +Sensor_Current:HO15-NPxSP33 +Sensor_Current:HO15-NSM +Sensor_Current:HO150-NP +Sensor_Current:HO25-NP +Sensor_Current:HO25-NPxSP33 +Sensor_Current:HO25-NSM +Sensor_Current:HO40-NP +Sensor_Current:HO60-NP +Sensor_Current:HO8-NP +Sensor_Current:HO8-NPxSP33 +Sensor_Current:HO8-NSM +Sensor_Current:HTFS200-P +Sensor_Current:HTFS400-P +Sensor_Current:HTFS600-P +Sensor_Current:HTFS800-P +Sensor_Current:HX02-P +Sensor_Current:HX03-P-SP2 +Sensor_Current:HX04-P +Sensor_Current:HX05-NP +Sensor_Current:HX05-P-SP2 +Sensor_Current:HX06-P +Sensor_Current:HX10-NP +Sensor_Current:HX10-P-SP2 +Sensor_Current:HX15-NP +Sensor_Current:HX15-P-SP2 +Sensor_Current:HX20-P-SP2 +Sensor_Current:HX25-P-SP2 +Sensor_Current:HX50-P-SP2 +Sensor_Current:IR2175 +Sensor_Current:IR21771S +Sensor_Current:IR2177S +Sensor_Current:IR22771S +Sensor_Current:IR2277S +Sensor_Current:IR25750L +Sensor_Current:LA100-P +Sensor_Current:LA25-NP +Sensor_Current:LA25-P +Sensor_Current:LA55-P +Sensor_Current:LTSR15-NP +Sensor_Current:LTSR25-NP +Sensor_Current:LTSR6-NP +Sensor_Current:MCA1101-20-3 +Sensor_Current:MCA1101-20-5 +Sensor_Current:MCA1101-5-3 +Sensor_Current:MCA1101-5-5 +Sensor_Current:MCA1101-50-3 +Sensor_Current:MCA1101-50-5 +Sensor_Current:MCA1101-65-5 +Sensor_Distance:TMF8820 +Sensor_Distance:TMF8821 +Sensor_Distance:TMF8828 +Sensor_Distance:VL53L0CXV0DH1 +Sensor_Distance:VL53L1CXV0FY1 +Sensor_Energy:ATM90E26-YU +Sensor_Energy:INA219AxD +Sensor_Energy:INA219AxDCN +Sensor_Energy:INA219BxD +Sensor_Energy:INA219BxDCN +Sensor_Energy:INA226 +Sensor_Energy:INA228 +Sensor_Energy:INA233 +Sensor_Energy:INA237 +Sensor_Energy:INA238 +Sensor_Energy:LTC4151xMS +Sensor_Energy:MCP39F521 +Sensor_Energy:PAC1931x-xJ6CX +Sensor_Energy:PAC1932x-xJ6CX +Sensor_Energy:PAC1932x-xJQ +Sensor_Energy:PAC1933x-xJ6CX +Sensor_Energy:PAC1933x-xJQ +Sensor_Energy:PAC1934x-xJ6CX +Sensor_Energy:PAC1934x-xJQ +Sensor_Energy:PAC1941x-1x-4MX +Sensor_Energy:PAC1941x-1x-J6CX +Sensor_Energy:PAC1941x-2x-4MX +Sensor_Energy:PAC1941x-2x-J6CX +Sensor_Energy:PAC1942x-1x-4MX +Sensor_Energy:PAC1942x-1x-J6CX +Sensor_Energy:PAC1942x-2x-4MX +Sensor_Energy:PAC1942x-2x-J6CX +Sensor_Energy:PAC1943x-x4MX +Sensor_Energy:PAC1943x-xJ6CX +Sensor_Energy:PAC1944x-x4MX +Sensor_Energy:PAC1944x-xJ6CX +Sensor_Energy:PAC1951x-1x-4MX +Sensor_Energy:PAC1951x-1x-J6CX +Sensor_Energy:PAC1951x-2x-4MX +Sensor_Energy:PAC1951x-2x-J6CX +Sensor_Energy:PAC1952x-1x-4MX +Sensor_Energy:PAC1952x-1x-J6CX +Sensor_Energy:PAC1952x-2x-4MX +Sensor_Energy:PAC1952x-2x-J6CX +Sensor_Energy:PAC1953x-x4MX +Sensor_Energy:PAC1953x-xJ6CX +Sensor_Energy:PAC1954x-x4MX +Sensor_Energy:PAC1954x-xJ6CX +Sensor_Gas:004-0-0010 +Sensor_Gas:004-0-0013 +Sensor_Gas:004-0-0050 +Sensor_Gas:004-0-0053 +Sensor_Gas:004-0-0071 +Sensor_Gas:004-0-0075 +Sensor_Gas:3SP-H2S-50_110-304 +Sensor_Gas:CCS811 +Sensor_Gas:GM-402B +Sensor_Gas:LuminOX_LOX-O2 +Sensor_Gas:MQ-6 +Sensor_Gas:MiCS-5524 +Sensor_Gas:SCD40-D-R2 +Sensor_Gas:SCD41-D-R2 +Sensor_Gas:TGS-5141 +Sensor_Humidity:ENS210 +Sensor_Humidity:HDC1080 +Sensor_Humidity:HDC2080 +Sensor_Humidity:SHT30-DIS +Sensor_Humidity:SHT30A-DIS +Sensor_Humidity:SHT31-DIS +Sensor_Humidity:SHT31A-DIS +Sensor_Humidity:SHT35-DIS +Sensor_Humidity:SHT35A-DIS +Sensor_Humidity:SHT4x +Sensor_Humidity:SHTC1 +Sensor_Humidity:SHTC3 +Sensor_Humidity:Si7020-A20 +Sensor_Humidity:Si7021-A20 +Sensor_Magnetic:A1101ELHL +Sensor_Magnetic:A1101LLHL +Sensor_Magnetic:A1102ELHL +Sensor_Magnetic:A1102LLHL +Sensor_Magnetic:A1103ELHL +Sensor_Magnetic:A1103LLHL +Sensor_Magnetic:A1104LLHL +Sensor_Magnetic:A1106LLHL +Sensor_Magnetic:A1301EUA-T +Sensor_Magnetic:A1301KLHLT-T +Sensor_Magnetic:A1301KUA-T +Sensor_Magnetic:A1302ELHLT-T +Sensor_Magnetic:A1302KLHLT-T +Sensor_Magnetic:A1302KUA-T +Sensor_Magnetic:A3214ELHLT-T +Sensor_Magnetic:AH1806-P +Sensor_Magnetic:AH1806-W +Sensor_Magnetic:AH1806-Z +Sensor_Magnetic:AK7452 +Sensor_Magnetic:AS5045B +Sensor_Magnetic:AS5047D +Sensor_Magnetic:AS5048A +Sensor_Magnetic:AS5048B +Sensor_Magnetic:AS5050A +Sensor_Magnetic:AS5055A +Sensor_Magnetic:BM1422AGMV +Sensor_Magnetic:BMM150 +Sensor_Magnetic:DRV5033AJxDBZ +Sensor_Magnetic:DRV5033AJxLPG +Sensor_Magnetic:DRV5033FAxDBZ +Sensor_Magnetic:DRV5033FAxLPG +Sensor_Magnetic:DRV5055A1xDBZxQ1 +Sensor_Magnetic:DRV5055A1xLPGxQ1 +Sensor_Magnetic:DRV5055A2xDBZxQ1 +Sensor_Magnetic:DRV5055A2xLPGxQ1 +Sensor_Magnetic:DRV5055A3xDBZxQ1 +Sensor_Magnetic:DRV5055A3xLPGxQ1 +Sensor_Magnetic:DRV5055A4xDBZxQ1 +Sensor_Magnetic:DRV5055A4xLPGxQ1 +Sensor_Magnetic:IST8308 +Sensor_Magnetic:IST8310 +Sensor_Magnetic:LIS2MDL +Sensor_Magnetic:LIS3MDL +Sensor_Magnetic:MA730 +Sensor_Magnetic:MMC5633NJL +Sensor_Magnetic:MMC5883MA +Sensor_Magnetic:MT6701CT +Sensor_Magnetic:MT6701QT +Sensor_Magnetic:MT6816CT +Sensor_Magnetic:SM351LT +Sensor_Magnetic:SM353LT +Sensor_Magnetic:Si7210-B-xx-IM2 +Sensor_Magnetic:Si7210-B-xx-IV +Sensor_Magnetic:TLE5012B +Sensor_Magnetic:TLV493D +Sensor_Magnetic:TMAG5110A2xxDBV +Sensor_Magnetic:TMAG5110A4xxDBV +Sensor_Magnetic:TMAG5110B2xxDBV +Sensor_Magnetic:TMAG5110B4xxDBV +Sensor_Magnetic:TMAG5110C2xxDBV +Sensor_Magnetic:TMAG5110C4xxDBV +Sensor_Magnetic:TMAG5111A2xxDBV +Sensor_Magnetic:TMAG5111A4xxDBV +Sensor_Magnetic:TMAG5111B2xxDBV +Sensor_Magnetic:TMAG5111B4xxDBV +Sensor_Magnetic:TMAG5111C2xxDBV +Sensor_Magnetic:TMAG5111C4xxDBV +Sensor_Magnetic:TMAG5170-Q1 +Sensor_Motion:ADXL343 +Sensor_Motion:ADXL363 +Sensor_Motion:BMF055 +Sensor_Motion:BMI160 +Sensor_Motion:BNO055 +Sensor_Motion:ICM-20602 +Sensor_Motion:ICM-20948 +Sensor_Motion:IIM-42652 +Sensor_Motion:IIS3DWB +Sensor_Motion:IPS2200 +Sensor_Motion:ISM330DHCX +Sensor_Motion:KX022-1020 +Sensor_Motion:KX122-1042 +Sensor_Motion:KX222-1054 +Sensor_Motion:KXTJ3-1057 +Sensor_Motion:L3GD20 +Sensor_Motion:LIS2DE12 +Sensor_Motion:LIS2DH +Sensor_Motion:LIS2HH12 +Sensor_Motion:LIS331HH +Sensor_Motion:LIS3DH +Sensor_Motion:LSM303C +Sensor_Motion:LSM303D +Sensor_Motion:LSM303DLHC +Sensor_Motion:LSM6DS3 +Sensor_Motion:LSM6DSL +Sensor_Motion:LSM6DSM +Sensor_Motion:LSM9DS1 +Sensor_Motion:MMA8653FCR1 +Sensor_Motion:MPU-6000 +Sensor_Motion:MPU-6050 +Sensor_Motion:MPU-9150 +Sensor_Motion:MPU-9250 +Sensor_Motion:SC7A20 +Sensor_Optical:A1050 +Sensor_Optical:A1060 +Sensor_Optical:A9013 +Sensor_Optical:A9050 +Sensor_Optical:A9060 +Sensor_Optical:APDS-9251-001 +Sensor_Optical:APDS-9301 +Sensor_Optical:APDS-9306 +Sensor_Optical:APDS-9306-065 +Sensor_Optical:AS7261 +Sensor_Optical:AS7262 +Sensor_Optical:AS7263 +Sensor_Optical:AS72651 +Sensor_Optical:AS7341DLG +Sensor_Optical:AS7343xDLG +Sensor_Optical:BP103 +Sensor_Optical:BP103B +Sensor_Optical:BP103BF +Sensor_Optical:BP104 +Sensor_Optical:BP104-SMD +Sensor_Optical:BPW21 +Sensor_Optical:BPW34 +Sensor_Optical:BPW34-SMD +Sensor_Optical:BPW40 +Sensor_Optical:BPW42 +Sensor_Optical:BPW82 +Sensor_Optical:BPW85 +Sensor_Optical:BPW85A +Sensor_Optical:BPW85B +Sensor_Optical:BPW85C +Sensor_Optical:BPX61 +Sensor_Optical:BPX65 +Sensor_Optical:BPY62 +Sensor_Optical:C12880MA +Sensor_Optical:Flir_LEPTON +Sensor_Optical:ISL29035 +Sensor_Optical:KPS-3227 +Sensor_Optical:KPS-5130 +Sensor_Optical:LDR03 +Sensor_Optical:LDR07 +Sensor_Optical:LPT80A +Sensor_Optical:LTR-303ALS-01 +Sensor_Optical:M9960 +Sensor_Optical:NOA1305 +Sensor_Optical:PMTx08Dyn +Sensor_Optical:PMTx08Dyn_Shld +Sensor_Optical:S13360-3025CS +Sensor_Optical:S13360-3050CS +Sensor_Optical:S13360-3075CS +Sensor_Optical:S5971 +Sensor_Optical:S5972 +Sensor_Optical:S5973 +Sensor_Optical:SFH203 +Sensor_Optical:SFH203FA +Sensor_Optical:SFH205F +Sensor_Optical:SFH205FA +Sensor_Optical:SFH206K +Sensor_Optical:SFH216 +Sensor_Optical:SFH225FA +Sensor_Optical:SFH235FA +Sensor_Optical:SFH2400 +Sensor_Optical:SFH2430 +Sensor_Optical:SFH2440 +Sensor_Optical:SFH2701 +Sensor_Optical:SFH300 +Sensor_Optical:SFH309 +Sensor_Optical:SFH320 +Sensor_Optical:SFH3201 +Sensor_Optical:TEPT4400 +Sensor_Optical:TSL2550D +Sensor_Optical:TSL2550T +Sensor_Optical:TSL25911FN +Sensor_Optical:VT93xx +Sensor_Pressure:40PC015G +Sensor_Pressure:40PC100G +Sensor_Pressure:40PC150G +Sensor_Pressure:40PC250G +Sensor_Pressure:BMP280 +Sensor_Pressure:LPS22DF +Sensor_Pressure:LPS22HB +Sensor_Pressure:LPS22HH +Sensor_Pressure:LPS25HB +Sensor_Pressure:MPL115A1 +Sensor_Pressure:MPL3115A2 +Sensor_Pressure:MPXA6115A +Sensor_Pressure:MPXAZ6115A +Sensor_Pressure:MPXH6115A +Sensor_Pressure:MPXHZ6115A +Sensor_Pressure:MS5525DSO +Sensor_Pressure:MS5607-02BA +Sensor_Pressure:MS5611-01BA +Sensor_Pressure:MS5837-xxBA +Sensor_Pressure:WSEN-PADS_2511020213301 +Sensor_Pressure:XGZP6897D +Sensor_Pressure:XGZP6899D +Sensor_Proximity:AD7150BRMZ +Sensor_Proximity:AD7151BRMZ +Sensor_Proximity:APDS-9160-003 +Sensor_Proximity:BPR-105 +Sensor_Proximity:BPR-105F +Sensor_Proximity:BPR-205 +Sensor_Proximity:CNY70 +Sensor_Proximity:GP2S700HCP +Sensor_Proximity:ITR1201SR10AR +Sensor_Proximity:ITR8307 +Sensor_Proximity:ITR8307-F43 +Sensor_Proximity:ITR8307-L24-TR8 +Sensor_Proximity:ITR8307-S17-TR8 +Sensor_Proximity:ITR9608-F +Sensor_Proximity:KRC011 +Sensor_Proximity:LDC1312 +Sensor_Proximity:LDC1314 +Sensor_Proximity:LDC1612 +Sensor_Proximity:LDC1614 +Sensor_Proximity:LG206D +Sensor_Proximity:LG206L +Sensor_Proximity:QRE1113 +Sensor_Proximity:QRE1113GR +Sensor_Proximity:RPR-0720 +Sensor_Proximity:SFH900 +Sensor_Proximity:SFH9201 +Sensor_Proximity:SFH9202 +Sensor_Proximity:SFH9206 +Sensor_Proximity:SG-105 +Sensor_Proximity:SG-105F +Sensor_Proximity:SG-107 +Sensor_Proximity:SG-107F +Sensor_Proximity:TSSP58038 +Sensor_Proximity:TSSP58038SS1XB +Sensor_Proximity:TSSP58P38 +Sensor_Temperature:AD8494 +Sensor_Temperature:AD8495 +Sensor_Temperature:AD8496 +Sensor_Temperature:AD8497 +Sensor_Temperature:BD1020HFV +Sensor_Temperature:DS1621 +Sensor_Temperature:DS1621S +Sensor_Temperature:DS1621V +Sensor_Temperature:DS1804 +Sensor_Temperature:DS1821C +Sensor_Temperature:DS1822 +Sensor_Temperature:DS1822-PAR +Sensor_Temperature:DS1822Z +Sensor_Temperature:DS1825 +Sensor_Temperature:DS18B20 +Sensor_Temperature:DS18B20-PAR +Sensor_Temperature:DS18B20U +Sensor_Temperature:DS18B20Z +Sensor_Temperature:DS18S20 +Sensor_Temperature:DS18S20-PAR +Sensor_Temperature:DS18S20Z +Sensor_Temperature:DS28EA00 +Sensor_Temperature:KT100 +Sensor_Temperature:KTY10 +Sensor_Temperature:KTY81 +Sensor_Temperature:KTY82 +Sensor_Temperature:KTY83 +Sensor_Temperature:KTY84 +Sensor_Temperature:KTY85 +Sensor_Temperature:LM20BIM7 +Sensor_Temperature:LM20CIM7 +Sensor_Temperature:LM35-D +Sensor_Temperature:LM35-LP +Sensor_Temperature:LM35-NEB +Sensor_Temperature:LM73 +Sensor_Temperature:LM73-1 +Sensor_Temperature:LM74CIM +Sensor_Temperature:LM74CITP +Sensor_Temperature:LM75B +Sensor_Temperature:LM75C +Sensor_Temperature:LM92CIM +Sensor_Temperature:LM94021 +Sensor_Temperature:LMT01DQX +Sensor_Temperature:LMT01LPG +Sensor_Temperature:LMT84DCK +Sensor_Temperature:LMT85DCK +Sensor_Temperature:LMT86DCK +Sensor_Temperature:LMT87DCK +Sensor_Temperature:LTC2983 +Sensor_Temperature:MAX31820 +Sensor_Temperature:MAX31820PAR +Sensor_Temperature:MAX31826 +Sensor_Temperature:MAX31855EASA +Sensor_Temperature:MAX31855JASA +Sensor_Temperature:MAX31855KASA +Sensor_Temperature:MAX31855NASA +Sensor_Temperature:MAX31855RASA +Sensor_Temperature:MAX31855SASA +Sensor_Temperature:MAX31855TASA +Sensor_Temperature:MAX31856 +Sensor_Temperature:MAX31865xAP +Sensor_Temperature:MAX31865xTP +Sensor_Temperature:MAX6654 +Sensor_Temperature:MCP9501 +Sensor_Temperature:MCP9502 +Sensor_Temperature:MCP9503 +Sensor_Temperature:MCP9504 +Sensor_Temperature:MCP9700Ax-ELT +Sensor_Temperature:MCP9700Ax-ETT +Sensor_Temperature:MCP9700Ax-HLT +Sensor_Temperature:MCP9700Ax-HTT +Sensor_Temperature:MCP9700x-ELT +Sensor_Temperature:MCP9700x-ETT +Sensor_Temperature:MCP9700x-HLT +Sensor_Temperature:MCP9700x-HTT +Sensor_Temperature:MCP9800Ax-xOT +Sensor_Temperature:MCP9802Ax-xOT +Sensor_Temperature:MCP9804_DFN +Sensor_Temperature:MCP9804_MSOP +Sensor_Temperature:MCP9808_DFN +Sensor_Temperature:MCP9808_MSOP +Sensor_Temperature:MCP9844x-xMN +Sensor_Temperature:PCT2075D +Sensor_Temperature:PCT2075DP +Sensor_Temperature:PT100 +Sensor_Temperature:PT1000 +Sensor_Temperature:PT500 +Sensor_Temperature:Si7050-A20 +Sensor_Temperature:Si7051-A20 +Sensor_Temperature:Si7053-A20 +Sensor_Temperature:Si7054-A20 +Sensor_Temperature:Si7055-A20 +Sensor_Temperature:TC1047AxNB +Sensor_Temperature:TC1047xNB +Sensor_Temperature:TMP100 +Sensor_Temperature:TMP101 +Sensor_Temperature:TMP102xxDRL +Sensor_Temperature:TMP1075D +Sensor_Temperature:TMP1075DGK +Sensor_Temperature:TMP1075DSG +Sensor_Temperature:TMP110D +Sensor_Temperature:TMP112xxDRL +Sensor_Temperature:TMP114 +Sensor_Temperature:TMP116xxDRV +Sensor_Temperature:TMP117xxDRV +Sensor_Temperature:TMP117xxYBG +Sensor_Temperature:TMP119AIYBGR +Sensor_Temperature:TMP20AIDCK +Sensor_Temperature:TMP20AIDRL +Sensor_Temperature:TMP36xS +Sensor_Temperature:TMP411 +Sensor_Temperature:TMP461xxRUN +Sensor_Temperature:TMP464xxRGT +Sensor_Temperature:TMP468xxRGT +Sensor_Temperature:TSIC206-SO8 +Sensor_Temperature:TSIC206-TO92 +Sensor_Temperature:TSIC306-SO8 +Sensor_Temperature:TSIC306-TO92 +Sensor_Touch:AT42QT1010-M +Sensor_Touch:AT42QT1010-TSHR +Sensor_Touch:AT42QT1011-M +Sensor_Touch:AT42QT1011-TSHR +Sensor_Touch:AT42QT1012-M +Sensor_Touch:AT42QT1012-T +Sensor_Touch:AT42QT1040-M +Sensor_Touch:AT42QT1050-M +Sensor_Touch:AT42QT1050-U +Sensor_Touch:AT42QT1060-M +Sensor_Touch:AT42QT1070-M +Sensor_Touch:AT42QT1070-S +Sensor_Touch:AT42QT1110-M +Sensor_Touch:CAP1206-x-AIA +Sensor_Touch:CAP1206-x-SL +Sensor_Touch:CY8CMBR3002 +Sensor_Touch:CY8CMBR3102 +Sensor_Touch:CY8CMBR3106S +Sensor_Touch:CY8CMBR3108 +Sensor_Touch:CY8CMBR3110 +Sensor_Touch:CY8CMBR3116 +Sensor_Touch:MPR121QR2 +Sensor_Touch:PCA8886 +Sensor_Touch:PCF8883 +Sensor_Voltage:LV25-P +Simulation_SPICE:0 +Simulation_SPICE:BSOURCE +Simulation_SPICE:D +Simulation_SPICE:ESOURCE +Simulation_SPICE:GSOURCE +Simulation_SPICE:IAM +Simulation_SPICE:IBIS_DEVICE +Simulation_SPICE:IBIS_DEVICE_DIFF +Simulation_SPICE:IBIS_DRIVER +Simulation_SPICE:IBIS_DRIVER_DIFF +Simulation_SPICE:IDC +Simulation_SPICE:IEXP +Simulation_SPICE:IPULSE +Simulation_SPICE:IPWL +Simulation_SPICE:ISFFM +Simulation_SPICE:ISIN +Simulation_SPICE:ITRNOISE +Simulation_SPICE:ITRRANDOM +Simulation_SPICE:NJFET +Simulation_SPICE:NMOS +Simulation_SPICE:NMOS_Substrate +Simulation_SPICE:NPN +Simulation_SPICE:NPN_Substrate +Simulation_SPICE:OPAMP +Simulation_SPICE:PJFET +Simulation_SPICE:PMOS +Simulation_SPICE:PMOS_Substrate +Simulation_SPICE:PNP +Simulation_SPICE:PNP_Substrate +Simulation_SPICE:SWITCH +Simulation_SPICE:TLINE +Simulation_SPICE:VAM +Simulation_SPICE:VDC +Simulation_SPICE:VEXP +Simulation_SPICE:VOLTMETER_DIFF +Simulation_SPICE:VPULSE +Simulation_SPICE:VPWL +Simulation_SPICE:VSFFM +Simulation_SPICE:VSIN +Simulation_SPICE:VTRNOISE +Simulation_SPICE:VTRRANDOM +Switch:CK_KMS2xxG +Switch:CK_KMS2xxGP +Switch:SW_Coded +Switch:SW_Coded_SH-7010 +Switch:SW_Coded_SH-7030 +Switch:SW_Coded_SH-7040 +Switch:SW_Coded_SH-7050 +Switch:SW_Coded_SH-7070 +Switch:SW_Coded_SH-7080 +Switch:SW_DIP_x01 +Switch:SW_DIP_x02 +Switch:SW_DIP_x03 +Switch:SW_DIP_x04 +Switch:SW_DIP_x05 +Switch:SW_DIP_x06 +Switch:SW_DIP_x07 +Switch:SW_DIP_x08 +Switch:SW_DIP_x09 +Switch:SW_DIP_x10 +Switch:SW_DIP_x11 +Switch:SW_DIP_x12 +Switch:SW_DP3T +Switch:SW_DPDT_x2 +Switch:SW_DPST +Switch:SW_DPST_Temperature +Switch:SW_DPST_x2 +Switch:SW_E3_SA3216 +Switch:SW_E3_SA3624 +Switch:SW_E3_SA6432 +Switch:SW_MEC_5E +Switch:SW_MEC_5G +Switch:SW_MEC_5G_2LED +Switch:SW_MEC_5G_LED +Switch:SW_MMI_Q5-100 +Switch:SW_NKK_GW12LJPCF +Switch:SW_Nidec_CAS-120A1 +Switch:SW_Omron_B3FS +Switch:SW_Push +Switch:SW_Push_45deg +Switch:SW_Push_DPDT +Switch:SW_Push_Dual +Switch:SW_Push_Dual_x2 +Switch:SW_Push_LED +Switch:SW_Push_Lamp +Switch:SW_Push_Open +Switch:SW_Push_Open_Dual +Switch:SW_Push_Open_Dual_x2 +Switch:SW_Push_SPDT +Switch:SW_Push_Shielded +Switch:SW_Reed +Switch:SW_Reed_Opener +Switch:SW_Reed_SPDT +Switch:SW_Rotary_1x12 +Switch:SW_Rotary_1x3_MP +Switch:SW_Rotary_1x4_MP +Switch:SW_Rotary_1x5_MP +Switch:SW_Rotary_1x6_MP +Switch:SW_Rotary_1x7_MP +Switch:SW_Rotary_1x8_MP +Switch:SW_Rotary_1x9_MP +Switch:SW_Rotary_2x6 +Switch:SW_Rotary_3x4 +Switch:SW_Rotary_4x3 +Switch:SW_SP3T +Switch:SW_SP3T_NR01103 +Switch:SW_SP4T_NR01104 +Switch:SW_SP5T_NR01105 +Switch:SW_SPDT +Switch:SW_SPDT_312 +Switch:SW_SPDT_321 +Switch:SW_SPDT_MSM +Switch:SW_SPDT_XKB_DMx-xxxx-1 +Switch:SW_SPST +Switch:SW_SPST_LED +Switch:SW_SPST_Lamp +Switch:SW_SPST_Temperature +Switch:SW_Slide_DPDT +Switch:SW_Wuerth_450301014042 +Timer:8253 +Timer:8254 +Timer:8284 +Timer:82C54 +Timer:82C54_PLCC +Timer:AD9513 +Timer:AD9514 +Timer:AD9515 +Timer:CD4541BE +Timer:CD4541BM +Timer:CD4541BPW +Timer:DS1023S +Timer:ICM7209 +Timer:ICM7555xB +Timer:ICM7555xP +Timer:ICM7556 +Timer:LM555xM +Timer:LM555xMM +Timer:LM555xN +Timer:LM556 +Timer:LMC555xM +Timer:LMC555xMM +Timer:LMC555xN +Timer:LMC555xTP +Timer:LTC6902 +Timer:LTC6909 +Timer:LTC6993xS6-1 +Timer:LTC6993xS6-2 +Timer:LTC6993xS6-3 +Timer:LTC6993xS6-4 +Timer:LTC6994xDCB-1 +Timer:LTC6994xDCB-2 +Timer:LTC6994xS6-1 +Timer:LTC6994xS6-2 +Timer:MC14541BD +Timer:MC14541BDT +Timer:MC1455B +Timer:MC1455P +Timer:MN3101 +Timer:MN3102 +Timer:NA555D +Timer:NA555P +Timer:NA556 +Timer:NE555D +Timer:NE555P +Timer:NE556 +Timer:NE567 +Timer:NLV14541BD +Timer:NLV14541BDT +Timer:PL611-01-xxxT +Timer:SA555D +Timer:SA555P +Timer:SA556 +Timer:SE555D +Timer:SE555P +Timer:SE556 +Timer:SE567 +Timer:SY58031U +Timer:SY58032U +Timer:SY58033U +Timer:TLC555xD +Timer:TLC555xP +Timer:TLC555xPS +Timer:TLC555xPW +Timer:TPL5010 +Timer:TPL5110 +Timer:TPL5111 +Timer_PLL:ADF4002BCPZ +Timer_PLL:ADF4002BRUZ +Timer_PLL:ADF4158 +Timer_PLL:ADF4350 +Timer_PLL:ADF4351 +Timer_PLL:CDCVF2505 +Timer_PLL:CS2000-CP +Timer_PLL:ICS525-01R +Timer_PLL:ICS525R-02 +Timer_PLL:Si5342A-D +Timer_PLL:Si5342B-D +Timer_PLL:Si5342C-D +Timer_PLL:Si5342D-D +Timer_PLL:Si5344A-D +Timer_PLL:Si5344B-D +Timer_PLL:Si5344C-D +Timer_PLL:Si5344D-D +Timer_PLL:Si5345A-D +Timer_PLL:Si5345B-D +Timer_PLL:Si5345C-D +Timer_PLL:Si5345D-D +Timer_RTC:AB0805 +Timer_RTC:AB0815 +Timer_RTC:AB1805 +Timer_RTC:AB1815 +Timer_RTC:BQ32000 +Timer_RTC:BQ32002 +Timer_RTC:DS1302+ +Timer_RTC:DS1302N+ +Timer_RTC:DS1302S+ +Timer_RTC:DS1302SN+ +Timer_RTC:DS1302Z+ +Timer_RTC:DS1302ZN+ +Timer_RTC:DS1307+ +Timer_RTC:DS1307N+ +Timer_RTC:DS1307Z+ +Timer_RTC:DS1307ZN+ +Timer_RTC:DS1602 +Timer_RTC:DS3231M +Timer_RTC:DS3231MZ +Timer_RTC:DS3232M +Timer_RTC:M41T62Q +Timer_RTC:MCP7940N-xMNY +Timer_RTC:MCP7940N-xMS +Timer_RTC:MCP7940N-xP +Timer_RTC:MCP7940N-xSN +Timer_RTC:MCP7940N-xST +Timer_RTC:PCF85063ATL +Timer_RTC:PCF8523T +Timer_RTC:PCF8523TK +Timer_RTC:PCF8523TS +Timer_RTC:PCF85263AT +Timer_RTC:PCF85263ATL +Timer_RTC:PCF85263ATT +Timer_RTC:PCF85263ATT1 +Timer_RTC:PCF85363ATT +Timer_RTC:PCF85363ATT1 +Timer_RTC:PCF8563T +Timer_RTC:PCF8563TS +Timer_RTC:RV-1805-C3 +Timer_RTC:RV-3028-C7 +Timer_RTC:RV-8523-C3 +Timer_RTC:RX8901CE +Transformer:0433BM15A0001 +Transformer:0868BM15C0001 +Transformer:0896BM15A0001 +Transformer:0915BM15A0001 +Transformer:30F-51NL +Transformer:5400BL15B050 +Transformer:ADT1-1 +Transformer:ADT1-1WT +Transformer:ADT1-1WT-1 +Transformer:ADT1-6T +Transformer:ADT1.5-1 +Transformer:ADT1.5-122 +Transformer:ADT1.5-17 +Transformer:ADT1.5-2 +Transformer:ADT16-1T +Transformer:ADT16-6 +Transformer:ADT16-6T +Transformer:ADT2-1T +Transformer:ADT2-1T-1P +Transformer:ADT2-71T +Transformer:ADT3-1T +Transformer:ADT3-1T-75 +Transformer:ADT3-6T +Transformer:ADT4-1T +Transformer:ADT4-1WT +Transformer:ADT4-5WT +Transformer:ADT4-6 +Transformer:ADT4-6T +Transformer:ADT4-6WT +Transformer:ADT8-1T +Transformer:ADT9-1T +Transformer:ADTL1-12 +Transformer:ADTL1-15-75 +Transformer:ADTL1-18-75 +Transformer:ADTL1-4-75 +Transformer:ADTL2-18 +Transformer:ADTT1-1 +Transformer:ADTT1-6 +Transformer:ADTT1.5-1 +Transformer:ADTT3-2 +Transformer:ADTT4-1 +Transformer:B0322J5050AHF +Transformer:CST1 +Transformer:CST1_Split +Transformer:CST2 +Transformer:CST2010 +Transformer:CST2010_Split +Transformer:CST2_Split +Transformer:ED8_4 +Transformer:ETC1-1-13 +Transformer:LL1587 +Transformer:P0544NL +Transformer:P0926NL +Transformer:PA0173NL +Transformer:PA0184NL +Transformer:PA0184NL1 +Transformer:PA0185NL +Transformer:PA0185NL1 +Transformer:PA0264NL +Transformer:PA0297NL +Transformer:PA0510NL +Transformer:PA1323NL +Transformer:PA2001NL +Transformer:PA2002NL +Transformer:PA2004NL +Transformer:PA2005NL +Transformer:PA2006NL +Transformer:PA2007NL +Transformer:PA2008NL +Transformer:PA2009NL +Transformer:PA2777NL +Transformer:PA3493NL +Transformer:PE-68386NL +Transformer:PT61017PEL +Transformer:PT61020EL +Transformer:TC1-1-13M+ +Transformer:TEZ0.5-D-1 +Transformer:TEZ0.5-D-2 +Transformer:TEZ1.5-D-1 +Transformer:TEZ1.5-D-2 +Transformer:TEZ10.0-D-1 +Transformer:TEZ10.0-D-2 +Transformer:TEZ16.0-D-1 +Transformer:TEZ16.0-D-2 +Transformer:TEZ2.0-D-1 +Transformer:TEZ2.0-D-2 +Transformer:TEZ2.5-D-1 +Transformer:TEZ2.5-D-2 +Transformer:TEZ2.6-D-1 +Transformer:TEZ2.6-D-2 +Transformer:TEZ4.0-D-1 +Transformer:TEZ4.0-D-2 +Transformer:TEZ6.0-D-1 +Transformer:TEZ6.0-D-2 +Transformer:TG110-E050N5xx +Transformer:TG110-S050N2xx +Transformer:TG111-MSC13LF +Transformer:TR1-SO8 +Transformer:TR60_FC +Transformer:TR60_IC +Transformer:TR60_IC2 +Transformer:TRANSF1 +Transformer:TRANSF2 +Transformer:TRANSF3 +Transformer:TRANSF4 +Transformer:TRANSF5 +Transformer:TRANSF6 +Transformer:TRANSF7 +Transformer:TRANSF8 +Transformer:Triad_VPP16-310 +Transformer:Wuerth_749013011A +Transformer:Wuerth_750315371 +Transformer:Wuerth_750343373 +Transformer:Wuerth_760871131 +Transformer:Wurth_750319177 +Transformer:ZMCT103C +Transformer:ZMPT101K +Transistor_Array:A2982 +Transistor_Array:MC1413BD +Transistor_Array:MC1413BP +Transistor_Array:MC1413D +Transistor_Array:MC1413P +Transistor_Array:NCV1413B +Transistor_Array:SN75468 +Transistor_Array:SN75469 +Transistor_Array:TBD62783A +Transistor_Array:TBD62785AFWG +Transistor_Array:TBD62785APG +Transistor_Array:ULN2002 +Transistor_Array:ULN2002A +Transistor_Array:ULN2003 +Transistor_Array:ULN2003A +Transistor_Array:ULN2004 +Transistor_Array:ULN2004A +Transistor_Array:ULN2801A +Transistor_Array:ULN2802A +Transistor_Array:ULN2803A +Transistor_Array:ULN2804A +Transistor_Array:ULN2805A +Transistor_Array:ULQ2003A +Transistor_Array:ULQ2004A +Transistor_BJT:2N2219 +Transistor_BJT:2N2646 +Transistor_BJT:2N2647 +Transistor_BJT:2N3055 +Transistor_BJT:2N3904 +Transistor_BJT:2N3905 +Transistor_BJT:2N3906 +Transistor_BJT:2SA1015 +Transistor_BJT:2SB631 +Transistor_BJT:2SB817 +Transistor_BJT:2SC1815 +Transistor_BJT:2SC1941 +Transistor_BJT:2SC1945 +Transistor_BJT:2SC4213 +Transistor_BJT:2SD1047 +Transistor_BJT:2SD600 +Transistor_BJT:320S14-U +Transistor_BJT:BC107 +Transistor_BJT:BC108 +Transistor_BJT:BC109 +Transistor_BJT:BC140 +Transistor_BJT:BC141 +Transistor_BJT:BC160 +Transistor_BJT:BC161 +Transistor_BJT:BC212 +Transistor_BJT:BC237 +Transistor_BJT:BC240 +Transistor_BJT:BC307 +Transistor_BJT:BC327 +Transistor_BJT:BC328 +Transistor_BJT:BC337 +Transistor_BJT:BC338 +Transistor_BJT:BC413 +Transistor_BJT:BC413B +Transistor_BJT:BC413C +Transistor_BJT:BC414 +Transistor_BJT:BC414B +Transistor_BJT:BC414C +Transistor_BJT:BC516 +Transistor_BJT:BC517 +Transistor_BJT:BC546 +Transistor_BJT:BC547 +Transistor_BJT:BC548 +Transistor_BJT:BC549 +Transistor_BJT:BC550 +Transistor_BJT:BC556 +Transistor_BJT:BC557 +Transistor_BJT:BC558 +Transistor_BJT:BC559 +Transistor_BJT:BC560 +Transistor_BJT:BC636 +Transistor_BJT:BC807 +Transistor_BJT:BC807W +Transistor_BJT:BC808 +Transistor_BJT:BC808W +Transistor_BJT:BC817 +Transistor_BJT:BC817W +Transistor_BJT:BC818 +Transistor_BJT:BC818W +Transistor_BJT:BC846 +Transistor_BJT:BC846BDW1 +Transistor_BJT:BC846BPDW1 +Transistor_BJT:BC846BPN +Transistor_BJT:BC846BS +Transistor_BJT:BC847 +Transistor_BJT:BC847BDW1 +Transistor_BJT:BC847BPDW1 +Transistor_BJT:BC847BPN +Transistor_BJT:BC847BS +Transistor_BJT:BC847W +Transistor_BJT:BC848 +Transistor_BJT:BC848W +Transistor_BJT:BC849 +Transistor_BJT:BC849W +Transistor_BJT:BC850 +Transistor_BJT:BC850W +Transistor_BJT:BC856 +Transistor_BJT:BC856BDW1 +Transistor_BJT:BC856BS +Transistor_BJT:BC856W +Transistor_BJT:BC857 +Transistor_BJT:BC857BDW1 +Transistor_BJT:BC857BS +Transistor_BJT:BC857W +Transistor_BJT:BC858 +Transistor_BJT:BC858W +Transistor_BJT:BC859 +Transistor_BJT:BC859W +Transistor_BJT:BC860 +Transistor_BJT:BC860W +Transistor_BJT:BCP51 +Transistor_BJT:BCP53 +Transistor_BJT:BCP56 +Transistor_BJT:BCV29 +Transistor_BJT:BCV49 +Transistor_BJT:BCV61 +Transistor_BJT:BCV62 +Transistor_BJT:BCX51 +Transistor_BJT:BCX52 +Transistor_BJT:BCX53 +Transistor_BJT:BCX56 +Transistor_BJT:BD135 +Transistor_BJT:BD136 +Transistor_BJT:BD137 +Transistor_BJT:BD138 +Transistor_BJT:BD139 +Transistor_BJT:BD140 +Transistor_BJT:BD233 +Transistor_BJT:BD234 +Transistor_BJT:BD235 +Transistor_BJT:BD236 +Transistor_BJT:BD237 +Transistor_BJT:BD238 +Transistor_BJT:BD249 +Transistor_BJT:BD249A +Transistor_BJT:BD249B +Transistor_BJT:BD249C +Transistor_BJT:BD250 +Transistor_BJT:BD250A +Transistor_BJT:BD250B +Transistor_BJT:BD250C +Transistor_BJT:BD433 +Transistor_BJT:BD434 +Transistor_BJT:BD435 +Transistor_BJT:BD436 +Transistor_BJT:BD437 +Transistor_BJT:BD438 +Transistor_BJT:BD439 +Transistor_BJT:BD440 +Transistor_BJT:BD441 +Transistor_BJT:BD442 +Transistor_BJT:BD909 +Transistor_BJT:BD910 +Transistor_BJT:BD911 +Transistor_BJT:BD912 +Transistor_BJT:BDW93 +Transistor_BJT:BDW93A +Transistor_BJT:BDW93B +Transistor_BJT:BDW93C +Transistor_BJT:BDW94 +Transistor_BJT:BDW94A +Transistor_BJT:BDW94B +Transistor_BJT:BDW94C +Transistor_BJT:BF199 +Transistor_BJT:BF457 +Transistor_BJT:BF458 +Transistor_BJT:BF459 +Transistor_BJT:BFR92 +Transistor_BJT:BFT92 +Transistor_BJT:BUT11 +Transistor_BJT:BUT11A +Transistor_BJT:DMMT5401 +Transistor_BJT:DTA113T +Transistor_BJT:DTA113Z +Transistor_BJT:DTA114E +Transistor_BJT:DTA114G +Transistor_BJT:DTA114T +Transistor_BJT:DTA114W +Transistor_BJT:DTA114Y +Transistor_BJT:DTA115E +Transistor_BJT:DTA115G +Transistor_BJT:DTA115T +Transistor_BJT:DTA115U +Transistor_BJT:DTA123E +Transistor_BJT:DTA123J +Transistor_BJT:DTA123Y +Transistor_BJT:DTA124E +Transistor_BJT:DTA124G +Transistor_BJT:DTA124T +Transistor_BJT:DTA124X +Transistor_BJT:DTA125T +Transistor_BJT:DTA143E +Transistor_BJT:DTA143T +Transistor_BJT:DTA143X +Transistor_BJT:DTA143Y +Transistor_BJT:DTA143Z +Transistor_BJT:DTA144E +Transistor_BJT:DTA144G +Transistor_BJT:DTA144T +Transistor_BJT:DTA144V +Transistor_BJT:DTA144W +Transistor_BJT:DTA1D3R +Transistor_BJT:DTA214Y +Transistor_BJT:DTB113E +Transistor_BJT:DTB113Z +Transistor_BJT:DTB114E +Transistor_BJT:DTB114G +Transistor_BJT:DTB114T +Transistor_BJT:DTB122J +Transistor_BJT:DTB123E +Transistor_BJT:DTB123T +Transistor_BJT:DTB123Y +Transistor_BJT:DTB133H +Transistor_BJT:DTB143T +Transistor_BJT:DTB163T +Transistor_BJT:DTC113T +Transistor_BJT:DTC113Z +Transistor_BJT:DTC114E +Transistor_BJT:DTC114G +Transistor_BJT:DTC114T +Transistor_BJT:DTC114W +Transistor_BJT:DTC114Y +Transistor_BJT:DTC115E +Transistor_BJT:DTC115G +Transistor_BJT:DTC115T +Transistor_BJT:DTC115U +Transistor_BJT:DTC123E +Transistor_BJT:DTC123J +Transistor_BJT:DTC123Y +Transistor_BJT:DTC124E +Transistor_BJT:DTC124G +Transistor_BJT:DTC124T +Transistor_BJT:DTC124X +Transistor_BJT:DTC125T +Transistor_BJT:DTC143E +Transistor_BJT:DTC143T +Transistor_BJT:DTC143X +Transistor_BJT:DTC143Y +Transistor_BJT:DTC143Z +Transistor_BJT:DTC144E +Transistor_BJT:DTC144G +Transistor_BJT:DTC144T +Transistor_BJT:DTC144V +Transistor_BJT:DTC144W +Transistor_BJT:DTC1D3R +Transistor_BJT:DTC214Y +Transistor_BJT:DTD113E +Transistor_BJT:DTD113Z +Transistor_BJT:DTD114E +Transistor_BJT:DTD114G +Transistor_BJT:DTD114T +Transistor_BJT:DTD122J +Transistor_BJT:DTD123E +Transistor_BJT:DTD123T +Transistor_BJT:DTD123Y +Transistor_BJT:DTD133H +Transistor_BJT:DTD143T +Transistor_BJT:DTD163T +Transistor_BJT:EMH3 +Transistor_BJT:FFB2222A +Transistor_BJT:FFB2227A +Transistor_BJT:FFB3904 +Transistor_BJT:FFB3906 +Transistor_BJT:FFB3946 +Transistor_BJT:FFB5551 +Transistor_BJT:FMB2227A +Transistor_BJT:FMB3946 +Transistor_BJT:IMH3A +Transistor_BJT:KTD1624 +Transistor_BJT:MAT02 +Transistor_BJT:MBT2222ADW1T1 +Transistor_BJT:MBT3904DW1 +Transistor_BJT:MBT3906DW1 +Transistor_BJT:MBT3946DW1T1 +Transistor_BJT:MJ2955 +Transistor_BJT:MJE13003 +Transistor_BJT:MJE13005G +Transistor_BJT:MJE13007G +Transistor_BJT:MJE13009G +Transistor_BJT:MMBT2222A +Transistor_BJT:MMBT3904 +Transistor_BJT:MMBT3906 +Transistor_BJT:MMBT5550L +Transistor_BJT:MMBT5551L +Transistor_BJT:MMBTA06 +Transistor_BJT:MMBTA42 +Transistor_BJT:MMBTA44 +Transistor_BJT:MMBTA56 +Transistor_BJT:MMBTA92 +Transistor_BJT:MMBTA94 +Transistor_BJT:MMDT2222A +Transistor_BJT:MMDT3904 +Transistor_BJT:MMDT3906 +Transistor_BJT:MMDT3946 +Transistor_BJT:MMDT5401 +Transistor_BJT:MMDT5551 +Transistor_BJT:MMDTA06 +Transistor_BJT:MPSA42 +Transistor_BJT:MPSA92 +Transistor_BJT:MUN5111DW1 +Transistor_BJT:MUN5112DW1 +Transistor_BJT:MUN5113DW1 +Transistor_BJT:MUN5114DW1 +Transistor_BJT:MUN5211DW1 +Transistor_BJT:MUN5212DW1 +Transistor_BJT:MUN5213DW1 +Transistor_BJT:MUN5214DW1 +Transistor_BJT:MUN5311DW1 +Transistor_BJT:MUN5312DW1 +Transistor_BJT:MUN5313DW1 +Transistor_BJT:MUN5314DW1 +Transistor_BJT:MUN5330DW1 +Transistor_BJT:MUN5331DW1 +Transistor_BJT:MUN5332DW1 +Transistor_BJT:MUN5333DW1 +Transistor_BJT:MUN5334DW1 +Transistor_BJT:MUN5335DW1 +Transistor_BJT:MUN5336DW1 +Transistor_BJT:PBSS301PZ +Transistor_BJT:PMBT2222A +Transistor_BJT:PMBT2222AYS +Transistor_BJT:PMBT3904YS +Transistor_BJT:PMBT3906YS +Transistor_BJT:PMBT3946YPN +Transistor_BJT:PN2222A +Transistor_BJT:PUMT1 +Transistor_BJT:PUMX1 +Transistor_BJT:PZT2222A +Transistor_BJT:PZT3904 +Transistor_BJT:PZT3906 +Transistor_BJT:PZTA42 +Transistor_BJT:PZTA92 +Transistor_BJT:Q_Dual_NPN_C2C1E1E2 +Transistor_BJT:Q_Dual_NPN_NPN_B1E2B2C2E1C1 +Transistor_BJT:Q_Dual_NPN_NPN_BRT_E1B1C2E2B2C1 +Transistor_BJT:Q_Dual_NPN_NPN_BRT_No_R2_C1B2E2C2B1E1 +Transistor_BJT:Q_Dual_NPN_NPN_BRT_No_R2_E1B1C2E2B2C1 +Transistor_BJT:Q_Dual_NPN_NPN_C1E1C2E2B2B1 +Transistor_BJT:Q_Dual_NPN_NPN_E1B1C2E2B2C1 +Transistor_BJT:Q_Dual_NPN_PNP_B1E2B2C2E1C1 +Transistor_BJT:Q_Dual_NPN_PNP_BRT_E1B1C2E2B2C1 +Transistor_BJT:Q_Dual_NPN_PNP_E1B1C2E2B2C1 +Transistor_BJT:Q_Dual_PNP_C2C1E1E2 +Transistor_BJT:Q_Dual_PNP_NPN_BRT_E1B1C2E2B2C1 +Transistor_BJT:Q_Dual_PNP_PNP_BRT_E1B1C2E2B2C1 +Transistor_BJT:Q_Dual_PNP_PNP_C1B1B2C2E2E1 +Transistor_BJT:Q_Dual_PNP_PNP_C1E1C2E2B2B1 +Transistor_BJT:Q_Dual_PNP_PNP_E1B1C2E2B2C1 +Transistor_BJT:Q_NPN_BCE +Transistor_BJT:Q_NPN_BCEC +Transistor_BJT:Q_NPN_BEC +Transistor_BJT:Q_NPN_BRT_BEC +Transistor_BJT:Q_NPN_BRT_ECB +Transistor_BJT:Q_NPN_CBE +Transistor_BJT:Q_NPN_CEB +Transistor_BJT:Q_NPN_Darlington_BCE +Transistor_BJT:Q_NPN_Darlington_BCEC +Transistor_BJT:Q_NPN_Darlington_BEC +Transistor_BJT:Q_NPN_Darlington_CBE +Transistor_BJT:Q_NPN_Darlington_CEB +Transistor_BJT:Q_NPN_Darlington_EBC +Transistor_BJT:Q_NPN_Darlington_ECB +Transistor_BJT:Q_NPN_Darlington_ECBC +Transistor_BJT:Q_NPN_EBC +Transistor_BJT:Q_NPN_ECB +Transistor_BJT:Q_NPN_ECBC +Transistor_BJT:Q_PNP_BCE +Transistor_BJT:Q_PNP_BCEC +Transistor_BJT:Q_PNP_BEC +Transistor_BJT:Q_PNP_BRT_BEC +Transistor_BJT:Q_PNP_BRT_ECB +Transistor_BJT:Q_PNP_CBE +Transistor_BJT:Q_PNP_CEB +Transistor_BJT:Q_PNP_Darlington_BCE +Transistor_BJT:Q_PNP_Darlington_BCEC +Transistor_BJT:Q_PNP_Darlington_BEC +Transistor_BJT:Q_PNP_Darlington_CBE +Transistor_BJT:Q_PNP_Darlington_CEB +Transistor_BJT:Q_PNP_Darlington_EBC +Transistor_BJT:Q_PNP_Darlington_ECB +Transistor_BJT:Q_PNP_Darlington_ECBC +Transistor_BJT:Q_PNP_EBC +Transistor_BJT:Q_PNP_ECB +Transistor_BJT:Q_PNP_ECBC +Transistor_BJT:S8050 +Transistor_BJT:S8550 +Transistor_BJT:SS8050 +Transistor_BJT:SS8550 +Transistor_BJT:SSM2210 +Transistor_BJT:SSM2220 +Transistor_BJT:TIP120 +Transistor_BJT:TIP121 +Transistor_BJT:TIP122 +Transistor_BJT:TIP125 +Transistor_BJT:TIP126 +Transistor_BJT:TIP127 +Transistor_BJT:TIP2955 +Transistor_BJT:TIP2955G +Transistor_BJT:TIP3055 +Transistor_BJT:TIP3055G +Transistor_BJT:TIP41 +Transistor_BJT:TIP41A +Transistor_BJT:TIP41B +Transistor_BJT:TIP41C +Transistor_BJT:TIP42 +Transistor_BJT:TIP42A +Transistor_BJT:TIP42B +Transistor_BJT:TIP42C +Transistor_BJT:UMH3N +Transistor_FET:2N3819 +Transistor_FET:2N7000 +Transistor_FET:2N7002 +Transistor_FET:2N7002E +Transistor_FET:2N7002H +Transistor_FET:2N7002K +Transistor_FET:3SK263 +Transistor_FET:AO3400A +Transistor_FET:AO3401A +Transistor_FET:AO4842 +Transistor_FET:AO4892 +Transistor_FET:AON6411 +Transistor_FET:BF244A +Transistor_FET:BF244B +Transistor_FET:BF244C +Transistor_FET:BF245A +Transistor_FET:BF245B +Transistor_FET:BF245C +Transistor_FET:BF545A +Transistor_FET:BF545B +Transistor_FET:BF545C +Transistor_FET:BF994S +Transistor_FET:BS107 +Transistor_FET:BS108 +Transistor_FET:BS170 +Transistor_FET:BS170F +Transistor_FET:BS250 +Transistor_FET:BS870 +Transistor_FET:BSB008NE2LX +Transistor_FET:BSB012NE2LXI +Transistor_FET:BSB013NE2LXI +Transistor_FET:BSB014N04LX3 +Transistor_FET:BSB015N04NX3 +Transistor_FET:BSB028N06NN3 +Transistor_FET:BSB044N08NN3 +Transistor_FET:BSB056N10NN3 +Transistor_FET:BSB104N08NP3 +Transistor_FET:BSB165N15NZ3 +Transistor_FET:BSB280N15NZ3 +Transistor_FET:BSC026N08NS5 +Transistor_FET:BSC028N06LS3 +Transistor_FET:BSC030N08NS5 +Transistor_FET:BSC035N10NS5 +Transistor_FET:BSC037N08NS5 +Transistor_FET:BSC040N08NS5 +Transistor_FET:BSC040N10NS5 +Transistor_FET:BSC046N10NS3G +Transistor_FET:BSC047N08NS3G +Transistor_FET:BSC052N08NS5 +Transistor_FET:BSC057N08NS3G +Transistor_FET:BSC060N10NS3G +Transistor_FET:BSC061N08NS5 +Transistor_FET:BSC070N10NS3G +Transistor_FET:BSC070N10NS5 +Transistor_FET:BSC072N08NS5 +Transistor_FET:BSC079N10NSG +Transistor_FET:BSC082N10LSG +Transistor_FET:BSC098N10NS5 +Transistor_FET:BSC100N10NSFG +Transistor_FET:BSC105N10LSFG +Transistor_FET:BSC109N10NS3G +Transistor_FET:BSC117N08NS5 +Transistor_FET:BSC118N10NSG +Transistor_FET:BSC123N08NS3G +Transistor_FET:BSC123N10LSG +Transistor_FET:BSC13DN30NSFD +Transistor_FET:BSC159N10LSFG +Transistor_FET:BSC160N10NS3G +Transistor_FET:BSC196N10NSG +Transistor_FET:BSC252N10NSFG +Transistor_FET:BSC265N10LSFG +Transistor_FET:BSC340N08NS3G +Transistor_FET:BSC440N10NS3G +Transistor_FET:BSD235C +Transistor_FET:BSD840N +Transistor_FET:BSF030NE2LQ +Transistor_FET:BSF035NE2LQ +Transistor_FET:BSF450NE7NH3 +Transistor_FET:BSN20 +Transistor_FET:BSP129 +Transistor_FET:BSP89 +Transistor_FET:BSR56 +Transistor_FET:BSR57 +Transistor_FET:BSR58 +Transistor_FET:BSS123 +Transistor_FET:BSS127S +Transistor_FET:BSS138 +Transistor_FET:BSS214NW +Transistor_FET:BSS83P +Transistor_FET:BSS84 +Transistor_FET:BUK7880-55A +Transistor_FET:BUK7M10-40EX +Transistor_FET:BUK7M12-40EX +Transistor_FET:BUK7M12-60EX +Transistor_FET:BUK7M15-60EX +Transistor_FET:BUK7M17-80EX +Transistor_FET:BUK7M19-60EX +Transistor_FET:BUK7M21-40EX +Transistor_FET:BUK7M22-80EX +Transistor_FET:BUK7M27-80EX +Transistor_FET:BUK7M33-60EX +Transistor_FET:BUK7M42-60EX +Transistor_FET:BUK7M45-40EX +Transistor_FET:BUK7M67-60EX +Transistor_FET:BUK7M6R3-40EX +Transistor_FET:BUK7M8R0-40EX +Transistor_FET:BUK7M9R9-60EX +Transistor_FET:BUK9832-55A +Transistor_FET:BUK9M10-30EX +Transistor_FET:BUK9M11-40EX +Transistor_FET:BUK9M12-60EX +Transistor_FET:BUK9M120-100EX +Transistor_FET:BUK9M14-40EX +Transistor_FET:BUK9M15-60EX +Transistor_FET:BUK9M156-100EX +Transistor_FET:BUK9M17-30EX +Transistor_FET:BUK9M19-60EX +Transistor_FET:BUK9M23-80EX +Transistor_FET:BUK9M24-40EX +Transistor_FET:BUK9M24-60EX +Transistor_FET:BUK9M28-80EX +Transistor_FET:BUK9M34-100EX +Transistor_FET:BUK9M35-80EX +Transistor_FET:BUK9M42-60EX +Transistor_FET:BUK9M43-100EX +Transistor_FET:BUK9M52-40EX +Transistor_FET:BUK9M53-60EX +Transistor_FET:BUK9M5R2-30EX +Transistor_FET:BUK9M6R6-30EX +Transistor_FET:BUK9M7R2-40EX +Transistor_FET:BUK9M85-60EX +Transistor_FET:BUK9M9R1-40EX +Transistor_FET:BUZ11 +Transistor_FET:C2M0025120D +Transistor_FET:C2M0040120D +Transistor_FET:C2M0045170D +Transistor_FET:C2M0080120D +Transistor_FET:C2M0160120D +Transistor_FET:C2M0280120D +Transistor_FET:C2M1000170D +Transistor_FET:C2M1000170J +Transistor_FET:C3M0030090K +Transistor_FET:C3M0065090D +Transistor_FET:C3M0065090J +Transistor_FET:C3M0065100J +Transistor_FET:C3M0065100K +Transistor_FET:C3M0075120J +Transistor_FET:C3M0075120K +Transistor_FET:C3M0120090D +Transistor_FET:C3M0120090J +Transistor_FET:C3M0120100J +Transistor_FET:C3M0120100K +Transistor_FET:C3M0280090D +Transistor_FET:C3M0280090J +Transistor_FET:CSD13380F3 +Transistor_FET:CSD16301Q2 +Transistor_FET:CSD16321Q5 +Transistor_FET:CSD16322Q5 +Transistor_FET:CSD16325Q5 +Transistor_FET:CSD16327Q3 +Transistor_FET:CSD16342Q5A +Transistor_FET:CSD16401Q5 +Transistor_FET:CSD16403Q5A +Transistor_FET:CSD16404Q5A +Transistor_FET:CSD16407Q5 +Transistor_FET:CSD16408Q5 +Transistor_FET:CSD16410Q5A +Transistor_FET:CSD16412Q5A +Transistor_FET:CSD16413Q5A +Transistor_FET:CSD16414Q5 +Transistor_FET:CSD16415Q5 +Transistor_FET:CSD16570Q5B +Transistor_FET:CSD17301Q5A +Transistor_FET:CSD17302Q5A +Transistor_FET:CSD17303Q5 +Transistor_FET:CSD17305Q5A +Transistor_FET:CSD17306Q5A +Transistor_FET:CSD17307Q5A +Transistor_FET:CSD17310Q5A +Transistor_FET:CSD17311Q5 +Transistor_FET:CSD17312Q5 +Transistor_FET:CSD17313Q2 +Transistor_FET:CSD17322Q5A +Transistor_FET:CSD17327Q5A +Transistor_FET:CSD17501Q5A +Transistor_FET:CSD17505Q5A +Transistor_FET:CSD17506Q5A +Transistor_FET:CSD17507Q5A +Transistor_FET:CSD17510Q5A +Transistor_FET:CSD17522Q5A +Transistor_FET:CSD17527Q5A +Transistor_FET:CSD17551Q5A +Transistor_FET:CSD17552Q5A +Transistor_FET:CSD17553Q5A +Transistor_FET:CSD17555Q5A +Transistor_FET:CSD17556Q5B +Transistor_FET:CSD17559Q5 +Transistor_FET:CSD17570Q5B +Transistor_FET:CSD17573Q5B +Transistor_FET:CSD17576Q5B +Transistor_FET:CSD17577Q3A +Transistor_FET:CSD17577Q5A +Transistor_FET:CSD17578Q5A +Transistor_FET:CSD17579Q5A +Transistor_FET:CSD17581Q3A +Transistor_FET:CSD18501Q5A +Transistor_FET:CSD18502Q5B +Transistor_FET:CSD18503Q5A +Transistor_FET:CSD18504Q5A +Transistor_FET:CSD18509Q5B +Transistor_FET:CSD18531Q5A +Transistor_FET:CSD18532NQ5B +Transistor_FET:CSD18532Q5B +Transistor_FET:CSD18533Q5A +Transistor_FET:CSD18534Q5A +Transistor_FET:CSD18537NQ5A +Transistor_FET:CSD18540Q5B +Transistor_FET:CSD18543Q3A +Transistor_FET:CSD18563Q5A +Transistor_FET:CSD19502Q5B +Transistor_FET:CSD19531Q5A +Transistor_FET:CSD19532Q5B +Transistor_FET:CSD19533Q5A +Transistor_FET:CSD19534Q5A +Transistor_FET:CSD19537Q3 +Transistor_FET:CSD25302Q2 +Transistor_FET:CSD25402Q3A +Transistor_FET:CSD25480F3 +Transistor_FET:DMC2053UVT +Transistor_FET:DMC3071LVT +Transistor_FET:DMG1012T +Transistor_FET:DMG2301L +Transistor_FET:DMG2302U +Transistor_FET:DMG3402L +Transistor_FET:DMG3404L +Transistor_FET:DMG3406L +Transistor_FET:DMG3414U +Transistor_FET:DMG3418L +Transistor_FET:DMG9926UDM +Transistor_FET:DMN10H220L +Transistor_FET:DMN10H700S +Transistor_FET:DMN13H750S +Transistor_FET:DMN2040U +Transistor_FET:DMN2041L +Transistor_FET:DMN2050L +Transistor_FET:DMN2056U +Transistor_FET:DMN2058U +Transistor_FET:DMN2075U +Transistor_FET:DMN2230U +Transistor_FET:DMN24H11DS +Transistor_FET:DMN24H3D5L +Transistor_FET:DMN3008SFG +Transistor_FET:DMN3033LDM +Transistor_FET:DMN3042L +Transistor_FET:DMN3051L +Transistor_FET:DMN30H4D0L +Transistor_FET:DMN3110S +Transistor_FET:DMN3150L +Transistor_FET:DMN32D2LDF +Transistor_FET:DMN3300U +Transistor_FET:DMN3404L +Transistor_FET:DMN6075S +Transistor_FET:DMN60H080DS +Transistor_FET:DMN6140L +Transistor_FET:DMN61D8LQ +Transistor_FET:DMN67D7L +Transistor_FET:DMN67D8L +Transistor_FET:DMP3013SFV +Transistor_FET:DMP6050SSD +Transistor_FET:DMT6008LFG +Transistor_FET:EPC2035 +Transistor_FET:EPC2036 +Transistor_FET:EPC2037 +Transistor_FET:EPC2038 +Transistor_FET:EPC2203 +Transistor_FET:EPC2219 +Transistor_FET:FDC2512 +Transistor_FET:FDC6330L +Transistor_FET:FDC86244 +Transistor_FET:FDG1024NZ +Transistor_FET:FDG6335N +Transistor_FET:FDMC8032L +Transistor_FET:FDMS8050 +Transistor_FET:FDMS8050ET30 +Transistor_FET:FDMS8350L +Transistor_FET:FDMS8350LET40 +Transistor_FET:FDMS86150 +Transistor_FET:FDMS86150ET100 +Transistor_FET:FDMS86152 +Transistor_FET:FDMS86202 +Transistor_FET:FDMS86202ET120 +Transistor_FET:FDMS86255 +Transistor_FET:FDMS86255ET150 +Transistor_FET:FDMS86350 +Transistor_FET:FDMS86350ET80 +Transistor_FET:FDMS86550 +Transistor_FET:FDMS86550ET60 +Transistor_FET:FDMT800100DC +Transistor_FET:FDMT800120DC +Transistor_FET:FDMT800150DC +Transistor_FET:FDMT800152DC +Transistor_FET:FDMT80060DC +Transistor_FET:FDMT80080DC +Transistor_FET:FDN340P +Transistor_FET:FDS2734 +Transistor_FET:FDS4559 +Transistor_FET:FDS4897AC +Transistor_FET:FDS4897C +Transistor_FET:FDS6630A +Transistor_FET:FDS6890A +Transistor_FET:FDS6892A +Transistor_FET:FDS6898A +Transistor_FET:FDS6930A +Transistor_FET:FDS6930B +Transistor_FET:FDS8960C +Transistor_FET:FDS9435A +Transistor_FET:FDS9926A +Transistor_FET:FDS9934C +Transistor_FET:FQP27P06 +Transistor_FET:GS66502B +Transistor_FET:GS66504B +Transistor_FET:GS66508B +Transistor_FET:IF3602 +Transistor_FET:IGLD60R070D1 +Transistor_FET:IGLD60R190D1 +Transistor_FET:IGO60R070D1 +Transistor_FET:IGOT60R070D1 +Transistor_FET:IGT40R070D1_E8220 +Transistor_FET:IGT60R070D1 +Transistor_FET:IGT60R190D1S +Transistor_FET:IPB180N10S4-02 +Transistor_FET:IPD50R380CE +Transistor_FET:IPD50R3K0CE +Transistor_FET:IPDD60R050G7 +Transistor_FET:IPDD60R080G7 +Transistor_FET:IPDD60R102G7 +Transistor_FET:IPDD60R125G7 +Transistor_FET:IPDD60R150G7 +Transistor_FET:IPDD60R190G7 +Transistor_FET:IPP060N06N +Transistor_FET:IPT012N08N5 +Transistor_FET:IPT015N10N5 +Transistor_FET:IPT020N10N3 +Transistor_FET:IRF3205 +Transistor_FET:IRF40DM229 +Transistor_FET:IRF4905 +Transistor_FET:IRF540N +Transistor_FET:IRF60DM206 +Transistor_FET:IRF6613 +Transistor_FET:IRF6614 +Transistor_FET:IRF6616 +Transistor_FET:IRF6617 +Transistor_FET:IRF6618 +Transistor_FET:IRF6620 +Transistor_FET:IRF6621 +Transistor_FET:IRF6622 +Transistor_FET:IRF6623 +Transistor_FET:IRF6628 +Transistor_FET:IRF6631 +Transistor_FET:IRF6635 +Transistor_FET:IRF6636 +Transistor_FET:IRF6637 +Transistor_FET:IRF6641 +Transistor_FET:IRF6643 +Transistor_FET:IRF6644 +Transistor_FET:IRF6646 +Transistor_FET:IRF6648 +Transistor_FET:IRF6655 +Transistor_FET:IRF6662 +Transistor_FET:IRF6665 +Transistor_FET:IRF6668 +Transistor_FET:IRF6674 +Transistor_FET:IRF6710S2 +Transistor_FET:IRF6711S +Transistor_FET:IRF6712S +Transistor_FET:IRF6713S +Transistor_FET:IRF6714M +Transistor_FET:IRF6715M +Transistor_FET:IRF6716M +Transistor_FET:IRF6717M +Transistor_FET:IRF6718L2 +Transistor_FET:IRF6721S +Transistor_FET:IRF6722M +Transistor_FET:IRF6724M +Transistor_FET:IRF6725M +Transistor_FET:IRF6726M +Transistor_FET:IRF6727M +Transistor_FET:IRF6728M +Transistor_FET:IRF6775M +Transistor_FET:IRF6785 +Transistor_FET:IRF6795M +Transistor_FET:IRF6797M +Transistor_FET:IRF6798M +Transistor_FET:IRF6802SD +Transistor_FET:IRF6810S +Transistor_FET:IRF6811S +Transistor_FET:IRF6892S +Transistor_FET:IRF6893M +Transistor_FET:IRF6894M +Transistor_FET:IRF6898M +Transistor_FET:IRF7171M +Transistor_FET:IRF7309IPBF +Transistor_FET:IRF7324 +Transistor_FET:IRF7343PBF +Transistor_FET:IRF740 +Transistor_FET:IRF7403 +Transistor_FET:IRF7404 +Transistor_FET:IRF7480M +Transistor_FET:IRF7483M +Transistor_FET:IRF7486M +Transistor_FET:IRF7580M +Transistor_FET:IRF7606PBF +Transistor_FET:IRF7607PBF +Transistor_FET:IRF7665S2 +Transistor_FET:IRF7739L1 +Transistor_FET:IRF7748L1 +Transistor_FET:IRF7759L2 +Transistor_FET:IRF7769L1 +Transistor_FET:IRF7779L2 +Transistor_FET:IRF7780M +Transistor_FET:IRF7799L2 +Transistor_FET:IRF7946 +Transistor_FET:IRF8301M +Transistor_FET:IRF8302M +Transistor_FET:IRF8304M +Transistor_FET:IRF8306M +Transistor_FET:IRF8308M +Transistor_FET:IRF8327S +Transistor_FET:IRF8721PBF-1 +Transistor_FET:IRF9383M +Transistor_FET:IRF9540N +Transistor_FET:IRFI4019H +Transistor_FET:IRFI4020H +Transistor_FET:IRFI4212H +Transistor_FET:IRFP4468PbF +Transistor_FET:IRFP4668PbF +Transistor_FET:IRFS4115 +Transistor_FET:IRFS4127 +Transistor_FET:IRFS4227 +Transistor_FET:IRFS4229 +Transistor_FET:IRFS4310Z +Transistor_FET:IRFS4321 +Transistor_FET:IRFTS9342PBF +Transistor_FET:IRL6283M +Transistor_FET:IRL6297SD +Transistor_FET:IRL7472L1 +Transistor_FET:IRLB8721PBF +Transistor_FET:IRLIZ44N +Transistor_FET:IRLML0030 +Transistor_FET:IRLML2060 +Transistor_FET:IRLML5203 +Transistor_FET:IRLML6244 +Transistor_FET:IRLML6401 +Transistor_FET:IRLML6402 +Transistor_FET:IRLML9301 +Transistor_FET:IRLZ24 +Transistor_FET:IRLZ34N +Transistor_FET:IRLZ44N +Transistor_FET:JFE150DBV +Transistor_FET:JFE150DCK +Transistor_FET:JFE2140D +Transistor_FET:MMBF170 +Transistor_FET:MMBF4391 +Transistor_FET:MMBF4392 +Transistor_FET:MMBF4393 +Transistor_FET:MMBFJ111 +Transistor_FET:MMBFJ112 +Transistor_FET:MMBFJ113 +Transistor_FET:NDT3055L +Transistor_FET:NTR2101P +Transistor_FET:PGA26E07BA +Transistor_FET:PGA26E19BA +Transistor_FET:PMDT290UCE +Transistor_FET:PMN48XP +Transistor_FET:PSMN5R2-60YL +Transistor_FET:QM6006D +Transistor_FET:QM6015D +Transistor_FET:Q_Dual_NMOS_G1S2G2D2S1D1 +Transistor_FET:Q_Dual_NMOS_S1G1D2S2G2D1 +Transistor_FET:Q_Dual_NMOS_S1G1S2G2D2D1 +Transistor_FET:Q_Dual_NMOS_S1G1S2G2D2D2D1D1 +Transistor_FET:Q_Dual_PMOS_G1S2G2D2S1D1 +Transistor_FET:Q_Dual_PMOS_S1G1D2S2G2D1 +Transistor_FET:Q_Dual_PMOS_S1G1S2G2D2D2D1D1 +Transistor_FET:Q_NMOS_DGS +Transistor_FET:Q_NMOS_DSG +Transistor_FET:Q_NMOS_GDS +Transistor_FET:Q_NMOS_GDSD +Transistor_FET:Q_NMOS_GSD +Transistor_FET:Q_NMOS_SDGD +Transistor_FET:Q_NMOS_SGD +Transistor_FET:Q_PMOS_DGS +Transistor_FET:Q_PMOS_DSG +Transistor_FET:Q_PMOS_GDS +Transistor_FET:Q_PMOS_GDSD +Transistor_FET:Q_PMOS_GSD +Transistor_FET:Q_PMOS_SDG +Transistor_FET:Q_PMOS_SDGD +Transistor_FET:Q_PMOS_SGD +Transistor_FET:RQ6E080AJ +Transistor_FET:RS9N50D +Transistor_FET:RSQ030N08HZG +Transistor_FET:SCTL35N65G2V +Transistor_FET:SGT65R65AL +Transistor_FET:SQJQ100E +Transistor_FET:SQJQ100EL +Transistor_FET:SQJQ112E +Transistor_FET:SQJQ114EL +Transistor_FET:SQJQ116EL +Transistor_FET:SQJQ130EL +Transistor_FET:SQJQ140E +Transistor_FET:SQJQ142E +Transistor_FET:SQJQ144AE +Transistor_FET:SQJQ146E +Transistor_FET:SQJQ148E +Transistor_FET:SQJQ150E +Transistor_FET:SQJQ160E +Transistor_FET:SQJQ160EL +Transistor_FET:SQJQ184E +Transistor_FET:SQJQ186E +Transistor_FET:SQJQ402E +Transistor_FET:SQJQ404E +Transistor_FET:SQJQ410EL +Transistor_FET:SQJQ466E +Transistor_FET:SQJQ480E +Transistor_FET:STB15N80K5 +Transistor_FET:STB33N65M2 +Transistor_FET:STB40N60M2 +Transistor_FET:STD7NK40Z +Transistor_FET:STS2DNE60 +Transistor_FET:SUD08P06-155L +Transistor_FET:SUD09P10-195 +Transistor_FET:SUD19P06-60 +Transistor_FET:SUD45P03-09 +Transistor_FET:SUD50P04-08 +Transistor_FET:SUD50P06-15 +Transistor_FET:SUD50P08-25L +Transistor_FET:SUD50P10-43L +Transistor_FET:Si1308EDL +Transistor_FET:Si1442DH +Transistor_FET:Si2319CDS +Transistor_FET:Si2371EDS +Transistor_FET:Si3456DDV +Transistor_FET:Si4162DY +Transistor_FET:Si4532DY +Transistor_FET:Si4542DY +Transistor_FET:Si7141DP +Transistor_FET:Si7336ADP +Transistor_FET:Si7450DP +Transistor_FET:Si7617DN +Transistor_FET:SiA449DJ +Transistor_FET:SiA453EDJ +Transistor_FET:SiA462DJ +Transistor_FET:SiR696DP +Transistor_FET:SiS415DNT +Transistor_FET:SiS443DN +Transistor_FET:SiS454DN +Transistor_FET:SiSS27DN +Transistor_FET:T2N7002AK +Transistor_FET:TP0610L +Transistor_FET:TP0610T +Transistor_FET:TSM2301ACX +Transistor_FET:TSM2302CX +Transistor_FET:VN10LF +Transistor_FET:VNP10N07 +Transistor_FET:VP0610L +Transistor_FET:VP0610T +Transistor_FET:ZVN3306F +Transistor_FET:ZVN3310F +Transistor_FET:ZVN3320F +Transistor_FET:ZVN4106F +Transistor_FET:ZXM61N02F +Transistor_FET:ZXM61N03F +Transistor_FET:ZXMN10A07F +Transistor_FET:ZXMN2A01F +Transistor_FET:ZXMN2A14F +Transistor_FET:ZXMN2B01F +Transistor_FET:ZXMN2B14FH +Transistor_FET:ZXMN2F30FH +Transistor_FET:ZXMN2F34FH +Transistor_FET:ZXMN3A01F +Transistor_FET:ZXMN3A14F +Transistor_FET:ZXMN3B01F +Transistor_FET:ZXMN3B14F +Transistor_FET:ZXMN3F30FH +Transistor_FET:ZXMN6A07F +Transistor_FET:ZXMP4A16G +Transistor_FET_Other:DN2540N3-G +Transistor_FET_Other:DN2540N5-G +Transistor_FET_Other:DN2540N8-G +Transistor_FET_Other:Q_NMOS_Depletion_DGS +Transistor_FET_Other:Q_NMOS_Depletion_DSG +Transistor_FET_Other:Q_NMOS_Depletion_GDS +Transistor_FET_Other:Q_NMOS_Depletion_GSD +Transistor_FET_Other:Q_NMOS_Depletion_SDG +Transistor_FET_Other:Q_NMOS_Depletion_SGD +Transistor_IGBT:IRG4PF50W +Transistor_IGBT:STGP7NC60HD +Transistor_Power_Module:A2C25S12M3 +Transistor_Power_Module:A2C25S12M3-F +Transistor_Power_Module:A2C35S12M3 +Transistor_Power_Module:A2C35S12M3-F +Transistor_Power_Module:A2C50S65M2 +Transistor_Power_Module:A2C50S65M2-F +Transistor_Power_Module:FP10R06W1E3 +Transistor_Power_Module:FP15R06W1E3 +Transistor_Power_Module:FP15R12W2T4 +Transistor_Power_Module:FP25R12W2T4 +Transistor_Power_Module:FP25R12W2T4P +Transistor_Power_Module:FP35R12W2T4 +Transistor_Power_Module:FP35R12W2T4P +Transistor_Power_Module:FP50R06W2E3 +Transistor_Power_Module:FS75R07N2E4 +Transistor_Power_Module:MG12100W-XN2MM +Transistor_Power_Module:MG1215H-XBN2MM +Transistor_Power_Module:MG1225H-XBN2MM +Transistor_Power_Module:MG1225H-XN2MM +Transistor_Power_Module:MG1240H-XBN2MM +Transistor_Power_Module:MG1250H-XN2MM +Transistor_Power_Module:MG1250W-XBN2MM +Transistor_Power_Module:MG1275W-XBN2MM +Transistor_Power_Module:MG1275W-XN2MM +Transistor_Power_Module:STGIPS10C60-H +Transistor_Power_Module:STGIPS10K60A +Transistor_Power_Module:STGIPS10K60A2 +Transistor_Power_Module:STGIPS10K60T +Transistor_Power_Module:STGIPS14K60 +Transistor_Power_Module:STGIPS14K60T +Transistor_Power_Module:STGIPS20K60 +Triac_Thyristor:BT136-500 +Triac_Thyristor:BT136-600 +Triac_Thyristor:BT136-800 +Triac_Thyristor:BT138-600 +Triac_Thyristor:BT138-800 +Triac_Thyristor:BT139-600 +Triac_Thyristor:BT169B +Triac_Thyristor:BT169D +Triac_Thyristor:BT169G +Triac_Thyristor:BTA16-600B +Triac_Thyristor:BTA16-600BW +Triac_Thyristor:BTA16-600C +Triac_Thyristor:BTA16-600CW +Triac_Thyristor:BTA16-600SW +Triac_Thyristor:BTA16-800B +Triac_Thyristor:BTA16-800BW +Triac_Thyristor:BTA16-800C +Triac_Thyristor:BTA16-800CW +Triac_Thyristor:BTA16-800SW +Triac_Thyristor:BTB16-600B +Triac_Thyristor:BTB16-600BW +Triac_Thyristor:BTB16-600C +Triac_Thyristor:BTB16-600CW +Triac_Thyristor:BTB16-600SW +Triac_Thyristor:BTB16-800B +Triac_Thyristor:BTB16-800BW +Triac_Thyristor:BTB16-800C +Triac_Thyristor:BTB16-800CW +Triac_Thyristor:BTB16-800SW +Triac_Thyristor:CT401T +Triac_Thyristor:Generic_Triac_A1A2G +Triac_Thyristor:Generic_Triac_A1GA2 +Triac_Thyristor:Generic_Triac_A2A1G +Triac_Thyristor:Generic_Triac_A2GA1 +Triac_Thyristor:Generic_Triac_GA1A2 +Triac_Thyristor:Generic_Triac_GA2A1 +Triac_Thyristor:TIC106 +Triac_Thyristor:TIC116 +Triac_Thyristor:TIC126 +Triac_Thyristor:TIC206 +Triac_Thyristor:TIC216 +Triac_Thyristor:TIC226 +Triac_Thyristor:X0202MN +Triac_Thyristor:X0202NN +Triac_Thyristor:Z0103MN +Triac_Thyristor:Z0103NN +Triac_Thyristor:Z0107MN +Triac_Thyristor:Z0107NN +Triac_Thyristor:Z0109MN +Triac_Thyristor:Z0109NN +Triac_Thyristor:Z0110MN +Triac_Thyristor:Z0110NN +Valve:6AK8 +Valve:9AK8 +Valve:CK548DX +Valve:CK6418 +Valve:EABC80 +Valve:EC92 +Valve:ECC81 +Valve:ECC83 +Valve:ECC88 +Valve:ECH81 +Valve:ECL82 +Valve:ECL86 +Valve:EF80 +Valve:EF83 +Valve:EF85 +Valve:EF86 +Valve:EL34 +Valve:EL84 +Valve:EM84 +Valve:JAN6418 +Valve:NOS-6418 +Valve:PABC80 +Valve:STABI +Valve:UABC80 +Video:AD725 +Video:AD9708AR +Video:AD9891 +Video:AD9895 +Video:AD9984AKST +Video:ADA4430-1WYRTZ +Video:ADA4430-1YKSZ +Video:ADV7280xCP +Video:ADV7390BCPZ +Video:ADV7391BCPZ +Video:AV9173 +Video:CX7930 +Video:CXD3400N +Video:HD63484 +Video:HD63484_PLCC +Video:ICX415AQ +Video:ISL59885 +Video:LM1881 +Video:MAX310 +Video:MAX311 +Video:MB88303P +Video:S178 +Video:SAA7182 +Video:SI582 +Video:TDA1950 +Video:TDA1950F +Video:TDA2593 +Video:TDA7260 +Video:TDA8501 +Video:TDA8702 +Video:TDA8702T +Video:TDA8772 +Video:TDA9500 +Video:TDA9503 +Video:TDA9513 +Video:TEA2014 +Video:TEA5115 +Video:TFP410PAP diff --git a/rector.php b/rector.php new file mode 100644 index 00000000..40eee9f7 --- /dev/null +++ b/rector.php @@ -0,0 +1,81 @@ +symfonyContainerXml(__DIR__ . '/var/cache/dev/App_KernelDevDebugContainer.xml'); + $rectorConfig->symfonyContainerPhp(__DIR__ . '/tests/symfony-container.php'); + + //Import class names instead of using fully qualified class names + $rectorConfig->importNames(); + //But keep the fully qualified class names for classes in the global namespace + $rectorConfig->importShortClasses(false); + + $rectorConfig->paths([ + __DIR__ . '/config', + __DIR__ . '/public', + __DIR__ . '/src', + __DIR__ . '/tests', + ]); + + // register a single rule + //$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class); + + $rectorConfig->rules([ + DeclareStrictTypesRector::class, + ]); + + // define sets of rules + $rectorConfig->sets([ + //PHP rules + SetList::CODE_QUALITY, + LevelSetList::UP_TO_PHP_81, + + //Symfony rules + SymfonySetList::SYMFONY_CODE_QUALITY, + SymfonySetList::SYMFONY_64, + + //Doctrine rules + DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES, + DoctrineSetList::DOCTRINE_CODE_QUALITY, + + //PHPUnit rules + PHPUnitSetList::PHPUNIT_CODE_QUALITY, + PHPUnitSetList::PHPUNIT_90, + ]); + + $rectorConfig->skip([ + CountArrayToEmptyArrayComparisonRector::class, + //Leave our !== null checks alone + FlipTypeControlToUseExclusiveTypeRector::class, + //Leave our PartList TableAction alone + ActionSuffixRemoverRector::class, + //We declare event listeners via attributes, therefore no need to migrate them to subscribers + EventListenerToEventSubscriberRector::class, + PreferPHPUnitThisCallRector::class, + //Do not replace 'GET' with class constant, + LiteralGetToRequestClassConstantRector::class, + ]); + + //Do not apply rules to Symfony own files + $rectorConfig->skip([ + __DIR__ . '/public/index.php', + __DIR__ . '/src/Kernel.php', + __DIR__ . '/config/preload.php', + __DIR__ . '/config/bundles.php', + ]); +}; diff --git a/src/ApiPlatform/DocumentedAPIProperties/DocumentedAPIProperty.php b/src/ApiPlatform/DocumentedAPIProperties/DocumentedAPIProperty.php new file mode 100644 index 00000000..57d275be --- /dev/null +++ b/src/ApiPlatform/DocumentedAPIProperties/DocumentedAPIProperty.php @@ -0,0 +1,120 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform\DocumentedAPIProperties; + +use ApiPlatform\Metadata\ApiProperty; + +/** + * When this attribute is applied to a class, an property will be added to the API documentation using the given parameters. + * This is useful for adding properties to the API documentation, that are not existing in the entity class itself, + * but get added by a normalizer. + */ +#[\Attribute(\Attribute::TARGET_CLASS| \Attribute::IS_REPEATABLE)] +final class DocumentedAPIProperty +{ + public function __construct( + /** + * @param string $schemaName The name of the schema to add the property to (e.g. "Part-Read") + */ + public readonly string $schemaName, + /** + * @var string $property The name of the property to add to the schema + */ + public readonly string $property, + public readonly string $type = 'string', + public readonly bool $nullable = true, + /** + * @var string $description The description of the property + */ + public readonly ?string $description = null, + /** + * @var bool True if the property is readable, false otherwise + */ + public readonly bool $readable = true, + /** + * @var bool True if the property is writable, false otherwise + */ + public readonly bool $writeable = false, + /** + * @var string|null The deprecation reason of the property + */ + public readonly ?string $deprecationReason = null, + /** @var mixed The default value of this property */ + public readonly mixed $default = null, + public readonly mixed $example = null, + ) + { + } + + public function toAPIProperty(bool $use_swagger = false): ApiProperty + { + $openApiContext = []; + + if (false === $this->writeable) { + $openApiContext['readOnly'] = true; + } + if (!$use_swagger && false === $this->readable) { + $openApiContext['writeOnly'] = true; + } + if (null !== $description = $this->description) { + $openApiContext['description'] = $description; + } + + $deprecationReason = $this->deprecationReason; + + // see https://github.com/json-schema-org/json-schema-spec/pull/737 + if (!$use_swagger && null !== $deprecationReason) { + $openApiContext['deprecated'] = true; + } + + if (!empty($default = $this->default)) { + if ($default instanceof \BackedEnum) { + $default = $default->value; + } + $openApiContext['default'] = $default; + } + + if (!empty($example = $this->example)) { + $openApiContext['example'] = $example; + } + + if (!isset($openApiContext['example']) && isset($openApiContext['default'])) { + $openApiContext['example'] = $openApiContext['default']; + } + + $openApiContext['type'] = $this->type; + $openApiContext['nullable'] = $this->nullable; + + + + return new ApiProperty( + description: $this->description, + readable: $this->readable, + writable: $this->writeable, + openapiContext: $openApiContext, + types: $this->type, + property: $this->property + ); + } +} \ No newline at end of file diff --git a/src/ApiPlatform/DocumentedAPIProperties/PropertyMetadataFactory.php b/src/ApiPlatform/DocumentedAPIProperties/PropertyMetadataFactory.php new file mode 100644 index 00000000..49e9a031 --- /dev/null +++ b/src/ApiPlatform/DocumentedAPIProperties/PropertyMetadataFactory.php @@ -0,0 +1,73 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform\DocumentedAPIProperties; + +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface; +use ReflectionClass; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; + +/** + * This decorator adds the virtual properties defined by the DocumentedAPIProperty attribute to the property metadata + * which then get picked up by the openapi schema generator + */ +#[AsDecorator('api_platform.metadata.property.metadata_factory')] +class PropertyMetadataFactory implements PropertyMetadataFactoryInterface +{ + public function __construct(private PropertyMetadataFactoryInterface $decorated) + { + } + + public function create(string $resourceClass, string $property, array $options = []): ApiProperty + { + $metadata = $this->decorated->create($resourceClass, $property, $options); + + //Only become active in the context of the openapi schema generation + if (!isset($options['schema_type'])) { + return $metadata; + } + + if (!class_exists($resourceClass)) { + return $metadata; + } + + $refClass = new ReflectionClass($resourceClass); + $attributes = $refClass->getAttributes(DocumentedAPIProperty::class); + + //Look for the DocumentedAPIProperty attribute with the given property name + foreach ($attributes as $attribute) { + /** @var DocumentedAPIProperty $api_property */ + $api_property = $attribute->newInstance(); + //If attribute not matches the property name, skip it + if ($api_property->property !== $property) { + continue; + } + + //Return the virtual property + return $api_property->toAPIProperty(); + } + + return $metadata; + } +} \ No newline at end of file diff --git a/src/ApiPlatform/DocumentedAPIProperties/PropertyNameCollectionFactory.php b/src/ApiPlatform/DocumentedAPIProperties/PropertyNameCollectionFactory.php new file mode 100644 index 00000000..3157cbf3 --- /dev/null +++ b/src/ApiPlatform/DocumentedAPIProperties/PropertyNameCollectionFactory.php @@ -0,0 +1,68 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform\DocumentedAPIProperties; + +use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; +use ApiPlatform\Metadata\Property\PropertyNameCollection; +use ReflectionClass; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; + +/** + * This decorator adds the virtual property names defined by the DocumentedAPIProperty attribute to the property name collection + * which then get picked up by the openapi schema generator + */ +#[AsDecorator('api_platform.metadata.property.name_collection_factory')] +class PropertyNameCollectionFactory implements PropertyNameCollectionFactoryInterface +{ + public function __construct(private readonly PropertyNameCollectionFactoryInterface $decorated) + { + } + + public function create(string $resourceClass, array $options = []): PropertyNameCollection + { + // Get the default properties from the decorated service + $propertyNames = $this->decorated->create($resourceClass, $options); + + //Only become active in the context of the openapi schema generation + if (!isset($options['schema_type'])) { + return $propertyNames; + } + + if (!class_exists($resourceClass)) { + return $propertyNames; + } + + $properties = iterator_to_array($propertyNames); + + $refClass = new ReflectionClass($resourceClass); + + foreach ($refClass->getAttributes(DocumentedAPIProperty::class) as $attribute) { + /** @var DocumentedAPIProperty $instance */ + $instance = $attribute->newInstance(); + $properties[] = $instance->property; + } + + return new PropertyNameCollection($properties); + } +} \ No newline at end of file diff --git a/src/ApiPlatform/ErrorHandler.php b/src/ApiPlatform/ErrorHandler.php new file mode 100644 index 00000000..7704347d --- /dev/null +++ b/src/ApiPlatform/ErrorHandler.php @@ -0,0 +1,75 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform; + +use ApiPlatform\Metadata\Operation; +use ApiPlatform\State\ProviderInterface; +use Doctrine\ORM\ORMInvalidArgumentException; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use ApiPlatform\State\ApiResource\Error; +use Symfony\Component\DependencyInjection\Attribute\Autowire; + +/** + * This class adds a custom error if the user tries to create a new entity through a relation, and suggests to do reference it through an IRI instead. + * This class decorates the default error handler of API Platform. + */ +#[AsDecorator('api_platform.state.error_provider')] +final class ErrorHandler implements ProviderInterface +{ + public function __construct(private readonly ProviderInterface $decorated, #[Autowire('%kernel.debug%')] private readonly bool $debug) + { + + } + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $request = $context['request']; + $format = $request->getRequestFormat(); + $exception = $request->attributes->get('exception'); + + //Check if the exception is a ORM InvalidArgument exception and complains about a not-persisted entity through relation + if ($exception instanceof ORMInvalidArgumentException && str_contains($exception->getMessage(), 'A new entity was found through the relationship')) { + //Extract the entity class and property name from the exception message + $matches = []; + preg_match('/A new entity was found through the relationship \'(?.*)\'/i', $exception->getMessage(), $matches); + + $property = $matches['property'] ?? "unknown"; + + //Create a new error response + $error = Error::createFromException($exception, 400); + + //Return the error response + $detail = "You tried to create a new entity through the relation '$property', but this is not allowed. Please create the entity first and then reference it through an IRI!"; + //If we are in debug mode, add the exception message to the error response + if ($this->debug) { + $detail .= " Original exception message: " . $exception->getMessage(); + } + $error->setDetail($detail); + return $error; + } + + + return $this->decorated->provide($operation, $uriVariables, $context); + } +} \ No newline at end of file diff --git a/src/ApiPlatform/Filter/EntityFilter.php b/src/ApiPlatform/Filter/EntityFilter.php new file mode 100644 index 00000000..85bc3833 --- /dev/null +++ b/src/ApiPlatform/Filter/EntityFilter.php @@ -0,0 +1,84 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform\Filter; + +use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter; +use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; +use ApiPlatform\Metadata\Operation; +use Doctrine\ORM\QueryBuilder; +use Doctrine\Persistence\ManagerRegistry; +use Psr\Log\LoggerInterface; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; + +class EntityFilter extends AbstractFilter +{ + + public function __construct( + ManagerRegistry $managerRegistry, + private readonly EntityFilterHelper $filter_helper, + ?LoggerInterface $logger = null, + ?array $properties = null, + ?NameConverterInterface $nameConverter = null + ) { + parent::__construct($managerRegistry, $logger, $properties, $nameConverter); + } + + protected function filterProperty( + string $property, + $value, + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $resourceClass, + ?Operation $operation = null, + array $context = [] + ): void { + if ( + !$this->isPropertyEnabled($property, $resourceClass) || + !$this->isPropertyMapped($property, $resourceClass, true) + ) { + return; + } + + $metadata = $this->getClassMetadata($resourceClass); + $target_class = $metadata->getAssociationTargetClass($property); + //If it is not an association we can not filter the property + if (!$target_class) { + return; + } + + $elements = $this->filter_helper->valueToEntityArray($value, $target_class); + + $parameterName = $queryNameGenerator->generateParameterName($property); // Generate a unique parameter name to avoid collisions with other filters + $queryBuilder + ->andWhere(sprintf('o.%s IN (:%s)', $property, $parameterName)) + ->setParameter($parameterName, $elements); + } + + + + public function getDescription(string $resourceClass): array + { + return $this->filter_helper->getDescription($this->properties); + } +} \ No newline at end of file diff --git a/src/ApiPlatform/Filter/EntityFilterHelper.php b/src/ApiPlatform/Filter/EntityFilterHelper.php new file mode 100644 index 00000000..45e04fde --- /dev/null +++ b/src/ApiPlatform/Filter/EntityFilterHelper.php @@ -0,0 +1,99 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform\Filter; + +use App\Entity\Base\AbstractStructuralDBElement; +use App\Services\Trees\NodesListBuilder; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\PropertyInfo\Type; + +class EntityFilterHelper +{ + public function __construct( + private readonly NodesListBuilder $nodesListBuilder, + private readonly EntityManagerInterface $entityManager) + { + + } + + public function valueToEntityArray(string $value, string $target_class): array + { + //Convert value to IDs: + $elements = []; + + //Split the given value by comm + foreach (explode(',', $value) as $id) { + if (trim($id) === '') { + continue; + } + + //Check if the given value ends with a plus, then we want to include all direct children + $include_children = false; + $include_recursive = false; + if (str_ends_with($id, '++')) { //Plus Plus means include all children recursively + $id = substr($id, 0, -2); + $include_recursive = true; + } elseif (str_ends_with($id, '+')) { + $id = substr($id, 0, -1); + $include_children = true; + } + + //Get a (shallow) reference to the entitity + $element = $this->entityManager->getReference($target_class, (int) $id); + $elements[] = $element; + + //If $element is not structural we are done + if (!is_a($element, AbstractStructuralDBElement::class)) { + continue; + } + + //Get the recursive list of children + if ($include_recursive) { + $elements = array_merge($elements, $this->nodesListBuilder->getChildrenFlatList($element)); + } elseif ($include_children) { + $elements = array_merge($elements, $element->getChildren()->toArray()); + } + } + + return $elements; + } + + public function getDescription(array $properties): array + { + if ($properties === []) { + return []; + } + + $description = []; + foreach (array_keys($properties) as $property) { + $description[(string)$property] = [ + 'property' => $property, + 'type' => Type::BUILTIN_TYPE_STRING, + 'required' => false, + 'description' => 'Filter using a comma seperated list of element IDs. Use + to include all direct children and ++ to include all children recursively.', + ]; + } + return $description; + } +} \ No newline at end of file diff --git a/src/ApiPlatform/Filter/LikeFilter.php b/src/ApiPlatform/Filter/LikeFilter.php new file mode 100644 index 00000000..a8e96eb9 --- /dev/null +++ b/src/ApiPlatform/Filter/LikeFilter.php @@ -0,0 +1,74 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform\Filter; + +use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter; +use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; +use ApiPlatform\Metadata\Operation; +use Doctrine\ORM\QueryBuilder; +use Symfony\Component\PropertyInfo\Type; + +final class LikeFilter extends AbstractFilter +{ + + protected function filterProperty( + string $property, + $value, + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $resourceClass, + ?Operation $operation = null, + array $context = [] + ): void { + // Otherwise filter is applied to order and page as well + if ( + !$this->isPropertyEnabled($property, $resourceClass) || + !$this->isPropertyMapped($property, $resourceClass) + ) { + return; + } + $parameterName = $queryNameGenerator->generateParameterName($property); // Generate a unique parameter name to avoid collisions with other filters + $queryBuilder + ->andWhere(sprintf('ILIKE(o.%s, :%s) = TRUE', $property, $parameterName)) + ->setParameter($parameterName, $value); + } + + public function getDescription(string $resourceClass): array + { + if (!$this->properties) { + return []; + } + + $description = []; + foreach (array_keys($this->properties) as $property) { + $description[(string)$property] = [ + 'property' => $property, + 'type' => Type::BUILTIN_TYPE_STRING, + 'required' => false, + 'description' => 'Filter using a LIKE SQL expression. Use % as wildcard for multiple characters and _ for single characters. For example, to search for all items containing foo, use foo. To search for all items starting with foo, use foo%. To search for all items ending with foo, use %foo', + ]; + } + return $description; + } +} \ No newline at end of file diff --git a/src/ApiPlatform/Filter/PartStoragelocationFilter.php b/src/ApiPlatform/Filter/PartStoragelocationFilter.php new file mode 100644 index 00000000..4d0ad2df --- /dev/null +++ b/src/ApiPlatform/Filter/PartStoragelocationFilter.php @@ -0,0 +1,79 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform\Filter; + +use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter; +use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; +use ApiPlatform\Metadata\Operation; +use App\Entity\Parts\StorageLocation; +use Doctrine\ORM\QueryBuilder; +use Doctrine\Persistence\ManagerRegistry; +use Psr\Log\LoggerInterface; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; + +class PartStoragelocationFilter extends AbstractFilter +{ + + public function __construct( + ManagerRegistry $managerRegistry, + private readonly EntityFilterHelper $filter_helper, + ?LoggerInterface $logger = null, + ?array $properties = null, + ?NameConverterInterface $nameConverter = null + ) { + parent::__construct($managerRegistry, $logger, $properties, $nameConverter); + } + + protected function filterProperty( + string $property, + $value, + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $resourceClass, + ?Operation $operation = null, + array $context = [] + ): void { + //Do not check for mapping here, as we are using a virtual property + if ( + !$this->isPropertyEnabled($property, $resourceClass) + ) { + return; + } + + $elements = $this->filter_helper->valueToEntityArray($value, StorageLocation::class); + + $parameterName = $queryNameGenerator->generateParameterName($property); // Generate a unique parameter name to avoid collisions with other filters + $queryBuilder + ->leftJoin('o.partLots', 'partLots') + ->andWhere(sprintf('partLots.storage_location IN (:%s)', $parameterName)) + ->setParameter($parameterName, $elements); + } + + + + public function getDescription(string $resourceClass): array + { + return $this->filter_helper->getDescription($this->properties); + } +} \ No newline at end of file diff --git a/src/ApiPlatform/Filter/TagFilter.php b/src/ApiPlatform/Filter/TagFilter.php new file mode 100644 index 00000000..98648ee9 --- /dev/null +++ b/src/ApiPlatform/Filter/TagFilter.php @@ -0,0 +1,96 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform\Filter; + +use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter; +use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; +use ApiPlatform\Metadata\Operation; +use Doctrine\ORM\QueryBuilder; +use Symfony\Component\PropertyInfo\Type; + +/** + * Due to their nature, tags are stored in a single string, separated by commas, which requires some more complex search logic. + * This filter allows to easily search for tags in a part entity. + */ +final class TagFilter extends AbstractFilter +{ + + protected function filterProperty( + string $property, + $value, + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $resourceClass, + ?Operation $operation = null, + array $context = [] + ): void { + // Ignore filter if property is not enabled or mapped + if ( + !$this->isPropertyEnabled($property, $resourceClass) || + !$this->isPropertyMapped($property, $resourceClass) + ) { + return; + } + + //Escape any %, _ or \ in the tag + $value = addcslashes($value, '%_\\'); + + $tag_identifier_prefix = $queryNameGenerator->generateParameterName($property); + + $expr = $queryBuilder->expr(); + + $tmp = $expr->orX( + 'ILIKE(o.'.$property.', :' . $tag_identifier_prefix . '_1) = TRUE', + 'ILIKE(o.'.$property.', :' . $tag_identifier_prefix . '_2) = TRUE', + 'ILIKE(o.'.$property.', :' . $tag_identifier_prefix . '_3) = TRUE', + 'ILIKE(o.'.$property.', :' . $tag_identifier_prefix . '_4) = TRUE', + ); + + $queryBuilder->andWhere($tmp); + + //Set the parameters for the LIKE expression, in each variation of the tag (so with a comma, at the end, at the beginning, and on both ends, and equaling the tag) + $queryBuilder->setParameter($tag_identifier_prefix . '_1', '%,' . $value . ',%'); + $queryBuilder->setParameter($tag_identifier_prefix . '_2', '%,' . $value); + $queryBuilder->setParameter($tag_identifier_prefix . '_3', $value . ',%'); + $queryBuilder->setParameter($tag_identifier_prefix . '_4', $value); + } + + public function getDescription(string $resourceClass): array + { + if (!$this->properties) { + return []; + } + + $description = []; + foreach (array_keys($this->properties) as $property) { + $description[(string)$property] = [ + 'property' => $property, + 'type' => Type::BUILTIN_TYPE_STRING, + 'required' => false, + 'description' => 'Filter for tags of a part', + ]; + } + return $description; + } +} \ No newline at end of file diff --git a/src/ApiPlatform/FixInheritanceMappingMetadataFacory.php b/src/ApiPlatform/FixInheritanceMappingMetadataFacory.php new file mode 100644 index 00000000..c65e57a0 --- /dev/null +++ b/src/ApiPlatform/FixInheritanceMappingMetadataFacory.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform; + +use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; +use ApiPlatform\Metadata\Resource\ResourceMetadataCollection; +use App\Entity\Attachments\Attachment; +use App\Entity\Parameters\AbstractParameter; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; + +/** + * API Platform has problems with single table inheritance, as it assumes that they all have different endpoints. + * This decorator fixes this problem by using the parent class for the metadata collection. + */ +#[AsDecorator('api_platform.metadata.resource.metadata_collection_factory')] +class FixInheritanceMappingMetadataFacory implements ResourceMetadataCollectionFactoryInterface +{ + private const SINGLE_INHERITANCE_ENTITY_CLASSES = [ + Attachment::class, + AbstractParameter::class, + ]; + + private array $cache = []; + + public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $decorated) + { + } + + public function create(string $resourceClass): ResourceMetadataCollection + { + //If we already have a cached value, we can return it + if (isset($this->cache[$resourceClass])) { + return $this->decorated->create($this->cache[$resourceClass]); + } + + //Check if the resourceClass is a single inheritance class, then we can use the parent class to access it + foreach (self::SINGLE_INHERITANCE_ENTITY_CLASSES as $class) { + if (is_a($resourceClass, $class, true)) { + $this->cache[$resourceClass] = $class; + break; + } + } + + //If it was not found in the list of single inheritance classes, we can use the original class + if (!isset($this->cache[$resourceClass])) { + $this->cache[$resourceClass] = $resourceClass; + } + + return $this->decorated->create($this->cache[$resourceClass] ?? $resourceClass); + } +} \ No newline at end of file diff --git a/src/ApiPlatform/HandleAttachmentsUploadsProcessor.php b/src/ApiPlatform/HandleAttachmentsUploadsProcessor.php new file mode 100644 index 00000000..b5149442 --- /dev/null +++ b/src/ApiPlatform/HandleAttachmentsUploadsProcessor.php @@ -0,0 +1,67 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform; + +use ApiPlatform\Metadata\DeleteOperationInterface; +use ApiPlatform\Metadata\Operation; +use ApiPlatform\State\ProcessorInterface; +use App\Entity\Attachments\Attachment; +use App\Services\Attachments\AttachmentSubmitHandler; +use Symfony\Component\DependencyInjection\Attribute\Autowire; + +/** + * This state processor handles the upload property set on the deserialized attachment entity and + * calls the upload handler service to handle the upload. + */ +final class HandleAttachmentsUploadsProcessor implements ProcessorInterface +{ + public function __construct( + #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')] + private readonly ProcessorInterface $persistProcessor, + #[Autowire(service: 'api_platform.doctrine.orm.state.remove_processor')] + private readonly ProcessorInterface $removeProcessor, + private readonly AttachmentSubmitHandler $attachmentSubmitHandler + ) { + + } + + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed + { + if ($operation instanceof DeleteOperationInterface) { + return $this->removeProcessor->process($data, $operation, $uriVariables, $context); + } + + //Check if the attachment has any upload data we need to handle + //This have to happen before the persist processor is called, because the changes on the entity must be saved! + if ($data instanceof Attachment && $data->getUpload()) { + $upload = $data->getUpload(); + //Reset the upload data + $data->setUpload(null); + + $this->attachmentSubmitHandler->handleUpload($data, $upload); + } + + return $this->persistProcessor->process($data, $operation, $uriVariables, $context); + } +} \ No newline at end of file diff --git a/src/ApiPlatform/NormalizePropertyNameCollectionFactory.php b/src/ApiPlatform/NormalizePropertyNameCollectionFactory.php new file mode 100644 index 00000000..c6a8220e --- /dev/null +++ b/src/ApiPlatform/NormalizePropertyNameCollectionFactory.php @@ -0,0 +1,77 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform; + +use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; +use ApiPlatform\Metadata\Property\PropertyNameCollection; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use function Symfony\Component\String\u; + +/** + * This decorator removes all camelCase property names from the property name collection, if a snake_case version exists. + * This is a fix for https://github.com/Part-DB/Part-DB-server/issues/862, as the openapi schema generator wrongly collects + * both camelCase and snake_case property names, which leads to duplicate properties in the schema. + * This seems to come from the fact that the openapi schema generator uses no serializerContext, which seems then to collect + * the getters too... + */ +#[AsDecorator('api_platform.metadata.property.name_collection_factory')] +class NormalizePropertyNameCollectionFactory implements PropertyNameCollectionFactoryInterface +{ + public function __construct(private readonly PropertyNameCollectionFactoryInterface $decorated) + { + } + + public function create(string $resourceClass, array $options = []): PropertyNameCollection + { + // Get the default properties from the decorated service + $propertyNames = $this->decorated->create($resourceClass, $options); + + //Only become active in the context of the openapi schema generation + if (!isset($options['schema_type'])) { + return $propertyNames; + } + + //If we are not in the jsonapi generator (which sets no serializer groups), return the property names as is + if (isset($options['serializer_groups'])) { + return $propertyNames; + } + + //Remove all camelCase property names from the collection, if a snake_case version exists + $properties = iterator_to_array($propertyNames); + + foreach ($properties as $property) { + if (str_contains($property, '_')) { + $camelized = u($property)->camel()->toString(); + + //If the camelized version exists, remove it from the collection + $index = array_search($camelized, $properties, true); + if ($index !== false) { + unset($properties[$index]); + } + } + } + + return new PropertyNameCollection($properties); + } +} \ No newline at end of file diff --git a/src/ApiPlatform/OpenApiFactoryDecorator.php b/src/ApiPlatform/OpenApiFactoryDecorator.php new file mode 100644 index 00000000..af213e14 --- /dev/null +++ b/src/ApiPlatform/OpenApiFactoryDecorator.php @@ -0,0 +1,50 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform; + +use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface; +use ApiPlatform\OpenApi\Model\SecurityScheme; +use ApiPlatform\OpenApi\OpenApi; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; + +#[AsDecorator('api_platform.openapi.factory')] +class OpenApiFactoryDecorator implements OpenApiFactoryInterface +{ + public function __construct(private readonly OpenApiFactoryInterface $decorated) + { + } + + public function __invoke(array $context = []): OpenApi + { + $openApi = $this->decorated->__invoke($context); + $securitySchemes = $openApi->getComponents()->getSecuritySchemes() ?: new \ArrayObject(); + $securitySchemes['access_token'] = new SecurityScheme( + type: 'http', + description: 'Use an API token to authenticate', + name: 'Authorization', + scheme: 'bearer', + ); + return $openApi; + } +} \ No newline at end of file diff --git a/src/ApiResource/PartDBInfo.php b/src/ApiResource/PartDBInfo.php new file mode 100644 index 00000000..25aed05e --- /dev/null +++ b/src/ApiResource/PartDBInfo.php @@ -0,0 +1,67 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiResource; + +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\State\PartDBInfoProvider; + +/** + * This class is used to provide various information about the system. + */ +#[ApiResource( + uriTemplate: '/info.{_format}', + description: 'Basic information about Part-DB like version, title, etc.', + operations: [new Get(openapi: new Operation(summary: 'Get basic information about the installed Part-DB instance.'))], + provider: PartDBInfoProvider::class +)] +#[ApiFilter(PropertyFilter::class)] +class PartDBInfo +{ + public function __construct( + /** The installed Part-DB version */ + public readonly string $version, + /** The Git branch name of the Part-DB version (or null, if not installed via git) */ + public readonly string|null $git_branch, + /** The Git branch commit of the Part-DB version (or null, if not installed via git) */ + public readonly string|null $git_commit, + /** The name of this Part-DB instance */ + public readonly string $title, + /** The banner, shown on homepage (markdown encoded) */ + public readonly string $banner, + /** The configured default URI for Part-DB */ + public readonly string $default_uri, + /** The global timezone of this Part-DB */ + public readonly string $global_timezone, + /** The base currency of Part-DB, used as internal representation of monetary values */ + public readonly string $base_currency, + /** The configured default language of Part-DB */ + public readonly string $global_locale, + ) { + + } +} \ No newline at end of file diff --git a/src/Command/Attachments/CleanAttachmentsCommand.php b/src/Command/Attachments/CleanAttachmentsCommand.php index 40f568bf..59bc99ee 100644 --- a/src/Command/Attachments/CleanAttachmentsCommand.php +++ b/src/Command/Attachments/CleanAttachmentsCommand.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace App\Command\Attachments; +use Symfony\Component\Console\Attribute\AsCommand; use App\Services\Attachments\AttachmentManager; use App\Services\Attachments\AttachmentPathResolver; use App\Services\Attachments\AttachmentReverseSearch; @@ -40,30 +41,21 @@ use function count; use const DIRECTORY_SEPARATOR; +#[AsCommand('partdb:attachments:clean-unused|app:clean-attachments', 'Lists (and deletes if wanted) attachments files that are not used anymore (abandoned files).')] class CleanAttachmentsCommand extends Command { - protected static $defaultName = 'partdb:attachments:clean-unused|app:clean-attachments'; - - protected AttachmentManager $attachment_helper; - protected AttachmentReverseSearch $reverseSearch; protected MimeTypes $mimeTypeGuesser; - protected AttachmentPathResolver $pathResolver; - public function __construct(AttachmentManager $attachmentHelper, AttachmentReverseSearch $reverseSearch, AttachmentPathResolver $pathResolver) + public function __construct(protected AttachmentManager $attachment_helper, protected AttachmentReverseSearch $reverseSearch, protected AttachmentPathResolver $pathResolver) { - $this->attachment_helper = $attachmentHelper; - $this->pathResolver = $pathResolver; - $this->reverseSearch = $reverseSearch; $this->mimeTypeGuesser = new MimeTypes(); parent::__construct(); } protected function configure(): void { - $this - ->setDescription('Lists (and deletes if wanted) attachments files that are not used anymore (abandoned files).') - ->setHelp('This command allows to find all files in the media folder which are not associated with an attachment anymore.'. - ' These files are not needed and can eventually deleted.'); + $this->setHelp('This command allows to find all files in the media folder which are not associated with an attachment anymore.'. + ' These files are not needed and can eventually deleted.'); } protected function execute(InputInterface $input, OutputInterface $output): int @@ -81,6 +73,9 @@ class CleanAttachmentsCommand extends Command //Ignore image cache folder $finder->exclude('cache'); + //Ignore automigration folder + $finder->exclude('.automigration-backup'); + $fs = new Filesystem(); $file_list = []; @@ -91,7 +86,7 @@ class CleanAttachmentsCommand extends Command foreach ($finder as $file) { //If not attachment object uses this file, print it - if (0 === count($this->reverseSearch->findAttachmentsByFile($file))) { + if ([] === $this->reverseSearch->findAttachmentsByFile($file)) { $file_list[] = $file; $table->addRow([ $fs->makePathRelative($file->getPathname(), $mediaPath), @@ -101,14 +96,14 @@ class CleanAttachmentsCommand extends Command } } - if (count($file_list) > 0) { + if ($file_list !== []) { $table->render(); $continue = $io->confirm(sprintf('Found %d abandoned files. Do you want to delete them? This can not be undone!', count($file_list)), false); if (!$continue) { //We are finished here, when no files should be deleted - return 0; + return Command::SUCCESS; } //Delete the files @@ -121,7 +116,7 @@ class CleanAttachmentsCommand extends Command $io->success('No abandoned files found.'); } - return 0; + return Command::SUCCESS; } /** diff --git a/src/Command/Attachments/DownloadAttachmentsCommand.php b/src/Command/Attachments/DownloadAttachmentsCommand.php new file mode 100644 index 00000000..34deef0e --- /dev/null +++ b/src/Command/Attachments/DownloadAttachmentsCommand.php @@ -0,0 +1,136 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Command\Attachments; + +use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\AttachmentUpload; +use App\Exceptions\AttachmentDownloadException; +use App\Services\Attachments\AttachmentManager; +use App\Services\Attachments\AttachmentSubmitHandler; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +#[AsCommand('partdb:attachments:download', "Downloads all attachments which have only an external URL to the local filesystem.")] +class DownloadAttachmentsCommand extends Command +{ + public function __construct(private readonly AttachmentSubmitHandler $attachmentSubmitHandler, + private EntityManagerInterface $entityManager) + { + parent::__construct(); + } + + public function configure(): void + { + $this->setHelp('This command downloads all attachments, which only have an external URL, to the local filesystem, so that you have an offline copy of the attachments.'); + $this->addOption('--private', null, null, 'If set, the attachments will be downloaded to the private storage.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $qb = $this->entityManager->createQueryBuilder(); + $qb->select('attachment') + ->from(Attachment::class, 'attachment') + ->where('attachment.external_path IS NOT NULL') + ->andWhere('attachment.external_path != \'\'') + ->andWhere('attachment.internal_path IS NULL'); + + $query = $qb->getQuery(); + $attachments = $query->getResult(); + + if (count($attachments) === 0) { + $io->success('No attachments with external URL found.'); + return Command::SUCCESS; + } + + $io->note('Found ' . count($attachments) . ' attachments with external URL, that will be downloaded.'); + + //If the option --private is set, the attachments will be downloaded to the private storage. + $private = $input->getOption('private'); + if ($private) { + if (!$io->confirm('Attachments will be downloaded to the private storage. Continue?')) { + return Command::SUCCESS; + } + } else { + if (!$io->confirm('Attachments will be downloaded to the public storage, where everybody knowing the correct URL can access it. Continue?')){ + return Command::SUCCESS; + } + } + + $progressBar = $io->createProgressBar(count($attachments)); + $progressBar->setFormat("%current%/%max% [%bar%] %percent:3s%% %elapsed:16s%/%estimated:-16s% \n%message%"); + + $progressBar->setMessage('Starting download...'); + $progressBar->start(); + + + $errors = []; + + foreach ($attachments as $attachment) { + /** @var Attachment $attachment */ + $progressBar->setMessage(sprintf('%s (ID: %s) from %s', $attachment->getName(), $attachment->getID(), $attachment->getHost())); + $progressBar->advance(); + + try { + $attachmentUpload = new AttachmentUpload(file: null, downloadUrl: true, private: $private); + $this->attachmentSubmitHandler->handleUpload($attachment, $attachmentUpload); + + //Write changes to the database + $this->entityManager->flush(); + } catch (AttachmentDownloadException $e) { + $errors[] = [ + 'attachment' => $attachment, + 'error' => $e->getMessage() + ]; + } + } + + $progressBar->finish(); + + //Fix the line break after the progress bar + $io->newLine(); + $io->newLine(); + + if (count($errors) > 0) { + $io->warning('Some attachments could not be downloaded:'); + foreach ($errors as $error) { + $io->warning(sprintf("Attachment %s (ID %s) could not be downloaded from %s:\n%s", + $error['attachment']->getName(), + $error['attachment']->getID(), + $error['attachment']->getExternalPath(), + $error['error']) + ); + } + } else { + $io->success('All attachments downloaded successfully.'); + } + + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/src/Command/Attachments/SanitizeSVGAttachmentsCommand.php b/src/Command/Attachments/SanitizeSVGAttachmentsCommand.php new file mode 100644 index 00000000..7f6550f0 --- /dev/null +++ b/src/Command/Attachments/SanitizeSVGAttachmentsCommand.php @@ -0,0 +1,90 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Command\Attachments; + +use App\Entity\Attachments\Attachment; +use App\Services\Attachments\AttachmentSubmitHandler; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +#[AsCommand('partdb:attachments:sanitize-svg', "Sanitize uploaded SVG files.")] +class SanitizeSVGAttachmentsCommand extends Command +{ + public function __construct(private readonly EntityManagerInterface $entityManager, private readonly AttachmentSubmitHandler $attachmentSubmitHandler, ?string $name = null) + { + parent::__construct($name); + } + + public function configure(): void + { + $this->setHelp('This command allows to sanitize SVG files uploaded via attachments. This happens automatically since version 1.17.1, this command is intended to be used for older files.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $io->info('This command will sanitize all uploaded SVG files. This is only required if you have uploaded (untrusted) SVG files before version 1.17.1. If you are running a newer version, you don\'t need to run this command (again).'); + if (!$io->confirm('Do you want to continue?', false)) { + $io->success('Command aborted.'); + return Command::FAILURE; + } + + $io->info('Sanitizing SVG files...'); + + //Finding all attachments with svg files + $qb = $this->entityManager->createQueryBuilder(); + $qb->select('a') + ->from(Attachment::class, 'a') + ->where('a.internal_path LIKE :pattern ESCAPE \'#\'') + ->orWhere('a.original_filename LIKE :pattern ESCAPE \'#\'') + ->setParameter('pattern', '%.svg'); + + $attachments = $qb->getQuery()->getResult(); + $io->note('Found '.count($attachments).' attachments with SVG files.'); + + if (count($attachments) === 0) { + $io->success('No SVG files found.'); + return Command::FAILURE; + } + + $io->info('Sanitizing SVG files...'); + $io->progressStart(count($attachments)); + foreach ($attachments as $attachment) { + /** @var Attachment $attachment */ + $io->note('Sanitizing attachment '.$attachment->getId().' ('.($attachment->getFilename() ?? '???').')'); + $this->attachmentSubmitHandler->sanitizeSVGAttachment($attachment); + $io->progressAdvance(); + + } + $io->progressFinish(); + + $io->success('Sanitization finished. All SVG files have been sanitized.'); + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/src/Command/BackupCommand.php b/src/Command/BackupCommand.php index 7ee535c6..085c552a 100644 --- a/src/Command/BackupCommand.php +++ b/src/Command/BackupCommand.php @@ -1,7 +1,13 @@ project_dir = $project_dir; - $this->entityManager = $entityManager; - parent::__construct(); } @@ -55,6 +52,7 @@ class BackupCommand extends Command $backup_attachments = $input->getOption('attachments'); $backup_config = $input->getOption('config'); $backup_full = $input->getOption('full'); + $overwrite = $input->getOption('overwrite'); if ($backup_full) { $backup_database = true; @@ -72,13 +70,12 @@ class BackupCommand extends Command $io->info('Backup Part-DB to '.$output_filepath); //Check if the file already exists - if (file_exists($output_filepath)) { - //Then ask the user, if he wants to overwrite the file - if (!$io->confirm('The file '.realpath($output_filepath).' already exists. Do you want to overwrite it?', false)) { - $io->error('Backup aborted!'); - - return Command::FAILURE; - } + //Then ask the user, if he wants to overwrite the file + if (!$overwrite + && file_exists($output_filepath) + && !$io->confirm('The file '.realpath($output_filepath).' already exists. Do you want to overwrite it?', false)) { + $io->error('Backup aborted!'); + return Command::FAILURE; } $io->note('Starting backup...'); @@ -116,8 +113,6 @@ class BackupCommand extends Command /** * Constructs the MySQL PDO DSN. * Taken from https://github.com/doctrine/dbal/blob/3.5.x/src/Driver/PDO/MySQL/Driver.php - * - * @param array $params */ private function configureDumper(array $params, DbDumper $dumper): void { @@ -146,30 +141,42 @@ class BackupCommand extends Command } } + private function runSQLDumper(DbDumper $dumper, ZipFile $zip, array $connectionParams): void + { + $this->configureDumper($connectionParams, $dumper); + + $tmp_file = tempnam(sys_get_temp_dir(), 'partdb_sql_dump'); + + $dumper->dumpToFile($tmp_file); + $zip->addFile($tmp_file, 'database.sql'); + } + protected function backupDatabase(ZipFile $zip, SymfonyStyle $io): void { $io->note('Backup database...'); //Determine if we use MySQL or SQLite $connection = $this->entityManager->getConnection(); - if ($connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) { + $params = $connection->getParams(); + $platform = $connection->getDatabasePlatform(); + if ($platform instanceof AbstractMySQLPlatform) { try { $io->note('MySQL database detected. Dump DB to SQL using mysqldump...'); - $params = $connection->getParams(); - $dumper = MySql::create(); - $this->configureDumper($params, $dumper); - - $tmp_file = tempnam(sys_get_temp_dir(), 'partdb_sql_dump'); - - $dumper->dumpToFile($tmp_file); - $zip->addFile($tmp_file, 'mysql_dump.sql'); + $this->runSQLDumper(MySql::create(), $zip, $params); } catch (\Exception $e) { $io->error('Could not dump database: '.$e->getMessage()); $io->error('This can maybe be fixed by installing the mysqldump binary and adding it to the PATH variable!'); } - } elseif ($connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { + } elseif ($platform instanceof PostgreSQLPlatform) { + try { + $io->note('PostgreSQL database detected. Dump DB to SQL using pg_dump...'); + $this->runSQLDumper(PostgreSql::create(), $zip, $params); + } catch (\Exception $e) { + $io->error('Could not dump database: '.$e->getMessage()); + $io->error('This can maybe be fixed by installing the pg_dump binary and adding it to the PATH variable!'); + } + } elseif ($platform instanceof SQLitePlatform) { $io->note('SQLite database detected. Copy DB file to ZIP...'); - $params = $connection->getParams(); $zip->addFile($params['path'], 'var/app.db'); } else { $io->error('Unknown database platform. Could not backup database!'); diff --git a/src/Command/CheckRequirementsCommand.php b/src/Command/CheckRequirementsCommand.php index 4d9f9d96..5e15e8e2 100644 --- a/src/Command/CheckRequirementsCommand.php +++ b/src/Command/CheckRequirementsCommand.php @@ -1,4 +1,7 @@ . */ - namespace App\Command; +use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -27,23 +30,17 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; +#[AsCommand('partdb:check-requirements', 'Checks if the requirements Part-DB needs or recommends are fulfilled.')] class CheckRequirementsCommand extends Command { - protected static $defaultName = 'partdb:check-requirements'; - - protected ContainerBagInterface $params; - - public function __construct(ContainerBagInterface $params) + public function __construct(protected ContainerBagInterface $params) { - $this->params = $params; parent::__construct(); } protected function configure(): void { - $this - ->setDescription('Checks if the requirements Part-DB needs or recommends are fulfilled.') - ->addOption('only_issues', 'i', InputOption::VALUE_NONE, 'Only show issues, not success messages.') + $this->addOption('only_issues', 'i', InputOption::VALUE_NONE, 'Only show issues, not success messages.') ; } @@ -66,105 +63,135 @@ class CheckRequirementsCommand extends Command } - protected function checkPHP(SymfonyStyle $io, $only_issues = false): void + protected function checkPHP(SymfonyStyle $io, bool $only_issues = false): void { //Check PHP versions - $io->isVerbose() && $io->comment('Checking PHP version...'); - if (PHP_VERSION_ID < 80100) { + if ($io->isVerbose()) { + $io->comment('Checking PHP version...'); + } + //We recommend PHP 8.2, but 8.1 is the minimum + if (PHP_VERSION_ID < 80200) { $io->warning('You are using PHP '. PHP_VERSION .'. This will work, but a newer version is recommended.'); + } elseif (!$only_issues) { + $io->success('PHP version is sufficient.'); + } + + //Checking 32-bit system + if (PHP_INT_SIZE === 4) { + $io->warning('You are using a 32-bit system. You will have problems with working with dates after the year 2038, therefore a 64-bit system is recommended.'); + } elseif (PHP_INT_SIZE === 8) { //@phpstan-ignore-line //PHP_INT_SIZE is always 4 or 8 + if (!$only_issues) { + $io->success('You are using a 64-bit system.'); + } } else { - !$only_issues && $io->success('PHP version is sufficient.'); + $io->warning('You are using a system with an unknown bit size. That is interesting xD'); } //Check if opcache is enabled - $io->isVerbose() && $io->comment('Checking Opcache...'); + if ($io->isVerbose()) { + $io->comment('Checking Opcache...'); + } $opcache_enabled = ini_get('opcache.enable') === '1'; if (!$opcache_enabled) { $io->warning('Opcache is not enabled. This will work, but performance will be better with opcache enabled. Set opcache.enable=1 in your php.ini to enable it'); - } else { - !$only_issues && $io->success('Opcache is enabled.'); + } elseif (!$only_issues) { + $io->success('Opcache is enabled.'); } //Check if opcache is configured correctly - $io->isVerbose() && $io->comment('Checking Opcache configuration...'); + if ($io->isVerbose()) { + $io->comment('Checking Opcache configuration...'); + } if ($opcache_enabled && (ini_get('opcache.memory_consumption') < 256 || ini_get('opcache.max_accelerated_files') < 20000)) { $io->warning('Opcache configuration can be improved. See https://symfony.com/doc/current/performance.html for more info.'); - } else { - !$only_issues && $io->success('Opcache configuration is already performance optimized.'); + } elseif (!$only_issues) { + $io->success('Opcache configuration is already performance optimized.'); } } - protected function checkPartDBConfig(SymfonyStyle $io, $only_issues = false): void + protected function checkPartDBConfig(SymfonyStyle $io, bool $only_issues = false): void { //Check if APP_ENV is set to prod - $io->isVerbose() && $io->comment('Checking debug mode...'); - if($this->params->get('kernel.debug')) { + if ($io->isVerbose()) { + $io->comment('Checking debug mode...'); + } + if ($this->params->get('kernel.debug')) { $io->warning('You have activated debug mode, this is will leak informations in a production environment.'); - } else { - !$only_issues && $io->success('Debug mode disabled.'); + } elseif (!$only_issues) { + $io->success('Debug mode disabled.'); } } - protected function checkPHPExtensions(SymfonyStyle $io, $only_issues = false): void + protected function checkPHPExtensions(SymfonyStyle $io, bool $only_issues = false): void { //Get all installed PHP extensions $extensions = get_loaded_extensions(); - $io->isVerbose() && $io->comment('Your PHP installation has '. count($extensions) .' extensions installed: '. implode(', ', $extensions)); + if ($io->isVerbose()) { + $io->comment('Your PHP installation has '. count($extensions) .' extensions installed: '. implode(', ', $extensions)); + } $db_drivers_count = 0; - if(!in_array('pdo_mysql', $extensions)) { + if(!in_array('pdo_mysql', $extensions, true)) { $io->error('pdo_mysql is not installed. You will not be able to use MySQL databases.'); } else { - !$only_issues && $io->success('PHP extension pdo_mysql is installed.'); + if (!$only_issues) { + $io->success('PHP extension pdo_mysql is installed.'); + } $db_drivers_count++; } - if(!in_array('pdo_sqlite', $extensions)) { + if(!in_array('pdo_sqlite', $extensions, true)) { $io->error('pdo_sqlite is not installed. You will not be able to use SQLite. databases'); } else { - !$only_issues && $io->success('PHP extension pdo_sqlite is installed.'); + if (!$only_issues) { + $io->success('PHP extension pdo_sqlite is installed.'); + } $db_drivers_count++; } - $io->isVerbose() && $io->comment('You have '. $db_drivers_count .' database drivers installed.'); + if ($io->isVerbose()) { + $io->comment('You have '. $db_drivers_count .' database drivers installed.'); + } if ($db_drivers_count === 0) { $io->error('You have no database drivers installed. You have to install at least one database driver!'); } - if(!in_array('curl', $extensions)) { + if (!in_array('curl', $extensions, true)) { $io->warning('curl extension is not installed. Install curl extension for better performance'); - } else { - !$only_issues && $io->success('PHP extension curl is installed.'); + } elseif (!$only_issues) { + $io->success('PHP extension curl is installed.'); } - $gd_installed = in_array('gd', $extensions); - if(!$gd_installed) { + $gd_installed = in_array('gd', $extensions, true); + if (!$gd_installed) { $io->error('GD is not installed. GD is required for image processing.'); - } else { - !$only_issues && $io->success('PHP extension GD is installed.'); + } elseif (!$only_issues) { + $io->success('PHP extension GD is installed.'); } //Check if GD has jpeg support - $io->isVerbose() && $io->comment('Checking if GD has jpeg support...'); + if ($io->isVerbose()) { + $io->comment('Checking if GD has jpeg support...'); + } if ($gd_installed) { $gd_info = gd_info(); - if($gd_info['JPEG Support'] === false) { + if ($gd_info['JPEG Support'] === false) { $io->warning('Your GD does not have jpeg support. You will not be able to generate thumbnails of jpeg images.'); - } else { - !$only_issues && $io->success('GD has jpeg support.'); + } elseif (!$only_issues) { + $io->success('GD has jpeg support.'); } - if($gd_info['PNG Support'] === false) { + if ($gd_info['PNG Support'] === false) { $io->warning('Your GD does not have png support. You will not be able to generate thumbnails of png images.'); - } else { - !$only_issues && $io->success('GD has png support.'); + } elseif (!$only_issues) { + $io->success('GD has png support.'); } - if($gd_info['WebP Support'] === false) { + if ($gd_info['WebP Support'] === false) { $io->warning('Your GD does not have WebP support. You will not be able to generate thumbnails of WebP images.'); - } else { - !$only_issues && $io->success('GD has WebP support.'); + } elseif (!$only_issues) { + $io->success('GD has WebP support.'); } } @@ -173,4 +200,4 @@ class CheckRequirementsCommand extends Command } -} \ No newline at end of file +} diff --git a/src/Command/Currencies/UpdateExchangeRatesCommand.php b/src/Command/Currencies/UpdateExchangeRatesCommand.php index b0735cbc..0f3eb11f 100644 --- a/src/Command/Currencies/UpdateExchangeRatesCommand.php +++ b/src/Command/Currencies/UpdateExchangeRatesCommand.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace App\Command\Currencies; +use Symfony\Component\Console\Attribute\AsCommand; use App\Entity\PriceInformations\Currency; use App\Services\Tools\ExchangeRateUpdater; use Doctrine\ORM\EntityManagerInterface; @@ -35,30 +36,17 @@ use Symfony\Component\Console\Style\SymfonyStyle; use function count; 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 { - protected static $defaultName = 'partdb:currencies:update-exchange-rates|partdb:update-exchange-rates|app:update-exchange-rates'; - - protected string $base_current; - protected EntityManagerInterface $em; - protected ExchangeRateUpdater $exchangeRateUpdater; - - public function __construct(string $base_current, EntityManagerInterface $entityManager, ExchangeRateUpdater $exchangeRateUpdater) + public function __construct(protected string $base_current, protected EntityManagerInterface $em, protected ExchangeRateUpdater $exchangeRateUpdater) { - //$this->swap = $swap; - $this->base_current = $base_current; - - $this->em = $entityManager; - $this->exchangeRateUpdater = $exchangeRateUpdater; - parent::__construct(); } protected function configure(): void { - $this - ->setDescription('Updates the currency exchange rates.') - ->addArgument('iso_code', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The ISO Codes of the currencies that should be updated.'); + $this->addArgument('iso_code', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The ISO Codes of the currencies that should be updated.'); } protected function execute(InputInterface $input, OutputInterface $output): int @@ -69,7 +57,7 @@ class UpdateExchangeRatesCommand extends Command if (3 !== strlen($this->base_current)) { $io->error('Chosen Base current is not valid. Check your settings!'); - return 1; + return Command::FAILURE; } $io->note('Update currency exchange rates with base currency: '.$this->base_current); @@ -78,11 +66,7 @@ class UpdateExchangeRatesCommand extends Command $iso_code = $input->getArgument('iso_code'); $repo = $this->em->getRepository(Currency::class); - if (!empty($iso_code)) { - $candidates = $repo->findBy(['iso_code' => $iso_code]); - } else { - $candidates = $repo->findAll(); - } + $candidates = empty($iso_code) ? $repo->findAll() : $repo->findBy(['iso_code' => $iso_code]); $success_counter = 0; @@ -106,6 +90,6 @@ class UpdateExchangeRatesCommand extends Command $io->success(sprintf('%d (of %d) currency exchange rates were updated.', $success_counter, count($candidates))); - return 0; + return Command::SUCCESS; } } diff --git a/src/Command/LoadFixturesCommand.php b/src/Command/LoadFixturesCommand.php new file mode 100644 index 00000000..d01d19c3 --- /dev/null +++ b/src/Command/LoadFixturesCommand.php @@ -0,0 +1,73 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Command; + +use App\Doctrine\Purger\ResetAutoIncrementPurgerFactory; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * This command does basically the same as doctrine:fixtures:load, but it purges the database before loading the fixtures. + * It does so in another transaction, so we can modify the purger to reset the autoincrement, which would not be possible + * because the implicit commit otherwise. + */ +#[AsCommand(name: 'partdb:fixtures:load', description: 'Load test fixtures into the database and allows to reset the autoincrement before loading the fixtures.', hidden: true)] +class LoadFixturesCommand extends Command +{ + public function __construct(private readonly EntityManagerInterface $entityManager) + { + parent::__construct(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $ui = new SymfonyStyle($input, $output); + + $ui->warning('This command is for development and testing purposes only. It will purge the database and load fixtures afterwards. Do not use in production!'); + + if (! $ui->confirm(sprintf('Careful, database "%s" will be purged. Do you want to continue?', $this->entityManager->getConnection()->getDatabase()), ! $input->isInteractive())) { + return 0; + } + + $factory = new ResetAutoIncrementPurgerFactory(); + $purger = $factory->createForEntityManager(null, $this->entityManager); + + $purger->purge(); + + //Afterwards run the load fixtures command as normal, but with the --append option + $new_input = new ArrayInput([ + 'command' => 'doctrine:fixtures:load', + '--append' => true, + ]); + + $returnCode = $this->getApplication()?->doRun($new_input, $output); + + return $returnCode ?? Command::FAILURE; + } +} \ No newline at end of file diff --git a/src/Command/Logs/ShowEventLogCommand.php b/src/Command/Logs/ShowEventLogCommand.php index d3ac5c7f..505b1275 100644 --- a/src/Command/Logs/ShowEventLogCommand.php +++ b/src/Command/Logs/ShowEventLogCommand.php @@ -22,6 +22,8 @@ declare(strict_types=1); namespace App\Command\Logs; +use Symfony\Component\Console\Attribute\AsCommand; +use App\Entity\UserSystem\User; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\LogSystem\AbstractLogEntry; use App\Repository\LogEntryRepository; @@ -36,23 +38,14 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Contracts\Translation\TranslatorInterface; +#[AsCommand('partdb:logs:show|app:show-logs', 'List the last event log entries.')] class ShowEventLogCommand extends Command { - protected static $defaultName = 'partdb:logs:show|app:show-logs'; - protected EntityManagerInterface $entityManager; - protected TranslatorInterface $translator; - protected ElementTypeNameGenerator $elementTypeNameGenerator; protected LogEntryRepository $repo; - protected LogEntryExtraFormatter $formatter; - public function __construct(EntityManagerInterface $entityManager, - TranslatorInterface $translator, ElementTypeNameGenerator $elementTypeNameGenerator, LogEntryExtraFormatter $formatter) + public function __construct(protected EntityManagerInterface $entityManager, + protected TranslatorInterface $translator, protected ElementTypeNameGenerator $elementTypeNameGenerator, protected LogEntryExtraFormatter $formatter) { - $this->entityManager = $entityManager; - $this->translator = $translator; - $this->elementTypeNameGenerator = $elementTypeNameGenerator; - $this->formatter = $formatter; - $this->repo = $this->entityManager->getRepository(AbstractLogEntry::class); parent::__construct(); } @@ -72,33 +65,31 @@ class ShowEventLogCommand extends Command $max_page = (int) ceil($total_count / $limit); if ($page > $max_page && $max_page > 0) { - $io->error("There is no page ${page}! The maximum page is ${max_page}."); + $io->error("There is no page $page! The maximum page is $max_page."); - return 1; + return Command::FAILURE; } - $io->note("There are a total of ${total_count} log entries in the DB."); + $io->note("There are a total of $total_count log entries in the DB."); $continue = true; while ($continue && $page <= $max_page) { $this->showPage($output, $desc, $limit, $page, $max_page, $showExtra); if ($onePage) { - return 0; + return Command::SUCCESS; } $continue = $io->confirm('Do you want to show the next page?'); ++$page; } - return 0; + return Command::SUCCESS; } protected function configure(): void { - $this - ->setDescription('List the last event log entries.') - ->addOption('count', 'c', InputOption::VALUE_REQUIRED, 'How many log entries should be shown per page.', 50) + $this->addOption('count', 'c', InputOption::VALUE_REQUIRED, 'How many log entries should be shown per page.', 50) ->addOption('oldest_first', null, InputOption::VALUE_NONE, 'Show older entries first.') ->addOption('page', 'p', InputOption::VALUE_REQUIRED, 'Which page should be shown?', 1) ->addOption('onePage', null, InputOption::VALUE_NONE, 'Show only one page (dont ask to go to next).') @@ -114,7 +105,7 @@ class ShowEventLogCommand extends Command $entries = $this->repo->getLogsOrderedByTimestamp($sorting, $limit, $offset); $table = new Table($output); - $table->setHeaderTitle("Page ${page} / ${max_page}"); + $table->setHeaderTitle("Page $page / $max_page"); $headers = ['ID', 'Timestamp', 'Type', 'User', 'Target Type', 'Target']; if ($showExtra) { $headers[] = 'Extra data'; @@ -147,11 +138,19 @@ class ShowEventLogCommand extends Command $target_class = $this->elementTypeNameGenerator->getLocalizedTypeLabel($entry->getTargetClass()); } + if ($entry->getUser() instanceof User) { + $user = $entry->getUser()->getFullName(true); + } elseif ($entry->isCLIEntry()) { + $user = $entry->getCLIUsername() . ' [CLI]'; + } else { + $user = $entry->getUsername() . ' [deleted]'; + } + $row = [ $entry->getID(), $entry->getTimestamp()->format('Y-m-d H:i:s'), $entry->getType(), - $entry->getUser()->getFullName(true), + $user, $target_class, $target_name, ]; diff --git a/src/Command/Migrations/ConvertBBCodeCommand.php b/src/Command/Migrations/ConvertBBCodeCommand.php index 6b2b3fd7..201263ff 100644 --- a/src/Command/Migrations/ConvertBBCodeCommand.php +++ b/src/Command/Migrations/ConvertBBCodeCommand.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace App\Command\Migrations; +use Symfony\Component\Console\Attribute\AsCommand; use App\Entity\Attachments\AttachmentType; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\ProjectSystem\Project; @@ -29,7 +30,7 @@ use App\Entity\Parts\Category; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\UserSystem\Group; @@ -45,30 +46,23 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use function count; /** - * This command converts the BBCode used by old Part-DB versions (<1.0), to the current used markdown format. + * This command converts the BBCode used by old Part-DB versions (<1.0), to the current used Markdown format. */ +#[AsCommand('partdb:migrations:convert-bbcode|app:convert-bbcode', 'Converts BBCode used in old Part-DB versions to newly used Markdown')] class ConvertBBCodeCommand extends Command { /** - * @var string The LIKE criteria used to detect on SQL server if a entry contains BBCode + * @var string The LIKE criteria used to detect on SQL server if an entry contains BBCode */ protected const BBCODE_CRITERIA = '%[%]%[/%]%'; /** * @var string The regex (performed in PHP) used to check if a property really contains BBCODE */ protected const BBCODE_REGEX = '/\\[.+\\].*\\[\\/.+\\]/'; - - protected static $defaultName = 'partdb:migrations:convert-bbcode|app:convert-bbcode'; - - protected EntityManagerInterface $em; - protected PropertyAccessorInterface $propertyAccessor; protected BBCodeToMarkdownConverter $converter; - public function __construct(EntityManagerInterface $entityManager, PropertyAccessorInterface $propertyAccessor) + public function __construct(protected EntityManagerInterface $em, protected PropertyAccessorInterface $propertyAccessor) { - $this->em = $entityManager; - $this->propertyAccessor = $propertyAccessor; - $this->converter = new BBCodeToMarkdownConverter(); parent::__construct(); @@ -76,9 +70,7 @@ class ConvertBBCodeCommand extends Command protected function configure(): void { - $this - ->setDescription('Converts BBCode used in old Part-DB versions to newly used Markdown') - ->setHelp('Older versions of Part-DB (<1.0) used BBCode for rich text formatting. + $this->setHelp('Older versions of Part-DB (<1.0) used BBCode for rich text formatting. Part-DB now uses Markdown which offers more features but is incompatible with BBCode. When you upgrade from an pre 1.0 version you have to run this command to convert your comment fields'); @@ -87,13 +79,14 @@ class ConvertBBCodeCommand extends Command /** * Returns a list which entities and which properties need to be checked. + * @return array, string[]> */ protected function getTargetsLists(): array { return [ Part::class => ['description', 'comment'], AttachmentType::class => ['comment'], - Storelocation::class => ['comment'], + StorageLocation::class => ['comment'], Project::class => ['comment'], Category::class => ['comment'], Manufacturer::class => ['comment'], @@ -117,7 +110,6 @@ class ConvertBBCodeCommand extends Command $class )); //Determine which entities of this type we need to modify - /** @var EntityRepository $repo */ $repo = $this->em->getRepository($class); $qb = $repo->createQueryBuilder('e') ->select('e'); @@ -129,25 +121,25 @@ class ConvertBBCodeCommand extends Command //Fetch resulting classes $results = $qb->getQuery()->getResult(); - $io->note(sprintf('Found %d entities, that need to be converted!', count($results))); + $io->note(sprintf('Found %d entities, that need to be converted!', is_countable($results) ? count($results) : 0)); //In verbose mode print the names of the entities foreach ($results as $result) { /** @var AbstractNamedDBElement $result */ $io->writeln( - 'Convert entity: '.$result->getName().' ('.get_class($result).': '.$result->getID().')', + 'Convert entity: '.$result->getName().' ('.$result::class.': '.$result->getID().')', OutputInterface::VERBOSITY_VERBOSE ); foreach ($properties as $property) { //Retrieve bbcode from entity $bbcode = $this->propertyAccessor->getValue($result, $property); //Check if the current property really contains BBCode - if (!preg_match(static::BBCODE_REGEX, $bbcode)) { + if (!preg_match(static::BBCODE_REGEX, (string) $bbcode)) { continue; } $io->writeln( 'BBCode (old): ' - .str_replace('\n', ' ', substr($bbcode, 0, 255)), + .str_replace('\n', ' ', substr((string) $bbcode, 0, 255)), OutputInterface::VERBOSITY_VERY_VERBOSE ); $markdown = $this->converter->convert($bbcode); @@ -168,6 +160,6 @@ class ConvertBBCodeCommand extends Command $io->success('Changes saved to DB successfully!'); } - return 0; + return Command::SUCCESS; } } diff --git a/src/Command/Migrations/ImportPartKeeprCommand.php b/src/Command/Migrations/ImportPartKeeprCommand.php new file mode 100644 index 00000000..98272440 --- /dev/null +++ b/src/Command/Migrations/ImportPartKeeprCommand.php @@ -0,0 +1,146 @@ +. + */ +namespace App\Command\Migrations; + +use Symfony\Component\Console\Attribute\AsCommand; +use App\Services\ImportExportSystem\PartKeeprImporter\PKDatastructureImporter; +use App\Services\ImportExportSystem\PartKeeprImporter\MySQLDumpXMLConverter; +use App\Services\ImportExportSystem\PartKeeprImporter\PKImportHelper; +use App\Services\ImportExportSystem\PartKeeprImporter\PKPartImporter; +use App\Services\ImportExportSystem\PartKeeprImporter\PKOptionalImporter; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +#[AsCommand('partdb:migrations:import-partkeepr', 'Import a PartKeepr database XML dump into Part-DB')] +class ImportPartKeeprCommand extends Command +{ + + public function __construct(protected EntityManagerInterface $em, protected MySQLDumpXMLConverter $xml_converter, + protected PKDatastructureImporter $datastructureImporter, protected PKPartImporter $partImporter, protected PKImportHelper $importHelper, + protected PKOptionalImporter $optionalImporter) + { + parent::__construct(self::$defaultName); + } + + protected function configure(): void + { + $this->setHelp('This command allows you to import a PartKeepr database exported by mysqldump as XML file into Part-DB'); + + $this->addArgument('file', InputArgument::REQUIRED, 'The file to which should be imported.'); + + $this->addOption('--no-projects', null, InputOption::VALUE_NONE, 'Do not import projects.'); + $this->addOption('--import-users', null, InputOption::VALUE_NONE, 'Import users (passwords will not be imported).'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $input_path = $input->getArgument('file'); + $no_projects_import = $input->getOption('no-projects'); + $import_users = $input->getOption('import-users'); + + $io->note('This command is still in development. If you encounter any problems, please report them to the issue tracker on GitHub.'); + $io->warning('This command will delete all existing data in the database (except users). Make sure that you have no important data in the database before you continue!'); + + $io->ask('Please type "DELETE ALL DATA" to continue.', '', function ($answer) { + if (strtoupper($answer) !== 'DELETE ALL DATA') { + throw new \RuntimeException('You did not type "DELETE ALL DATA"!'); + } + return $answer; + }); + + //Make more checks here + //$io->confirm('This will delete all data in the database. Do you want to continue?', false); + + //Purge the databse, so we will not have any conflicts + $this->importHelper->purgeDatabaseForImport(); + + //Convert the XML file to an array + $xml = file_get_contents($input_path); + $data = $this->xml_converter->convertMySQLDumpXMLDataToArrayStructure($xml); + + if (!$this->importHelper->checkVersion($data)) { + $db_version = $this->importHelper->getDatabaseSchemaVersion($data); + $io->error('The version of the imported database is not supported! (Version: '.$db_version.')'); + return Command::FAILURE; + } + + //Import the mandatory data + $this->doImport($io, $data); + + if (!$no_projects_import) { + $io->info('Importing projects...'); + $count = $this->optionalImporter->importProjects($data); + $io->success('Imported '.$count.' projects.'); + } + + if ($import_users) { + $io->info('Importing users...'); + $count = $this->optionalImporter->importUsers($data); + $io->success('Imported '.$count.' users.'); + } + + return Command::SUCCESS; + } + + private function doImport(SymfonyStyle $io, array $data): void + { + //First import the distributors + $io->info('Importing distributors...'); + $count = $this->datastructureImporter->importDistributors($data); + $io->success('Imported '.$count.' distributors.'); + + //Import the measurement units + $io->info('Importing part measurement units...'); + $count = $this->datastructureImporter->importPartUnits($data); + $io->success('Imported '.$count.' measurement units.'); + + //Import manufacturers + $io->info('Importing manufacturers...'); + $count = $this->datastructureImporter->importManufacturers($data); + $io->success('Imported '.$count.' manufacturers.'); + + $io->info('Importing categories...'); + $count = $this->datastructureImporter->importCategories($data); + $io->success('Imported '.$count.' categories.'); + + $io->info('Importing Footprints...'); + $count = $this->datastructureImporter->importFootprints($data); + $io->success('Imported '.$count.' footprints.'); + + $io->info('Importing storage locations...'); + $count = $this->datastructureImporter->importStorelocations($data); + $io->success('Imported '.$count.' storage locations.'); + + $io->info('Importing parts...'); + $count = $this->partImporter->importParts($data); + $io->success('Imported '.$count.' parts.'); + } + +} diff --git a/src/Command/User/ConvertToSAMLUserCommand.php b/src/Command/User/ConvertToSAMLUserCommand.php new file mode 100644 index 00000000..98892ecd --- /dev/null +++ b/src/Command/User/ConvertToSAMLUserCommand.php @@ -0,0 +1,110 @@ +. + */ +namespace App\Command\User; + +use Symfony\Component\Console\Attribute\AsCommand; +use App\Entity\UserSystem\User; +use App\Security\SamlUserFactory; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +#[AsCommand('partdb:user:convert-to-saml-user|partdb:users:convert-to-saml-user', 'Converts a local user to a SAML user (and vice versa)')] +class ConvertToSAMLUserCommand extends Command +{ + public function __construct(protected EntityManagerInterface $entityManager, protected bool $saml_enabled) + { + parent::__construct(); + } + + protected function configure(): void + { + $this->setHelp('This converts a local user, which can login via the login form, to a SAML user, which can only login via SAML. This is useful if you want to migrate from a local user system to a SAML user system.') + ->addArgument('user', InputArgument::REQUIRED, 'The username (or email) of the user') + ->addOption('to-local', null, InputOption::VALUE_NONE, 'Converts a SAML user to a local user') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $user_name = $input->getArgument('user'); + $to_local = $input->getOption('to-local'); + + if (!$this->saml_enabled && !$to_local) { + $io->confirm('SAML login is not configured. You will not be able to login with this user anymore, when SSO is not configured. Do you really want to continue?'); + } + + /** @var User $user */ + $user = $this->entityManager->getRepository(User::class)->findByEmailOrName($user_name); + + if (!$user) { + $io->error('User not found!'); + + return Command::FAILURE; + } + + $io->info('User found: '.$user->getFullName(true) . ': '.$user->getEmail().' [ID: ' . $user->getID() . ']'); + + if ($to_local) { + return $this->toLocal($user, $io); + } + + return $this->toSAML($user, $io); + } + + public function toLocal(User $user, SymfonyStyle $io): int + { + $io->confirm('You are going to convert a SAML user to a local user. This means, that the user can only login via the login form. ' + . 'The permissions and groups settings of the user will remain unchanged. Do you really want to continue?'); + + $user->setSamlUser(false); + $user->setPassword(SamlUserFactory::SAML_PASSWORD_PLACEHOLDER); + + $this->entityManager->flush(); + + $io->success('User converted to local user! You will need to set a password for this user, before you can login with it.'); + + return 0; + } + + public function toSAML(User $user, SymfonyStyle $io): int + { + $io->confirm('You are going to convert a local user to a SAML user. This means, that the user can only login via SAML afterwards. The password in the DB will be removed. ' + . 'The permissions and groups settings of the user will remain unchanged. Do you really want to continue?'); + + $user->setSamlUser(true); + $user->setPassword(SamlUserFactory::SAML_PASSWORD_PLACEHOLDER); + + $this->entityManager->flush(); + + $io->success('User converted to SAML user! You can now login with this user via SAML.'); + + return 0; + } + +} diff --git a/src/Command/User/SetPasswordCommand.php b/src/Command/User/SetPasswordCommand.php index aeab0d36..30ef867b 100644 --- a/src/Command/User/SetPasswordCommand.php +++ b/src/Command/User/SetPasswordCommand.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace App\Command\User; +use Symfony\Component\Console\Attribute\AsCommand; use App\Entity\UserSystem\User; use App\Events\SecurityEvent; use App\Events\SecurityEvents; @@ -34,29 +35,18 @@ use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; +#[AsCommand('partdb:users:set-password|app:set-password|users:set-password|partdb:user:set-password', 'Sets the password of a user')] class SetPasswordCommand extends Command { - protected static $defaultName = 'partdb:users:set-password|app:set-password|users:set-password|partdb:user:set-password'; - - protected EntityManagerInterface $entityManager; - protected UserPasswordHasherInterface $encoder; - protected EventDispatcherInterface $eventDispatcher; - - public function __construct(EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordEncoder, EventDispatcherInterface $eventDispatcher) + public function __construct(protected EntityManagerInterface $entityManager, protected UserPasswordHasherInterface $encoder, protected EventDispatcherInterface $eventDispatcher) { - $this->entityManager = $entityManager; - $this->encoder = $passwordEncoder; - $this->eventDispatcher = $eventDispatcher; - parent::__construct(); } protected function configure(): void { - $this - ->setDescription('Sets the password of a user') - ->setHelp('This password allows you to set the password of a user, without knowing the old password.') - ->addArgument('user', InputArgument::REQUIRED, 'The name of the user') + $this->setHelp('This password allows you to set the password of a user, without knowing the old password.') + ->addArgument('user', InputArgument::REQUIRED, 'The username or email of the user') ; } @@ -65,25 +55,27 @@ class SetPasswordCommand extends Command $io = new SymfonyStyle($input, $output); $user_name = $input->getArgument('user'); - /** @var User[] $users */ - $users = $this->entityManager->getRepository(User::class)->findBy(['name' => $user_name]); + $user = $this->entityManager->getRepository(User::class)->findByEmailOrName($user_name); - if (empty($users)) { + if (!$user instanceof User) { $io->error(sprintf('No user with the given username %s found in the database!', $user_name)); - return 1; + return Command::FAILURE; } - $user = $users[0]; - $io->note('User found!'); + if ($user->isSamlUser()) { + $io->error('This user is a SAML user, so you can not change the password!'); + return Command::FAILURE; + } + $proceed = $io->confirm( sprintf('You are going to change the password of %s with ID %d. Proceed?', $user->getFullName(true), $user->getID())); if (!$proceed) { - return 1; + return Command::FAILURE; } $success = false; @@ -91,6 +83,19 @@ class SetPasswordCommand extends Command while (!$success) { $pw1 = $io->askHidden('Please enter new password:'); + + if ($pw1 === null) { + $io->error('No password entered! Please try again.'); + + //If we are in non-interactive mode, we can not ask again + if (!$input->isInteractive()) { + $io->warning('Non-interactive mode detected. No password can be entered that way! If you are using docker exec, please use -it flag.'); + return Command::FAILURE; + } + + continue; + } + $pw2 = $io->askHidden('Please confirm:'); if ($pw1 !== $pw2) { $io->error('The entered password did not match! Please try again.'); @@ -114,6 +119,6 @@ class SetPasswordCommand extends Command $security_event = new SecurityEvent($user); $this->eventDispatcher->dispatch($security_event, SecurityEvents::PASSWORD_CHANGED); - return 0; + return Command::SUCCESS; } } diff --git a/src/Command/User/UpgradePermissionsSchemaCommand.php b/src/Command/User/UpgradePermissionsSchemaCommand.php index ae5adfff..4947fd5c 100644 --- a/src/Command/User/UpgradePermissionsSchemaCommand.php +++ b/src/Command/User/UpgradePermissionsSchemaCommand.php @@ -1,4 +1,7 @@ . */ - namespace App\Command\User; +use Symfony\Component\Console\Attribute\AsCommand; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\PermissionData; use App\Entity\UserSystem\User; @@ -27,28 +30,16 @@ use App\Services\LogSystem\EventCommentHelper; use App\Services\UserSystem\PermissionSchemaUpdater; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +#[AsCommand('partdb:users:upgrade-permissions-schema', '(Manually) upgrades the permissions schema of all users to the latest version.')] final class UpgradePermissionsSchemaCommand extends Command { - protected static $defaultName = 'partdb:users:upgrade-permissions-schema'; - protected static $defaultDescription = '(Manually) upgrades the permissions schema of all users to the latest version.'; - - private PermissionSchemaUpdater $permissionSchemaUpdater; - private EntityManagerInterface $em; - private EventCommentHelper $eventCommentHelper; - - public function __construct(PermissionSchemaUpdater $permissionSchemaUpdater, EntityManagerInterface $entityManager, EventCommentHelper $eventCommentHelper) + public function __construct(private readonly PermissionSchemaUpdater $permissionSchemaUpdater, private readonly EntityManagerInterface $em, private readonly EventCommentHelper $eventCommentHelper) { parent::__construct(self::$defaultName); - - $this->permissionSchemaUpdater = $permissionSchemaUpdater; - $this->eventCommentHelper = $eventCommentHelper; - $this->em = $entityManager; } protected function configure(): void @@ -83,26 +74,22 @@ final class UpgradePermissionsSchemaCommand extends Command } $io->info('Found '. count($groups_to_upgrade) .' groups and '. count($users_to_upgrade) .' users that need an update.'); - if (empty($groups_to_upgrade) && empty($users_to_upgrade)) { + if ($groups_to_upgrade === [] && $users_to_upgrade === []) { $io->success('All users and group permissions schemas are up-to-date. No update needed.'); - return 0; + return Command::SUCCESS; } //List all users and groups that need an update $io->section('Groups that need an update:'); - $io->listing(array_map(function (Group $group) { - return $group->getName() . ' (ID: '. $group->getID() .', Current version: ' . $group->getPermissions()->getSchemaVersion() . ')'; - }, $groups_to_upgrade)); + $io->listing(array_map(static fn(Group $group): string => $group->getName() . ' (ID: '. $group->getID() .', Current version: ' . $group->getPermissions()->getSchemaVersion() . ')', $groups_to_upgrade)); $io->section('Users that need an update:'); - $io->listing(array_map(function (User $user) { - return $user->getUsername() . ' (ID: '. $user->getID() .', Current version: ' . $user->getPermissions()->getSchemaVersion() . ')'; - }, $users_to_upgrade)); + $io->listing(array_map(static fn(User $user): string => $user->getUsername() . ' (ID: '. $user->getID() .', Current version: ' . $user->getPermissions()->getSchemaVersion() . ')', $users_to_upgrade)); if(!$io->confirm('Continue with the update?', false)) { $io->warning('Update aborted.'); - return 0; + return Command::SUCCESS; } //Update all users and groups diff --git a/src/Command/User/UserEnableCommand.php b/src/Command/User/UserEnableCommand.php index 39878a91..51ff2280 100644 --- a/src/Command/User/UserEnableCommand.php +++ b/src/Command/User/UserEnableCommand.php @@ -1,4 +1,7 @@ . */ - namespace App\Command\User; +use Symfony\Component\Console\Attribute\AsCommand; use App\Entity\UserSystem\User; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Command\Command; @@ -29,24 +32,17 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +#[AsCommand('partdb:users:enable|partdb:user:enable', 'Enables/Disable the login of one or more users')] class UserEnableCommand extends Command { - protected static $defaultName = 'partdb:users:enable|partdb:user:enable'; - - protected EntityManagerInterface $entityManager; - - public function __construct(EntityManagerInterface $entityManager, string $name = null) + public function __construct(protected EntityManagerInterface $entityManager, ?string $name = null) { - $this->entityManager = $entityManager; - parent::__construct($name); } protected function configure(): void { - $this - ->setDescription('Enables/Disable the login of one or more users') - ->setHelp('This allows you to allow or prevent the login of certain user. Use the --disable option to disable the login for the given users') + $this->setHelp('This allows you to allow or prevent the login of certain user. Use the --disable option to disable the login for the given users') ->addArgument('users', InputArgument::IS_ARRAY, 'The usernames of the users to use') ->addOption('all', 'a', InputOption::VALUE_NONE, 'Enable/Disable all users') ->addOption('disable', 'd', InputOption::VALUE_NONE, 'Disable the login of the given users') @@ -73,7 +69,7 @@ class UserEnableCommand extends Command } else { //Otherwise, fetch the users from DB foreach ($usernames as $username) { $user = $repo->findByEmailOrName($username); - if ($user === null) { + if (!$user instanceof User) { $io->error('No user found with username: '.$username); return self::FAILURE; } @@ -87,9 +83,7 @@ class UserEnableCommand extends Command $io->note('The following users will be enabled:'); } $io->table(['Username', 'Enabled/Disabled'], - array_map(function(User $user) { - return [$user->getFullName(true), $user->isDisabled() ? 'Disabled' : 'Enabled']; - }, $users)); + array_map(static fn(User $user) => [$user->getFullName(true), $user->isDisabled() ? 'Disabled' : 'Enabled'], $users)); if(!$io->confirm('Do you want to continue?')) { $io->warning('Aborting!'); @@ -107,4 +101,4 @@ class UserEnableCommand extends Command return self::SUCCESS; } -} \ No newline at end of file +} diff --git a/src/Command/User/UserListCommand.php b/src/Command/User/UserListCommand.php index 0f0e52b7..11d50fbf 100644 --- a/src/Command/User/UserListCommand.php +++ b/src/Command/User/UserListCommand.php @@ -1,4 +1,7 @@ . */ - namespace App\Command\User; +use Symfony\Component\Console\Attribute\AsCommand; +use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Command\Command; @@ -28,49 +32,60 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +#[AsCommand('partdb:users:list|users:list', 'Lists all users')] class UserListCommand extends Command { - protected static $defaultName = 'partdb:users:list|users:list'; - - protected EntityManagerInterface $entityManager; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(protected EntityManagerInterface $entityManager) { - $this->entityManager = $entityManager; - parent::__construct(); } protected function configure(): void { - $this - ->setDescription('Lists all users') - ->setHelp('This command lists all users in the database.') + $this->setHelp('This command lists all users in the database.') + ->addOption('local', 'l', null, 'Only list local users') + ->addOption('saml', 's', null, 'Only list SAML users') ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); + $only_local = $input->getOption('local'); + $only_saml = $input->getOption('saml'); - //Get all users from database - $users = $this->entityManager->getRepository(User::class)->findAll(); + if ($only_local && $only_saml) { + $io->error('You can not use --local and --saml at the same time!'); + + return Command::FAILURE; + } + + $repo = $this->entityManager->getRepository(User::class); + + if ($only_local) { + $users = $repo->onlyLocalUsers(); + } elseif ($only_saml) { + $users = $repo->onlySAMLUsers(); + } else { + $users = $repo->findAll(); + } $io->info(sprintf("Found %d users in database.", count($users))); $io->title('Users:'); $table = new Table($output); - $table->setHeaders(['ID', 'Username', 'Name', 'Email', 'Group', 'Login Disabled']); + $table->setHeaders(['ID', 'Username', 'Name', 'Email', 'Group', 'Login Disabled', 'Type']); foreach ($users as $user) { $table->addRow([ - $user->getId(), + $user->getID(), $user->getUsername(), $user->getFullName(), $user->getEmail(), - $user->getGroup() !== null ? $user->getGroup()->getName() . ' (ID: ' . $user->getGroup()->getID() . ')' : 'No group', + $user->getGroup() instanceof Group ? $user->getGroup()->getName() . ' (ID: ' . $user->getGroup()->getID() . ')' : 'No group', $user->isDisabled() ? 'Yes' : 'No', + $user->isSamlUser() ? 'SAML' : 'Local', ]); } @@ -80,4 +95,4 @@ class UserListCommand extends Command return self::SUCCESS; } -} \ No newline at end of file +} diff --git a/src/Command/User/UsersPermissionsCommand.php b/src/Command/User/UsersPermissionsCommand.php index 3cccd8fd..6408e9c9 100644 --- a/src/Command/User/UsersPermissionsCommand.php +++ b/src/Command/User/UsersPermissionsCommand.php @@ -1,4 +1,7 @@ . */ - namespace App\Command\User; +use Symfony\Component\Console\Attribute\AsCommand; use App\Entity\UserSystem\User; use App\Repository\UserRepository; use App\Services\UserSystem\PermissionManager; @@ -34,22 +37,14 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Contracts\Translation\TranslatorInterface; +#[AsCommand('partdb:users:permissions|partdb:user:permissions', 'View and edit the permissions of a given user')] class UsersPermissionsCommand extends Command { - protected static $defaultName = 'partdb:users:permissions|partdb:user:permissions'; - protected static $defaultDescription = 'View and edit the permissions of a given user'; - - protected EntityManagerInterface $entityManager; protected UserRepository $userRepository; - protected PermissionManager $permissionResolver; - protected TranslatorInterface $translator; - public function __construct(EntityManagerInterface $entityManager, PermissionManager $permissionResolver, TranslatorInterface $translator) + public function __construct(protected EntityManagerInterface $entityManager, protected PermissionManager $permissionResolver, protected TranslatorInterface $translator) { - $this->entityManager = $entityManager; $this->userRepository = $entityManager->getRepository(User::class); - $this->permissionResolver = $permissionResolver; - $this->translator = $translator; parent::__construct(self::$defaultName); } @@ -57,7 +52,7 @@ class UsersPermissionsCommand extends Command protected function configure(): void { $this - ->addArgument('user', InputArgument::REQUIRED, 'The username of the user to view') + ->addArgument('user', InputArgument::REQUIRED, 'The username or email of the user to view') ->addOption('noInherit', null, InputOption::VALUE_NONE, 'Do not inherit permissions from groups') ->addOption('edit', '', InputOption::VALUE_NONE, 'Edit the permissions of the user') ; @@ -73,12 +68,12 @@ class UsersPermissionsCommand extends Command //Find user $io->note('Finding user with username: ' . $username); $user = $this->userRepository->findByEmailOrName($username); - if ($user === null) { + if (!$user instanceof User) { $io->error('No user found with username: ' . $username); return Command::FAILURE; } - $io->note(sprintf('Found user %s with ID %d', $user->getFullName(true), $user->getId())); + $io->note(sprintf('Found user %s with ID %d', $user->getFullName(true), $user->getID())); $edit_mapping = $this->renderPermissionTable($output, $user, $inherit); @@ -102,7 +97,7 @@ class UsersPermissionsCommand extends Command $new_value_str = $io->ask('Enter the new value for the permission (A = allow, D = disallow, I = inherit)'); - switch (strtolower($new_value_str)) { + switch (strtolower((string) $new_value_str)) { case 'a': case 'allow': $new_value = true; @@ -209,14 +204,17 @@ class UsersPermissionsCommand extends Command if ($permission_value === true) { return 'Allow'; - } else if ($permission_value === false) { + } elseif ($permission_value === false) { return 'Disallow'; - } else if ($permission_value === null && !$inherit) { + } + // Permission value is null by this point + elseif (!$inherit) { return 'Inherit'; - } else if ($permission_value === null && $inherit) { + } elseif ($inherit) { return 'Disallow (Inherited)'; } + //@phpstan-ignore-next-line This line is never reached, but PHPstorm complains otherwise return '???'; } } diff --git a/src/Command/VersionCommand.php b/src/Command/VersionCommand.php index a5437684..d2ce75e1 100644 --- a/src/Command/VersionCommand.php +++ b/src/Command/VersionCommand.php @@ -1,4 +1,7 @@ . */ - namespace App\Command; +use Symfony\Component\Console\Attribute\AsCommand; use App\Services\Misc\GitVersionInfo; use Shivas\VersioningBundle\Service\VersionManagerInterface; use Symfony\Component\Console\Command\Command; @@ -27,25 +30,16 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +#[AsCommand('partdb:version|app:version', 'Shows the currently installed version of Part-DB.')] class VersionCommand extends Command { - protected static $defaultName = 'partdb:version|app:version'; - - protected VersionManagerInterface $versionManager; - protected GitVersionInfo $gitVersionInfo; - - public function __construct(VersionManagerInterface $versionManager, GitVersionInfo $gitVersionInfo) + public function __construct(protected VersionManagerInterface $versionManager, protected GitVersionInfo $gitVersionInfo) { - $this->versionManager = $versionManager; - $this->gitVersionInfo = $gitVersionInfo; parent::__construct(); } protected function configure(): void { - $this - ->setDescription('Shows the currently installed version of Part-DB.') - ; } protected function execute(InputInterface $input, OutputInterface $output): int @@ -66,6 +60,6 @@ class VersionCommand extends Command $io->info('OS: '. php_uname()); $io->info('PHP extension: '. implode(', ', get_loaded_extensions())); - return 0; + return Command::SUCCESS; } -} \ No newline at end of file +} diff --git a/src/Configuration/PermissionsConfiguration.php b/src/Configuration/PermissionsConfiguration.php index 307e3794..c069ab24 100644 --- a/src/Configuration/PermissionsConfiguration.php +++ b/src/Configuration/PermissionsConfiguration.php @@ -55,6 +55,7 @@ final class PermissionsConfiguration implements ConfigurationInterface ->scalarNode('name')->end() ->scalarNode('label')->end() ->scalarNode('bit')->end() + ->scalarNode('apiTokenRole')->defaultNull()->end() ->arrayNode('alsoSet') ->beforeNormalization()->castToArray()->end()->scalarPrototype()->end(); diff --git a/src/Controller/AdminPages/AttachmentTypeController.php b/src/Controller/AdminPages/AttachmentTypeController.php index 6ff5d930..08c10da1 100644 --- a/src/Controller/AdminPages/AttachmentTypeController.php +++ b/src/Controller/AdminPages/AttachmentTypeController.php @@ -34,11 +34,12 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/attachment_type") + * @see \App\Tests\Controller\AdminPages\AttachmentTypeControllerTest */ +#[Route(path: '/attachment_type')] class AttachmentTypeController extends BaseAdminController { protected string $entity_class = AttachmentType::class; @@ -48,44 +49,34 @@ class AttachmentTypeController extends BaseAdminController protected string $attachment_class = AttachmentTypeAttachment::class; protected ?string $parameter_class = AttachmentTypeParameter::class; - /** - * @Route("/{id}", name="attachment_type_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'attachment_type_delete', methods: ['DELETE'])] public function delete(Request $request, AttachmentType $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="attachment_type_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'attachment_type_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function edit(AttachmentType $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="attachment_type_new") - * @Route("/{id}/clone", name="attachment_type_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'attachment_type_new')] + #[Route(path: '/{id}/clone', name: 'attachment_type_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?AttachmentType $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="attachment_type_export_all") - */ + #[Route(path: '/export', name: 'attachment_type_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="attachment_type_export") - */ + #[Route(path: '/{id}/export', name: 'attachment_type_export')] public function exportEntity(AttachmentType $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AdminPages/BaseAdminController.php b/src/Controller/AdminPages/BaseAdminController.php index cf365d5c..edc5917a 100644 --- a/src/Controller/AdminPages/BaseAdminController.php +++ b/src/Controller/AdminPages/BaseAdminController.php @@ -24,22 +24,25 @@ namespace App\Controller\AdminPages; use App\DataTables\LogDataTable; use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\AttachmentContainingDBElement; +use App\Entity\Attachments\AttachmentUpload; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractPartsContainingDBElement; use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Base\PartsContainingRepositoryInterface; +use App\Entity\LabelSystem\LabelProcessMode; use App\Entity\LabelSystem\LabelProfile; use App\Entity\Parameters\AbstractParameter; -use App\Entity\UserSystem\User; use App\Exceptions\AttachmentDownloadException; +use App\Exceptions\TwigModeException; use App\Form\AdminPages\ImportType; use App\Form\AdminPages\MassCreationForm; use App\Repository\AbstractPartsContainingRepository; use App\Services\Attachments\AttachmentSubmitHandler; use App\Services\ImportExportSystem\EntityExporter; use App\Services\ImportExportSystem\EntityImporter; -use App\Services\LabelSystem\Barcodes\BarcodeExampleElementsGenerator; +use App\Services\LabelSystem\LabelExampleElementsGenerator; use App\Services\LabelSystem\LabelGenerator; use App\Services\LogSystem\EventCommentHelper; use App\Services\LogSystem\HistoryHelper; @@ -50,15 +53,17 @@ use Doctrine\ORM\EntityManagerInterface; use InvalidArgumentException; use Omines\DataTablesBundle\DataTableFactory; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; -use Symfony\Component\Validator\ConstraintViolationList; +use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Validator\ConstraintViolationInterface; +use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Contracts\Translation\TranslatorInterface; use function Symfony\Component\Translation\t; @@ -72,28 +77,11 @@ abstract class BaseAdminController extends AbstractController protected string $attachment_class = ''; protected ?string $parameter_class = ''; - protected UserPasswordHasherInterface $passwordEncoder; - protected TranslatorInterface $translator; - protected AttachmentSubmitHandler $attachmentSubmitHandler; - protected EventCommentHelper $commentHelper; - - protected HistoryHelper $historyHelper; - protected TimeTravel $timeTravel; - protected DataTableFactory $dataTableFactory; - /** - * @var EventDispatcher|EventDispatcherInterface - */ - protected $eventDispatcher; - protected LabelGenerator $labelGenerator; - protected BarcodeExampleElementsGenerator $barcodeExampleGenerator; - - protected EntityManagerInterface $entityManager; - - public function __construct(TranslatorInterface $translator, UserPasswordHasherInterface $passwordEncoder, - AttachmentSubmitHandler $attachmentSubmitHandler, - EventCommentHelper $commentHelper, HistoryHelper $historyHelper, TimeTravel $timeTravel, - DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, BarcodeExampleElementsGenerator $barcodeExampleGenerator, - LabelGenerator $labelGenerator, EntityManagerInterface $entityManager) + public function __construct(protected TranslatorInterface $translator, protected UserPasswordHasherInterface $passwordEncoder, + protected AttachmentSubmitHandler $attachmentSubmitHandler, + protected EventCommentHelper $commentHelper, protected HistoryHelper $historyHelper, protected TimeTravel $timeTravel, + protected DataTableFactory $dataTableFactory, protected EventDispatcherInterface $eventDispatcher, protected LabelExampleElementsGenerator $barcodeExampleGenerator, + protected LabelGenerator $labelGenerator, protected EntityManagerInterface $entityManager) { if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) { throw new InvalidArgumentException('You have to override the $entity_class, $form_class, $route_base and $twig_template value in your subclasss!'); @@ -106,18 +94,6 @@ abstract class BaseAdminController extends AbstractController if ('' === $this->parameter_class || ($this->parameter_class && !is_a($this->parameter_class, AbstractParameter::class, true))) { throw new InvalidArgumentException('You have to override the $parameter_class value with a valid Parameter class in your subclass!'); } - - $this->translator = $translator; - $this->passwordEncoder = $passwordEncoder; - $this->attachmentSubmitHandler = $attachmentSubmitHandler; - $this->commentHelper = $commentHelper; - $this->historyHelper = $historyHelper; - $this->timeTravel = $timeTravel; - $this->dataTableFactory = $dataTableFactory; - $this->eventDispatcher = $eventDispatcher; - $this->barcodeExampleGenerator = $barcodeExampleGenerator; - $this->labelGenerator = $labelGenerator; - $this->entityManager = $entityManager; } protected function revertElementIfNeeded(AbstractDBElement $entity, ?string $timestamp): ?DateTime @@ -176,13 +152,13 @@ abstract class BaseAdminController extends AbstractController $form_options = [ 'attachment_class' => $this->attachment_class, 'parameter_class' => $this->parameter_class, - 'disabled' => null !== $timeTravel_timestamp, + 'disabled' => $timeTravel_timestamp instanceof \DateTime, ]; //Disable editing of options, if user is not allowed to use twig... if ( $entity instanceof LabelProfile - && 'twig' === $entity->getOptions()->getLinesMode() + && LabelProcessMode::TWIG === $entity->getOptions()->getProcessMode() && !$this->isGranted('@labels.use_twig') ) { $form_options['disable_options'] = true; @@ -197,16 +173,10 @@ abstract class BaseAdminController extends AbstractController $attachments = $form['attachments']; foreach ($attachments as $attachment) { /** @var FormInterface $attachment */ - $options = [ - 'secure_attachment' => $attachment['secureFile']->getData(), - 'download_url' => $attachment['downloadURL']->getData(), - ]; - try { - $this->attachmentSubmitHandler->handleFormSubmit( + $this->attachmentSubmitHandler->handleUpload( $attachment->getData(), - $attachment['file']->getData(), - $options + AttachmentUpload::fromAttachmentForm($attachment) ); } catch (AttachmentDownloadException $attachmentDownloadException) { $this->addFlash( @@ -218,6 +188,11 @@ abstract class BaseAdminController extends AbstractController } } + //Ensure that the master picture is still part of the attachments + if ($entity instanceof AttachmentContainingDBElement && ($entity->getMasterPictureAttachment() !== null && !$entity->getAttachments()->contains($entity->getMasterPictureAttachment()))) { + $entity->setMasterPictureAttachment(null); + } + $this->commentHelper->setMessage($form['log_comment']->getData()); $em->persist($entity); @@ -238,13 +213,17 @@ abstract class BaseAdminController extends AbstractController //Show preview for LabelProfile if needed. if ($entity instanceof LabelProfile) { $example = $this->barcodeExampleGenerator->getElement($entity->getOptions()->getSupportedElement()); - $pdf_data = $this->labelGenerator->generateLabel($entity->getOptions(), $example); + $pdf_data = null; + try { + $pdf_data = $this->labelGenerator->generateLabel($entity->getOptions(), $example); + } catch (TwigModeException $exception) { + $form->get('options')->get('lines')->addError(new FormError($exception->getSafeMessage())); + } } - /** @var AbstractPartsContainingRepository $repo */ $repo = $this->entityManager->getRepository($this->entity_class); - return $this->renderForm($this->twig_template, [ + return $this->render($this->twig_template, [ 'entity' => $entity, 'form' => $form, 'route_base' => $this->route_base, @@ -266,16 +245,9 @@ abstract class BaseAdminController extends AbstractController return true; } - protected function _new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?AbstractNamedDBElement $entity = null) + protected function _new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?AbstractNamedDBElement $entity = null): Response { - $master_picture_backup = null; - if (null === $entity) { - /** @var AbstractStructuralDBElement|User $new_entity */ - $new_entity = new $this->entity_class(); - } else { - /** @var AbstractStructuralDBElement|User $new_entity */ - $new_entity = clone $entity; - } + $new_entity = $entity instanceof AbstractNamedDBElement ? clone $entity : new $this->entity_class(); $this->denyAccessUnlessGranted('read', $new_entity); @@ -287,42 +259,38 @@ abstract class BaseAdminController extends AbstractController $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - //Perform additional actions - if ($this->additionalActionNew($form, $new_entity)) { - //Upload passed files - $attachments = $form['attachments']; - foreach ($attachments as $attachment) { - /** @var FormInterface $attachment */ - $options = [ - 'secure_attachment' => $attachment['secureFile']->getData(), - 'download_url' => $attachment['downloadURL']->getData(), - ]; + //Perform additional actions + if ($form->isSubmitted() && $form->isValid() && $this->additionalActionNew($form, $new_entity)) { + //Upload passed files + $attachments = $form['attachments']; + foreach ($attachments as $attachment) { + /** @var FormInterface $attachment */ - try { - $this->attachmentSubmitHandler->handleFormSubmit( - $attachment->getData(), - $attachment['file']->getData(), - $options - ); - } catch (AttachmentDownloadException $attachmentDownloadException) { - $this->addFlash( - 'error', - $this->translator->trans( - 'attachment.download_failed' - ).' '.$attachmentDownloadException->getMessage() - ); - } + try { + $this->attachmentSubmitHandler->handleUpload( + $attachment->getData(), + AttachmentUpload::fromAttachmentForm($attachment) + ); + } catch (AttachmentDownloadException $attachmentDownloadException) { + $this->addFlash( + 'error', + $this->translator->trans( + 'attachment.download_failed' + ).' '.$attachmentDownloadException->getMessage() + ); } - - $this->commentHelper->setMessage($form['log_comment']->getData()); - - $em->persist($new_entity); - $em->flush(); - $this->addFlash('success', 'entity.created_flash'); - - return $this->redirectToRoute($this->route_base.'_edit', ['id' => $new_entity->getID()]); } + + //Ensure that the master picture is still part of the attachments + if ($new_entity instanceof AttachmentContainingDBElement && ($new_entity->getMasterPictureAttachment() !== null && !$new_entity->getAttachments()->contains($new_entity->getMasterPictureAttachment()))) { + $new_entity->setMasterPictureAttachment(null); + } + + $this->commentHelper->setMessage($form['log_comment']->getData()); + $em->persist($new_entity); + $em->flush(); + $this->addFlash('success', 'entity.created_flash'); + return $this->redirectToRoute($this->route_base.'_edit', ['id' => $new_entity->getID()]); } if ($form->isSubmitted() && !$form->isValid()) { @@ -338,23 +306,42 @@ abstract class BaseAdminController extends AbstractController $file = $import_form['file']->getData(); $data = $import_form->getData(); + if ($data['format'] === 'auto') { + $format = $importer->determineFormat($file->getClientOriginalExtension()); + if (null === $format) { + $this->addFlash('error', 'parts.import.flash.error.unknown_format'); + goto ret; + } + } else { + $format = $data['format']; + } + $options = [ - 'parent' => $data['parent'], - 'preserve_children' => $data['preserve_children'], - 'format' => $data['format'], - 'csv_separator' => $data['csv_separator'], + 'parent' => $data['parent'] ?? null, + 'preserve_children' => $data['preserve_children'] ?? false, + 'format' => $format, + 'class' => $this->entity_class, + 'csv_delimiter' => $data['csv_delimiter'], + 'abort_on_validation_error' => $data['abort_on_validation_error'], ]; $this->commentHelper->setMessage('Import '.$file->getClientOriginalName()); - $errors = $importer->fileToDBEntities($file, $this->entity_class, $options); + try { + $errors = $importer->importFileAndPersistToDB($file, $options); - foreach ($errors as $name => $error) { - /** @var ConstraintViolationList $error */ - $this->addFlash('error', $name.':'.$error); + foreach ($errors as $name => ['violations' => $violations]) { + foreach ($violations as $violation) { + $this->addFlash('error', $name.': '.$violation->getMessage()); + } + } + } + catch (UnexpectedValueException) { + $this->addFlash('error', 'parts.import.flash.error.invalid_file'); } } + ret: //Mass creation form $mass_creation_form = $this->createForm(MassCreationForm::class, ['entity_class' => $this->entity_class]); $mass_creation_form->handleRequest($request); @@ -367,11 +354,14 @@ abstract class BaseAdminController extends AbstractController $results = $importer->massCreation($data['lines'], $this->entity_class, $data['parent'] ?? null, $errors); //Show errors to user: - foreach ($errors as $error) { - if ($error['entity'] instanceof AbstractStructuralDBElement) { - $this->addFlash('error', $error['entity']->getFullPath().':'.$error['violations']); - } else { //When we dont have a structural element, we can only show the name - $this->addFlash('error', $error['entity']->getName().':'.$error['violations']); + foreach ($errors as ['entity' => $new_entity, 'violations' => $violations]) { + /** @var ConstraintViolationInterface $violation */ + foreach ($violations as $violation) { + if ($new_entity instanceof AbstractStructuralDBElement) { + $this->addFlash('error', $new_entity->getFullPath().':'.$violation->getMessage()); + } else { //When we don't have a structural element, we can only show the name + $this->addFlash('error', $new_entity->getName().':'.$violation->getMessage()); + } } } @@ -380,9 +370,13 @@ abstract class BaseAdminController extends AbstractController $em->persist($result); } $em->flush(); + + if (count($results) > 0) { + $this->addFlash('success', t('entity.mass_creation_flash', ['%COUNT%' => count($results)])); + } } - return $this->renderForm($this->twig_template, [ + return $this->render($this->twig_template, [ 'entity' => $new_entity, 'form' => $form, 'import_form' => $import_form, @@ -392,17 +386,17 @@ abstract class BaseAdminController extends AbstractController } /** - * Performs checks if the element can be deleted safely. Otherwise an flash message is added. + * Performs checks if the element can be deleted safely. Otherwise, a flash message is added. * * @param AbstractNamedDBElement $entity the element that should be checked * - * @return bool True if the the element can be deleted, false if not + * @return bool True if the element can be deleted, false if not */ protected function deleteCheck(AbstractNamedDBElement $entity): bool { if ($entity instanceof AbstractPartsContainingDBElement) { /** @var AbstractPartsContainingRepository $repo */ - $repo = $this->entityManager->getRepository($this->entity_class); + $repo = $this->entityManager->getRepository($this->entity_class); //@phpstan-ignore-line if ($repo->getPartsCount($entity) > 0) { $this->addFlash('error', t('entity.delete.must_not_contain_parts', ['%PATH%' => $entity->getFullPath()])); @@ -417,7 +411,7 @@ abstract class BaseAdminController extends AbstractController { $this->denyAccessUnlessGranted('delete', $entity); - if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) { + if ($this->isCsrfTokenValid('delete'.$entity->getID(), $request->request->get('_token'))) { $entityManager = $this->entityManager; @@ -473,6 +467,11 @@ abstract class BaseAdminController extends AbstractController $this->denyAccessUnlessGranted('read', $entity); $entities = $em->getRepository($this->entity_class)->findAll(); + if (count($entities) === 0) { + $this->addFlash('error', 'entity.export.flash.error.no_entities'); + return $this->redirectToRoute($this->route_base.'_new'); + } + return $exporter->exportEntityFromRequest($entities, $request); } diff --git a/src/Controller/AdminPages/CategoryController.php b/src/Controller/AdminPages/CategoryController.php index 4f247a1d..361df625 100644 --- a/src/Controller/AdminPages/CategoryController.php +++ b/src/Controller/AdminPages/CategoryController.php @@ -33,11 +33,12 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/category") + * @see \App\Tests\Controller\AdminPages\CategoryControllerTest */ +#[Route(path: '/category')] class CategoryController extends BaseAdminController { protected string $entity_class = Category::class; @@ -47,44 +48,34 @@ class CategoryController extends BaseAdminController protected string $attachment_class = CategoryAttachment::class; protected ?string $parameter_class = CategoryParameter::class; - /** - * @Route("/{id}", name="category_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'category_delete', methods: ['DELETE'])] public function delete(Request $request, Category $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="category_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'category_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function edit(Category $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="category_new") - * @Route("/{id}/clone", name="category_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'category_new')] + #[Route(path: '/{id}/clone', name: 'category_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Category $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="category_export_all") - */ + #[Route(path: '/export', name: 'category_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="category_export") - */ + #[Route(path: '/{id}/export', name: 'category_export')] public function exportEntity(Category $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AdminPages/CurrencyController.php b/src/Controller/AdminPages/CurrencyController.php index 133c3a50..4450e157 100644 --- a/src/Controller/AdminPages/CurrencyController.php +++ b/src/Controller/AdminPages/CurrencyController.php @@ -31,14 +31,13 @@ use App\Services\Attachments\AttachmentSubmitHandler; use App\Services\ImportExportSystem\EntityExporter; use App\Services\ImportExportSystem\EntityImporter; use App\Services\Tools\ExchangeRateUpdater; -use App\Services\LabelSystem\Barcodes\BarcodeExampleElementsGenerator; +use App\Services\LabelSystem\LabelExampleElementsGenerator; use App\Services\LabelSystem\LabelGenerator; use App\Services\LogSystem\EventCommentHelper; use App\Services\LogSystem\HistoryHelper; use App\Services\LogSystem\TimeTravel; use App\Services\Trees\StructuralElementRecursionHelper; use Doctrine\ORM\EntityManagerInterface; -use Exchanger\Exception\ChainException; use Exchanger\Exception\Exception; use Exchanger\Exception\UnsupportedCurrencyPairException; use Omines\DataTablesBundle\DataTableFactory; @@ -48,14 +47,13 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Contracts\Translation\TranslatorInterface; /** - * @Route("/currency") - * * Class CurrencyController */ +#[Route(path: '/currency')] class CurrencyController extends BaseAdminController { protected string $entity_class = Currency::class; @@ -65,8 +63,6 @@ class CurrencyController extends BaseAdminController protected string $attachment_class = CurrencyAttachment::class; protected ?string $parameter_class = CurrencyParameter::class; - protected ExchangeRateUpdater $exchangeRateUpdater; - public function __construct( TranslatorInterface $translator, UserPasswordHasherInterface $passwordEncoder, @@ -76,13 +72,11 @@ class CurrencyController extends BaseAdminController TimeTravel $timeTravel, DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, - BarcodeExampleElementsGenerator $barcodeExampleGenerator, + LabelExampleElementsGenerator $barcodeExampleGenerator, LabelGenerator $labelGenerator, EntityManagerInterface $entityManager, - ExchangeRateUpdater $exchangeRateUpdater + protected ExchangeRateUpdater $exchangeRateUpdater ) { - $this->exchangeRateUpdater = $exchangeRateUpdater; - parent::__construct( $translator, $passwordEncoder, @@ -98,9 +92,7 @@ class CurrencyController extends BaseAdminController ); } - /** - * @Route("/{id}", name="currency_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'currency_delete', methods: ['DELETE'])] public function delete(Request $request, Currency $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); @@ -131,36 +123,28 @@ class CurrencyController extends BaseAdminController return true; } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="currency_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'currency_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function edit(Currency $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="currency_new") - * @Route("/{id}/clone", name="currency_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'currency_new')] + #[Route(path: '/{id}/clone', name: 'currency_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Currency $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="currency_export_all") - */ + #[Route(path: '/export', name: 'currency_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="currency_export") - */ + #[Route(path: '/{id}/export', name: 'currency_export')] public function exportEntity(Currency $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AdminPages/FootprintController.php b/src/Controller/AdminPages/FootprintController.php index ba37256c..3932e939 100644 --- a/src/Controller/AdminPages/FootprintController.php +++ b/src/Controller/AdminPages/FootprintController.php @@ -34,11 +34,12 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/footprint") + * @see \App\Tests\Controller\AdminPages\FootprintControllerTest */ +#[Route(path: '/footprint')] class FootprintController extends BaseAdminController { protected string $entity_class = Footprint::class; @@ -48,44 +49,34 @@ class FootprintController extends BaseAdminController protected string $attachment_class = FootprintAttachment::class; protected ?string $parameter_class = FootprintParameter::class; - /** - * @Route("/{id}", name="footprint_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'footprint_delete', methods: ['DELETE'])] public function delete(Request $request, Footprint $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="footprint_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'footprint_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function edit(Footprint $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="footprint_new") - * @Route("/{id}/clone", name="footprint_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'footprint_new')] + #[Route(path: '/{id}/clone', name: 'footprint_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Footprint $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="footprint_export_all") - */ + #[Route(path: '/export', name: 'footprint_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="footprint_export") - */ + #[Route(path: '/{id}/export', name: 'footprint_export')] public function exportEntity(AttachmentType $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AdminPages/LabelProfileController.php b/src/Controller/AdminPages/LabelProfileController.php index 96b70009..7e7dfa1c 100644 --- a/src/Controller/AdminPages/LabelProfileController.php +++ b/src/Controller/AdminPages/LabelProfileController.php @@ -22,10 +22,8 @@ declare(strict_types=1); namespace App\Controller\AdminPages; -use App\Entity\Attachments\AttachmentType; use App\Entity\Attachments\LabelAttachment; use App\Entity\LabelSystem\LabelProfile; -use App\Entity\Parameters\AbstractParameter; use App\Form\AdminPages\LabelProfileAdminForm; use App\Services\ImportExportSystem\EntityExporter; use App\Services\ImportExportSystem\EntityImporter; @@ -34,10 +32,11 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/label_profile") + * @see \App\Tests\Controller\AdminPages\LabelProfileControllerTest */ +#[Route(path: '/label_profile')] class LabelProfileController extends BaseAdminController { protected string $entity_class = LabelProfile::class; @@ -48,44 +47,34 @@ class LabelProfileController extends BaseAdminController //Just a placeholder protected ?string $parameter_class = null; - /** - * @Route("/{id}", name="label_profile_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'label_profile_delete', methods: ['DELETE'])] public function delete(Request $request, LabelProfile $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="label_profile_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'label_profile_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function edit(LabelProfile $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="label_profile_new") - * @Route("/{id}/clone", name="label_profile_clone") - * @Route("/") - */ - public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?AttachmentType $entity = null): Response + #[Route(path: '/new', name: 'label_profile_new')] + #[Route(path: '/{id}/clone', name: 'label_profile_clone')] + #[Route(path: '/')] + public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?LabelProfile $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="label_profile_export_all") - */ + #[Route(path: '/export', name: 'label_profile_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="label_profile_export") - */ + #[Route(path: '/{id}/export', name: 'label_profile_export')] public function exportEntity(LabelProfile $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AdminPages/ManufacturerController.php b/src/Controller/AdminPages/ManufacturerController.php index 2ded7d10..5fc4d4ac 100644 --- a/src/Controller/AdminPages/ManufacturerController.php +++ b/src/Controller/AdminPages/ManufacturerController.php @@ -33,11 +33,12 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/manufacturer") + * @see \App\Tests\Controller\AdminPages\ManufacturerControllerTest */ +#[Route(path: '/manufacturer')] class ManufacturerController extends BaseAdminController { protected string $entity_class = Manufacturer::class; @@ -47,46 +48,34 @@ class ManufacturerController extends BaseAdminController protected string $attachment_class = ManufacturerAttachment::class; protected ?string $parameter_class = ManufacturerParameter::class; - /** - * @Route("/{id}", name="manufacturer_delete", methods={"DELETE"}) - * - * @return RedirectResponse - */ + #[Route(path: '/{id}', name: 'manufacturer_delete', methods: ['DELETE'])] public function delete(Request $request, Manufacturer $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="manufacturer_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'manufacturer_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function edit(Manufacturer $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="manufacturer_new") - * @Route("/{id}/clone", name="manufacturer_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'manufacturer_new')] + #[Route(path: '/{id}/clone', name: 'manufacturer_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Manufacturer $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="manufacturer_export_all") - */ + #[Route(path: '/export', name: 'manufacturer_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="manufacturer_export") - */ + #[Route(path: '/{id}/export', name: 'manufacturer_export')] public function exportEntity(Manufacturer $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AdminPages/MeasurementUnitController.php b/src/Controller/AdminPages/MeasurementUnitController.php index 402c2018..59c38ba4 100644 --- a/src/Controller/AdminPages/MeasurementUnitController.php +++ b/src/Controller/AdminPages/MeasurementUnitController.php @@ -34,11 +34,12 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/measurement_unit") + * @see \App\Tests\Controller\AdminPages\MeasurementUnitControllerTest */ +#[Route(path: '/measurement_unit')] class MeasurementUnitController extends BaseAdminController { protected string $entity_class = MeasurementUnit::class; @@ -48,44 +49,34 @@ class MeasurementUnitController extends BaseAdminController protected string $attachment_class = MeasurementUnitAttachment::class; protected ?string $parameter_class = MeasurementUnitParameter::class; - /** - * @Route("/{id}", name="measurement_unit_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'measurement_unit_delete', methods: ['DELETE'])] public function delete(Request $request, MeasurementUnit $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="measurement_unit_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'measurement_unit_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function edit(MeasurementUnit $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="measurement_unit_new") - * @Route("/{id}/clone", name="measurement_unit_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'measurement_unit_new')] + #[Route(path: '/{id}/clone', name: 'measurement_unit_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?MeasurementUnit $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="measurement_unit_export_all") - */ + #[Route(path: '/export', name: 'measurement_unit_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="measurement_unit_export") - */ + #[Route(path: '/{id}/export', name: 'measurement_unit_export')] public function exportEntity(AttachmentType $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AdminPages/ProjectAdminController.php b/src/Controller/AdminPages/ProjectAdminController.php index ae14ea27..41287b69 100644 --- a/src/Controller/AdminPages/ProjectAdminController.php +++ b/src/Controller/AdminPages/ProjectAdminController.php @@ -25,7 +25,6 @@ namespace App\Controller\AdminPages; use App\Entity\Attachments\ProjectAttachment; use App\Entity\ProjectSystem\Project; use App\Entity\Parameters\ProjectParameter; -use App\Form\AdminPages\BaseEntityAdminForm; use App\Form\AdminPages\ProjectAdminForm; use App\Services\ImportExportSystem\EntityExporter; use App\Services\ImportExportSystem\EntityImporter; @@ -34,11 +33,9 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; -/** - * @Route("/project") - */ +#[Route(path: '/project')] class ProjectAdminController extends BaseAdminController { protected string $entity_class = Project::class; @@ -48,44 +45,34 @@ class ProjectAdminController extends BaseAdminController protected string $attachment_class = ProjectAttachment::class; protected ?string $parameter_class = ProjectParameter::class; - /** - * @Route("/{id}", name="project_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'project_delete', methods: ['DELETE'])] public function delete(Request $request, Project $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="project_edit") - * @Route("/{id}/edit", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'project_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}/edit', requirements: ['id' => '\d+'])] public function edit(Project $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="project_new") - * @Route("/{id}/clone", name="device_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'project_new')] + #[Route(path: '/{id}/clone', name: 'device_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Project $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="project_export_all") - */ + #[Route(path: '/export', name: 'project_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="project_export") - */ + #[Route(path: '/{id}/export', name: 'project_export')] public function exportEntity(Project $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AdminPages/StorelocationController.php b/src/Controller/AdminPages/StorageLocationController.php similarity index 57% rename from src/Controller/AdminPages/StorelocationController.php rename to src/Controller/AdminPages/StorageLocationController.php index 080690ab..def6d3c6 100644 --- a/src/Controller/AdminPages/StorelocationController.php +++ b/src/Controller/AdminPages/StorageLocationController.php @@ -22,9 +22,9 @@ declare(strict_types=1); namespace App\Controller\AdminPages; -use App\Entity\Attachments\StorelocationAttachment; -use App\Entity\Parameters\StorelocationParameter; -use App\Entity\Parts\Storelocation; +use App\Entity\Attachments\StorageLocationAttachment; +use App\Entity\Parameters\StorageLocationParameter; +use App\Entity\Parts\StorageLocation; use App\Form\AdminPages\StorelocationAdminForm; use App\Services\ImportExportSystem\EntityExporter; use App\Services\ImportExportSystem\EntityImporter; @@ -33,59 +33,50 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/store_location") + * @see \App\Tests\Controller\AdminPages\StorelocationControllerTest */ -class StorelocationController extends BaseAdminController +#[Route(path: '/store_location')] +class StorageLocationController extends BaseAdminController { - protected string $entity_class = Storelocation::class; + protected string $entity_class = StorageLocation::class; protected string $twig_template = 'admin/storelocation_admin.html.twig'; protected string $form_class = StorelocationAdminForm::class; protected string $route_base = 'store_location'; - protected string $attachment_class = StorelocationAttachment::class; - protected ?string $parameter_class = StorelocationParameter::class; + protected string $attachment_class = StorageLocationAttachment::class; + protected ?string $parameter_class = StorageLocationParameter::class; - /** - * @Route("/{id}", name="store_location_delete", methods={"DELETE"}) - */ - public function delete(Request $request, Storelocation $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse + #[Route(path: '/{id}', name: 'store_location_delete', methods: ['DELETE'])] + public function delete(Request $request, StorageLocation $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="store_location_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ - public function edit(Storelocation $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response + #[Route(path: '/{id}/edit/{timestamp}', name: 'store_location_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] + public function edit(StorageLocation $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="store_location_new") - * @Route("/{id}/clone", name="store_location_clone") - * @Route("/") - */ - public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Storelocation $entity = null): Response + #[Route(path: '/new', name: 'store_location_new')] + #[Route(path: '/{id}/clone', name: 'store_location_clone')] + #[Route(path: '/')] + public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?StorageLocation $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="store_location_export_all") - */ + #[Route(path: '/export', name: 'store_location_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="store_location_export") - */ - public function exportEntity(Storelocation $entity, EntityExporter $exporter, Request $request): Response + #[Route(path: '/{id}/export', name: 'store_location_export')] + public function exportEntity(StorageLocation $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); } diff --git a/src/Controller/AdminPages/SupplierController.php b/src/Controller/AdminPages/SupplierController.php index f7505c6a..195b9e18 100644 --- a/src/Controller/AdminPages/SupplierController.php +++ b/src/Controller/AdminPages/SupplierController.php @@ -33,11 +33,12 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/supplier") + * @see \App\Tests\Controller\AdminPages\SupplierControllerTest */ +#[Route(path: '/supplier')] class SupplierController extends BaseAdminController { protected string $entity_class = Supplier::class; @@ -47,44 +48,34 @@ class SupplierController extends BaseAdminController protected string $attachment_class = SupplierAttachment::class; protected ?string $parameter_class = SupplierParameter::class; - /** - * @Route("/{id}", name="supplier_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'supplier_delete', methods: ['DELETE'])] public function delete(Request $request, Supplier $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="supplier_edit") - * @Route("/{id}", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'supplier_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function edit(Supplier $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response { return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="supplier_new") - * @Route("/{id}/clone", name="supplier_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'supplier_new')] + #[Route(path: '/{id}/clone', name: 'supplier_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Supplier $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/export", name="supplier_export_all") - */ + #[Route(path: '/export', name: 'supplier_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="supplier_export") - */ + #[Route(path: '/{id}/export', name: 'supplier_export')] public function exportEntity(Supplier $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/AttachmentFileController.php b/src/Controller/AttachmentFileController.php index 952237d8..d8bd8d87 100644 --- a/src/Controller/AttachmentFileController.php +++ b/src/Controller/AttachmentFileController.php @@ -24,31 +24,25 @@ namespace App\Controller; use App\DataTables\AttachmentDataTable; use App\DataTables\Filters\AttachmentFilter; -use App\DataTables\Filters\PartFilter; -use App\DataTables\PartsDataTable; use App\Entity\Attachments\Attachment; -use App\Entity\Attachments\PartAttachment; use App\Form\Filters\AttachmentFilterType; -use App\Form\Filters\PartFilterType; use App\Services\Attachments\AttachmentManager; use App\Services\Trees\NodesListBuilder; use Omines\DataTablesBundle\DataTableFactory; use RuntimeException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class AttachmentFileController extends AbstractController { /** * Download the selected attachment. - * - * @Route("/attachment/{id}/download", name="attachment_download") */ + #[Route(path: '/attachment/{id}/download', name: 'attachment_download')] public function download(Attachment $attachment, AttachmentManager $helper): BinaryFileResponse { $this->denyAccessUnlessGranted('read', $attachment); @@ -57,15 +51,15 @@ class AttachmentFileController extends AbstractController $this->denyAccessUnlessGranted('show_private', $attachment); } - if ($attachment->isExternal()) { - throw new RuntimeException('You can not download external attachments!'); + if (!$attachment->hasInternal()) { + throw $this->createNotFoundException('The file for this attachment is external and not stored locally!'); } - if (!$helper->isFileExisting($attachment)) { - throw new RuntimeException('The file associated with the attachment is not existing!'); + if (!$helper->isInternalFileExisting($attachment)) { + throw $this->createNotFoundException('The file associated with the attachment is not existing!'); } - $file_path = $helper->toAbsoluteFilePath($attachment); + $file_path = $helper->toAbsoluteInternalFilePath($attachment); $response = new BinaryFileResponse($file_path); //Set header content disposition, so that the file will be downloaded @@ -76,9 +70,8 @@ class AttachmentFileController extends AbstractController /** * View the attachment. - * - * @Route("/attachment/{id}/view", name="attachment_view") */ + #[Route(path: '/attachment/{id}/view', name: 'attachment_view')] public function view(Attachment $attachment, AttachmentManager $helper): BinaryFileResponse { $this->denyAccessUnlessGranted('read', $attachment); @@ -87,15 +80,15 @@ class AttachmentFileController extends AbstractController $this->denyAccessUnlessGranted('show_private', $attachment); } - if ($attachment->isExternal()) { - throw new RuntimeException('You can not download external attachments!'); + if (!$attachment->hasInternal()) { + throw $this->createNotFoundException('The file for this attachment is external and not stored locally!'); } - if (!$helper->isFileExisting($attachment)) { - throw new RuntimeException('The file associated with the attachment is not existing!'); + if (!$helper->isInternalFileExisting($attachment)) { + throw $this->createNotFoundException('The file associated with the attachment is not existing!'); } - $file_path = $helper->toAbsoluteFilePath($attachment); + $file_path = $helper->toAbsoluteInternalFilePath($attachment); $response = new BinaryFileResponse($file_path); //Set header content disposition, so that the file will be downloaded @@ -104,12 +97,8 @@ class AttachmentFileController extends AbstractController return $response; } - /** - * @Route("/attachment/list", name="attachment_list") - * - * @return JsonResponse|Response - */ - public function attachmentsTable(Request $request, DataTableFactory $dataTableFactory, NodesListBuilder $nodesListBuilder) + #[Route(path: '/attachment/list', name: 'attachment_list')] + public function attachmentsTable(Request $request, DataTableFactory $dataTableFactory, NodesListBuilder $nodesListBuilder): Response { $this->denyAccessUnlessGranted('@attachments.list_attachments'); @@ -130,7 +119,7 @@ class AttachmentFileController extends AbstractController return $this->render('attachment_list.html.twig', [ 'datatable' => $table, - 'filterForm' => $filterForm->createView(), + 'filterForm' => $filterForm, ]); } } diff --git a/src/Controller/ErrorHandling/FixedErrorController.php b/src/Controller/ErrorHandling/FixedErrorController.php new file mode 100644 index 00000000..610a07b1 --- /dev/null +++ b/src/Controller/ErrorHandling/FixedErrorController.php @@ -0,0 +1,64 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Controller\ErrorHandling; + +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ErrorController; + +/** + * This class decorates the default error decorator and changes the content type of responses, if it went through a + * Turbo request. + * The problem is, that the default error controller returns in the format of the preferred content type of the request. + * This is turbo-stream. This causes Turbo to try to integrate it into the content frame and not trigger the ajax failed + * events to show the error in a popup like intended. + */ +#[AsDecorator("error_controller")] +class FixedErrorController +{ + public function __construct(private readonly ErrorController $decorated) + {} + + public function __invoke(\Throwable $exception): Response + { + $response = ($this->decorated)($exception); + + //Check the content type of the response + $contentType = $response->headers->get('Content-Type'); + + //If the content type is turbo stream, change the content type to html + //This prevents Turbo to render the response as a turbo stream, and forces to render it in the popup + if ($contentType === 'text/vnd.turbo-stream.html') { + $response->headers->set('Content-Type', 'text/html'); + } + + return $response; + } + + public function preview(Request $request, int $code): Response + { + return ($this->decorated)->preview($request, $code); + } +} \ No newline at end of file diff --git a/src/Controller/GroupController.php b/src/Controller/GroupController.php index 1eb10d76..cf7f0ab8 100644 --- a/src/Controller/GroupController.php +++ b/src/Controller/GroupController.php @@ -37,11 +37,9 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; -/** - * @Route("/group") - */ +#[Route(path: '/group')] class GroupController extends BaseAdminController { protected string $entity_class = Group::class; @@ -51,10 +49,8 @@ class GroupController extends BaseAdminController protected string $attachment_class = GroupAttachment::class; protected ?string $parameter_class = GroupParameter::class; - /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="group_edit") - * @Route("/{id}/", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/edit/{timestamp}', name: 'group_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}/', requirements: ['id' => '\d+'])] public function edit(Group $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, PermissionSchemaUpdater $permissionSchemaUpdater, ?string $timestamp = null): Response { //Do an upgrade of the permission schema if needed (so the user can see the permissions a user get on next request (even if it was not done yet) @@ -63,7 +59,7 @@ class GroupController extends BaseAdminController //Handle permissions presets if ($request->request->has('permission_preset')) { $this->denyAccessUnlessGranted('edit_permissions', $entity); - if ($this->isCsrfTokenValid('group'.$entity->getId(), $request->request->get('_token'))) { + if ($this->isCsrfTokenValid('group'.$entity->getID(), $request->request->get('_token'))) { $preset = $request->request->get('permission_preset'); $permissionPresetsHelper->applyPreset($entity, $preset); @@ -74,43 +70,35 @@ class GroupController extends BaseAdminController //We need to stop the execution here, or our permissions changes will be overwritten by the form values return $this->redirectToRoute('group_edit', ['id' => $entity->getID()]); - } else { - $this->addFlash('danger', 'csfr_invalid'); } + + $this->addFlash('danger', 'csfr_invalid'); } return $this->_edit($entity, $request, $em, $timestamp); } - /** - * @Route("/new", name="group_new") - * @Route("/{id}/clone", name="group_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'group_new')] + #[Route(path: '/{id}/clone', name: 'group_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Group $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/{id}", name="group_delete", methods={"DELETE"}) - */ + #[Route(path: '/{id}', name: 'group_delete', methods: ['DELETE'])] public function delete(Request $request, Group $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/export", name="group_export_all") - */ + #[Route(path: '/export', name: 'group_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="group_export") - */ + #[Route(path: '/{id}/export', name: 'group_export')] public function exportEntity(Group $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); diff --git a/src/Controller/HomepageController.php b/src/Controller/HomepageController.php index 51447c17..076e790b 100644 --- a/src/Controller/HomepageController.php +++ b/src/Controller/HomepageController.php @@ -25,47 +25,29 @@ namespace App\Controller; use App\DataTables\LogDataTable; use App\Entity\Parts\Part; use App\Services\Misc\GitVersionInfo; +use App\Services\System\BannerHelper; +use App\Services\System\UpdateAvailableManager; use Doctrine\ORM\EntityManagerInterface; -use const DIRECTORY_SEPARATOR; use Omines\DataTablesBundle\DataTableFactory; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\KernelInterface; -use Symfony\Component\Routing\Annotation\Route; -use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Component\Routing\Attribute\Route; class HomepageController extends AbstractController { - protected CacheInterface $cache; - protected KernelInterface $kernel; - protected DataTableFactory $dataTable; - - public function __construct(CacheInterface $cache, KernelInterface $kernel, DataTableFactory $dataTable) + public function __construct(private readonly DataTableFactory $dataTable, private readonly BannerHelper $bannerHelper) { - $this->cache = $cache; - $this->kernel = $kernel; - $this->dataTable = $dataTable; } - public function getBanner(): string + + + #[Route(path: '/', name: 'homepage')] + public function homepage(Request $request, GitVersionInfo $versionInfo, EntityManagerInterface $entityManager, + UpdateAvailableManager $updateAvailableManager): Response { - $banner = $this->getParameter('partdb.banner'); - if (empty($banner)) { - $banner_path = $this->kernel->getProjectDir() - .DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'banner.md'; + $this->denyAccessUnlessGranted('HAS_ACCESS_PERMISSIONS'); - return file_get_contents($banner_path); - } - - return $banner; - } - - /** - * @Route("/", name="homepage") - */ - public function homepage(Request $request, GitVersionInfo $versionInfo, EntityManagerInterface $entityManager): Response - { if ($this->isGranted('@tools.lastActivity')) { $table = $this->dataTable->createFromType( LogDataTable::class, @@ -94,11 +76,14 @@ class HomepageController extends AbstractController } return $this->render('homepage.html.twig', [ - 'banner' => $this->getBanner(), + 'banner' => $this->bannerHelper->getBanner(), 'git_branch' => $versionInfo->getGitBranchName(), 'git_commit' => $versionInfo->getGitCommitHash(), 'show_first_steps' => $show_first_steps, 'datatable' => $table, + 'new_version_available' => $updateAvailableManager->isUpdateAvailable(), + 'new_version' => $updateAvailableManager->getLatestVersionString(), + 'new_version_url' => $updateAvailableManager->getLatestVersionUrl(), ]); } } diff --git a/src/Controller/InfoProviderController.php b/src/Controller/InfoProviderController.php new file mode 100644 index 00000000..a6ce3f1b --- /dev/null +++ b/src/Controller/InfoProviderController.php @@ -0,0 +1,131 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Controller; + +use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\Part; +use App\Form\InfoProviderSystem\PartSearchType; +use App\Services\InfoProviderSystem\ExistingPartFinder; +use App\Services\InfoProviderSystem\PartInfoRetriever; +use App\Services\InfoProviderSystem\ProviderRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Psr\Log\LoggerInterface; +use Symfony\Bridge\Doctrine\Attribute\MapEntity; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpClient\Exception\ClientException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; + +use function Symfony\Component\Translation\t; + +#[Route('/tools/info_providers')] +class InfoProviderController extends AbstractController +{ + + public function __construct(private readonly ProviderRegistry $providerRegistry, + private readonly PartInfoRetriever $infoRetriever, + private readonly ExistingPartFinder $existingPartFinder + ) + { + + } + + #[Route('/providers', name: 'info_providers_list')] + public function listProviders(): Response + { + $this->denyAccessUnlessGranted('@info_providers.create_parts'); + + return $this->render('info_providers/providers_list/providers_list.html.twig', [ + 'active_providers' => $this->providerRegistry->getActiveProviders(), + 'disabled_providers' => $this->providerRegistry->getDisabledProviders(), + ]); + } + + #[Route('/search', name: 'info_providers_search')] + #[Route('/update/{target}', name: 'info_providers_update_part_search')] + public function search(Request $request, #[MapEntity(id: 'target')] ?Part $update_target, LoggerInterface $exceptionLogger): Response + { + $this->denyAccessUnlessGranted('@info_providers.create_parts'); + + $form = $this->createForm(PartSearchType::class); + $form->handleRequest($request); + + $results = null; + + //When we are updating a part, use its name as keyword, to make searching easier + //However we can only do this, if the form was not submitted yet + if ($update_target !== null && !$form->isSubmitted()) { + //Use the provider reference if available, otherwise use the manufacturer product number + $keyword = $update_target->getProviderReference()->getProviderId() ?? $update_target->getManufacturerProductNumber(); + //Or the name if both are not available + if ($keyword === "") { + $keyword = $update_target->getName(); + } + + $form->get('keyword')->setData($keyword); + + //If we are updating a part, which already has a provider, preselect that provider in the form + if ($update_target->getProviderReference()->getProviderKey() !== null) { + try { + $form->get('providers')->setData([$this->providerRegistry->getProviderByKey($update_target->getProviderReference()->getProviderKey())]); + } catch (\InvalidArgumentException $e) { + //If the provider is not found, just ignore it + } + } + } + + if ($form->isSubmitted() && $form->isValid()) { + $keyword = $form->get('keyword')->getData(); + $providers = $form->get('providers')->getData(); + + $dtos = []; + + try { + $dtos = $this->infoRetriever->searchByKeyword(keyword: $keyword, providers: $providers); + } catch (ClientException $e) { + $this->addFlash('error', t('info_providers.search.error.client_exception')); + $this->addFlash('error',$e->getMessage()); + //Log the exception + $exceptionLogger->error('Error during info provider search: ' . $e->getMessage(), ['exception' => $e]); + } + + // modify the array to an array of arrays that has a field for a matching local Part + // the advantage to use that format even when we don't look for local parts is that we + // always work with the same interface + $results = array_map(function ($result) {return ['dto' => $result, 'localPart' => null];}, $dtos); + if(!$update_target) { + foreach ($results as $index => $result) { + $results[$index]['localPart'] = $this->existingPartFinder->findFirstExisting($result['dto']); + } + } + } + + return $this->render('info_providers/search/part_search.html.twig', [ + 'form' => $form, + 'results' => $results, + 'update_target' => $update_target + ]); + } +} \ No newline at end of file diff --git a/src/Controller/KiCadApiController.php b/src/Controller/KiCadApiController.php new file mode 100644 index 00000000..c28e87a6 --- /dev/null +++ b/src/Controller/KiCadApiController.php @@ -0,0 +1,85 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Controller; + +use App\Entity\Parts\Category; +use App\Entity\Parts\Part; +use App\Services\EDA\KiCadHelper; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; + +/** + * @see \App\Tests\Controller\KiCadApiControllerTest + */ +#[Route('/kicad-api/v1')] +class KiCadApiController extends AbstractController +{ + public function __construct( + private readonly KiCadHelper $kiCADHelper, + ) + { + } + + #[Route('/', name: 'kicad_api_root')] + public function root(): Response + { + $this->denyAccessUnlessGranted('HAS_ACCESS_PERMISSIONS'); + + //The API documentation says this can be either blank or the URL to the endpoints + return $this->json([ + 'categories' => '', + 'parts' => '', + ]); + } + + #[Route('/categories.json', name: 'kicad_api_categories')] + public function categories(): Response + { + $this->denyAccessUnlessGranted('@categories.read'); + + return $this->json($this->kiCADHelper->getCategories()); + } + + #[Route('/parts/category/{category}.json', name: 'kicad_api_category')] + public function categoryParts(?Category $category): Response + { + if ($category !== null) { + $this->denyAccessUnlessGranted('read', $category); + } else { + $this->denyAccessUnlessGranted('@categories.read'); + } + $this->denyAccessUnlessGranted('@parts.read'); + + return $this->json($this->kiCADHelper->getCategoryParts($category)); + } + + #[Route('/parts/{part}.json', name: 'kicad_api_part')] + public function partDetails(Part $part): Response + { + $this->denyAccessUnlessGranted('read', $part); + + return $this->json($this->kiCADHelper->getKiCADPart($part)); + } +} \ No newline at end of file diff --git a/src/Controller/LabelController.php b/src/Controller/LabelController.php index 769639d4..4950628b 100644 --- a/src/Controller/LabelController.php +++ b/src/Controller/LabelController.php @@ -43,7 +43,9 @@ namespace App\Controller; use App\Entity\Base\AbstractDBElement; use App\Entity\LabelSystem\LabelOptions; +use App\Entity\LabelSystem\LabelProcessMode; use App\Entity\LabelSystem\LabelProfile; +use App\Entity\LabelSystem\LabelSupportedElement; use App\Exceptions\TwigModeException; use App\Form\LabelSystem\LabelDialogType; use App\Repository\DBElementRepository; @@ -51,67 +53,46 @@ use App\Services\ElementTypeNameGenerator; use App\Services\LabelSystem\LabelGenerator; use App\Services\Misc\RangeParser; use Doctrine\ORM\EntityManagerInterface; -use InvalidArgumentException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\FormError; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Contracts\Translation\TranslatorInterface; -/** - * @Route("/label") - */ +#[Route(path: '/label')] class LabelController extends AbstractController { - protected LabelGenerator $labelGenerator; - protected EntityManagerInterface $em; - protected ElementTypeNameGenerator $elementTypeNameGenerator; - protected RangeParser $rangeParser; - protected TranslatorInterface $translator; - - public function __construct(LabelGenerator $labelGenerator, EntityManagerInterface $em, ElementTypeNameGenerator $elementTypeNameGenerator, - RangeParser $rangeParser, TranslatorInterface $translator) + public function __construct(protected LabelGenerator $labelGenerator, protected EntityManagerInterface $em, protected ElementTypeNameGenerator $elementTypeNameGenerator, protected RangeParser $rangeParser, protected TranslatorInterface $translator) { - $this->labelGenerator = $labelGenerator; - $this->em = $em; - $this->elementTypeNameGenerator = $elementTypeNameGenerator; - $this->rangeParser = $rangeParser; - $this->translator = $translator; } - /** - * @Route("/dialog", name="label_dialog") - * @Route("/{profile}/dialog", name="label_dialog_profile") - */ + #[Route(path: '/dialog', name: 'label_dialog')] + #[Route(path: '/{profile}/dialog', name: 'label_dialog_profile')] public function generator(Request $request, ?LabelProfile $profile = null): Response { $this->denyAccessUnlessGranted('@labels.create_labels'); //If we inherit a LabelProfile, the user need to have access to it... - if (null !== $profile) { + if ($profile instanceof LabelProfile) { $this->denyAccessUnlessGranted('read', $profile); } - if ($profile) { - $label_options = $profile->getOptions(); - } else { - $label_options = new LabelOptions(); - } + $label_options = $profile instanceof LabelProfile ? $profile->getOptions() : new LabelOptions(); //We have to disable the options, if twig mode is selected and user is not allowed to use it. - $disable_options = 'twig' === $label_options->getLinesMode() && !$this->isGranted('@labels.use_twig'); + $disable_options = (LabelProcessMode::TWIG === $label_options->getProcessMode()) && !$this->isGranted('@labels.use_twig'); $form = $this->createForm(LabelDialogType::class, null, [ 'disable_options' => $disable_options, ]); //Try to parse given target_type and target_id - $target_type = $request->query->get('target_type', null); + $target_type = $request->query->getEnum('target_type', LabelSupportedElement::class, null); $target_id = $request->query->get('target_id', null); $generate = $request->query->getBoolean('generate', false); - if (null === $profile && is_string($target_type)) { + if (!$profile instanceof LabelProfile && $target_type instanceof LabelSupportedElement) { $label_options->setSupportedElement($target_type); } if (is_string($target_id)) { @@ -127,16 +108,39 @@ class LabelController extends AbstractController $pdf_data = null; $filename = 'invalid.pdf'; - //Generate PDF either when the form is submitted and valid, or the form was not submit yet, and generate is set - if (($form->isSubmitted() && $form->isValid()) || ($generate && !$form->isSubmitted() && null !== $profile)) { + if (($form->isSubmitted() && $form->isValid()) || ($generate && !$form->isSubmitted() && $profile instanceof LabelProfile)) { + + //Check if the label should be saved as profile + if ($form->get('save_profile')->isClicked() && $this->isGranted('@labels.create_profiles')) { //@phpstan-ignore-line Phpstan does not recognize the isClicked method + //Retrieve the profile name from the form + $new_name = $form->get('save_profile_name')->getData(); + //ensure that the name is not empty + if ($new_name === '' || $new_name === null) { + $form->get('save_profile_name')->addError(new FormError($this->translator->trans('label_generator.profile_name_empty'))); + goto render; + } + + $profile = new LabelProfile(); + $profile->setName($form->get('save_profile_name')->getData()); + $profile->setOptions($form_options); + $this->em->persist($profile); + $this->em->flush(); + $this->addFlash('success', 'label_generator.profile_saved'); + + return $this->redirectToRoute('label_dialog_profile', [ + 'profile' => $profile->getID(), + 'target_id' => (string) $form->get('target_id')->getData() + ]); + } + $target_id = (string) $form->get('target_id')->getData(); $targets = $this->findObjects($form_options->getSupportedElement(), $target_id); - if (!empty($targets)) { + if ($targets !== []) { try { $pdf_data = $this->labelGenerator->generateLabel($form_options, $targets); $filename = $this->getLabelName($targets[0], $profile); } catch (TwigModeException $exception) { - $form->get('options')->get('lines')->addError(new FormError($exception->getMessage())); + $form->get('options')->get('lines')->addError(new FormError($exception->getSafeMessage())); } } else { //$this->addFlash('warning', 'label_generator.no_entities_found'); @@ -144,9 +148,15 @@ class LabelController extends AbstractController new FormError($this->translator->trans('label_generator.no_entities_found')) ); } + + //When the profile lines are empty, show a notice flash + if (trim($form_options->getLines()) === '') { + $this->addFlash('notice', 'label_generator.no_lines_given'); + } } - return $this->renderForm('label_system/dialog.html.twig', [ + render: + return $this->render('label_system/dialog.html.twig', [ 'form' => $form, 'pdf_data' => $pdf_data, 'filename' => $filename, @@ -162,16 +172,12 @@ class LabelController extends AbstractController return $ret.'.pdf'; } - protected function findObjects(string $type, string $ids): array + protected function findObjects(LabelSupportedElement $type, string $ids): array { - if (!isset(LabelGenerator::CLASS_SUPPORT_MAPPING[$type])) { - throw new InvalidArgumentException('The given type is not known and can not be mapped to a class!'); - } - $id_array = $this->rangeParser->parse($ids); - /** @var DBElementRepository $repo */ - $repo = $this->em->getRepository(LabelGenerator::CLASS_SUPPORT_MAPPING[$type]); + /** @var DBElementRepository $repo */ + $repo = $this->em->getRepository($type->getEntityClass()); return $repo->getElementsFromIDArray($id_array); } diff --git a/src/Controller/LogController.php b/src/Controller/LogController.php index 91b37269..a849539d 100644 --- a/src/Controller/LogController.php +++ b/src/Controller/LogController.php @@ -33,40 +33,32 @@ use App\Entity\LogSystem\ElementEditedLogEntry; use App\Form\Filters\LogFilterType; use App\Repository\DBElementRepository; use App\Services\LogSystem\EventUndoHelper; +use App\Services\LogSystem\EventUndoMode; +use App\Services\LogSystem\LogEntryExtraFormatter; +use App\Services\LogSystem\LogLevelHelper; +use App\Services\LogSystem\LogTargetHelper; use App\Services\LogSystem\TimeTravel; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\EntityRepository; use InvalidArgumentException; use Omines\DataTablesBundle\DataTableFactory; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; -/** - * @Route("/log") - */ +#[Route(path: '/log')] class LogController extends AbstractController { - protected EntityManagerInterface $entityManager; - protected TimeTravel $timeTravel; protected DBElementRepository $dbRepository; - public function __construct(EntityManagerInterface $entityManager, TimeTravel $timeTravel) + public function __construct(protected EntityManagerInterface $entityManager, protected TimeTravel $timeTravel) { - $this->entityManager = $entityManager; - $this->timeTravel = $timeTravel; $this->dbRepository = $entityManager->getRepository(AbstractDBElement::class); } - /** - * @Route("/", name="log_view") - * - * @return JsonResponse|Response - */ - public function showLogs(Request $request, DataTableFactory $dataTable) + #[Route(path: '/', name: 'log_view')] + public function showLogs(Request $request, DataTableFactory $dataTable): Response { $this->denyAccessUnlessGranted('@system.show_logs'); @@ -89,26 +81,66 @@ class LogController extends AbstractController return $this->render('log_system/log_list.html.twig', [ 'datatable' => $table, - 'filterForm' => $filterForm->createView(), + 'filterForm' => $filterForm, ]); } - /** - * @Route("/undo", name="log_undo", methods={"POST"}) - */ + #[Route(path: '/{id}/details', name: 'log_details')] + public function logDetails(AbstractLogEntry $logEntry, LogEntryExtraFormatter $logEntryExtraFormatter, + LogLevelHelper $logLevelHelper, LogTargetHelper $logTargetHelper, EntityManagerInterface $entityManager): Response + { + $this->denyAccessUnlessGranted('show_details', $logEntry); + + $extra_html = $logEntryExtraFormatter->format($logEntry); + $target_html = $logTargetHelper->formatTarget($logEntry); + + $repo = $entityManager->getRepository(AbstractLogEntry::class); + $target_element = $repo->getTargetElement($logEntry); + + return $this->render('log_system/details/log_details.html.twig', [ + 'log_entry' => $logEntry, + 'target_element' => $target_element, + 'extra_html' => $extra_html, + 'target_html' => $target_html, + 'log_level_helper' => $logLevelHelper, + ]); + } + + #[Route(path: '/{id}/delete', name: 'log_delete', methods: ['DELETE'])] + public function deleteLogEntry(Request $request, AbstractLogEntry $logEntry, EntityManagerInterface $entityManager): RedirectResponse + { + $this->denyAccessUnlessGranted('delete', $logEntry); + + if ($this->isCsrfTokenValid('delete'.$logEntry->getID(), $request->request->get('_token'))) { + //Remove part + $entityManager->remove($logEntry); + //Flush changes + $entityManager->flush(); + $this->addFlash('success', 'log.delete.success'); + } + + return $this->redirectToRoute('homepage'); + } + + + #[Route(path: '/undo', name: 'log_undo', methods: ['POST'])] public function undoRevertLog(Request $request, EventUndoHelper $eventUndoHelper): RedirectResponse { - $mode = EventUndoHelper::MODE_UNDO; - $id = $request->request->get('undo'); + $mode = EventUndoMode::UNDO; + $id = $request->request->getInt('undo'); //If no undo value was set check if a revert was set - if (null === $id) { - $id = $request->get('revert'); - $mode = EventUndoHelper::MODE_REVERT; + if (0 === $id) { + $id = $request->request->getInt('revert'); + $mode = EventUndoMode::REVERT; + } + + if (0 === $id) { + throw new InvalidArgumentException('No log entry ID was given!'); } $log_element = $this->entityManager->find(AbstractLogEntry::class, $id); - if (null === $log_element) { + if (!$log_element instanceof AbstractLogEntry) { throw new InvalidArgumentException('No log entry with the given ID is existing!'); } @@ -117,9 +149,9 @@ class LogController extends AbstractController $eventUndoHelper->setMode($mode); $eventUndoHelper->setUndoneEvent($log_element); - if (EventUndoHelper::MODE_UNDO === $mode) { + if (EventUndoMode::UNDO === $mode) { $this->undoLog($log_element); - } elseif (EventUndoHelper::MODE_REVERT === $mode) { + } else { $this->revertLog($log_element); } diff --git a/src/Controller/OAuthClientController.php b/src/Controller/OAuthClientController.php new file mode 100644 index 00000000..9606a4e4 --- /dev/null +++ b/src/Controller/OAuthClientController.php @@ -0,0 +1,67 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Controller; + +use App\Services\OAuth\OAuthTokenManager; +use KnpU\OAuth2ClientBundle\Client\ClientRegistry; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; + +use function Symfony\Component\Translation\t; + +#[Route('/oauth/client')] +class OAuthClientController extends AbstractController +{ + public function __construct(private readonly ClientRegistry $clientRegistry, private readonly OAuthTokenManager $tokenManager) + { + + } + + #[Route('/{name}/connect', name: 'oauth_client_connect')] + public function connect(string $name): Response + { + $this->denyAccessUnlessGranted('@system.manage_oauth_tokens'); + + return $this->clientRegistry + ->getClient($name) // key used in config/packages/knpu_oauth2_client.yaml + ->redirect([], []); + } + + #[Route('/{name}/check', name: 'oauth_client_check')] + public function check(string $name): Response + { + $this->denyAccessUnlessGranted('@system.manage_oauth_tokens'); + + $client = $this->clientRegistry->getClient($name); + + $access_token = $client->getAccessToken(); + $this->tokenManager->saveToken($name, $access_token); + + $this->addFlash('success', t('oauth_client.flash.connection_successful')); + + return $this->redirectToRoute('homepage'); + } +} \ No newline at end of file diff --git a/src/Controller/PartController.php b/src/Controller/PartController.php index c02a6b4f..b11a5c90 100644 --- a/src/Controller/PartController.php +++ b/src/Controller/PartController.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace App\Controller; use App\DataTables\LogDataTable; +use App\Entity\Attachments\AttachmentUpload; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Orderdetail; use App\Entity\ProjectSystem\Project; @@ -36,6 +37,8 @@ use App\Exceptions\AttachmentDownloadException; use App\Form\Part\PartBaseType; use App\Services\Attachments\AttachmentSubmitHandler; use App\Services\Attachments\PartPreviewGenerator; +use App\Services\EntityMergers\Mergers\PartMerger; +use App\Services\InfoProviderSystem\PartInfoRetriever; use App\Services\LogSystem\EventCommentHelper; use App\Services\LogSystem\HistoryHelper; use App\Services\LogSystem\TimeTravel; @@ -47,39 +50,35 @@ use DateTime; use Doctrine\ORM\EntityManagerInterface; use Exception; use Omines\DataTablesBundle\DataTableFactory; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; +use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Contracts\Translation\TranslatorInterface; -/** - * @Route("/part") - */ +use function Symfony\Component\Translation\t; + +#[Route(path: '/part')] class PartController extends AbstractController { - protected PricedetailHelper $pricedetailHelper; - protected PartPreviewGenerator $partPreviewGenerator; - protected EventCommentHelper $commentHelper; - - public function __construct(PricedetailHelper $pricedetailHelper, - PartPreviewGenerator $partPreviewGenerator, EventCommentHelper $commentHelper) + public function __construct(protected PricedetailHelper $pricedetailHelper, + protected PartPreviewGenerator $partPreviewGenerator, + private readonly TranslatorInterface $translator, + private readonly AttachmentSubmitHandler $attachmentSubmitHandler, private readonly EntityManagerInterface $em, + protected EventCommentHelper $commentHelper) { - $this->pricedetailHelper = $pricedetailHelper; - $this->partPreviewGenerator = $partPreviewGenerator; - $this->commentHelper = $commentHelper; } /** - * @Route("/{id}/info/{timestamp}", name="part_info") - * @Route("/{id}", requirements={"id"="\d+"}) * * @throws Exception */ + #[Route(path: '/{id}/info/{timestamp}', name: 'part_info')] + #[Route(path: '/{id}', requirements: ['id' => '\d+'])] public function show(Part $part, Request $request, TimeTravel $timeTravel, HistoryHelper $historyHelper, DataTableFactory $dataTable, ParameterExtractor $parameterExtractor, PartLotWithdrawAddHelper $withdrawAddHelper, ?string $timestamp = null): Response { @@ -127,82 +126,28 @@ class PartController extends AbstractController ); } - /** - * @Route("/{id}/edit", name="part_edit") - */ - public function edit(Part $part, Request $request, EntityManagerInterface $em, TranslatorInterface $translator, - AttachmentSubmitHandler $attachmentSubmitHandler): Response + #[Route(path: '/{id}/edit', name: 'part_edit')] + public function edit(Part $part, Request $request): Response { $this->denyAccessUnlessGranted('edit', $part); - $form = $this->createForm(PartBaseType::class, $part); - - $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - //Upload passed files - $attachments = $form['attachments']; - foreach ($attachments as $attachment) { - /** @var FormInterface $attachment */ - $options = [ - 'secure_attachment' => $attachment['secureFile']->getData(), - 'download_url' => $attachment['downloadURL']->getData(), - ]; - - try { - $attachmentSubmitHandler->handleFormSubmit($attachment->getData(), $attachment['file']->getData(), $options); - } catch (AttachmentDownloadException $attachmentDownloadException) { - $this->addFlash( - 'error', - $translator->trans('attachment.download_failed').' '.$attachmentDownloadException->getMessage() - ); - } - } - - $this->commentHelper->setMessage($form['log_comment']->getData()); - - $em->persist($part); - $em->flush(); - $this->addFlash('success', 'part.edited_flash'); - - //Redirect to clone page if user wished that... - //@phpstan-ignore-next-line - if ('save_and_clone' === $form->getClickedButton()->getName()) { - return $this->redirectToRoute('part_clone', ['id' => $part->getID()]); - } - //@phpstan-ignore-next-line - if ('save_and_new' === $form->getClickedButton()->getName()) { - return $this->redirectToRoute('part_new'); - } - - //Reload form, so the SIUnitType entries use the new part unit - $form = $this->createForm(PartBaseType::class, $part); - } elseif ($form->isSubmitted() && !$form->isValid()) { - $this->addFlash('error', 'part.edited_flash.invalid'); - } - - return $this->renderForm('parts/edit/edit_part_info.html.twig', - [ - 'part' => $part, - 'form' => $form, - ]); + return $this->renderPartForm('edit', $request, $part); } - /** - * @Route("/{id}/delete", name="part_delete", methods={"DELETE"}) - */ - public function delete(Request $request, Part $part, EntityManagerInterface $entityManager): RedirectResponse + #[Route(path: '/{id}/delete', name: 'part_delete', methods: ['DELETE'])] + public function delete(Request $request, Part $part): RedirectResponse { $this->denyAccessUnlessGranted('delete', $part); - if ($this->isCsrfTokenValid('delete'.$part->getId(), $request->request->get('_token'))) { + if ($this->isCsrfTokenValid('delete'.$part->getID(), $request->request->get('_token'))) { $this->commentHelper->setMessage($request->request->get('log_comment', null)); //Remove part - $entityManager->remove($part); + $this->em->remove($part); //Flush changes - $entityManager->flush(); + $this->em->flush(); $this->addFlash('success', 'part.deleted'); } @@ -210,23 +155,22 @@ class PartController extends AbstractController return $this->redirectToRoute('homepage'); } - /** - * @Route("/new", name="part_new") - * @Route("/{id}/clone", name="part_clone") - * @Route("/new_build_part/{project_id}", name="part_new_build_part") - * @ParamConverter("part", options={"id" = "id"}) - * @ParamConverter("project", options={"id" = "project_id"}) - */ + #[Route(path: '/new', name: 'part_new')] + #[Route(path: '/{id}/clone', name: 'part_clone')] + #[Route(path: '/new_build_part/{project_id}', name: 'part_new_build_part')] public function new(Request $request, EntityManagerInterface $em, TranslatorInterface $translator, AttachmentSubmitHandler $attachmentSubmitHandler, ProjectBuildPartHelper $projectBuildPartHelper, - ?Part $part = null, ?Project $project = null): Response + #[MapEntity(mapping: ['id' => 'id'])] ?Part $part = null, + #[MapEntity(mapping: ['project_id' => 'id'])] ?Project $project = null): Response { - if ($part) { //Clone part + if ($part instanceof Part) { + //Clone part $new_part = clone $part; - } else if ($project) { //Initialize a new part for a build part from the given project + } elseif ($project instanceof Project) { + //Initialize a new part for a build part from the given project //Ensure that the project has not already a build part - if ($project->getBuildPart() !== null) { + if ($project->getBuildPart() instanceof Part) { $this->addFlash('error', 'part.new_build_part.error.build_part_already_exists'); return $this->redirectToRoute('part_edit', ['id' => $project->getBuildPart()->getID()]); } @@ -239,7 +183,7 @@ class PartController extends AbstractController $cid = $request->get('category', null); $category = $cid ? $em->find(Category::class, $cid) : null; - if (null !== $category && null === $new_part->getCategory()) { + if ($category instanceof Category && !$new_part->getCategory() instanceof Category) { $new_part->setCategory($category); $new_part->setDescription($category->getDefaultDescription()); $new_part->setComment($category->getDefaultComment()); @@ -247,19 +191,19 @@ class PartController extends AbstractController $fid = $request->get('footprint', null); $footprint = $fid ? $em->find(Footprint::class, $fid) : null; - if (null !== $footprint && null === $new_part->getFootprint()) { + if ($footprint instanceof Footprint && !$new_part->getFootprint() instanceof Footprint) { $new_part->setFootprint($footprint); } $mid = $request->get('manufacturer', null); $manufacturer = $mid ? $em->find(Manufacturer::class, $mid) : null; - if (null !== $manufacturer && null === $new_part->getManufacturer()) { + if ($manufacturer instanceof Manufacturer && !$new_part->getManufacturer() instanceof Manufacturer) { $new_part->setManufacturer($manufacturer); } $store_id = $request->get('storelocation', null); - $storelocation = $store_id ? $em->find(Storelocation::class, $store_id) : null; - if (null !== $storelocation && $new_part->getPartLots()->isEmpty()) { + $storelocation = $store_id ? $em->find(StorageLocation::class, $store_id) : null; + if ($storelocation instanceof StorageLocation && $new_part->getPartLots()->isEmpty()) { $partLot = new PartLot(); $partLot->setStorageLocation($storelocation); $partLot->setInstockUnknown(true); @@ -268,13 +212,91 @@ class PartController extends AbstractController $supplier_id = $request->get('supplier', null); $supplier = $supplier_id ? $em->find(Supplier::class, $supplier_id) : null; - if (null !== $supplier && $new_part->getOrderdetails()->isEmpty()) { + if ($supplier instanceof Supplier && $new_part->getOrderdetails()->isEmpty()) { $orderdetail = new Orderdetail(); $orderdetail->setSupplier($supplier); $new_part->addOrderdetail($orderdetail); } - $form = $this->createForm(PartBaseType::class, $new_part); + return $this->renderPartForm('new', $request, $new_part); + } + + #[Route('/from_info_provider/{providerKey}/{providerId}/create', name: 'info_providers_create_part', requirements: ['providerId' => '.+'])] + public function createFromInfoProvider(Request $request, string $providerKey, string $providerId, PartInfoRetriever $infoRetriever): Response + { + $this->denyAccessUnlessGranted('@info_providers.create_parts'); + + $dto = $infoRetriever->getDetails($providerKey, $providerId); + $new_part = $infoRetriever->dtoToPart($dto); + + if ($new_part->getCategory() === null || $new_part->getCategory()->getID() === null) { + $this->addFlash('warning', t("part.create_from_info_provider.no_category_yet")); + } + + return $this->renderPartForm('new', $request, $new_part, [ + 'info_provider_dto' => $dto, + ]); + } + + #[Route('/{target}/merge/{other}', name: 'part_merge')] + public function merge(Request $request, Part $target, Part $other, PartMerger $partMerger): Response + { + $this->denyAccessUnlessGranted('edit', $target); + $this->denyAccessUnlessGranted('delete', $other); + + //Save the old name of the target part for the template + $target_name = $target->getName(); + + $this->addFlash('notice', t('part.merge.flash.please_review')); + + $merged = $partMerger->merge($target, $other); + return $this->renderPartForm('merge', $request, $merged, [], [ + 'tname_before' => $target_name, + 'other_part' => $other, + ]); + } + + #[Route(path: '/{id}/from_info_provider/{providerKey}/{providerId}/update', name: 'info_providers_update_part', requirements: ['providerId' => '.+'])] + public function updateFromInfoProvider(Part $part, Request $request, string $providerKey, string $providerId, + PartInfoRetriever $infoRetriever, PartMerger $partMerger): Response + { + $this->denyAccessUnlessGranted('edit', $part); + $this->denyAccessUnlessGranted('@info_providers.create_parts'); + + //Save the old name of the target part for the template + $old_name = $part->getName(); + + $dto = $infoRetriever->getDetails($providerKey, $providerId); + $provider_part = $infoRetriever->dtoToPart($dto); + + $part = $partMerger->merge($part, $provider_part); + + $this->addFlash('notice', t('part.merge.flash.please_review')); + + return $this->renderPartForm('update_from_ip', $request, $part, [ + 'info_provider_dto' => $dto, + ], [ + 'tname_before' => $old_name + ]); + } + + /** + * This function provides a common implementation for methods, which use the part form. + * @param Request $request + * @param Part $data + * @param array $form_options + * @return Response + */ + private function renderPartForm(string $mode, Request $request, Part $data, array $form_options = [], array $merge_infos = []): Response + { + //Ensure that mode is either 'new' or 'edit + if (!in_array($mode, ['new', 'edit', 'merge', 'update_from_ip'], true)) { + throw new \InvalidArgumentException('Invalid mode given'); + } + + $new_part = $data; + + $form = $this->createForm(PartBaseType::class, $new_part, $form_options); $form->handleRequest($request); @@ -283,26 +305,37 @@ class PartController extends AbstractController $attachments = $form['attachments']; foreach ($attachments as $attachment) { /** @var FormInterface $attachment */ - $options = [ - 'secure_attachment' => $attachment['secureFile']->getData(), - 'download_url' => $attachment['downloadURL']->getData(), - ]; try { - $attachmentSubmitHandler->handleFormSubmit($attachment->getData(), $attachment['file']->getData(), $options); + $this->attachmentSubmitHandler->handleUpload($attachment->getData(), AttachmentUpload::fromAttachmentForm($attachment)); } catch (AttachmentDownloadException $attachmentDownloadException) { $this->addFlash( 'error', - $translator->trans('attachment.download_failed').' '.$attachmentDownloadException->getMessage() + $this->translator->trans('attachment.download_failed').' '.$attachmentDownloadException->getMessage() ); } } + //Ensure that the master picture is still part of the attachments + if ($new_part->getMasterPictureAttachment() !== null && !$new_part->getAttachments()->contains($new_part->getMasterPictureAttachment())) { + $new_part->setMasterPictureAttachment(null); + } + $this->commentHelper->setMessage($form['log_comment']->getData()); - $em->persist($new_part); - $em->flush(); - $this->addFlash('success', 'part.created_flash'); + $this->em->persist($new_part); + + //When we are in merge mode, we have to remove the other part + if ($mode === 'merge') { + $this->em->remove($merge_infos['other_part']); + } + + $this->em->flush(); + if ($mode === 'new') { + $this->addFlash('success', 'part.created_flash'); + } elseif ($mode === 'edit') { + $this->addFlash('success', 'part.edited_flash'); + } //If a redirect URL was given, redirect there if ($request->query->get('_redirect')) { @@ -326,30 +359,44 @@ class PartController extends AbstractController $this->addFlash('error', 'part.created_flash.invalid'); } - return $this->renderForm('parts/edit/new_part.html.twig', + $template = ''; + if ($mode === 'new') { + $template = 'parts/edit/new_part.html.twig'; + } elseif ($mode === 'edit') { + $template = 'parts/edit/edit_part_info.html.twig'; + } elseif ($mode === 'merge') { + $template = 'parts/edit/merge_parts.html.twig'; + } elseif ($mode === 'update_from_ip') { + $template = 'parts/edit/update_from_ip.html.twig'; + } + + return $this->render($template, [ 'part' => $new_part, 'form' => $form, + 'merge_old_name' => $merge_infos['tname_before'] ?? null, + 'merge_other' => $merge_infos['other_part'] ?? null ]); } - /** - * @Route("/{id}/add_withdraw", name="part_add_withdraw", methods={"POST"}) - */ + + #[Route(path: '/{id}/add_withdraw', name: 'part_add_withdraw', methods: ['POST'])] public function withdrawAddHandler(Part $part, Request $request, EntityManagerInterface $em, PartLotWithdrawAddHelper $withdrawAddHelper): Response { if ($this->isCsrfTokenValid('part_withraw' . $part->getID(), $request->request->get('_csfr'))) { //Retrieve partlot from the request $partLot = $em->find(PartLot::class, $request->request->get('lot_id')); - if($partLot === null) { + if(!$partLot instanceof PartLot) { throw new \RuntimeException('Part lot not found!'); } //Ensure that the partlot belongs to the part if($partLot->getPart() !== $part) { throw new \RuntimeException("The origin partlot does not belong to the part!"); } - //Try to determine the target lot (used for move actions) - $targetLot = $em->find(PartLot::class, $request->request->get('target_id')); + + //Try to determine the target lot (used for move actions), if the parameter is existing + $targetId = $request->request->get('target_id', null); + $targetLot = $targetId ? $em->find(PartLot::class, $targetId) : null; if ($targetLot && $targetLot->getPart() !== $part) { throw new \RuntimeException("The target partlot does not belong to the part!"); } @@ -358,25 +405,48 @@ class PartController extends AbstractController $amount = (float) $request->request->get('amount'); $comment = $request->request->get('comment'); $action = $request->request->get('action'); + $delete_lot_if_empty = $request->request->getBoolean('delete_lot_if_empty', false); + $timestamp = null; + $timestamp_str = $request->request->getString('timestamp', ''); + //Try to parse the timestamp + if($timestamp_str !== '') { + $timestamp = new DateTime($timestamp_str); + } + //Ensure that the timestamp is not in the future + if($timestamp !== null && $timestamp > new DateTime("+20min")) { + throw new \LogicException("The timestamp must not be in the future!"); + } - switch ($action) { - case "withdraw": - case "remove": - $this->denyAccessUnlessGranted('withdraw', $partLot); - $withdrawAddHelper->withdraw($partLot, $amount, $comment); - break; - case "add": - $this->denyAccessUnlessGranted('add', $partLot); - $withdrawAddHelper->add($partLot, $amount, $comment); - break; - case "move": - $this->denyAccessUnlessGranted('move', $partLot); - $withdrawAddHelper->move($partLot, $targetLot, $amount, $comment); - break; - default: - throw new \RuntimeException("Unknown action!"); + //Ensure that the amount is not null or negative + if ($amount <= 0) { + $this->addFlash('warning', 'part.withdraw.zero_amount'); + goto err; + } + + try { + switch ($action) { + case "withdraw": + case "remove": + $this->denyAccessUnlessGranted('withdraw', $partLot); + $withdrawAddHelper->withdraw($partLot, $amount, $comment, $timestamp, $delete_lot_if_empty); + break; + case "add": + $this->denyAccessUnlessGranted('add', $partLot); + $withdrawAddHelper->add($partLot, $amount, $comment, $timestamp); + break; + case "move": + $this->denyAccessUnlessGranted('move', $partLot); + $this->denyAccessUnlessGranted('move', $targetLot); + $withdrawAddHelper->move($partLot, $targetLot, $amount, $comment, $timestamp, $delete_lot_if_empty); + break; + default: + throw new \RuntimeException("Unknown action!"); + } + } catch (AccessDeniedException) { + $this->addFlash('error', t('part.withdraw.access_denied')); + goto err; } //Save the changes to the DB @@ -387,7 +457,8 @@ class PartController extends AbstractController $this->addFlash('error', 'CSRF Token invalid!'); } - //If an redirect was passed, then redirect there + err: + //If a redirect was passed, then redirect there if($request->request->get('_redirect')) { return $this->redirect($request->request->get('_redirect')); } diff --git a/src/Controller/PartImportExportController.php b/src/Controller/PartImportExportController.php new file mode 100644 index 00000000..45f90d75 --- /dev/null +++ b/src/Controller/PartImportExportController.php @@ -0,0 +1,127 @@ +. + */ +namespace App\Controller; + +use App\Entity\Parts\Part; +use App\Form\AdminPages\ImportType; +use App\Services\ImportExportSystem\EntityExporter; +use App\Services\ImportExportSystem\EntityImporter; +use App\Services\LogSystem\EventCommentHelper; +use App\Services\Parts\PartsTableActionHandler; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Serializer\Exception\NotNormalizableValueException; +use UnexpectedValueException; + +class PartImportExportController extends AbstractController +{ + public function __construct(private readonly PartsTableActionHandler $partsTableActionHandler, private readonly EntityImporter $entityImporter, private readonly EventCommentHelper $commentHelper) + { + } + + #[Route(path: '/parts/import', name: 'parts_import')] + public function importParts(Request $request): Response + { + $this->denyAccessUnlessGranted('@parts.import'); + + $import_form = $this->createForm(ImportType::class, ['entity_class' => Part::class]); + $import_form->handleRequest($request); + + if ($import_form->isSubmitted() && $import_form->isValid()) { + /** @var UploadedFile $file */ + $file = $import_form['file']->getData(); + $data = $import_form->getData(); + + if ($data['format'] === 'auto') { + $format = $this->entityImporter->determineFormat($file->getClientOriginalExtension()); + if (null === $format) { + $this->addFlash('error', 'parts.import.flash.error.unknown_format'); + goto ret; + } + } else { + $format = $data['format']; + } + + $options = [ + 'create_unknown_datastructures' => $data['create_unknown_datastructures'], + 'path_delimiter' => $data['path_delimiter'], + 'format' => $format, + 'part_category' => $data['part_category'], + 'class' => Part::class, + 'csv_delimiter' => $data['csv_delimiter'], + 'part_needs_review' => $data['part_needs_review'], + 'abort_on_validation_error' => $data['abort_on_validation_error'], + ]; + + $this->commentHelper->setMessage('Import '.$file->getClientOriginalName()); + + $entities = []; + + try { + $errors = $this->entityImporter->importFileAndPersistToDB($file, $options, $entities); + } catch (UnexpectedValueException $e) { + $this->addFlash('error', 'parts.import.flash.error.invalid_file'); + if ($e instanceof NotNormalizableValueException) { + $this->addFlash('error', $e->getMessage()); + } + goto ret; + } + + if (!isset($errors) || $errors) { + $this->addFlash('error', 'parts.import.flash.error'); + } else { + $this->addFlash('success', 'parts.import.flash.success'); + } + } + + + ret: + return $this->render('parts/import/parts_import.html.twig', [ + 'import_form' => $import_form, + 'imported_entities' => $entities ?? [], + 'import_errors' => $errors ?? [], + ]); + } + + #[Route(path: '/parts/export', name: 'parts_export', methods: ['GET'])] + public function exportParts(Request $request, EntityExporter $entityExporter): Response + { + $ids = $request->query->get('ids', ''); + $parts = $this->partsTableActionHandler->idStringToArray($ids); + + if (count($parts) === 0) { + $this->addFlash('error', 'entity.export.flash.error.no_entities'); + return $this->redirectToRoute('homepage'); + } + + //Ensure that we have access to the parts + foreach ($parts as $part) { + $this->denyAccessUnlessGranted('read', $part); + } + + return $entityExporter->exportEntityFromRequest($parts, $request); + } +} diff --git a/src/Controller/PartListsController.php b/src/Controller/PartListsController.php index 77e5bef2..48995228 100644 --- a/src/Controller/PartListsController.php +++ b/src/Controller/PartListsController.php @@ -22,42 +22,40 @@ declare(strict_types=1); namespace App\Controller; +use App\DataTables\ErrorDataTable; use App\DataTables\Filters\PartFilter; use App\DataTables\Filters\PartSearchFilter; use App\DataTables\PartsDataTable; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\Part; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; +use App\Exceptions\InvalidRegexException; use App\Form\Filters\PartFilterType; use App\Services\Parts\PartsTableActionHandler; use App\Services\Trees\NodesListBuilder; +use Doctrine\DBAL\Exception\DriverException; use Doctrine\ORM\EntityManagerInterface; use Omines\DataTablesBundle\DataTableFactory; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\FormInterface; -use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Translation\TranslatableMessage; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function Symfony\Component\Translation\t; class PartListsController extends AbstractController { - private EntityManagerInterface $entityManager; - private NodesListBuilder $nodesListBuilder; - private DataTableFactory $dataTableFactory; - - public function __construct(EntityManagerInterface $entityManager, NodesListBuilder $nodesListBuilder, DataTableFactory $dataTableFactory) + public function __construct(private readonly EntityManagerInterface $entityManager, private readonly NodesListBuilder $nodesListBuilder, private readonly DataTableFactory $dataTableFactory, private readonly TranslatorInterface $translator) { - $this->entityManager = $entityManager; - $this->nodesListBuilder = $nodesListBuilder; - $this->dataTableFactory = $dataTableFactory; } - /** - * @Route("/table/action", name="table_action", methods={"POST"}) - */ + #[Route(path: '/table/action', name: 'table_action', methods: ['POST'])] public function tableAction(Request $request, PartsTableActionHandler $actionHandler): Response { $this->denyAccessUnlessGranted('@parts.edit'); @@ -66,6 +64,7 @@ class PartListsController extends AbstractController $ids = $request->request->get('ids'); $action = $request->request->get('action'); $target = $request->request->get('target'); + $redirectResponse = null; if (!$this->isCsrfTokenValid('table_action', $request->request->get('_token'))) { $this->addFlash('error', 'csfr_invalid'); @@ -76,17 +75,36 @@ class PartListsController extends AbstractController if (null === $action || null === $ids) { $this->addFlash('error', 'part.table.actions.no_params_given'); } else { + $errors = []; + $parts = $actionHandler->idStringToArray($ids); - $redirectResponse = $actionHandler->handleAction($action, $parts, $target ? (int) $target : null, $redirect); + $redirectResponse = $actionHandler->handleAction($action, $parts, $target ? (int) $target : null, $redirect, $errors); //Save changes $this->entityManager->flush(); - $this->addFlash('success', 'part.table.actions.success'); + if (count($errors) === 0) { + $this->addFlash('success', 'part.table.actions.success'); + } else { + $this->addFlash('error', t('part.table.actions.error', ['%count%' => count($errors)])); + //Create a flash message for each error + foreach ($errors as $error) { + /** @var Part $part */ + $part = $error['part']; + + $this->addFlash('error', + t('part.table.actions.error_detail', [ + '%part_name%' => $part->getName(), + '%part_id%' => $part->getID(), + '%message%' => $error['message'] + ]) + ); + } + } } //If the action handler returned a response, we use it, otherwise we redirect back to the previous page. - if (isset($redirectResponse) && $redirectResponse instanceof Response) { + if ($redirectResponse !== null) { return $redirectResponse; } @@ -95,8 +113,6 @@ class PartListsController extends AbstractController /** * Disable the given form interface after creation of the form by removing and reattaching the form. - * @param FormInterface $form - * @return void */ private function disableFormFieldAfterCreation(FormInterface $form, bool $disabled = true): void { @@ -104,12 +120,12 @@ class PartListsController extends AbstractController $attrs['disabled'] = $disabled; $parent = $form->getParent(); - if ($parent === null) { + if (!$parent instanceof FormInterface) { throw new \RuntimeException('This function can only be used on form fields that are children of another form!'); } $parent->remove($form->getName()); - $parent->add($form->getName(), get_class($form->getConfig()->getType()->getInnerType()), $attrs); + $parent->add($form->getName(), $form->getConfig()->getType()->getInnerType()::class, $attrs); } /** @@ -120,7 +136,6 @@ class PartListsController extends AbstractController * @param callable|null $form_changer A function that is called with the form object as parameter. This function can be used to customize the form * @param array $additonal_template_vars Any additional template variables that should be passed to the template * @param array $additional_table_vars Any additional variables that should be passed to the table creation - * @return Response */ protected function showListWithFilter(Request $request, string $template, ?callable $filter_changer = null, ?callable $form_changer = null, array $additonal_template_vars = [], array $additional_table_vars = []): Response { @@ -140,11 +155,29 @@ class PartListsController extends AbstractController $filterForm->handleRequest($formRequest); - $table = $this->dataTableFactory->createFromType(PartsDataTable::class, array_merge(['filter' => $filter], $additional_table_vars)) + $table = $this->dataTableFactory->createFromType( + PartsDataTable::class, + array_merge(['filter' => $filter], $additional_table_vars), + ['lengthMenu' => PartsDataTable::LENGTH_MENU] + ) ->handleRequest($request); if ($table->isCallback()) { - return $table->getResponse(); + try { + try { + return $table->getResponse(); + } catch (DriverException $driverException) { + if ($driverException->getCode() === 1139) { + //Convert the driver exception to InvalidRegexException so it has the same hanlder as for SQLite + throw InvalidRegexException::fromDriverException($driverException); + } else { + throw $driverException; + } + } + } catch (InvalidRegexException $exception) { + $errors = $this->translator->trans('part.table.invalid_regex').': '.$exception->getReason(); + return ErrorDataTable::errorTable($this->dataTableFactory, $request, $errors); + } } return $this->render($template, array_merge([ @@ -153,19 +186,15 @@ class PartListsController extends AbstractController ], $additonal_template_vars)); } - /** - * @Route("/category/{id}/parts", name="part_list_category") - * - * @return JsonResponse|Response - */ - public function showCategory(Category $category, Request $request) + #[Route(path: '/category/{id}/parts', name: 'part_list_category')] + public function showCategory(Category $category, Request $request): Response { $this->denyAccessUnlessGranted('@categories.read'); return $this->showListWithFilter($request, 'parts/lists/category_list.html.twig', function (PartFilter $filter) use ($category) { - $filter->getCategory()->setOperator('INCLUDING_CHILDREN')->setValue($category); + $filter->category->setOperator('INCLUDING_CHILDREN')->setValue($category); }, function (FormInterface $filterForm) { $this->disableFormFieldAfterCreation($filterForm->get('category')->get('value')); }, [ @@ -175,19 +204,15 @@ class PartListsController extends AbstractController ); } - /** - * @Route("/footprint/{id}/parts", name="part_list_footprint") - * - * @return JsonResponse|Response - */ - public function showFootprint(Footprint $footprint, Request $request) + #[Route(path: '/footprint/{id}/parts', name: 'part_list_footprint')] + public function showFootprint(Footprint $footprint, Request $request): Response { $this->denyAccessUnlessGranted('@footprints.read'); return $this->showListWithFilter($request, 'parts/lists/footprint_list.html.twig', function (PartFilter $filter) use ($footprint) { - $filter->getFootprint()->setOperator('INCLUDING_CHILDREN')->setValue($footprint); + $filter->footprint->setOperator('INCLUDING_CHILDREN')->setValue($footprint); }, function (FormInterface $filterForm) { $this->disableFormFieldAfterCreation($filterForm->get('footprint')->get('value')); }, [ @@ -197,19 +222,15 @@ class PartListsController extends AbstractController ); } - /** - * @Route("/manufacturer/{id}/parts", name="part_list_manufacturer") - * - * @return JsonResponse|Response - */ - public function showManufacturer(Manufacturer $manufacturer, Request $request) + #[Route(path: '/manufacturer/{id}/parts', name: 'part_list_manufacturer')] + public function showManufacturer(Manufacturer $manufacturer, Request $request): Response { $this->denyAccessUnlessGranted('@manufacturers.read'); return $this->showListWithFilter($request, 'parts/lists/manufacturer_list.html.twig', function (PartFilter $filter) use ($manufacturer) { - $filter->getManufacturer()->setOperator('INCLUDING_CHILDREN')->setValue($manufacturer); + $filter->manufacturer->setOperator('INCLUDING_CHILDREN')->setValue($manufacturer); }, function (FormInterface $filterForm) { $this->disableFormFieldAfterCreation($filterForm->get('manufacturer')->get('value')); }, [ @@ -219,41 +240,33 @@ class PartListsController extends AbstractController ); } - /** - * @Route("/store_location/{id}/parts", name="part_list_store_location") - * - * @return JsonResponse|Response - */ - public function showStorelocation(Storelocation $storelocation, Request $request) + #[Route(path: '/store_location/{id}/parts', name: 'part_list_store_location')] + public function showStorelocation(StorageLocation $storelocation, Request $request): Response { $this->denyAccessUnlessGranted('@storelocations.read'); return $this->showListWithFilter($request, 'parts/lists/store_location_list.html.twig', function (PartFilter $filter) use ($storelocation) { - $filter->getStorelocation()->setOperator('INCLUDING_CHILDREN')->setValue($storelocation); + $filter->storelocation->setOperator('INCLUDING_CHILDREN')->setValue($storelocation); }, function (FormInterface $filterForm) { $this->disableFormFieldAfterCreation($filterForm->get('storelocation')->get('value')); }, [ 'entity' => $storelocation, - 'repo' => $this->entityManager->getRepository(Storelocation::class), + 'repo' => $this->entityManager->getRepository(StorageLocation::class), ] ); } - /** - * @Route("/supplier/{id}/parts", name="part_list_supplier") - * - * @return JsonResponse|Response - */ - public function showSupplier(Supplier $supplier, Request $request) + #[Route(path: '/supplier/{id}/parts', name: 'part_list_supplier')] + public function showSupplier(Supplier $supplier, Request $request): Response { $this->denyAccessUnlessGranted('@suppliers.read'); return $this->showListWithFilter($request, 'parts/lists/supplier_list.html.twig', function (PartFilter $filter) use ($supplier) { - $filter->getSupplier()->setOperator('INCLUDING_CHILDREN')->setValue($supplier); + $filter->supplier->setOperator('INCLUDING_CHILDREN')->setValue($supplier); }, function (FormInterface $filterForm) { $this->disableFormFieldAfterCreation($filterForm->get('supplier')->get('value')); }, [ @@ -263,19 +276,15 @@ class PartListsController extends AbstractController ); } - /** - * @Route("/parts/by_tag/{tag}", name="part_list_tags", requirements={"tag": ".*"}) - * - * @return JsonResponse|Response - */ - public function showTag(string $tag, Request $request, DataTableFactory $dataTable) + #[Route(path: '/parts/by_tag/{tag}', name: 'part_list_tags', requirements: ['tag' => '.*'])] + public function showTag(string $tag, Request $request): Response { $tag = trim($tag); return $this->showListWithFilter($request, 'parts/lists/tags_list.html.twig', function (PartFilter $filter) use ($tag) { - $filter->getTags()->setOperator('ANY')->setValue($tag); + $filter->tags->setOperator('ANY')->setValue($tag); }, function (FormInterface $filterForm) { $this->disableFormFieldAfterCreation($filterForm->get('tags')->get('value')); }, [ @@ -288,28 +297,28 @@ class PartListsController extends AbstractController { $filter = new PartSearchFilter($request->query->get('keyword', '')); - $filter->setName($request->query->getBoolean('name', true)); - $filter->setCategory($request->query->getBoolean('category', true)); - $filter->setDescription($request->query->getBoolean('description', true)); - $filter->setTags($request->query->getBoolean('tags', true)); - $filter->setStorelocation($request->query->getBoolean('storelocation', true)); - $filter->setComment($request->query->getBoolean('comment', true)); - $filter->setOrdernr($request->query->getBoolean('ordernr', true)); - $filter->setSupplier($request->query->getBoolean('supplier', false)); - $filter->setManufacturer($request->query->getBoolean('manufacturer', false)); - $filter->setFootprint($request->query->getBoolean('footprint', false)); + //As an unchecked checkbox is not set in the query, the default value for all bools have to be false (which is the default argument value)! + $filter->setName($request->query->getBoolean('name')); + $filter->setCategory($request->query->getBoolean('category')); + $filter->setDescription($request->query->getBoolean('description')); + $filter->setMpn($request->query->getBoolean('mpn')); + $filter->setTags($request->query->getBoolean('tags')); + $filter->setStorelocation($request->query->getBoolean('storelocation')); + $filter->setComment($request->query->getBoolean('comment')); + $filter->setIPN($request->query->getBoolean('ipn')); + $filter->setOrdernr($request->query->getBoolean('ordernr')); + $filter->setSupplier($request->query->getBoolean('supplier')); + $filter->setManufacturer($request->query->getBoolean('manufacturer')); + $filter->setFootprint($request->query->getBoolean('footprint')); - $filter->setRegex($request->query->getBoolean('regex', false)); + + $filter->setRegex($request->query->getBoolean('regex')); return $filter; } - /** - * @Route("/parts/search", name="parts_search") - * - * @return JsonResponse|Response - */ - public function showSearch(Request $request, DataTableFactory $dataTable) + #[Route(path: '/parts/search', name: 'parts_search')] + public function showSearch(Request $request, DataTableFactory $dataTable): Response { $searchFilter = $this->searchRequestToFilter($request); @@ -327,12 +336,8 @@ class PartListsController extends AbstractController ); } - /** - * @Route("/parts", name="parts_show_all") - * - * @return JsonResponse|Response - */ - public function showAll(Request $request, DataTableFactory $dataTable) + #[Route(path: '/parts', name: 'parts_show_all')] + public function showAll(Request $request): Response { return $this->showListWithFilter($request,'parts/lists/all_list.html.twig'); } diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index 625e4ae1..761e498c 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -1,4 +1,7 @@ . */ - namespace App\Controller; use App\DataTables\ProjectBomEntriesDataTable; use App\Entity\Parts\Part; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; -use App\Form\ProjectSystem\ProjectBOMEntryCollectionType; +use App\Form\ProjectSystem\ProjectAddPartsType; use App\Form\ProjectSystem\ProjectBuildType; -use App\Form\Type\StructuralEntityType; use App\Helpers\Projects\ProjectBuildRequest; +use App\Services\ImportExportSystem\BOMImporter; use App\Services\ProjectSystem\ProjectBuildHelper; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; +use League\Csv\SyntaxError; use Omines\DataTablesBundle\DataTableFactory; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Validator\Validator\ValidatorInterface; -/** - * @Route("/project") - */ +use function Symfony\Component\Translation\t; + +#[Route(path: '/project')] class ProjectController extends AbstractController { - private DataTableFactory $dataTableFactory; - - public function __construct(DataTableFactory $dataTableFactory) + public function __construct(private readonly DataTableFactory $dataTableFactory) { - $this->dataTableFactory = $dataTableFactory; } - /** - * @Route("/{id}/info", name="project_info", requirements={"id"="\d+"}) - */ - public function info(Project $project, Request $request, ProjectBuildHelper $buildHelper) + #[Route(path: '/{id}/info', name: 'project_info', requirements: ['id' => '\d+'])] + public function info(Project $project, Request $request, ProjectBuildHelper $buildHelper): Response { $this->denyAccessUnlessGranted('read', $project); @@ -73,9 +73,7 @@ class ProjectController extends AbstractController ]); } - /** - * @Route("/{id}/build", name="project_build", requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}/build', name: 'project_build', requirements: ['id' => '\d+'])] public function build(Project $project, Request $request, ProjectBuildHelper $buildHelper, EntityManagerInterface $entityManager): Response { $this->denyAccessUnlessGranted('read', $project); @@ -105,12 +103,12 @@ class ProjectController extends AbstractController $request->get('_redirect', $this->generateUrl('project_info', ['id' => $project->getID()] ))); - } else { - $this->addFlash('error', 'project.build.flash.invalid_input'); } + + $this->addFlash('error', 'project.build.flash.invalid_input'); } - return $this->renderForm('projects/build/build.html.twig', [ + return $this->render('projects/build/build.html.twig', [ 'buildHelper' => $buildHelper, 'project' => $project, 'build_request' => $projectBuildRequest, @@ -119,37 +117,101 @@ class ProjectController extends AbstractController ]); } - /** - * @Route("/add_parts", name="project_add_parts_no_id") - * @Route("/{id}/add_parts", name="project_add_parts", requirements={"id"="\d+"}) - * @param Request $request - * @param Project|null $project - */ + #[Route(path: '/{id}/import_bom', name: 'project_import_bom', requirements: ['id' => '\d+'])] + public function importBOM(Request $request, EntityManagerInterface $entityManager, Project $project, + BOMImporter $BOMImporter, ValidatorInterface $validator): Response + { + $this->denyAccessUnlessGranted('edit', $project); + + $builder = $this->createFormBuilder(); + $builder->add('file', FileType::class, [ + 'label' => 'import.file', + 'required' => true, + 'attr' => [ + 'accept' => '.csv' + ] + ]); + $builder->add('type', ChoiceType::class, [ + 'label' => 'project.bom_import.type', + 'required' => true, + 'choices' => [ + 'project.bom_import.type.kicad_pcbnew' => 'kicad_pcbnew', + ] + ]); + $builder->add('clear_existing_bom', CheckboxType::class, [ + 'label' => 'project.bom_import.clear_existing_bom', + 'required' => false, + 'data' => false, + 'help' => 'project.bom_import.clear_existing_bom.help', + ]); + $builder->add('submit', SubmitType::class, [ + 'label' => 'import.btn', + ]); + + $form = $builder->getForm(); + + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + + //Clear existing BOM entries if requested + if ($form->get('clear_existing_bom')->getData()) { + $project->getBomEntries()->clear(); + $entityManager->flush(); + } + + try { + $entries = $BOMImporter->importFileIntoProject($form->get('file')->getData(), $project, [ + 'type' => $form->get('type')->getData(), + ]); + + //Validate the project entries + $errors = $validator->validateProperty($project, 'bom_entries'); + + //If no validation errors occured, save the changes and redirect to edit page + if (count ($errors) === 0) { + $this->addFlash('success', t('project.bom_import.flash.success', ['%count%' => count($entries)])); + $entityManager->flush(); + return $this->redirectToRoute('project_edit', ['id' => $project->getID()]); + } + + //When we get here, there were validation errors + $this->addFlash('error', t('project.bom_import.flash.invalid_entries')); + + } catch (\UnexpectedValueException|SyntaxError $e) { + $this->addFlash('error', t('project.bom_import.flash.invalid_file', ['%message%' => $e->getMessage()])); + } + } + + return $this->render('projects/import_bom.html.twig', [ + 'project' => $project, + 'form' => $form, + 'errors' => $errors ?? null, + ]); + } + + #[Route(path: '/add_parts', name: 'project_add_parts_no_id')] + #[Route(path: '/{id}/add_parts', name: 'project_add_parts', requirements: ['id' => '\d+'])] public function addPart(Request $request, EntityManagerInterface $entityManager, ?Project $project): Response { - if($project) { + if($project instanceof Project) { $this->denyAccessUnlessGranted('edit', $project); } else { $this->denyAccessUnlessGranted('@projects.edit'); } - $builder = $this->createFormBuilder(); - $builder->add('project', StructuralEntityType::class, [ - 'class' => Project::class, - 'required' => true, - 'disabled' => $project !== null, //If a project is given, disable the field - 'data' => $project, - 'constraints' => [ - new NotNull() - ] + $form = $this->createForm(ProjectAddPartsType::class, null, [ + 'project' => $project, ]); - $builder->add('bom_entries', ProjectBOMEntryCollectionType::class); - $builder->add('submit', SubmitType::class, ['label' => 'save']); - $form = $builder->getForm(); + //Preset the BOM entries with the selected parts, when the form was not submitted yet $preset_data = new ArrayCollection(); - foreach (explode(',', $request->get('parts', '')) as $part_id) { + foreach (explode(',', (string) $request->get('parts', '')) as $part_id) { + //Skip empty part IDs. Postgres seems to be especially sensitive to empty strings, as it does not allow them in integer columns + if ($part_id === '') { + continue; + } + $part = $entityManager->getRepository(Part::class)->find($part_id); if (null !== $part) { //If there is already a BOM entry for this part, we use this one (we edit it then) @@ -157,7 +219,7 @@ class ProjectController extends AbstractController 'project' => $project, 'part' => $part ]); - if ($bom_entry) { + if ($bom_entry !== null) { $preset_data->add($bom_entry); } else { //Otherwise create an empty one $entry = new ProjectBOMEntry(); @@ -181,8 +243,11 @@ class ProjectController extends AbstractController foreach ($bom_entries as $bom_entry){ $target_project->addBOMEntry($bom_entry); } + + $entityManager->flush(); + //If a redirect query parameter is set, redirect to this page if ($request->query->get('_redirect')) { return $this->redirect($request->query->get('_redirect')); @@ -191,9 +256,9 @@ class ProjectController extends AbstractController return $this->redirectToRoute('project_info', ['id' => $target_project->getID()]); } - return $this->renderForm('projects/add_parts.html.twig', [ + return $this->render('projects/add_parts.html.twig', [ 'project' => $project, 'form' => $form, ]); } -} \ No newline at end of file +} diff --git a/src/Controller/RedirectController.php b/src/Controller/RedirectController.php index 632399c1..65bd78f5 100644 --- a/src/Controller/RedirectController.php +++ b/src/Controller/RedirectController.php @@ -28,22 +28,15 @@ use function in_array; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Contracts\Translation\TranslatorInterface; +/** + * @see \App\Tests\Controller\RedirectControllerTest + */ class RedirectController extends AbstractController { - protected string $default_locale; - protected TranslatorInterface $translator; - protected SessionInterface $session; - protected bool $enforce_index_php; - - public function __construct(string $default_locale, TranslatorInterface $translator, SessionInterface $session, bool $enforce_index_php) + public function __construct(protected string $default_locale, protected TranslatorInterface $translator, protected bool $enforce_index_php) { - $this->default_locale = $default_locale; - $this->session = $session; - $this->translator = $translator; - $this->enforce_index_php = $enforce_index_php; } /** @@ -52,25 +45,27 @@ class RedirectController extends AbstractController */ public function addLocalePart(Request $request): RedirectResponse { - //By default we use the global default locale + //By default, we use the global default locale $locale = $this->default_locale; //Check if a user has set a preferred language setting: $user = $this->getUser(); - if (($user instanceof User) && !empty($user->getLanguage())) { + if (($user instanceof User) && ($user->getLanguage() !== null && $user->getLanguage() !== '')) { $locale = $user->getLanguage(); } - //$new_url = str_replace($request->getPathInfo(), '/' . $locale . $request->getPathInfo(), $request->getUri()); $new_url = $request->getUriForPath('/'.$locale.$request->getPathInfo()); //If either mod_rewrite is not enabled or the index.php version is enforced, add index.php to the string if (($this->enforce_index_php || !$this->checkIfModRewriteAvailable()) - && false === strpos($new_url, 'index.php')) { + && !str_contains($new_url, 'index.php')) { //Like Request::getUriForPath only with index.php $new_url = $request->getSchemeAndHttpHost().$request->getBaseUrl().'/index.php/'.$locale.$request->getPathInfo(); } + //Add the query string + $new_url .= $request->getQueryString() ? '?'.$request->getQueryString() : ''; + return $this->redirect($new_url); } @@ -78,6 +73,7 @@ class RedirectController extends AbstractController * Check if mod_rewrite is available (URL rewriting is possible). * If this is true, we can redirect to /en, otherwise we have to redirect to index.php/en. * When the PHP is not used via Apache SAPI, we just assume that URL rewriting is available. + * @noinspection PhpUndefinedFunctionInspection */ public function checkIfModRewriteAvailable(): bool { @@ -88,6 +84,6 @@ class RedirectController extends AbstractController } //Check if the mod_rewrite module is loaded - return in_array('mod_rewrite', apache_get_modules(), false); + return in_array('mod_rewrite', apache_get_modules(), true); } } diff --git a/src/Controller/ScanController.php b/src/Controller/ScanController.php index 8c0c9ad8..aebadd89 100644 --- a/src/Controller/ScanController.php +++ b/src/Controller/ScanController.php @@ -42,57 +42,65 @@ declare(strict_types=1); namespace App\Controller; use App\Form\LabelSystem\ScanDialogType; -use App\Services\LabelSystem\Barcodes\BarcodeNormalizer; -use App\Services\LabelSystem\Barcodes\BarcodeRedirector; +use App\Services\LabelSystem\BarcodeScanner\BarcodeRedirector; +use App\Services\LabelSystem\BarcodeScanner\BarcodeScanHelper; +use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType; +use App\Services\LabelSystem\BarcodeScanner\LocalBarcodeScanResult; use Doctrine\ORM\EntityNotFoundException; use InvalidArgumentException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; +use Symfony\Component\Routing\Attribute\Route; /** - * @Route("/scan") + * @see \App\Tests\Controller\ScanControllerTest */ +#[Route(path: '/scan')] class ScanController extends AbstractController { - protected BarcodeRedirector $barcodeParser; - protected BarcodeNormalizer $barcodeNormalizer; - - public function __construct(BarcodeRedirector $barcodeParser, BarcodeNormalizer $barcodeNormalizer) + public function __construct(protected BarcodeRedirector $barcodeParser, protected BarcodeScanHelper $barcodeNormalizer) { - $this->barcodeParser = $barcodeParser; - $this->barcodeNormalizer = $barcodeNormalizer; } - /** - * @Route("", name="scan_dialog") - */ - public function dialog(Request $request): Response + #[Route(path: '', name: 'scan_dialog')] + public function dialog(Request $request, #[MapQueryParameter] ?string $input = null): Response { $this->denyAccessUnlessGranted('@tools.label_scanner'); $form = $this->createForm(ScanDialogType::class); $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { + if ($input === null && $form->isSubmitted() && $form->isValid()) { $input = $form['input']->getData(); + $mode = $form['mode']->getData(); + } + $infoModeData = null; + + if ($input !== null) { try { - [$type, $id] = $this->barcodeNormalizer->normalizeBarcodeContent($input); + $scan_result = $this->barcodeNormalizer->scanBarcodeContent($input, $mode ?? null); + //Perform a redirect if the info mode is not enabled + if (!$form['info_mode']->getData()) { + try { + return $this->redirect($this->barcodeParser->getRedirectURL($scan_result)); + } catch (EntityNotFoundException) { + $this->addFlash('success', 'scan.qr_not_found'); + } + } else { //Otherwise retrieve infoModeData + $infoModeData = $scan_result->getDecodedForInfoMode(); - try { - return $this->redirect($this->barcodeParser->getRedirectURL($type, $id)); - } catch (EntityNotFoundException $exception) { - $this->addFlash('success', 'scan.qr_not_found'); } - } catch (InvalidArgumentException $exception) { + } catch (InvalidArgumentException) { $this->addFlash('error', 'scan.format_unknown'); } } - return $this->renderForm('label_system/scanner/scanner.html.twig', [ + return $this->render('label_system/scanner/scanner.html.twig', [ 'form' => $form, + 'infoModeData' => $infoModeData, ]); } @@ -101,11 +109,24 @@ class ScanController extends AbstractController */ public function scanQRCode(string $type, int $id): Response { + $type = strtolower($type); + try { $this->addFlash('success', 'scan.qr_success'); - return $this->redirect($this->barcodeParser->getRedirectURL($type, $id)); - } catch (EntityNotFoundException $exception) { + if (!isset(BarcodeScanHelper::QR_TYPE_MAP[$type])) { + throw new InvalidArgumentException('Unknown type: '.$type); + } + //Construct the scan result manually, as we don't have a barcode here + $scan_result = new LocalBarcodeScanResult( + target_type: BarcodeScanHelper::QR_TYPE_MAP[$type], + target_id: $id, + //The routes are only used on the internal generated QR codes + source_type: BarcodeSourceType::INTERNAL + ); + + return $this->redirect($this->barcodeParser->getRedirectURL($scan_result)); + } catch (EntityNotFoundException) { $this->addFlash('success', 'scan.qr_not_found'); return $this->redirectToRoute('homepage'); diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 026e18c1..4e2077c4 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -39,7 +39,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; @@ -48,18 +48,11 @@ use Symfony\Contracts\Translation\TranslatorInterface; class SecurityController extends AbstractController { - protected TranslatorInterface $translator; - protected bool $allow_email_pw_reset; - - public function __construct(TranslatorInterface $translator, bool $allow_email_pw_reset) + public function __construct(protected TranslatorInterface $translator, protected bool $allow_email_pw_reset) { - $this->translator = $translator; - $this->allow_email_pw_reset = $allow_email_pw_reset; } - /** - * @Route("/login", name="login", methods={"GET", "POST"}) - */ + #[Route(path: '/login', name: 'login', methods: ['GET', 'POST'])] public function login(AuthenticationUtils $authenticationUtils): Response { // get the login error if there is one @@ -75,11 +68,10 @@ class SecurityController extends AbstractController } /** - * @Route("/pw_reset/request", name="pw_reset_request") - * * @return RedirectResponse|Response */ - public function requestPwReset(PasswordResetManager $passwordReset, Request $request) + #[Route(path: '/pw_reset/request', name: 'pw_reset_request')] + public function requestPwReset(PasswordResetManager $passwordReset, Request $request): RedirectResponse|Response { if (!$this->allow_email_pw_reset) { throw new AccessDeniedHttpException('The password reset via email is disabled!'); @@ -113,17 +105,16 @@ class SecurityController extends AbstractController return $this->redirectToRoute('login'); } - return $this->renderForm('security/pw_reset_request.html.twig', [ + return $this->render('security/pw_reset_request.html.twig', [ 'form' => $form, ]); } /** - * @Route("/pw_reset/new_pw/{user}/{token}", name="pw_reset_new_pw") - * * @return RedirectResponse|Response */ - public function pwResetNewPw(PasswordResetManager $passwordReset, Request $request, EntityManagerInterface $em, EventDispatcherInterface $eventDispatcher, ?string $user = null, ?string $token = null) + #[Route(path: '/pw_reset/new_pw/{user}/{token}', name: 'pw_reset_new_pw')] + public function pwResetNewPw(PasswordResetManager $passwordReset, Request $request, EntityManagerInterface $em, EventDispatcherInterface $eventDispatcher, ?string $user = null, ?string $token = null): RedirectResponse|Response { if (!$this->allow_email_pw_reset) { throw new AccessDeniedHttpException('The password reset via email is disabled!'); @@ -150,6 +141,7 @@ class SecurityController extends AbstractController 'type' => PasswordType::class, 'first_options' => [ 'label' => 'user.settings.pw_new.label', + 'password_estimator' => true, ], 'second_options' => [ 'label' => 'user.settings.pw_confirm.label', @@ -178,7 +170,7 @@ class SecurityController extends AbstractController $this->addFlash('success', 'pw_reset.new_pw.success'); $repo = $em->getRepository(User::class); - $u = $repo->findOneBy(['name' => $data['username']]); + $u = $repo->findByUsername($data['username']); $event = new SecurityEvent($u); /** @var EventDispatcher $eventDispatcher */ $eventDispatcher->dispatch($event, SecurityEvents::PASSWORD_RESET); @@ -187,15 +179,13 @@ class SecurityController extends AbstractController } } - return $this->renderForm('security/pw_reset_new_pw.html.twig', [ + return $this->render('security/pw_reset_new_pw.html.twig', [ 'form' => $form, ]); } - /** - * @Route("/logout", name="logout") - */ - public function logout(): void + #[Route(path: '/logout', name: 'logout')] + public function logout(): never { throw new RuntimeException('Will be intercepted before getting here'); } diff --git a/src/Controller/SelectAPIController.php b/src/Controller/SelectAPIController.php index 0f30b648..c1e682c8 100644 --- a/src/Controller/SelectAPIController.php +++ b/src/Controller/SelectAPIController.php @@ -1,4 +1,7 @@ . */ - namespace App\Controller; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractStructuralDBElement; -use App\Entity\Contracts\NamedElementInterface; use App\Entity\LabelSystem\LabelProfile; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; +use App\Entity\Parts\StorageLocation; use App\Entity\ProjectSystem\Project; use App\Form\Type\Helper\StructuralEntityChoiceHelper; use App\Services\Trees\NodesListBuilder; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Contracts\Translation\TranslatorInterface; /** - * @Route("/select_api") - * * This endpoint is used by the select2 library to dynamically load data (used in the multiselect action helper in parts lists) */ +#[Route(path: '/select_api')] class SelectAPIController extends AbstractController { - private NodesListBuilder $nodesListBuilder; - private TranslatorInterface $translator; - private StructuralEntityChoiceHelper $choiceHelper; - - public function __construct(NodesListBuilder $nodesListBuilder, TranslatorInterface $translator, StructuralEntityChoiceHelper $choiceHelper) + public function __construct(private readonly NodesListBuilder $nodesListBuilder, private readonly TranslatorInterface $translator, private readonly StructuralEntityChoiceHelper $choiceHelper) { - $this->nodesListBuilder = $nodesListBuilder; - $this->translator = $translator; - $this->choiceHelper = $choiceHelper; } - /** - * @Route("/category", name="select_category") - */ + #[Route(path: '/category', name: 'select_category')] public function category(): Response { return $this->getResponseForClass(Category::class); } - /** - * @Route("/footprint", name="select_footprint") - */ + #[Route(path: '/footprint', name: 'select_footprint')] public function footprint(): Response { return $this->getResponseForClass(Footprint::class, true); } - /** - * @Route("/manufacturer", name="select_manufacturer") - */ + #[Route(path: '/manufacturer', name: 'select_manufacturer')] public function manufacturer(): Response { return $this->getResponseForClass(Manufacturer::class, true); } - /** - * @Route("/measurement_unit", name="select_measurement_unit") - */ + #[Route(path: '/measurement_unit', name: 'select_measurement_unit')] public function measurement_unit(): Response { return $this->getResponseForClass(MeasurementUnit::class, true); } - /** - * @Route("/project", name="select_project") - */ + #[Route(path: '/project', name: 'select_project')] public function projects(): Response { return $this->getResponseForClass(Project::class, false); } - /** - * @Route("/label_profiles", name="select_label_profiles") - * @return Response - */ + #[Route(path: '/storage_location', name: 'select_storage_location')] + public function locations(): Response + { + return $this->getResponseForClass(StorageLocation::class, true); + } + + #[Route(path: '/export_level', name: 'select_export_level')] + public function exportLevel(): Response + { + $entries = [ + 1 => $this->translator->trans('export.level.simple'), + 2 => $this->translator->trans('export.level.extended'), + 3 => $this->translator->trans('export.level.full'), + ]; + + return $this->json(array_map(static fn($key, $value) => [ + 'text' => $value, + 'value' => $key, + ], array_keys($entries), $entries)); + } + + #[Route(path: '/label_profiles', name: 'select_label_profiles')] public function labelProfiles(EntityManagerInterface $entityManager): Response { $this->denyAccessUnlessGranted('@labels.create_labels'); @@ -116,10 +118,7 @@ class SelectAPIController extends AbstractController return $this->json($nodes); } - /** - * @Route("/label_profiles_lot", name="select_label_profiles_lot") - * @return Response - */ + #[Route(path: '/label_profiles_lot', name: 'select_label_profiles_lot')] public function labelProfilesLot(EntityManagerInterface $entityManager): Response { $this->denyAccessUnlessGranted('@labels.create_labels'); @@ -179,8 +178,8 @@ class SelectAPIController extends AbstractController ]); //Remove the data-* prefix for each key $data = array_combine( - array_map(function ($key) { - if (strpos($key, 'data-') === 0) { + array_map(static function ($key) { + if (str_starts_with($key, 'data-')) { return substr($key, 5); } return $key; diff --git a/src/Controller/StatisticsController.php b/src/Controller/StatisticsController.php index d9c467f4..67c29781 100644 --- a/src/Controller/StatisticsController.php +++ b/src/Controller/StatisticsController.php @@ -44,13 +44,11 @@ namespace App\Controller; use App\Services\Tools\StatisticsHelper; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class StatisticsController extends AbstractController { - /** - * @Route("/statistics", name="statistics_view") - */ + #[Route(path: '/statistics', name: 'statistics_view')] public function showStatistics(StatisticsHelper $helper): Response { $this->denyAccessUnlessGranted('@tools.statistics'); diff --git a/src/Controller/ToolsController.php b/src/Controller/ToolsController.php index 28a01b8e..dbcb91a1 100644 --- a/src/Controller/ToolsController.php +++ b/src/Controller/ToolsController.php @@ -1,4 +1,7 @@ . */ - namespace App\Controller; -use App\Services\Attachments\AttachmentPathResolver; +use App\Services\Attachments\AttachmentSubmitHandler; use App\Services\Attachments\AttachmentURLGenerator; use App\Services\Attachments\BuiltinAttachmentsFinder; +use App\Services\Doctrine\DBInfoHelper; +use App\Services\Doctrine\NatsortDebugHelper; use App\Services\Misc\GitVersionInfo; -use App\Services\Misc\DBInfoHelper; -use Doctrine\ORM\EntityManagerInterface; +use App\Services\System\UpdateAvailableManager; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\Routing\Generator\UrlGenerator; +use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Runtime\SymfonyRuntime; -/** - * @Route("/tools") - */ +#[Route(path: '/tools')] class ToolsController extends AbstractController { - /** - * @Route("/reel_calc", name="tools_reel_calculator") - */ + #[Route(path: '/reel_calc', name: 'tools_reel_calculator')] public function reelCalculator(): Response { $this->denyAccessUnlessGranted('@tools.reel_calculator'); @@ -46,10 +45,9 @@ class ToolsController extends AbstractController return $this->render('tools/reel_calculator/reel_calculator.html.twig'); } - /** - * @Route("/server_infos", name="tools_server_infos") - */ - public function systemInfos(GitVersionInfo $versionInfo, DBInfoHelper $DBInfoHelper): Response + #[Route(path: '/server_infos', name: 'tools_server_infos')] + public function systemInfos(GitVersionInfo $versionInfo, DBInfoHelper $DBInfoHelper, NatsortDebugHelper $natsortDebugHelper, + AttachmentSubmitHandler $attachmentSubmitHandler, UpdateAvailableManager $updateAvailableManager): Response { $this->denyAccessUnlessGranted('@system.server_infos'); @@ -63,25 +61,32 @@ class ToolsController extends AbstractController 'default_theme' => $this->getParameter('partdb.global_theme'), 'enabled_locales' => $this->getParameter('partdb.locale_menu'), 'demo_mode' => $this->getParameter('partdb.demo_mode'), - 'gpdr_compliance' => $this->getParameter('partdb.gpdr_compliance'), + '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'), - 'enviroment' => $this->getParameter('kernel.environment'), + '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' => $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' => $this->getParameter('partdb.attachments.max_file_size'), + 'effective_max_file_size' => $attachmentSubmitHandler->getMaximumAllowedUploadSize(), + 'saml_enabled' => $this->getParameter('partdb.saml.enabled'), //PHP section 'php_version' => PHP_VERSION, 'php_uname' => php_uname('a'), 'php_sapi' => PHP_SAPI, - 'php_extensions' => array_merge(get_loaded_extensions()), + 'php_bit_size' => PHP_INT_SIZE * 8, + 'php_extensions' => [...get_loaded_extensions()], 'php_opcache_enabled' => ini_get('opcache.enable'), 'php_upload_max_filesize' => ini_get('upload_max_filesize'), 'php_post_max_size' => ini_get('post_max_size'), + 'kernel_runtime_environment' => $this->getParameter('kernel.runtime_environment'), + 'kernel_runtime_mode' => $this->getParameter('kernel.runtime_mode'), + 'kernel_runtime' => $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? SymfonyRuntime::class, //DB section 'db_type' => $DBInfoHelper->getDatabaseType() ?? 'Unknown', @@ -89,36 +94,33 @@ class ToolsController extends AbstractController 'db_size' => $DBInfoHelper->getDatabaseSize(), 'db_name' => $DBInfoHelper->getDatabaseName() ?? 'Unknown', 'db_user' => $DBInfoHelper->getDatabaseUsername() ?? 'Unknown', + 'db_natsort_method' => $natsortDebugHelper->getNaturalSortMethod(), + 'db_natsort_slow_allowed' => $natsortDebugHelper->isSlowNaturalSortAllowed(), + + //New version section + 'new_version_available' => $updateAvailableManager->isUpdateAvailable(), + 'new_version' => $updateAvailableManager->getLatestVersionString(), + 'new_version_url' => $updateAvailableManager->getLatestVersionUrl(), ]); } - /** - * @Route("/builtin_footprints", name="tools_builtin_footprints_viewer") - * @return Response - */ + #[Route(path: '/builtin_footprints', name: 'tools_builtin_footprints_viewer')] public function builtInFootprintsViewer(BuiltinAttachmentsFinder $builtinAttachmentsFinder, AttachmentURLGenerator $urlGenerator): Response { $this->denyAccessUnlessGranted('@tools.builtin_footprints_viewer'); $grouped_footprints = $builtinAttachmentsFinder->getListOfFootprintsGroupedByFolder(); - $grouped_footprints = array_map(function($group) use ($urlGenerator) { - return array_map(function($placeholder_filepath) use ($urlGenerator) { - return [ - 'filename' => basename($placeholder_filepath), - 'assets_path' => $urlGenerator->placeholderPathToAssetPath($placeholder_filepath), - ]; - }, $group); - }, $grouped_footprints); + $grouped_footprints = array_map(static fn($group) => array_map(static fn($placeholder_filepath) => [ + 'filename' => basename((string) $placeholder_filepath), + 'assets_path' => $urlGenerator->placeholderPathToAssetPath($placeholder_filepath), + ], $group), $grouped_footprints); return $this->render('tools/builtin_footprints_viewer/builtin_footprints_viewer.html.twig', [ 'grouped_footprints' => $grouped_footprints, ]); } - /** - * @Route("/ic_logos", name="tools_ic_logos") - * @return Response - */ + #[Route(path: '/ic_logos', name: 'tools_ic_logos')] public function icLogos(): Response { $this->denyAccessUnlessGranted('@tools.ic_logos'); diff --git a/src/Controller/TreeController.php b/src/Controller/TreeController.php index 6ab3b420..71f8ba5c 100644 --- a/src/Controller/TreeController.php +++ b/src/Controller/TreeController.php @@ -22,35 +22,30 @@ declare(strict_types=1); namespace App\Controller; +use Symfony\Component\HttpFoundation\Response; use App\Entity\ProjectSystem\Project; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Services\Trees\ToolsTreeBuilder; use App\Services\Trees\TreeViewGenerator; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; /** * This controller has the purpose to provide the data for all treeviews. - * - * @Route("/tree") */ +#[Route(path: '/tree')] class TreeController extends AbstractController { - protected TreeViewGenerator $treeGenerator; - - public function __construct(TreeViewGenerator $treeGenerator) + public function __construct(protected TreeViewGenerator $treeGenerator) { - $this->treeGenerator = $treeGenerator; } - /** - * @Route("/tools", name="tree_tools") - */ + #[Route(path: '/tools', name: 'tree_tools')] public function tools(ToolsTreeBuilder $builder): JsonResponse { $tree = $builder->getTree(); @@ -58,90 +53,78 @@ class TreeController extends AbstractController return new JsonResponse($tree); } - /** - * @Route("/category/{id}", name="tree_category") - * @Route("/categories", name="tree_category_root") - */ + #[Route(path: '/category/{id}', name: 'tree_category')] + #[Route(path: '/categories', name: 'tree_category_root')] public function categoryTree(?Category $category = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@categories.read')) { $tree = $this->treeGenerator->getTreeView(Category::class, $category, 'list_parts_root'); } else { - return new JsonResponse("Access denied", 403); + return new JsonResponse("Access denied", Response::HTTP_FORBIDDEN); } return new JsonResponse($tree); } - /** - * @Route("/footprint/{id}", name="tree_footprint") - * @Route("/footprints", name="tree_footprint_root") - */ + #[Route(path: '/footprint/{id}', name: 'tree_footprint')] + #[Route(path: '/footprints', name: 'tree_footprint_root')] public function footprintTree(?Footprint $footprint = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@footprints.read')) { $tree = $this->treeGenerator->getTreeView(Footprint::class, $footprint, 'list_parts_root'); } else { - return new JsonResponse("Access denied", 403); + return new JsonResponse("Access denied", Response::HTTP_FORBIDDEN); } return new JsonResponse($tree); } - /** - * @Route("/location/{id}", name="tree_location") - * @Route("/locations", name="tree_location_root") - */ - public function locationTree(?Storelocation $location = null): JsonResponse + #[Route(path: '/location/{id}', name: 'tree_location')] + #[Route(path: '/locations', name: 'tree_location_root')] + public function locationTree(?StorageLocation $location = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@storelocations.read')) { - $tree = $this->treeGenerator->getTreeView(Storelocation::class, $location, 'list_parts_root'); + $tree = $this->treeGenerator->getTreeView(StorageLocation::class, $location, 'list_parts_root'); } else { - return new JsonResponse("Access denied", 403); + return new JsonResponse("Access denied", Response::HTTP_FORBIDDEN); } return new JsonResponse($tree); } - /** - * @Route("/manufacturer/{id}", name="tree_manufacturer") - * @Route("/manufacturers", name="tree_manufacturer_root") - */ + #[Route(path: '/manufacturer/{id}', name: 'tree_manufacturer')] + #[Route(path: '/manufacturers', name: 'tree_manufacturer_root')] public function manufacturerTree(?Manufacturer $manufacturer = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@manufacturers.read')) { $tree = $this->treeGenerator->getTreeView(Manufacturer::class, $manufacturer, 'list_parts_root'); } else { - return new JsonResponse("Access denied", 403); + return new JsonResponse("Access denied", Response::HTTP_FORBIDDEN); } return new JsonResponse($tree); } - /** - * @Route("/supplier/{id}", name="tree_supplier") - * @Route("/suppliers", name="tree_supplier_root") - */ + #[Route(path: '/supplier/{id}', name: 'tree_supplier')] + #[Route(path: '/suppliers', name: 'tree_supplier_root')] public function supplierTree(?Supplier $supplier = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@suppliers.read')) { $tree = $this->treeGenerator->getTreeView(Supplier::class, $supplier, 'list_parts_root'); } else { - return new JsonResponse("Access denied", 403); + return new JsonResponse("Access denied", Response::HTTP_FORBIDDEN); } return new JsonResponse($tree); } - /** - * @Route("/device/{id}", name="tree_device") - * @Route("/devices", name="tree_device_root") - */ + #[Route(path: '/device/{id}', name: 'tree_device')] + #[Route(path: '/devices', name: 'tree_device_root')] public function deviceTree(?Project $device = null): JsonResponse { if ($this->isGranted('@projects.read')) { $tree = $this->treeGenerator->getTreeView(Project::class, $device, 'devices'); } else { - return new JsonResponse("Access denied", 403); + return new JsonResponse("Access denied", Response::HTTP_FORBIDDEN); } return new JsonResponse($tree); diff --git a/src/Controller/TypeaheadController.php b/src/Controller/TypeaheadController.php index 37ff6ec1..89eac7ff 100644 --- a/src/Controller/TypeaheadController.php +++ b/src/Controller/TypeaheadController.php @@ -22,6 +22,11 @@ declare(strict_types=1); namespace App\Controller; +use App\Entity\Parameters\AbstractParameter; +use Symfony\Component\HttpFoundation\Response; +use App\Entity\Attachments\Attachment; +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; use App\Entity\Parameters\AttachmentTypeParameter; use App\Entity\Parameters\CategoryParameter; use App\Entity\Parameters\ProjectParameter; @@ -30,7 +35,7 @@ use App\Entity\Parameters\GroupParameter; use App\Entity\Parameters\ManufacturerParameter; use App\Entity\Parameters\MeasurementUnitParameter; use App\Entity\Parameters\PartParameter; -use App\Entity\Parameters\StorelocationParameter; +use App\Entity\Parameters\StorageLocationParameter; use App\Entity\Parameters\SupplierParameter; use App\Entity\Parts\Part; use App\Entity\PriceInformations\Currency; @@ -44,30 +49,22 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Asset\Packages; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; /** * In this controller the endpoints for the typeaheads are collected. - * - * @Route("/typeahead") */ +#[Route(path: '/typeahead')] class TypeaheadController extends AbstractController { - protected AttachmentURLGenerator $urlGenerator; - protected Packages $assets; - - public function __construct(AttachmentURLGenerator $URLGenerator, Packages $assets) + public function __construct(protected AttachmentURLGenerator $urlGenerator, protected Packages $assets) { - $this->urlGenerator = $URLGenerator; - $this->assets = $assets; } - /** - * @Route("/builtInResources/search", name="typeahead_builtInRessources") - */ + #[Route(path: '/builtInResources/search', name: 'typeahead_builtInRessources')] public function builtInResources(Request $request, BuiltinAttachmentsFinder $finder): JsonResponse { $query = $request->get('query'); @@ -91,51 +88,32 @@ class TypeaheadController extends AbstractController $serializer = new Serializer($normalizers, $encoders); $data = $serializer->serialize($result, 'json'); - return new JsonResponse($data, 200, [], true); + return new JsonResponse($data, Response::HTTP_OK, [], true); } /** - * This functions map the parameter type to the class, so we can access its repository - * @param string $type - * @return class-string + * This function map the parameter type to the class, so we can access its repository + * @return class-string */ private function typeToParameterClass(string $type): string { - switch ($type) { - case 'category': - return CategoryParameter::class; - case 'part': - return PartParameter::class; - case 'device': - return ProjectParameter::class; - case 'footprint': - return FootprintParameter::class; - case 'manufacturer': - return ManufacturerParameter::class; - case 'storelocation': - return StorelocationParameter::class; - case 'supplier': - return SupplierParameter::class; - case 'attachment_type': - return AttachmentTypeParameter::class; - case 'group': - return GroupParameter::class; - case 'measurement_unit': - return MeasurementUnitParameter::class; - case 'currency': - return Currency::class; - - default: - throw new \InvalidArgumentException('Invalid parameter type: '.$type); - } + return match ($type) { + 'category' => CategoryParameter::class, + 'part' => PartParameter::class, + 'device' => ProjectParameter::class, + 'footprint' => FootprintParameter::class, + 'manufacturer' => ManufacturerParameter::class, + 'storelocation' => StorageLocationParameter::class, + 'supplier' => SupplierParameter::class, + 'attachment_type' => AttachmentTypeParameter::class, + 'group' => GroupParameter::class, + 'measurement_unit' => MeasurementUnitParameter::class, + 'currency' => Currency::class, + default => throw new \InvalidArgumentException('Invalid parameter type: '.$type), + }; } - /** - * @Route("/parts/search/{query}", name="typeahead_parts") - * @param string $query - * @param EntityManagerInterface $entityManager - * @return JsonResponse - */ + #[Route(path: '/parts/search/{query}', name: 'typeahead_parts')] public function parts(EntityManagerInterface $entityManager, PartPreviewGenerator $previewGenerator, AttachmentURLGenerator $attachmentURLGenerator, string $query = ""): JsonResponse { @@ -149,7 +127,7 @@ class TypeaheadController extends AbstractController foreach ($parts as $part) { //Determine the picture to show: $preview_attachment = $previewGenerator->getTablePreviewAttachment($part); - if($preview_attachment !== null) { + if($preview_attachment instanceof Attachment) { $preview_url = $attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm'); } else { $preview_url = ''; @@ -159,8 +137,8 @@ class TypeaheadController extends AbstractController $data[] = [ 'id' => $part->getID(), 'name' => $part->getName(), - 'category' => $part->getCategory() ? $part->getCategory()->getName() : 'Unknown', - 'footprint' => $part->getFootprint() ? $part->getFootprint()->getName() : '', + 'category' => $part->getCategory() instanceof Category ? $part->getCategory()->getName() : 'Unknown', + 'footprint' => $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getName() : '', 'description' => mb_strimwidth($part->getDescription(), 0, 127, '...'), 'image' => $preview_url, ]; @@ -169,11 +147,7 @@ class TypeaheadController extends AbstractController return new JsonResponse($data); } - /** - * @Route("/parameters/{type}/search/{query}", name="typeahead_parameters", requirements={"type" = ".+"}) - * @param string $query - * @return JsonResponse - */ + #[Route(path: '/parameters/{type}/search/{query}', name: 'typeahead_parameters', requirements: ['type' => '.+'])] public function parameters(string $type, EntityManagerInterface $entityManager, string $query = ""): JsonResponse { $class = $this->typeToParameterClass($type); @@ -182,7 +156,7 @@ class TypeaheadController extends AbstractController //Ensure user has the correct permissions $this->denyAccessUnlessGranted('read', $test_obj); - /** @var ParameterRepository $repository */ + /** @var ParameterRepository $repository */ $repository = $entityManager->getRepository($class); $data = $repository->autocompleteParamName($query); @@ -190,9 +164,7 @@ class TypeaheadController extends AbstractController return new JsonResponse($data); } - /** - * @Route("/tags/search/{query}", name="typeahead_tags", requirements={"query"= ".+"}) - */ + #[Route(path: '/tags/search/{query}', name: 'typeahead_tags', requirements: ['query' => '.+'])] public function tags(string $query, TagFinder $finder): JsonResponse { $this->denyAccessUnlessGranted('@parts.read'); @@ -209,6 +181,6 @@ class TypeaheadController extends AbstractController $serializer = new Serializer($normalizers, $encoders); $data = $serializer->serialize($array, 'json'); - return new JsonResponse($data, 200, [], true); + return new JsonResponse($data, Response::HTTP_OK, [], true); } } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 6dad4159..968bd1e3 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -22,10 +22,10 @@ declare(strict_types=1); namespace App\Controller; +use App\Controller\AdminPages\BaseAdminController; use App\DataTables\LogDataTable; use App\Entity\Attachments\UserAttachment; use App\Entity\Base\AbstractNamedDBElement; -use App\Entity\Parameters\AbstractParameter; use App\Entity\UserSystem\User; use App\Events\SecurityEvent; use App\Events\SecurityEvents; @@ -45,13 +45,11 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Validator\Validator\ValidatorInterface; -/** - * @Route("/user") - * Class UserController - */ -class UserController extends AdminPages\BaseAdminController +#[Route(path: '/user')] +class UserController extends BaseAdminController { protected string $entity_class = User::class; protected string $twig_template = 'admin/user_admin.html.twig'; @@ -62,11 +60,11 @@ class UserController extends AdminPages\BaseAdminController protected function additionalActionEdit(FormInterface $form, AbstractNamedDBElement $entity): bool { - //Check if we editing a user and if we need to change the password of it + //Check if we're editing a user and if we need to change the password of it if ($entity instanceof User && !empty($form['new_password']->getData())) { $password = $this->passwordEncoder->hashPassword($entity, $form['new_password']->getData()); $entity->setPassword($password); - //By default the user must change the password afterwards + //By default, the user must change the password afterward $entity->setNeedPwChange(true); $event = new SecurityEvent($entity); @@ -77,12 +75,13 @@ class UserController extends AdminPages\BaseAdminController } /** - * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="user_edit") - * @Route("/{id}/", requirements={"id"="\d+"}) * * @throws Exception */ - public function edit(User $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, PermissionSchemaUpdater $permissionSchemaUpdater, ?string $timestamp = null): Response + #[Route(path: '/{id}/edit/{timestamp}', name: 'user_edit', requirements: ['id' => '\d+'])] + #[Route(path: '/{id}/', requirements: ['id' => '\d+'])] + public function edit(User $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, + PermissionSchemaUpdater $permissionSchemaUpdater, ValidatorInterface $validator, ?string $timestamp = null): Response { //Do an upgrade of the permission schema if needed (so the user can see the permissions a user get on next request (even if it was not done yet) $permissionSchemaUpdater->userUpgradeSchemaRecursively($entity); @@ -91,7 +90,7 @@ class UserController extends AdminPages\BaseAdminController if ($request->request->has('reset_2fa')) { //Check if the admin has the needed permissions $this->denyAccessUnlessGranted('set_password', $entity); - if ($this->isCsrfTokenValid('reset_2fa'.$entity->getId(), $request->request->get('_token'))) { + if ($this->isCsrfTokenValid('reset_2fa'.$entity->getID(), $request->request->get('_token'))) { //Disable Google authenticator $entity->setGoogleAuthenticatorSecret(null); $entity->setBackupCodes([]); @@ -99,7 +98,7 @@ class UserController extends AdminPages\BaseAdminController foreach ($entity->getLegacyU2FKeys() as $key) { $em->remove($key); } - foreach ($entity->getWebAuthnKeys() as $key) { + foreach ($entity->getWebauthnKeys() as $key) { $em->remove($key); } //Invalidate trusted devices @@ -111,26 +110,36 @@ class UserController extends AdminPages\BaseAdminController $this->addFlash('success', 'user.edit.reset_success'); } else { - $this->addFlash('danger', 'csfr_invalid'); + $this->addFlash('error', 'csfr_invalid'); } } //Handle permissions presets if ($request->request->has('permission_preset')) { $this->denyAccessUnlessGranted('edit_permissions', $entity); - if ($this->isCsrfTokenValid('reset_2fa'.$entity->getId(), $request->request->get('_token'))) { + if ($this->isCsrfTokenValid('reset_2fa'.$entity->getID(), $request->request->get('_token'))) { $preset = $request->request->get('permission_preset'); $permissionPresetsHelper->applyPreset($entity, $preset); - $em->flush(); + //Ensure that the user is valid after applying the preset + $errors = $validator->validate($entity); + if (count($errors) > 0) { + $this->addFlash('error', 'validator.noLockout'); + //Refresh the entity to remove the changes + $em->refresh($entity); + } else { + $em->flush(); + + $this->addFlash('success', 'user.edit.permission_success'); + + //We need to stop the execution here, or our permissions changes will be overwritten by the form values + return $this->redirectToRoute('user_edit', ['id' => $entity->getID()]); + } - $this->addFlash('success', 'user.edit.permission_success'); - //We need to stop the execution here, or our permissions changes will be overwritten by the form values - return $this->redirectToRoute('user_edit', ['id' => $entity->getID()]); } else { - $this->addFlash('danger', 'csfr_invalid'); + $this->addFlash('error', 'csfr_invalid'); } } @@ -142,59 +151,55 @@ class UserController extends AdminPages\BaseAdminController if ($entity instanceof User && !empty($form['new_password']->getData())) { $password = $this->passwordEncoder->hashPassword($entity, $form['new_password']->getData()); $entity->setPassword($password); - //By default the user must change the password afterwards + //By default, the user must change the password afterward $entity->setNeedPwChange(true); } return true; } - /** - * @Route("/new", name="user_new") - * @Route("/{id}/clone", name="user_clone") - * @Route("/") - */ + #[Route(path: '/new', name: 'user_new')] + #[Route(path: '/{id}/clone', name: 'user_clone')] + #[Route(path: '/')] public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?User $entity = null): Response { return $this->_new($request, $em, $importer, $entity); } - /** - * @Route("/{id}", name="user_delete", methods={"DELETE"}, requirements={"id"="\d+"}) - */ + #[Route(path: '/{id}', name: 'user_delete', requirements: ['id' => '\d+'], methods: ['DELETE'])] public function delete(Request $request, User $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse { + //Disallow deleting the anonymous user if (User::ID_ANONYMOUS === $entity->getID()) { - throw new InvalidArgumentException('You can not delete the anonymous user! It is needed for permission checking without a logged in user'); + throw new \LogicException('You can not delete the anonymous user! It is needed for permission checking without a logged in user'); + } + + //Disallow deleting the current logged-in user + if ($entity === $this->getUser()) { + throw new \LogicException('You can not delete your own user account!'); } return $this->_delete($request, $entity, $recursionHelper); } - /** - * @Route("/export", name="user_export_all") - */ + #[Route(path: '/export', name: 'user_export_all')] public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response { return $this->_exportAll($em, $exporter, $request); } - /** - * @Route("/{id}/export", name="user_export") - */ + #[Route(path: '/{id}/export', name: 'user_export')] public function exportEntity(User $entity, EntityExporter $exporter, Request $request): Response { return $this->_exportEntity($entity, $exporter, $request); } - /** - * @Route("/info", name="user_info_self") - * @Route("/{id}/info", name="user_info") - */ + #[Route(path: '/info', name: 'user_info_self')] + #[Route(path: '/{id}/info', name: 'user_info')] public function userInfo(?User $user, Packages $packages, Request $request, DataTableFactory $dataTableFactory): Response { //If no user id was passed, then we show info about the current user - if (null === $user) { + if (!$user instanceof User) { $tmp = $this->getUser(); if (!$tmp instanceof User) { throw new InvalidArgumentException('Userinfo only works for database users!'); @@ -202,21 +207,24 @@ class UserController extends AdminPages\BaseAdminController $user = $tmp; } else { //Else we must check, if the current user is allowed to access $user - $this->denyAccessUnlessGranted('read', $user); + $this->denyAccessUnlessGranted('info', $user); } - $table = $this->dataTableFactory->createFromType( - LogDataTable::class, - [ - 'filter_elements' => $user, - 'mode' => 'element_history', - ], - ['pageLength' => 10] - ) - ->handleRequest($request); + //Only show the history table, if the user is the current user + if ($user === $this->getUser()) { + $table = $this->dataTableFactory->createFromType( + LogDataTable::class, + [ + 'filter_elements' => $user, + 'mode' => 'element_history', + ], + ['pageLength' => 10] + ) + ->handleRequest($request); - if ($table->isCallback()) { - return $table->getResponse(); + if ($table->isCallback()) { + return $table->getResponse(); + } } //Show permissions to user @@ -227,10 +235,10 @@ class UserController extends AdminPages\BaseAdminController 'data' => $user, ]); - return $this->renderForm('users/user_info.html.twig', [ + return $this->render('users/user_info.html.twig', [ 'user' => $user, 'form' => $builder->getForm(), - 'datatable' => $table, + 'datatable' => $table ?? null, ]); } } diff --git a/src/Controller/UserSettingsController.php b/src/Controller/UserSettingsController.php index 152189ff..4e56015a 100644 --- a/src/Controller/UserSettingsController.php +++ b/src/Controller/UserSettingsController.php @@ -22,6 +22,9 @@ declare(strict_types=1); namespace App\Controller; +use App\Entity\Attachments\Attachment; +use App\Entity\UserSystem\ApiToken; +use App\Entity\UserSystem\ApiTokenLevel; use App\Entity\UserSystem\U2FKey; use App\Entity\UserSystem\User; use App\Entity\UserSystem\WebauthnKey; @@ -33,10 +36,11 @@ use App\Services\UserSystem\TFA\BackupCodeManager; use App\Services\UserSystem\UserAvatarHelper; use Doctrine\ORM\EntityManagerInterface; use RuntimeException; -use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticator; +use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Form\Extension\Core\Type\DateTimeType; +use Symfony\Component\Form\Extension\Core\Type\EnumType; use Symfony\Component\Form\Extension\Core\Type\PasswordType; use Symfony\Component\Form\Extension\Core\Type\RepeatedType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; @@ -47,32 +51,19 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; use Symfony\Component\Validator\Constraints\Length; -/** - * @Route("/user") - */ +#[Route(path: '/user')] class UserSettingsController extends AbstractController { - protected bool $demo_mode; - - /** - * @var EventDispatcher|EventDispatcherInterface - */ - protected $eventDispatcher; - - public function __construct(bool $demo_mode, EventDispatcherInterface $eventDispatcher) + public function __construct(protected bool $demo_mode, protected EventDispatcherInterface $eventDispatcher) { - $this->demo_mode = $demo_mode; - $this->eventDispatcher = $eventDispatcher; } - /** - * @Route("/2fa_backup_codes", name="show_backup_codes") - */ - public function showBackupCodes() + #[Route(path: '/2fa_backup_codes', name: 'show_backup_codes')] + public function showBackupCodes(): Response { $user = $this->getUser(); @@ -80,10 +71,14 @@ class UserSettingsController extends AbstractController $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); if (!$user instanceof User) { - return new RuntimeException('This controller only works only for Part-DB User objects!'); + throw new RuntimeException('This controller only works only for Part-DB User objects!'); } - if (empty($user->getBackupCodes())) { + if ($user->isSamlUser()) { + throw new RuntimeException('You can not remove U2F keys from SAML users!'); + } + + if ($user->getBackupCodes() === []) { $this->addFlash('error', 'tfa_backup.no_codes_enabled'); throw new RuntimeException('You do not have any backup codes enabled, therefore you can not view them!'); @@ -94,9 +89,7 @@ class UserSettingsController extends AbstractController ]); } - /** - * @Route("/u2f_delete", name="u2f_delete", methods={"DELETE"}) - */ + #[Route(path: '/u2f_delete', name: 'u2f_delete', methods: ['DELETE'])] public function removeU2FToken(Request $request, EntityManagerInterface $entityManager, BackupCodeManager $backupCodeManager): RedirectResponse { if ($this->demo_mode) { @@ -112,56 +105,54 @@ class UserSettingsController extends AbstractController throw new RuntimeException('This controller only works only for Part-DB User objects!'); } - if ($this->isCsrfTokenValid('delete'.$user->getId(), $request->request->get('_token'))) { + if ($user->isSamlUser()) { + throw new RuntimeException('You can not remove U2F keys from SAML users!'); + } + + if ($this->isCsrfTokenValid('delete'.$user->getID(), $request->request->get('_token'))) { //Handle U2F key removal if ($request->request->has('key_id')) { $key_id = $request->request->get('key_id'); $key_repo = $entityManager->getRepository(U2FKey::class); /** @var U2FKey|null $u2f */ $u2f = $key_repo->find($key_id); - if (null === $u2f) { + if (!$u2f instanceof U2FKey) { $this->addFlash('danger', 'tfa_u2f.u2f_delete.not_existing'); return $this->redirectToRoute('user_settings'); } - //User can only delete its own U2F keys if ($u2f->getUser() !== $user) { $this->addFlash('danger', 'tfa_u2f.u2f_delete.access_denied'); return $this->redirectToRoute('user_settings'); } - $backupCodeManager->disableBackupCodesIfUnused($user); $entityManager->remove($u2f); $entityManager->flush(); $this->addFlash('success', 'tfa.u2f.u2f_delete.success'); - $security_event = new SecurityEvent($user); $this->eventDispatcher->dispatch($security_event, SecurityEvents::U2F_REMOVED); - } else if ($request->request->has('webauthn_key_id')) { + } elseif ($request->request->has('webauthn_key_id')) { $key_id = $request->request->get('webauthn_key_id'); $key_repo = $entityManager->getRepository(WebauthnKey::class); /** @var WebauthnKey|null $key */ $key = $key_repo->find($key_id); - if (null === $key) { + if (!$key instanceof WebauthnKey) { $this->addFlash('error', 'tfa_u2f.u2f_delete.not_existing'); return $this->redirectToRoute('user_settings'); } - //User can only delete its own U2F keys if ($key->getUser() !== $user) { $this->addFlash('error', 'tfa_u2f.u2f_delete.access_denied'); return $this->redirectToRoute('user_settings'); } - $backupCodeManager->disableBackupCodesIfUnused($user); $entityManager->remove($key); $entityManager->flush(); $this->addFlash('success', 'tfa.u2f.u2f_delete.success'); - $security_event = new SecurityEvent($user); $this->eventDispatcher->dispatch($security_event, SecurityEvents::U2F_REMOVED); } @@ -172,12 +163,8 @@ class UserSettingsController extends AbstractController return $this->redirectToRoute('user_settings'); } - /** - * @Route("/invalidate_trustedDevices", name="tfa_trustedDevices_invalidate", methods={"DELETE"}) - * - * @return RuntimeException|RedirectResponse - */ - public function resetTrustedDevices(Request $request, EntityManagerInterface $entityManager) + #[Route(path: '/invalidate_trustedDevices', name: 'tfa_trustedDevices_invalidate', methods: ['DELETE'])] + public function resetTrustedDevices(Request $request, EntityManagerInterface $entityManager): \RuntimeException|RedirectResponse { if ($this->demo_mode) { throw new RuntimeException('You can not do 2FA things in demo mode'); @@ -192,7 +179,11 @@ class UserSettingsController extends AbstractController return new RuntimeException('This controller only works only for Part-DB User objects!'); } - if ($this->isCsrfTokenValid('devices_reset'.$user->getId(), $request->request->get('_token'))) { + if ($user->isSamlUser()) { + throw new RuntimeException('You can not remove U2F keys from SAML users!'); + } + + if ($this->isCsrfTokenValid('devices_reset'.$user->getID(), $request->request->get('_token'))) { $user->invalidateTrustedDeviceTokens(); $entityManager->flush(); $this->addFlash('success', 'tfa_trustedDevice.invalidate.success'); @@ -207,13 +198,13 @@ class UserSettingsController extends AbstractController } /** - * @Route("/settings", name="user_settings") - * * @return RedirectResponse|Response */ - public function userSettings(Request $request, EntityManagerInterface $em, UserPasswordHasherInterface $passwordEncoder, GoogleAuthenticator $googleAuthenticator, BackupCodeManager $backupCodeManager, FormFactoryInterface $formFactory, UserAvatarHelper $avatarHelper) + #[Route(path: '/settings', name: 'user_settings')] + public function userSettings(Request $request, EntityManagerInterface $em, UserPasswordHasherInterface $passwordEncoder, + GoogleAuthenticatorInterface $googleAuthenticator, BackupCodeManager $backupCodeManager, FormFactoryInterface $formFactory, UserAvatarHelper $avatarHelper): RedirectResponse|Response { - /** @var User */ + /** @var User $user */ $user = $this->getUser(); $page_need_reload = false; @@ -249,14 +240,15 @@ class UserSettingsController extends AbstractController $page_need_reload = true; } - /** @var Form $form We need an form implementation for the next calls */ - if ($form->getClickedButton() && 'remove_avatar' === $form->getClickedButton()->getName()) { - //Remove the avatar attachment from the user if requested - if ($user->getMasterPictureAttachment() !== null) { - $em->remove($user->getMasterPictureAttachment()); - $user->setMasterPictureAttachment(null); - $page_need_reload = true; - } + if (!$form instanceof Form) { + throw new RuntimeException('Form is not an instance of Form, so we cannot retrieve the clicked button!'); + } + + //Remove the avatar attachment from the user if requested + if ($form->getClickedButton() && 'remove_avatar' === $form->getClickedButton()->getName() && $user->getMasterPictureAttachment() instanceof Attachment) { + $em->remove($user->getMasterPictureAttachment()); + $user->setMasterPictureAttachment(null); + $page_need_reload = true; } $em->flush(); @@ -281,17 +273,18 @@ class UserSettingsController extends AbstractController ]) ->add('old_password', PasswordType::class, [ 'label' => 'user.settings.pw_old.label', - 'disabled' => $this->demo_mode, + 'disabled' => $this->demo_mode || $user->isSamlUser(), 'attr' => [ 'autocomplete' => 'current-password', ], 'constraints' => [new UserPassword()], ]) //This constraint checks, if the current user pw was inputted. ->add('new_password', RepeatedType::class, [ - 'disabled' => $this->demo_mode, + 'disabled' => $this->demo_mode || $user->isSamlUser(), 'type' => PasswordType::class, 'first_options' => [ 'label' => 'user.settings.pw_new.label', + 'password_estimator' => true, ], 'second_options' => [ 'label' => 'user.settings.pw_confirm.label', @@ -307,12 +300,15 @@ class UserSettingsController extends AbstractController 'max' => 128, ])], ]) - ->add('submit', SubmitType::class, ['label' => 'save']) + ->add('submit', SubmitType::class, [ + 'label' => 'save', + 'disabled' => $this->demo_mode || $user->isSamlUser(), + ]) ->getForm(); $pw_form->handleRequest($request); - //Check if password if everything was correct, then save it to User and DB + //Check if everything was correct, then save it to User and DB if (!$this->demo_mode && $pw_form->isSubmitted() && $pw_form->isValid()) { $password = $passwordEncoder->hashPassword($user, $pw_form['new_password']->getData()); $user->setPassword($password); @@ -327,15 +323,17 @@ class UserSettingsController extends AbstractController } //Handle 2FA things - $google_form = $this->createForm(TFAGoogleSettingsType::class, $user); + $google_form = $this->createForm(TFAGoogleSettingsType::class, $user, [ + 'disabled' => $this->demo_mode || $user->isSamlUser(), + ]); $google_enabled = $user->isGoogleAuthenticatorEnabled(); - if (!$google_enabled && !$form->isSubmitted()) { + if (!$google_enabled && !$google_form->isSubmitted()) { $user->setGoogleAuthenticatorSecret($googleAuthenticator->generateSecret()); $google_form->get('googleAuthenticatorSecret')->setData($user->getGoogleAuthenticatorSecret()); } $google_form->handleRequest($request); - if (!$this->demo_mode && $google_form->isSubmitted() && $google_form->isValid()) { + if (!$this->demo_mode && !$user->isSamlUser() && $google_form->isSubmitted() && $google_form->isValid()) { if (!$google_enabled) { //Save 2FA settings (save secrets) $user->setGoogleAuthenticatorSecret($google_form->get('googleAuthenticatorSecret')->getData()); @@ -365,11 +363,11 @@ class UserSettingsController extends AbstractController 'attr' => [ 'class' => 'btn-danger', ], - 'disabled' => empty($user->getBackupCodes()), + 'disabled' => $user->getBackupCodes() === [], ])->getForm(); $backup_form->handleRequest($request); - if (!$this->demo_mode && $backup_form->isSubmitted() && $backup_form->isValid()) { + if (!$this->demo_mode && !$user->isSamlUser() && $backup_form->isSubmitted() && $backup_form->isValid()) { $backupCodeManager->regenerateBackupCodes($user); $em->flush(); $this->addFlash('success', 'user.settings.2fa.backup_codes.regenerated'); @@ -380,7 +378,7 @@ class UserSettingsController extends AbstractController * Output both forms *****************************/ - return $this->renderForm('users/user_settings.html.twig', [ + return $this->render('users/user_settings.html.twig', [ 'user' => $user, 'settings_form' => $form, 'pw_form' => $pw_form, @@ -396,4 +394,99 @@ class UserSettingsController extends AbstractController ], ]); } + + /** + * @return Response + */ + #[Route('/api_token/create', name: 'user_api_token_create')] + public function addApiToken(Request $request, EntityManagerInterface $entityManager): Response + { + $this->denyAccessUnlessGranted('@api.manage_tokens'); + //When user change its settings, he should be logged in fully. + $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); + + $token = new ApiToken(); + if (!$this->getUser() instanceof User) { + throw new RuntimeException('This controller only works only for Part-DB User objects!'); + } + $token->setUser($this->getUser()); + + $secret = null; + + $form = $this->createFormBuilder($token) + ->add('name', TextType::class, [ + 'label' => 'api_tokens.name', + ]) + ->add('level', EnumType::class, [ + 'class' => ApiTokenLevel::class, + 'label' => 'api_tokens.access_level', + 'help' => 'api_tokens.access_level.help', + 'choice_label' => fn (ApiTokenLevel $level) => $level->getTranslationKey(), + ]) + ->add('valid_until', DateTimeType::class, [ + 'label' => 'api_tokens.expiration_date', + 'widget' => 'single_text', + 'help' => 'api_tokens.expiration_date.help', + 'required' => false, + 'html5' => true + ]) + ->add('submit', SubmitType::class, [ + 'label' => 'save', + ]) + ->getForm(); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager->persist($token); + $entityManager->flush(); + + $secret = $token->getToken(); + } + + return $this->render('users/api_token_create.html.twig', [ + 'token' => $token, + 'form' => $form, + 'secret' => $secret, + ]); + } + + #[Route(path: '/api_token/delete', name: 'user_api_tokens_delete', methods: ['DELETE'])] + public function apiTokenRemove(Request $request, EntityManagerInterface $entityManager): Response + { + $this->denyAccessUnlessGranted('@api.manage_tokens'); + //When user change its settings, he should be logged in fully. + $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); + + $user = $this->getUser(); + if (!$user instanceof User) { + throw new RuntimeException('This controller only works only for Part-DB User objects!'); + } + + if (!$this->isCsrfTokenValid('delete'.$user->getID(), $request->request->get('_token'))) { + $this->addFlash('error', 'csfr_invalid'); + return $this->redirectToRoute('user_settings'); + } + + //Extract the token id from the request + $token_id = $request->request->getInt('token_id'); + + $token = $entityManager->find(ApiToken::class, $token_id); + if ($token === null) { + $this->addFlash('error', 'tfa_u2f.u2f_delete.not_existing'); + return $this->redirectToRoute('user_settings'); + } + //User can only delete its own API tokens + if ($token->getUser() !== $user) { + $this->addFlash('error', 'tfa_u2f.u2f_delete.access_denied'); + return $this->redirectToRoute('user_settings'); + } + + //Do the actual deletion + $entityManager->remove($token); + $entityManager->flush(); + + $this->addFlash('success', 'api_tokens.deleted'); + return $this->redirectToRoute('user_settings'); + } } diff --git a/src/Controller/WebauthnKeyRegistrationController.php b/src/Controller/WebauthnKeyRegistrationController.php index c60f8e94..b2c4f344 100644 --- a/src/Controller/WebauthnKeyRegistrationController.php +++ b/src/Controller/WebauthnKeyRegistrationController.php @@ -1,4 +1,7 @@ . */ - namespace App\Controller; +use App\Entity\UserSystem\User; use App\Entity\UserSystem\WebauthnKey; use Doctrine\ORM\EntityManagerInterface; use Jbtronics\TFAWebauthn\Services\TFAWebauthnRegistrationHelper; +use RuntimeException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; use function Symfony\Component\Translation\t; class WebauthnKeyRegistrationController extends AbstractController { - /** - * @Route("/webauthn/register", name="webauthn_register") - */ - public function register(Request $request, TFAWebauthnRegistrationHelper $registrationHelper, EntityManagerInterface $em) + public function __construct(private readonly bool $demo_mode) + { + } + + #[Route(path: '/webauthn/register', name: 'webauthn_register')] + public function register(Request $request, TFAWebauthnRegistrationHelper $registrationHelper, EntityManagerInterface $em): Response { //When user change its settings, he should be logged in fully. $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); + if ($this->demo_mode) { + throw new RuntimeException('You can not do 2FA things in demo mode'); + } + + $user = $this->getUser(); + + if (!$user instanceof User) { + throw new RuntimeException('This controller only works only for Part-DB User objects!'); + } + + if ($user->isSamlUser()) { + throw new RuntimeException('You can not remove U2F keys from SAML users!'); + } + //If form was submitted, check the auth response if ($request->getMethod() === 'POST') { $webauthnResponse = $request->request->get('_auth_code'); @@ -52,14 +73,19 @@ class WebauthnKeyRegistrationController extends AbstractController //Check the response try { $new_key = $registrationHelper->checkRegistrationResponse($webauthnResponse); - } catch (\Exception $exception) { + } catch (\Exception) { $this->addFlash('error', t('tfa_u2f.add_key.registration_error')); return $this->redirectToRoute('webauthn_register'); } + $user = $this->getUser(); + if (!$user instanceof User) { + throw new RuntimeException('This controller only works only for Part-DB User objects!'); + } + $keyEntity = WebauthnKey::fromRegistration($new_key); $keyEntity->setName($keyName); - $keyEntity->setUser($this->getUser()); + $keyEntity->setUser($user); $em->persist($keyEntity); $em->flush(); @@ -77,4 +103,4 @@ class WebauthnKeyRegistrationController extends AbstractController ] ); } -} \ No newline at end of file +} diff --git a/src/DataFixtures/APITokenFixtures.php b/src/DataFixtures/APITokenFixtures.php new file mode 100644 index 00000000..0ab0b7bb --- /dev/null +++ b/src/DataFixtures/APITokenFixtures.php @@ -0,0 +1,97 @@ +. + */ + +declare(strict_types=1); + + +namespace App\DataFixtures; + +use App\Entity\UserSystem\ApiToken; +use App\Entity\UserSystem\ApiTokenLevel; +use App\Entity\UserSystem\User; +use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Common\DataFixtures\DependentFixtureInterface; +use Doctrine\Persistence\ObjectManager; + +class APITokenFixtures extends Fixture implements DependentFixtureInterface +{ + public const TOKEN_READONLY = 'tcp_readonly'; + public const TOKEN_EDIT = 'tcp_edit'; + public const TOKEN_ADMIN = 'tcp_admin'; + public const TOKEN_FULL = 'tcp_full'; + public const TOKEN_EXPIRED = 'tcp_expired'; + + public function load(ObjectManager $manager): void + { + /** @var User $admin_user */ + $admin_user = $this->getReference(UserFixtures::ADMIN, User::class); + + $read_only_token = new ApiToken(); + $read_only_token->setUser($admin_user); + $read_only_token->setLevel(ApiTokenLevel::READ_ONLY); + $read_only_token->setName('read-only'); + $this->setTokenSecret($read_only_token, self::TOKEN_READONLY); + $manager->persist($read_only_token); + + $editor_token = new ApiToken(); + $editor_token->setUser($admin_user); + $editor_token->setLevel(ApiTokenLevel::EDIT); + $editor_token->setName('edit'); + $this->setTokenSecret($editor_token, self::TOKEN_EDIT); + $manager->persist($editor_token); + + $admin_token = new ApiToken(); + $admin_token->setUser($admin_user); + $admin_token->setLevel(ApiTokenLevel::ADMIN); + $admin_token->setName('admin'); + $this->setTokenSecret($admin_token, self::TOKEN_ADMIN); + $manager->persist($admin_token); + + $full_token = new ApiToken(); + $full_token->setUser($admin_user); + $full_token->setLevel(ApiTokenLevel::FULL); + $full_token->setName('full'); + $this->setTokenSecret($full_token, self::TOKEN_FULL); + $manager->persist($full_token); + + $expired_token = new ApiToken(); + $expired_token->setUser($admin_user); + $expired_token->setLevel(ApiTokenLevel::FULL); + $expired_token->setName('expired'); + $expired_token->setValidUntil(new \DateTimeImmutable('-1 day')); + $this->setTokenSecret($expired_token, self::TOKEN_EXPIRED); + $manager->persist($expired_token); + + $manager->flush(); + } + + private function setTokenSecret(ApiToken $token, string $secret): void + { + //Access private property + $reflection = new \ReflectionClass($token); + $property = $reflection->getProperty('token'); + $property->setValue($token, $secret); + } + + public function getDependencies(): array + { + return [UserFixtures::class]; + } +} \ No newline at end of file diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php deleted file mode 100644 index dff739ae..00000000 --- a/src/DataFixtures/AppFixtures.php +++ /dev/null @@ -1,37 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace App\DataFixtures; - -use Doctrine\Bundle\FixturesBundle\Fixture; -use Doctrine\Persistence\ObjectManager; - -class AppFixtures extends Fixture -{ - public function load(ObjectManager $manager): void - { - // $product = new Product(); - // $manager->persist($product); - - $manager->flush(); - } -} diff --git a/src/DataFixtures/DataStructureFixtures.php b/src/DataFixtures/DataStructureFixtures.php index c7416abe..fc713d4d 100644 --- a/src/DataFixtures/DataStructureFixtures.php +++ b/src/DataFixtures/DataStructureFixtures.php @@ -29,20 +29,18 @@ use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ObjectManager; use InvalidArgumentException; -class DataStructureFixtures extends Fixture +class DataStructureFixtures extends Fixture implements DependentFixtureInterface { - protected EntityManagerInterface $em; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $entityManager; } /** @@ -52,7 +50,7 @@ class DataStructureFixtures extends Fixture { //Reset autoincrement $types = [AttachmentType::class, Project::class, Category::class, Footprint::class, Manufacturer::class, - MeasurementUnit::class, Storelocation::class, Supplier::class, ]; + MeasurementUnit::class, StorageLocation::class, Supplier::class,]; foreach ($types as $type) { $this->createNodesForClass($type, $manager); @@ -76,30 +74,37 @@ class DataStructureFixtures extends Fixture /** @var AbstractStructuralDBElement $node1 */ $node1 = new $class(); $node1->setName('Node 1'); + $this->addReference($class . '_1', $node1); /** @var AbstractStructuralDBElement $node2 */ $node2 = new $class(); $node2->setName('Node 2'); + $this->addReference($class . '_2', $node2); /** @var AbstractStructuralDBElement $node3 */ $node3 = new $class(); $node3->setName('Node 3'); + $this->addReference($class . '_3', $node3); $node1_1 = new $class(); $node1_1->setName('Node 1.1'); $node1_1->setParent($node1); + $this->addReference($class . '_4', $node1_1); $node1_2 = new $class(); $node1_2->setName('Node 1.2'); $node1_2->setParent($node1); + $this->addReference($class . '_5', $node1_2); $node2_1 = new $class(); $node2_1->setName('Node 2.1'); $node2_1->setParent($node2); + $this->addReference($class . '_6', $node2_1); $node1_1_1 = new $class(); $node1_1_1->setName('Node 1.1.1'); $node1_1_1->setParent($node1_1); + $this->addReference($class . '_7', $node1_1_1); $manager->persist($node1); $manager->persist($node2); @@ -109,4 +114,11 @@ class DataStructureFixtures extends Fixture $manager->persist($node2_1); $manager->persist($node1_1_1); } + + public function getDependencies(): array + { + return [ + UserFixtures::class + ]; + } } diff --git a/src/DataFixtures/EDADataFixtures.php b/src/DataFixtures/EDADataFixtures.php new file mode 100644 index 00000000..1484f03e --- /dev/null +++ b/src/DataFixtures/EDADataFixtures.php @@ -0,0 +1,71 @@ +. + */ + +declare(strict_types=1); + + +namespace App\DataFixtures; + +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; +use App\Entity\Parts\Part; +use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Common\DataFixtures\DependentFixtureInterface; +use Doctrine\Persistence\ObjectManager; + +class EDADataFixtures extends Fixture implements DependentFixtureInterface +{ + + public function getDependencies(): array + { + return [PartFixtures::class]; + } + + public function load(ObjectManager $manager): void + { + //Load elements from DB + $category1 = $manager->find(Category::class, 1); + $footprint1 = $manager->find(Footprint::class, 1); + + $part1 = $manager->find(Part::class, 1); + + //Put some data into category1 and foorprint1 + $category1?->getEdaInfo() + ->setExcludeFromBoard(true) + ->setKicadSymbol('Category:1') + ->setReferencePrefix('C') + ; + + $footprint1?->getEdaInfo() + ->setKicadFootprint('Footprint:1') + ; + + //Put some data into part1 (which overrides the data from category1 and footprint1 on part1) + $part1?->getEdaInfo() + ->setExcludeFromSim(false) + ->setKicadSymbol('Part:1') + ->setKicadFootprint('Part:1') + ->setReferencePrefix('P') + ; + + //Flush the changes + $manager->flush(); + } +} \ No newline at end of file diff --git a/src/DataFixtures/GroupFixtures.php b/src/DataFixtures/GroupFixtures.php index 93e93b79..d8e54b9f 100644 --- a/src/DataFixtures/GroupFixtures.php +++ b/src/DataFixtures/GroupFixtures.php @@ -30,18 +30,12 @@ use Doctrine\Persistence\ObjectManager; class GroupFixtures extends Fixture { - public const ADMINS = 'group-admin'; - public const USERS = 'group-users'; - public const READONLY = 'group-readonly'; + final public const ADMINS = 'group-admin'; + final public const USERS = 'group-users'; + final public const READONLY = 'group-readonly'; - - private PermissionPresetsHelper $permission_presets; - private PermissionManager $permissionManager; - - public function __construct(PermissionPresetsHelper $permissionPresetsHelper, PermissionManager $permissionManager) + public function __construct(private readonly PermissionPresetsHelper $permission_presets, private readonly PermissionManager $permissionManager) { - $this->permission_presets = $permissionPresetsHelper; - $this->permissionManager = $permissionManager; } public function load(ObjectManager $manager): void diff --git a/src/DataFixtures/LabelProfileFixtures.php b/src/DataFixtures/LabelProfileFixtures.php index d2c23fad..c0eb85cd 100644 --- a/src/DataFixtures/LabelProfileFixtures.php +++ b/src/DataFixtures/LabelProfileFixtures.php @@ -41,19 +41,19 @@ declare(strict_types=1); namespace App\DataFixtures; +use App\Entity\LabelSystem\BarcodeType; use App\Entity\LabelSystem\LabelOptions; +use App\Entity\LabelSystem\LabelProcessMode; use App\Entity\LabelSystem\LabelProfile; +use App\Entity\LabelSystem\LabelSupportedElement; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ObjectManager; class LabelProfileFixtures extends Fixture { - protected EntityManagerInterface $em; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $entityManager; } public function load(ObjectManager $manager): void @@ -65,8 +65,8 @@ class LabelProfileFixtures extends Fixture $option1 = new LabelOptions(); $option1->setLines("[[NAME]]\n[[DESCRIPION]]"); - $option1->setBarcodeType('none'); - $option1->setSupportedElement('part'); + $option1->setBarcodeType(BarcodeType::NONE); + $option1->setSupportedElement(LabelSupportedElement::PART); $profile1->setOptions($option1); $manager->persist($profile1); @@ -77,8 +77,8 @@ class LabelProfileFixtures extends Fixture $option2 = new LabelOptions(); $option2->setLines("[[NAME]]\n[[DESCRIPION]]"); - $option2->setBarcodeType('qr'); - $option2->setSupportedElement('part'); + $option2->setBarcodeType(BarcodeType::QR); + $option2->setSupportedElement(LabelSupportedElement::PART); $profile2->setOptions($option2); $manager->persist($profile2); @@ -89,8 +89,8 @@ class LabelProfileFixtures extends Fixture $option3 = new LabelOptions(); $option3->setLines("[[NAME]]\n[[DESCRIPION]]"); - $option3->setBarcodeType('code128'); - $option3->setSupportedElement('part_lot'); + $option3->setBarcodeType(BarcodeType::CODE128); + $option3->setSupportedElement(LabelSupportedElement::PART_LOT); $profile3->setOptions($option3); $manager->persist($profile3); @@ -101,13 +101,20 @@ class LabelProfileFixtures extends Fixture $option4 = new LabelOptions(); $option4->setLines('{{ element.name }}'); - $option4->setBarcodeType('code39'); - $option4->setSupportedElement('part'); - $option4->setLinesMode('twig'); + $option4->setBarcodeType(BarcodeType::CODE39); + $option4->setSupportedElement(LabelSupportedElement::PART); + $option4->setProcessMode(LabelProcessMode::TWIG); $profile4->setOptions($option4); $manager->persist($profile4); $manager->flush(); } + + public function getDependencies(): array + { + return [ + PartFixtures::class, + ]; + } } diff --git a/src/DataFixtures/LogEntryFixtures.php b/src/DataFixtures/LogEntryFixtures.php new file mode 100644 index 00000000..eb9cf731 --- /dev/null +++ b/src/DataFixtures/LogEntryFixtures.php @@ -0,0 +1,106 @@ +. + */ + +declare(strict_types=1); + + +namespace App\DataFixtures; + +use App\Entity\LogSystem\ElementCreatedLogEntry; +use App\Entity\LogSystem\ElementDeletedLogEntry; +use App\Entity\LogSystem\ElementEditedLogEntry; +use App\Entity\Parts\Category; +use App\Entity\UserSystem\User; +use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Common\DataFixtures\DependentFixtureInterface; +use Doctrine\Persistence\ObjectManager; + +class LogEntryFixtures extends Fixture implements DependentFixtureInterface +{ + + public function load(ObjectManager $manager): void + { + $this->createCategoryEntries($manager); + $this->createDeletedCategory($manager); + } + + public function createCategoryEntries(ObjectManager $manager): void + { + $category = $this->getReference(Category::class . '_1', Category::class); + + $logEntry = new ElementCreatedLogEntry($category); + $logEntry->setTimestamp(new \DateTimeImmutable("+1 second")); + $logEntry->setUser($this->getReference(UserFixtures::ADMIN, User::class)); + $logEntry->setComment('Test'); + $manager->persist($logEntry); + + $logEntry = new ElementEditedLogEntry($category); + $logEntry->setTimestamp(new \DateTimeImmutable("+2 second")); + $logEntry->setUser($this->getReference(UserFixtures::ADMIN, User::class)); + $logEntry->setComment('Test'); + + $logEntry->setOldData(['name' => 'Test']); + $logEntry->setNewData(['name' => 'Node 1.1']); + + $manager->persist($logEntry); + $manager->flush(); + } + + public function createDeletedCategory(ObjectManager $manager): void + { + //We create a fictive category to test the deletion + $category = new Category(); + $category->setName('Node 100'); + + //Assume a category with id 100 was deleted + $reflClass = new \ReflectionClass($category); + $reflClass->getProperty('id')->setValue($category, 100); + + //The whole lifecycle from creation to deletion + $logEntry = new ElementCreatedLogEntry($category); + $logEntry->setUser($this->getReference(UserFixtures::ADMIN, User::class)); + $logEntry->setComment('Creation'); + $manager->persist($logEntry); + + $logEntry = new ElementEditedLogEntry($category); + $logEntry->setTimestamp(new \DateTimeImmutable("+1 second")); + $logEntry->setUser($this->getReference(UserFixtures::ADMIN, User::class)); + $logEntry->setComment('Edit'); + $logEntry->setOldData(['name' => 'Test']); + $logEntry->setNewData(['name' => 'Node 100']); + $manager->persist($logEntry); + + $logEntry = new ElementDeletedLogEntry($category); + $logEntry->setTimestamp(new \DateTimeImmutable("+2 second")); + $logEntry->setUser($this->getReference(UserFixtures::ADMIN, User::class)); + $logEntry->setOldData(['name' => 'Node 100', 'id' => 100, 'comment' => 'Test comment']); + $manager->persist($logEntry); + + $manager->flush(); + } + + public function getDependencies(): array + { + return [ + UserFixtures::class, + DataStructureFixtures::class + ]; + } +} \ No newline at end of file diff --git a/src/DataFixtures/PartFixtures.php b/src/DataFixtures/PartFixtures.php index 45789dd9..a60d037d 100644 --- a/src/DataFixtures/PartFixtures.php +++ b/src/DataFixtures/PartFixtures.php @@ -46,25 +46,24 @@ use App\Entity\Attachments\PartAttachment; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\ManufacturingStatus; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Orderdetail; use App\Entity\PriceInformations\Pricedetail; use Brick\Math\BigDecimal; use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ObjectManager; -class PartFixtures extends Fixture +class PartFixtures extends Fixture implements DependentFixtureInterface { - protected EntityManagerInterface $em; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $entityManager; } public function load(ObjectManager $manager): void @@ -74,6 +73,7 @@ class PartFixtures extends Fixture $part = new Part(); $part->setName('Part 1'); $part->setCategory($manager->find(Category::class, 1)); + $this->addReference(Part::class . '_1', $part); $manager->persist($part); /** More complex part */ @@ -84,8 +84,10 @@ class PartFixtures extends Fixture $part->setManufacturer($manager->find(Manufacturer::class, 1)); $part->setTags('test, Test, Part2'); $part->setMass(100.2); + $part->setIpn('IPN123'); $part->setNeedsReview(true); - $part->setManufacturingStatus('active'); + $part->setManufacturingStatus(ManufacturingStatus::ACTIVE); + $this->addReference(Part::class . '_2', $part); $manager->persist($part); /** Part with orderdetails, storelocations and Attachments */ @@ -95,14 +97,16 @@ class PartFixtures extends Fixture $part->setCategory($manager->find(Category::class, 1)); $partLot1 = new PartLot(); $partLot1->setAmount(1.0); - $partLot1->setStorageLocation($manager->find(Storelocation::class, 1)); + $partLot1->setStorageLocation($manager->find(StorageLocation::class, 1)); $part->addPartLot($partLot1); + $partLot2 = new PartLot(); - $partLot2->setExpirationDate(new DateTime()); + $partLot2->setExpirationDate(new \DateTimeImmutable()); $partLot2->setComment('Test'); $partLot2->setNeedsRefill(true); - $partLot2->setStorageLocation($manager->find(Storelocation::class, 3)); + $partLot2->setStorageLocation($manager->find(StorageLocation::class, 3)); + $partLot2->setUserBarcode('lot2_vendor_barcode'); $part->addPartLot($partLot2); $orderdetail = new Orderdetail(); @@ -127,12 +131,21 @@ class PartFixtures extends Fixture $attachment = new PartAttachment(); $attachment->setName('Test2'); - $attachment->setPath('invalid'); + $attachment->setInternalPath('invalid'); $attachment->setShowInTable(true); $attachment->setAttachmentType($manager->find(AttachmentType::class, 1)); $part->addAttachment($attachment); + $this->addReference(Part::class . '_3', $part); + $manager->persist($part); $manager->flush(); } + + public function getDependencies(): array + { + return [ + DataStructureFixtures::class + ]; + } } diff --git a/src/DataFixtures/UserFixtures.php b/src/DataFixtures/UserFixtures.php index f9e138b6..922d0b1e 100644 --- a/src/DataFixtures/UserFixtures.php +++ b/src/DataFixtures/UserFixtures.php @@ -22,28 +22,27 @@ declare(strict_types=1); namespace App\DataFixtures; +use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ObjectManager; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; -class UserFixtures extends Fixture +class UserFixtures extends Fixture implements DependentFixtureInterface { - protected UserPasswordHasherInterface $encoder; - protected EntityManagerInterface $em; + public const ADMIN = 'user-admin'; - public function __construct(UserPasswordHasherInterface $encoder, EntityManagerInterface $entityManager) + public function __construct(protected UserPasswordHasherInterface $encoder, protected EntityManagerInterface $em) { - $this->em = $entityManager; - $this->encoder = $encoder; } public function load(ObjectManager $manager): void { $anonymous = new User(); $anonymous->setName('anonymous'); - $anonymous->setGroup($this->getReference(GroupFixtures::READONLY)); + $anonymous->setGroup($this->getReference(GroupFixtures::READONLY, Group::class)); $anonymous->setNeedPwChange(false); $anonymous->setPassword($this->encoder->hashPassword($anonymous, 'test')); $manager->persist($anonymous); @@ -52,15 +51,17 @@ class UserFixtures extends Fixture $admin->setName('admin'); $admin->setPassword($this->encoder->hashPassword($admin, 'test')); $admin->setNeedPwChange(false); - $admin->setGroup($this->getReference(GroupFixtures::ADMINS)); + $admin->setGroup($this->getReference(GroupFixtures::ADMINS, Group::class)); $manager->persist($admin); + $this->addReference(self::ADMIN, $admin); $user = new User(); $user->setName('user'); $user->setNeedPwChange(false); + $user->setEmail('user@invalid.invalid'); $user->setFirstName('Test')->setLastName('User'); $user->setPassword($this->encoder->hashPassword($user, 'test')); - $user->setGroup($this->getReference(GroupFixtures::USERS)); + $user->setGroup($this->getReference(GroupFixtures::USERS, Group::class)); $manager->persist($user); $noread = new User(); @@ -70,5 +71,15 @@ class UserFixtures extends Fixture $manager->persist($noread); $manager->flush(); + + //Ensure that the anonymous user has the ID 0 + $manager->getRepository(User::class)->changeID($anonymous, User::ID_ANONYMOUS); + } + + public function getDependencies(): array + { + return [ + GroupFixtures::class, + ]; } } diff --git a/src/DataTables/Adapters/CustomFetchJoinORMAdapter.php b/src/DataTables/Adapters/CustomFetchJoinORMAdapter.php new file mode 100644 index 00000000..ff69a69e --- /dev/null +++ b/src/DataTables/Adapters/CustomFetchJoinORMAdapter.php @@ -0,0 +1,55 @@ +. + */ +namespace App\DataTables\Adapters; + +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Tools\Pagination\Paginator; +use Omines\DataTablesBundle\Adapter\Doctrine\FetchJoinORMAdapter; + +/** + * This class is a workaround for a bug (or edge case behavior) in the FetchJoinORMAdapter or better the used Paginator + * and CountOutputWalker. + * If the query contains multiple GROUP BY clauses, the result of the count query is wrong, as some lines are counted + * multiple times. This is because the CountOutputWalker does not use DISTINCT in the count query, if it contains a GROUP BY. + * + * We work around this by removing the GROUP BY clause from the query, and only adding the first root alias as GROUP BY (the part table). + * This way we get the correct count, without breaking the query (we need a GROUP BY for the HAVING clauses). + * + * As a side effect this also seems to improve the performance of the count query a bit (which makes up a lot of the total query time). + */ +class CustomFetchJoinORMAdapter extends FetchJoinORMAdapter +{ + public function getCount(QueryBuilder $queryBuilder, $identifier): int + { + $qb_without_group_by = clone $queryBuilder; + + //Remove the groupBy clause from the query for the count + //And add the root alias as group by, so we can use HAVING clauses + $qb_without_group_by->resetDQLPart('groupBy'); + $qb_without_group_by->addGroupBy($queryBuilder->getRootAliases()[0]); + + $paginator = new Paginator($qb_without_group_by); + + return $paginator->count(); + } +} diff --git a/src/DataTables/Adapters/FetchResultsAtOnceORMAdapter.php b/src/DataTables/Adapters/FetchResultsAtOnceORMAdapter.php new file mode 100644 index 00000000..eda48038 --- /dev/null +++ b/src/DataTables/Adapters/FetchResultsAtOnceORMAdapter.php @@ -0,0 +1,67 @@ +. + */ + +declare(strict_types=1); + + +namespace App\DataTables\Adapters; + +use Doctrine\ORM\QueryBuilder; +use Omines\DataTablesBundle\Adapter\AdapterQuery; +use Omines\DataTablesBundle\Adapter\Doctrine\Event\ORMAdapterQueryEvent; +use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter; +use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapterEvents; +use Omines\DataTablesBundle\Column\AbstractColumn; + +/** + * This class is very similar to the original ORMAdapter, but instead of getting each line one by one, it gets all the results at once, + * which can save some time in combination with fetch hints. + */ +class FetchResultsAtOnceORMAdapter extends ORMAdapter +{ + protected function getResults(AdapterQuery $query): \Traversable + { + /** @var QueryBuilder $builder */ + $builder = $query->get('qb'); + $state = $query->getState(); + + // Apply definitive view state for current 'page' of the table + foreach ($state->getOrderBy() as [$column, $direction]) { + /** @var AbstractColumn $column */ + if ($column->isOrderable()) { + $builder->addOrderBy($column->getOrderField(), $direction); + } + } + if (null !== $state->getLength()) { + $builder + ->setFirstResult($state->getStart()) + ->setMaxResults($state->getLength()) + ; + } + + $q = $builder->getQuery(); + $event = new ORMAdapterQueryEvent($q); + $state->getDataTable()->getEventDispatcher()->dispatch($event, ORMAdapterEvents::PRE_QUERY); + + foreach ($q->getResult() as $item) { + yield $item; + } + } +} \ No newline at end of file diff --git a/src/DataTables/Adapters/TwoStepORMAdapter.php b/src/DataTables/Adapters/TwoStepORMAdapter.php new file mode 100644 index 00000000..51315c32 --- /dev/null +++ b/src/DataTables/Adapters/TwoStepORMAdapter.php @@ -0,0 +1,208 @@ +. + */ + +declare(strict_types=1); + + +namespace App\DataTables\Adapters; + +use Doctrine\ORM\Query\Expr\From; +use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Tools\Pagination\Paginator; +use Doctrine\Persistence\ManagerRegistry; +use Omines\DataTablesBundle\Adapter\AdapterQuery; +use Omines\DataTablesBundle\Adapter\Doctrine\Event\ORMAdapterQueryEvent; +use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter; +use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapterEvents; +use Omines\DataTablesBundle\Column\AbstractColumn; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * This adapter fetches entities from the database in two steps: + * In the first step, it uses a simple query (filter_query option), which returns only the ids of the main entity, + * to get the count of results, doing filtering and pagination. + * + * In the next step the IDs are passed to the detail_query callback option, which then can do a more complex queries (like fetch join) + * to get the entities and related stuff in an efficient way. + * This way we save the overhead of the fetch join query for the count and counting, which can be very slow, cause + * no indexes can be used. + */ +class TwoStepORMAdapter extends ORMAdapter +{ + private \Closure $detailQueryCallable; + + private bool $use_simple_total = false; + + private \Closure|null $query_modifier = null; + + public function __construct(?ManagerRegistry $registry = null) + { + parent::__construct($registry); + $this->detailQueryCallable = static function (QueryBuilder $qb, array $ids): never { + throw new \RuntimeException('You need to set the detail_query option to use the TwoStepORMAdapter'); + }; + } + + protected function configureOptions(OptionsResolver $resolver): void + { + parent::configureOptions($resolver); + + $resolver->setRequired('filter_query'); + $resolver->setDefault('query', fn(Options $options) => $options['filter_query']); + + $resolver->setRequired('detail_query'); + $resolver->setAllowedTypes('detail_query', \Closure::class); + + /* + * Add the possibility to replace the query for total entity count through a very simple one, to improve performance. + * You can only use this option, if you did not apply any criteria to your total count. + */ + $resolver->setDefault('simple_total_query', false); + + //Add the possibility of a closure to modify the query builder before the query is executed + $resolver->setDefault('query_modifier', null); + $resolver->setAllowedTypes('query_modifier', ['null', \Closure::class]); + + } + + protected function afterConfiguration(array $options): void + { + parent::afterConfiguration($options); + $this->detailQueryCallable = $options['detail_query']; + $this->use_simple_total = $options['simple_total_query']; + $this->query_modifier = $options['query_modifier']; + } + + protected function prepareQuery(AdapterQuery $query): void + { + //Like the parent class, but we add the possibility to use the simple total query + + $state = $query->getState(); + $query->set('qb', $builder = $this->createQueryBuilder($state)); + $query->set('rootAlias', $rootAlias = $builder->getDQLPart('from')[0]->getAlias()); + + // Provide default field mappings if needed + foreach ($state->getDataTable()->getColumns() as $column) { + if (null === $column->getField() && isset($this->metadata->fieldMappings[$name = $column->getName()])) { + $column->setOption('field', "{$rootAlias}.{$name}"); + } + } + + /** @var From $fromClause */ + $fromClause = $builder->getDQLPart('from')[0]; + $identifier = "{$fromClause->getAlias()}.{$this->metadata->getSingleIdentifierFieldName()}"; + + // Use simpler (faster) total count query if the user wanted so... + if ($this->use_simple_total) { + $query->setTotalRows($this->getSimpleTotalCount($builder)); + } else { + $query->setTotalRows($this->getCount($builder, $identifier)); + } + + // Get record count after filtering + $this->buildCriteria($builder, $state); + $query->setFilteredRows($this->getCount($builder, $identifier)); + + // Perform mapping of all referred fields and implied fields + $aliases = $this->getAliases($query); + $query->set('aliases', $aliases); + $query->setIdentifierPropertyPath($this->mapFieldToPropertyPath($identifier, $aliases)); + } + + protected function hasGroupByPart(string $identifier, array $gbList): bool + { + //Always return true, to fix the issue with the count query, when having mutliple group by parts + return true; + } + + protected function getCount(QueryBuilder $queryBuilder, $identifier): int + { + if ($this->query_modifier !== null) { + $queryBuilder = $this->query_modifier->__invoke(clone $queryBuilder); + } + + //Check if the queryBuilder is having a HAVING clause, which would make the count query invalid + if (empty($queryBuilder->getDQLPart('having'))) { + //If not, we can use the simple count query + return parent::getCount($queryBuilder, $identifier); + } + + //Otherwise Use the paginator, which uses a subquery to get the right count even with HAVING clauses + return (new Paginator($queryBuilder, false))->count(); + } + + protected function getResults(AdapterQuery $query): \Traversable + { + //Very similar to the parent class + /** @var QueryBuilder $builder */ + $builder = $query->get('qb'); + $state = $query->getState(); + + // Apply definitive view state for current 'page' of the table + foreach ($state->getOrderBy() as [$column, $direction]) { + /** @var AbstractColumn $column */ + if ($column->isOrderable()) { + $builder->addOrderBy($column->getOrderField(), $direction); + } + } + if (null !== $state->getLength()) { + $builder + ->setFirstResult($state->getStart()) + ->setMaxResults($state->getLength()) + ; + } + + //Apply the query modifier, if set + if ($this->query_modifier !== null) { + $builder = $this->query_modifier->__invoke($builder); + } + + $id_query = $builder->getQuery(); + $event = new ORMAdapterQueryEvent($id_query); + $state->getDataTable()->getEventDispatcher()->dispatch($event, ORMAdapterEvents::PRE_QUERY); + + //In the first step we only get the ids of the main entity... + $ids = $id_query->getArrayResult(); + + //Which is then passed to the detailQuery, which filters for the entities based on the IDs + $detail_qb = $this->manager->createQueryBuilder(); + $this->detailQueryCallable->__invoke($detail_qb, $ids); + + $detail_query = $detail_qb->getQuery(); + + //We pass the results of the detail query to the datatable for view rendering + foreach ($detail_query->getResult() as $item) { + yield $item; + } + } + + protected function getSimpleTotalCount(QueryBuilder $queryBuilder): int + { + /** The paginator count queries can be rather slow, so when query for total count (100ms or longer), + * just return the entity count. + */ + /** @var From $from_expr */ + $from_expr = $queryBuilder->getDQLPart('from')[0]; + + return $this->manager->getRepository($from_expr->getFrom())->count([]); + } +} \ No newline at end of file diff --git a/src/DataTables/AttachmentDataTable.php b/src/DataTables/AttachmentDataTable.php index dd82f5a8..16e6a7a7 100644 --- a/src/DataTables/AttachmentDataTable.php +++ b/src/DataTables/AttachmentDataTable.php @@ -27,7 +27,6 @@ use App\DataTables\Column\PrettyBoolColumn; use App\DataTables\Column\RowClassColumn; use App\DataTables\Filters\AttachmentFilter; use App\Entity\Attachments\Attachment; -use App\Entity\LogSystem\AbstractLogEntry; use App\Services\Attachments\AttachmentManager; use App\Services\Attachments\AttachmentURLGenerator; use App\Services\ElementTypeNameGenerator; @@ -35,6 +34,7 @@ use App\Services\EntityURLGenerator; use Doctrine\ORM\QueryBuilder; use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider; use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter; +use Omines\DataTablesBundle\Column\NumberColumn; use Omines\DataTablesBundle\Column\TextColumn; use Omines\DataTablesBundle\DataTable; use Omines\DataTablesBundle\DataTableTypeInterface; @@ -42,29 +42,16 @@ use Symfony\Contracts\Translation\TranslatorInterface; final class AttachmentDataTable implements DataTableTypeInterface { - private TranslatorInterface $translator; - private EntityURLGenerator $entityURLGenerator; - private AttachmentManager $attachmentHelper; - private ElementTypeNameGenerator $elementTypeNameGenerator; - private AttachmentURLGenerator $attachmentURLGenerator; - - public function __construct(TranslatorInterface $translator, EntityURLGenerator $entityURLGenerator, - AttachmentManager $attachmentHelper, AttachmentURLGenerator $attachmentURLGenerator, - ElementTypeNameGenerator $elementTypeNameGenerator) + public function __construct(private readonly TranslatorInterface $translator, private readonly EntityURLGenerator $entityURLGenerator, private readonly AttachmentManager $attachmentHelper, private readonly AttachmentURLGenerator $attachmentURLGenerator, private readonly ElementTypeNameGenerator $elementTypeNameGenerator) { - $this->translator = $translator; - $this->entityURLGenerator = $entityURLGenerator; - $this->attachmentHelper = $attachmentHelper; - $this->elementTypeNameGenerator = $elementTypeNameGenerator; - $this->attachmentURLGenerator = $attachmentURLGenerator; } public function configure(DataTable $dataTable, array $options): void { $dataTable->add('dont_matter', RowClassColumn::class, [ - 'render' => function ($value, Attachment $context) { - //Mark attachments with missing files yellow - if(!$this->attachmentHelper->isFileExisting($context)){ + 'render' => function ($value, Attachment $context): string { + //Mark attachments yellow which have an internal file linked that doesn't exist + if($context->hasInternal() && !$this->attachmentHelper->isInternalFileExisting($context)){ return 'table-warning'; } @@ -75,10 +62,10 @@ final class AttachmentDataTable implements DataTableTypeInterface $dataTable->add('picture', TextColumn::class, [ 'label' => '', 'className' => 'no-colvis', - 'render' => function ($value, Attachment $context) { + 'render' => function ($value, Attachment $context): string { if ($context->isPicture() - && !$context->isExternal() - && $this->attachmentHelper->isFileExisting($context)) { + && $this->attachmentHelper->isInternalFileExisting($context)) { + $title = htmlspecialchars($context->getName()); if ($context->getFilename()) { $title .= ' ('.htmlspecialchars($context->getFilename()).')'; @@ -98,19 +85,43 @@ final class AttachmentDataTable implements DataTableTypeInterface }, ]); + $dataTable->add('id', NumberColumn::class, [ + 'label' => $this->translator->trans('part.table.id'), + 'visible' => false, + ]); + $dataTable->add('name', TextColumn::class, [ 'label' => 'attachment.edit.name', - 'render' => function ($value, Attachment $context) { - //Link to external source - if ($context->isExternal()) { - return sprintf( - '%s', - htmlspecialchars($context->getURL()), - htmlspecialchars($value) - ); - } + 'orderField' => 'NATSORT(attachment.name)', + ]); - if ($this->attachmentHelper->isFileExisting($context)) { + $dataTable->add('attachment_type', TextColumn::class, [ + 'label' => 'attachment.table.type', + 'field' => 'attachment_type.name', + 'orderField' => 'NATSORT(attachment_type.name)', + 'render' => fn($value, Attachment $context): string => sprintf( + '%s', + $this->entityURLGenerator->editURL($context->getAttachmentType()), + htmlspecialchars((string) $value) + ), + ]); + + $dataTable->add('element', TextColumn::class, [ + 'label' => 'attachment.table.element', + //'propertyPath' => 'element.name', + 'render' => fn($value, Attachment $context): string => sprintf( + '%s', + $this->entityURLGenerator->infoURL($context->getElement()), + $this->elementTypeNameGenerator->getTypeNameCombination($context->getElement(), true) + ), + ]); + + $dataTable->add('internal_link', TextColumn::class, [ + 'label' => 'attachment.table.internal_file', + 'propertyPath' => 'filename', + 'orderField' => 'NATSORT(attachment.original_filename)', + 'render' => function ($value, Attachment $context) { + if ($this->attachmentHelper->isInternalFileExisting($context)) { return sprintf( '%s', $this->entityURLGenerator->viewURL($context), @@ -119,52 +130,46 @@ final class AttachmentDataTable implements DataTableTypeInterface } return $value; - }, + } ]); - $dataTable->add('attachment_type', TextColumn::class, [ - 'label' => 'attachment.table.type', - 'field' => 'attachment_type.name', + $dataTable->add('external_link', TextColumn::class, [ + 'label' => 'attachment.table.external_link', + 'propertyPath' => 'host', + 'orderField' => 'attachment.external_path', 'render' => function ($value, Attachment $context) { - return sprintf( - '%s', - $this->entityURLGenerator->editURL($context->getAttachmentType()), - htmlspecialchars($value) - ); - }, - ]); + if ($context->hasExternal()) { + return sprintf( + '%s', + htmlspecialchars((string) $context->getExternalPath()), + htmlspecialchars((string) $context->getExternalPath()), + htmlspecialchars($value), + ); + } - $dataTable->add('element', TextColumn::class, [ - 'label' => 'attachment.table.element', - //'propertyPath' => 'element.name', - 'render' => function ($value, Attachment $context) { - return sprintf( - '%s', - $this->entityURLGenerator->infoURL($context->getElement()), - $this->elementTypeNameGenerator->getTypeNameCombination($context->getElement(), true) - ); - }, - ]); - - $dataTable->add('filename', TextColumn::class, [ - 'label' => $this->translator->trans('attachment.table.filename'), - 'propertyPath' => 'filename', + return $value; + } ]); $dataTable->add('filesize', TextColumn::class, [ 'label' => $this->translator->trans('attachment.table.filesize'), 'render' => function ($value, Attachment $context) { - if ($context->isExternal()) { + if (!$context->hasInternal()) { return sprintf( ' %s ', - $this->translator->trans('attachment.external') + $this->translator->trans('attachment.external_only') ); } - if ($this->attachmentHelper->isFileExisting($context)) { - return $this->attachmentHelper->getHumanFileSize($context); + if ($this->attachmentHelper->isInternalFileExisting($context)) { + return sprintf( + ' + %s + ', + $this->attachmentHelper->getHumanFileSize($context) + ); } return sprintf( @@ -238,7 +243,7 @@ final class AttachmentDataTable implements DataTableTypeInterface //We do the most stuff here in the filter class if (isset($options['filter'])) { if(!$options['filter'] instanceof AttachmentFilter) { - throw new \Exception('filter must be an instance of AttachmentFilter!'); + throw new \RuntimeException('filter must be an instance of AttachmentFilter!'); } $filter = $options['filter']; diff --git a/src/DataTables/Column/EntityColumn.php b/src/DataTables/Column/EntityColumn.php index d48d6da1..54ae3fb3 100644 --- a/src/DataTables/Column/EntityColumn.php +++ b/src/DataTables/Column/EntityColumn.php @@ -22,9 +22,8 @@ declare(strict_types=1); namespace App\DataTables\Column; -use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractNamedDBElement; -use App\Entity\Parts\Part; +use App\Entity\Base\AbstractStructuralDBElement; use App\Services\EntityURLGenerator; use Omines\DataTablesBundle\Column\AbstractColumn; use Symfony\Component\OptionsResolver\Options; @@ -33,13 +32,8 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; class EntityColumn extends AbstractColumn { - protected EntityURLGenerator $urlGenerator; - protected PropertyAccessorInterface $accessor; - - public function __construct(EntityURLGenerator $URLGenerator, PropertyAccessorInterface $accessor) + public function __construct(protected EntityURLGenerator $urlGenerator, protected PropertyAccessorInterface $accessor) { - $this->urlGenerator = $URLGenerator; - $this->accessor = $accessor; } /** @@ -48,46 +42,46 @@ class EntityColumn extends AbstractColumn * @param mixed $value The single value of the column * @return mixed */ - public function normalize($value) + public function normalize(mixed $value): mixed { /** @var AbstractNamedDBElement $value */ return $value; } - public function configureOptions(OptionsResolver $resolver): self + /** + * @return $this + */ + public function configureOptions(OptionsResolver $resolver): static { parent::configureOptions($resolver); $resolver->setRequired('property'); - $resolver->setDefault('field', static function (Options $option) { - return $option['property'].'.name'; - }); + $resolver->setDefault('field', static fn(Options $option): string => $option['property'].'.name'); - $resolver->setDefault('render', function (Options $options) { - return function ($value, $context) use ($options) { - if ($this->accessor->isReadable($context, $options['property'])) { - $entity = $this->accessor->getValue($context, $options['property']); - } else { - $entity = null; + $resolver->setDefault('render', fn(Options $options) => function ($value, $context) use ($options): string { + if ($this->accessor->isReadable($context, $options['property'])) { + $entity = $this->accessor->getValue($context, $options['property']); + } else { + $entity = null; + } + + /** @var AbstractNamedDBElement|null $entity */ + + if ($entity instanceof AbstractNamedDBElement) { + if (null !== $entity->getID()) { + return sprintf( + '%s', + $this->urlGenerator->listPartsURL($entity), + $entity instanceof AbstractStructuralDBElement ? htmlspecialchars($entity->getFullPath()) : htmlspecialchars($entity->getName()), + htmlspecialchars($entity->getName()) + ); } - /** @var AbstractNamedDBElement|null $entity */ + return sprintf('%s', $value); + } - if (null !== $entity) { - if (null !== $entity->getID()) { - return sprintf( - '%s', - $this->urlGenerator->listPartsURL($entity), - $entity->getName() - ); - } - - return sprintf('%s', $value); - } - - return ''; - }; + return ''; }); return $this; diff --git a/src/DataTables/Column/EnumColumn.php b/src/DataTables/Column/EnumColumn.php new file mode 100644 index 00000000..5a5d998d --- /dev/null +++ b/src/DataTables/Column/EnumColumn.php @@ -0,0 +1,70 @@ +. + */ +namespace App\DataTables\Column; + +use Omines\DataTablesBundle\Column\AbstractColumn; +use Symfony\Component\OptionsResolver\OptionsResolver; +use UnitEnum; + +/** + * @template T of UnitEnum + */ +class EnumColumn extends AbstractColumn +{ + + /** + * @phpstan-return T|null + */ + public function normalize($value): ?UnitEnum + { + if ($value === null) { + return null; + } + + if (is_a($value, $this->getEnumClass())) { + return $value; + } + + //@phpstan-ignore-next-line + return ($this->getEnumClass())::from($value); + } + + protected function configureOptions(OptionsResolver $resolver): static + { + parent::configureOptions($resolver); + + $resolver->setRequired('class'); + $resolver->setAllowedTypes('class', 'string'); + $resolver->addAllowedValues('class', enum_exists(...)); + + return $this; + } + + /** + * @return class-string + */ + public function getEnumClass(): string + { + return $this->options['class']; + } +} diff --git a/src/DataTables/Column/IconLinkColumn.php b/src/DataTables/Column/IconLinkColumn.php index d1034e56..6704cb4a 100644 --- a/src/DataTables/Column/IconLinkColumn.php +++ b/src/DataTables/Column/IconLinkColumn.php @@ -50,12 +50,15 @@ class IconLinkColumn extends AbstractColumn * @param $value * @return mixed */ - public function normalize($value) + public function normalize($value): mixed { return $value; } - public function configureOptions(OptionsResolver $resolver): self + /** + * @return $this + */ + public function configureOptions(OptionsResolver $resolver): static { parent::configureOptions($resolver); $resolver->setDefaults([ diff --git a/src/DataTables/Column/LocaleDateTimeColumn.php b/src/DataTables/Column/LocaleDateTimeColumn.php index 3904a4cd..e60b2867 100644 --- a/src/DataTables/Column/LocaleDateTimeColumn.php +++ b/src/DataTables/Column/LocaleDateTimeColumn.php @@ -31,22 +31,23 @@ use Omines\DataTablesBundle\Column\AbstractColumn; use Symfony\Component\OptionsResolver\OptionsResolver; /** - * Similar to the built in DateTimeColumn, but the datetime is formatted using a IntlDateFormatter, + * Similar to the built-in DateTimeColumn, but the datetime is formatted using a IntlDateFormatter, * to get prettier locale based formatting. */ class LocaleDateTimeColumn extends AbstractColumn { /** * @param $value - * @return string * @throws Exception */ public function normalize($value): string { if (null === $value) { return $this->options['nullValue']; - } elseif (!$value instanceof DateTimeInterface) { - $value = new DateTime((string) $value); + } + + if (!$value instanceof DateTimeInterface) { + $value = new \DateTimeImmutable((string) $value); } $formatValues = [ @@ -78,7 +79,7 @@ class LocaleDateTimeColumn extends AbstractColumn ); } - protected function configureOptions(OptionsResolver $resolver): self + protected function configureOptions(OptionsResolver $resolver): static { parent::configureOptions($resolver); diff --git a/src/DataTables/Column/LogEntryExtraColumn.php b/src/DataTables/Column/LogEntryExtraColumn.php index da6b4865..e0281e42 100644 --- a/src/DataTables/Column/LogEntryExtraColumn.php +++ b/src/DataTables/Column/LogEntryExtraColumn.php @@ -28,20 +28,15 @@ use Symfony\Contracts\Translation\TranslatorInterface; class LogEntryExtraColumn extends AbstractColumn { - protected TranslatorInterface $translator; - protected LogEntryExtraFormatter $formatter; - - public function __construct(TranslatorInterface $translator, LogEntryExtraFormatter $formatter) + public function __construct(protected TranslatorInterface $translator, protected LogEntryExtraFormatter $formatter) { - $this->translator = $translator; - $this->formatter = $formatter; } /** * @param $value * @return mixed */ - public function normalize($value) + public function normalize($value): mixed { return $value; } diff --git a/src/DataTables/Column/LogEntryTargetColumn.php b/src/DataTables/Column/LogEntryTargetColumn.php index 4aaeb069..7a329cd7 100644 --- a/src/DataTables/Column/LogEntryTargetColumn.php +++ b/src/DataTables/Column/LogEntryTargetColumn.php @@ -22,54 +22,26 @@ declare(strict_types=1); namespace App\DataTables\Column; -use App\Entity\Attachments\Attachment; -use App\Entity\Base\AbstractDBElement; -use App\Entity\Contracts\NamedElementInterface; -use App\Entity\LogSystem\AbstractLogEntry; -use App\Entity\LogSystem\UserNotAllowedLogEntry; -use App\Entity\Parameters\AbstractParameter; -use App\Entity\Parts\PartLot; -use App\Entity\PriceInformations\Orderdetail; -use App\Entity\PriceInformations\Pricedetail; -use App\Entity\ProjectSystem\ProjectBOMEntry; -use App\Exceptions\EntityNotSupportedException; -use App\Repository\LogEntryRepository; -use App\Services\ElementTypeNameGenerator; -use App\Services\EntityURLGenerator; -use Doctrine\ORM\EntityManagerInterface; +use App\Services\LogSystem\LogTargetHelper; use Omines\DataTablesBundle\Column\AbstractColumn; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Contracts\Translation\TranslatorInterface; class LogEntryTargetColumn extends AbstractColumn { - protected EntityManagerInterface $em; - protected LogEntryRepository $entryRepository; - protected EntityURLGenerator $entityURLGenerator; - protected ElementTypeNameGenerator $elementTypeNameGenerator; - protected TranslatorInterface $translator; - - public function __construct(EntityManagerInterface $entityManager, EntityURLGenerator $entityURLGenerator, - ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator) + public function __construct(private readonly LogTargetHelper $logTargetHelper) { - $this->em = $entityManager; - $this->entryRepository = $entityManager->getRepository(AbstractLogEntry::class); - - $this->entityURLGenerator = $entityURLGenerator; - $this->elementTypeNameGenerator = $elementTypeNameGenerator; - $this->translator = $translator; } /** * @param $value * @return mixed */ - public function normalize($value) + public function normalize($value): mixed { return $value; } - public function configureOptions(OptionsResolver $resolver): self + public function configureOptions(OptionsResolver $resolver): static { parent::configureOptions($resolver); $resolver->setDefault('show_associated', true); @@ -80,71 +52,9 @@ class LogEntryTargetColumn extends AbstractColumn public function render($value, $context): string { - if ($context instanceof UserNotAllowedLogEntry && $this->options['showAccessDeniedPath']) { - return htmlspecialchars($context->getPath()); - } - - /** @var AbstractLogEntry $context */ - $target = $this->entryRepository->getTargetElement($context); - - $tmp = ''; - - //The element is existing - if ($target instanceof NamedElementInterface && !empty($target->getName())) { - try { - $tmp = sprintf( - '%s', - $this->entityURLGenerator->infoURL($target), - $this->elementTypeNameGenerator->getTypeNameCombination($target, true) - ); - } catch (EntityNotSupportedException $exception) { - $tmp = $this->elementTypeNameGenerator->getTypeNameCombination($target, true); - } - } elseif ($target instanceof AbstractDBElement) { //Target does not have a name - $tmp = sprintf( - '%s: %s', - $this->elementTypeNameGenerator->getLocalizedTypeLabel($target), - $target->getID() - ); - } elseif (null === $target && $context->hasTarget()) { //Element was deleted - $tmp = sprintf( - '%s: %s [%s]', - $this->elementTypeNameGenerator->getLocalizedTypeLabel($context->getTargetClass()), - $context->getTargetID(), - $this->translator->trans('log.target_deleted') - ); - } - - //Add a hint to the associated element if possible - if (null !== $target && $this->options['show_associated']) { - if ($target instanceof Attachment && null !== $target->getElement()) { - $on = $target->getElement(); - } elseif ($target instanceof AbstractParameter && null !== $target->getElement()) { - $on = $target->getElement(); - } elseif ($target instanceof PartLot && null !== $target->getPart()) { - $on = $target->getPart(); - } elseif ($target instanceof Orderdetail && null !== $target->getPart()) { - $on = $target->getPart(); - } elseif ($target instanceof Pricedetail && null !== $target->getOrderdetail() && null !== $target->getOrderdetail()->getPart()) { - $on = $target->getOrderdetail()->getPart(); - } elseif ($target instanceof ProjectBOMEntry && null !== $target->getProject()) { - $on = $target->getProject(); - } - - if (isset($on) && is_object($on)) { - try { - $tmp .= sprintf( - ' (%s)', - $this->entityURLGenerator->infoURL($on), - $this->elementTypeNameGenerator->getTypeNameCombination($on, true) - ); - } catch (EntityNotSupportedException $exception) { - $tmp .= ' ('.$this->elementTypeNameGenerator->getTypeNameCombination($target, true).')'; - } - } - } - - //Log is not associated with an element - return $tmp; + return $this->logTargetHelper->formatTarget($context, [ + 'showAccessDeniedPath' => $this->options['showAccessDeniedPath'], + 'show_associated' => $this->options['show_associated'], + ]); } } diff --git a/src/DataTables/Column/MarkdownColumn.php b/src/DataTables/Column/MarkdownColumn.php index 4e3dc9ff..41f62649 100644 --- a/src/DataTables/Column/MarkdownColumn.php +++ b/src/DataTables/Column/MarkdownColumn.php @@ -27,20 +27,17 @@ use Omines\DataTablesBundle\Column\AbstractColumn; class MarkdownColumn extends AbstractColumn { - protected MarkdownParser $markdown; - - public function __construct(MarkdownParser $markdown) + public function __construct(protected MarkdownParser $markdown) { - $this->markdown = $markdown; } /** * The normalize function is responsible for converting parsed and processed data to a datatables-appropriate type. * * @param mixed $value The single value of the column - * @return mixed + * @return string */ - public function normalize($value) + public function normalize(mixed $value): string { return $this->markdown->markForRendering($value, true); } diff --git a/src/DataTables/Column/PartAttachmentsColumn.php b/src/DataTables/Column/PartAttachmentsColumn.php index 48ab3201..7b209028 100644 --- a/src/DataTables/Column/PartAttachmentsColumn.php +++ b/src/DataTables/Column/PartAttachmentsColumn.php @@ -33,15 +33,8 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class PartAttachmentsColumn extends AbstractColumn { - protected FAIconGenerator $FAIconGenerator; - protected EntityURLGenerator $urlGenerator; - protected AttachmentManager $attachmentManager; - - public function __construct(FAIconGenerator $FAIconGenerator, EntityURLGenerator $urlGenerator, AttachmentManager $attachmentManager) + public function __construct(protected FAIconGenerator $FAIconGenerator, protected EntityURLGenerator $urlGenerator, protected AttachmentManager $attachmentManager) { - $this->FAIconGenerator = $FAIconGenerator; - $this->urlGenerator = $urlGenerator; - $this->attachmentManager = $attachmentManager; } /** @@ -50,7 +43,7 @@ class PartAttachmentsColumn extends AbstractColumn * @param mixed $value The single value of the column * @return mixed */ - public function normalize($value) + public function normalize(mixed $value): mixed { return $value; } @@ -61,9 +54,7 @@ class PartAttachmentsColumn extends AbstractColumn throw new RuntimeException('$context must be a Part object!'); } $tmp = ''; - $attachments = $context->getAttachments()->filter(function (Attachment $attachment) { - return $attachment->getShowInTable() && $this->attachmentManager->isFileExisting($attachment); - }); + $attachments = $context->getAttachments()->filter(fn(Attachment $attachment) => $attachment->getShowInTable() && $this->attachmentManager->isFileExisting($attachment)); $count = 5; foreach ($attachments as $attachment) { @@ -88,7 +79,7 @@ class PartAttachmentsColumn extends AbstractColumn return $tmp; } - public function configureOptions(OptionsResolver $resolver): self + public function configureOptions(OptionsResolver $resolver): static { parent::configureOptions($resolver); diff --git a/src/DataTables/Column/PrettyBoolColumn.php b/src/DataTables/Column/PrettyBoolColumn.php index 3a74db6e..912a9122 100644 --- a/src/DataTables/Column/PrettyBoolColumn.php +++ b/src/DataTables/Column/PrettyBoolColumn.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Column; use Omines\DataTablesBundle\Column\AbstractColumn; @@ -25,11 +27,8 @@ use Symfony\Contracts\Translation\TranslatorInterface; class PrettyBoolColumn extends AbstractColumn { - protected TranslatorInterface $translator; - - public function __construct(TranslatorInterface $translator) + public function __construct(protected TranslatorInterface $translator) { - $this->translator = $translator; } public function normalize($value): ?bool @@ -41,7 +40,7 @@ class PrettyBoolColumn extends AbstractColumn return (bool) $value; } - public function render($value, $context) + public function render($value, $context): string { if ($value === true) { return ' ' @@ -63,4 +62,4 @@ class PrettyBoolColumn extends AbstractColumn throw new \RuntimeException('Unexpected value!'); } -} \ No newline at end of file +} diff --git a/src/DataTables/Column/RevertLogColumn.php b/src/DataTables/Column/RevertLogColumn.php index 2b71d8f0..6ad365c1 100644 --- a/src/DataTables/Column/RevertLogColumn.php +++ b/src/DataTables/Column/RevertLogColumn.php @@ -41,30 +41,25 @@ declare(strict_types=1); namespace App\DataTables\Column; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\LogSystem\CollectionElementDeleted; use App\Entity\LogSystem\ElementCreatedLogEntry; use App\Entity\LogSystem\ElementDeletedLogEntry; use App\Entity\LogSystem\ElementEditedLogEntry; use Omines\DataTablesBundle\Column\AbstractColumn; -use Symfony\Component\Security\Core\Security; use Symfony\Contracts\Translation\TranslatorInterface; class RevertLogColumn extends AbstractColumn { - protected TranslatorInterface $translator; - protected Security $security; - - public function __construct(TranslatorInterface $translator, Security $security) + public function __construct(protected TranslatorInterface $translator, protected Security $security) { - $this->translator = $translator; - $this->security = $security; } /** * @param $value * @return mixed */ - public function normalize($value) + public function normalize($value): mixed { return $value; } @@ -73,13 +68,13 @@ class RevertLogColumn extends AbstractColumn { if ( $context instanceof CollectionElementDeleted - || ($context instanceof ElementDeletedLogEntry && $context->hasOldDataInformations()) + || ($context instanceof ElementDeletedLogEntry && $context->hasOldDataInformation()) ) { $icon = 'fa-trash-restore'; $title = $this->translator->trans('log.undo.undelete'); } elseif ( $context instanceof ElementCreatedLogEntry - || ($context instanceof ElementEditedLogEntry && $context->hasOldDataInformations()) + || ($context instanceof ElementEditedLogEntry && $context->hasOldDataInformation()) ) { $icon = 'fa-undo'; $title = $this->translator->trans('log.undo.undo'); @@ -105,8 +100,6 @@ class RevertLogColumn extends AbstractColumn $this->translator->trans('log.undo.revert') ); - $tmp .= '
'; - - return $tmp; + return $tmp . '
'; } } diff --git a/src/DataTables/Column/RowClassColumn.php b/src/DataTables/Column/RowClassColumn.php index 4ac61c02..15bf8bf2 100644 --- a/src/DataTables/Column/RowClassColumn.php +++ b/src/DataTables/Column/RowClassColumn.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Column; use Omines\DataTablesBundle\Column\AbstractColumn; @@ -26,8 +28,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class RowClassColumn extends AbstractColumn { - - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): static { parent::configureOptions($resolver); @@ -42,14 +43,17 @@ class RowClassColumn extends AbstractColumn return $this; } - public function initialize(string $name, int $index, array $options, DataTable $dataTable) + public function initialize(string $name, int $index, array $options, DataTable $dataTable): void { //The field name is always "$$rowClass" as this is the name the frontend controller expects parent::initialize('$$rowClass', $index, $options, $dataTable); // TODO: Change the autogenerated stub } - public function normalize($value) + /** + * @return mixed + */ + public function normalize($value): mixed { return $value; } -} \ No newline at end of file +} diff --git a/src/DataTables/Column/SIUnitNumberColumn.php b/src/DataTables/Column/SIUnitNumberColumn.php index a66bc868..b64152be 100644 --- a/src/DataTables/Column/SIUnitNumberColumn.php +++ b/src/DataTables/Column/SIUnitNumberColumn.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Column; use App\Services\Formatters\SIFormatter; @@ -26,14 +28,11 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class SIUnitNumberColumn extends AbstractColumn { - protected SIFormatter $formatter; - - public function __construct(SIFormatter $formatter) + public function __construct(protected SIFormatter $formatter) { - $this->formatter = $formatter; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): static { parent::configureOptions($resolver); @@ -43,13 +42,13 @@ class SIUnitNumberColumn extends AbstractColumn return $this; } - public function normalize($value) + public function normalize($value): string { //Ignore null values if ($value === null) { return ''; } - return $this->formatter->format((float) $value, $this->options['unit'], $this->options['precision']); + return htmlspecialchars($this->formatter->format((float) $value, $this->options['unit'], $this->options['precision'])); } -} \ No newline at end of file +} diff --git a/src/DataTables/Column/SelectColumn.php b/src/DataTables/Column/SelectColumn.php index 218f022d..39445ac8 100644 --- a/src/DataTables/Column/SelectColumn.php +++ b/src/DataTables/Column/SelectColumn.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Column; use Omines\DataTablesBundle\Column\AbstractColumn; @@ -28,7 +30,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; */ class SelectColumn extends AbstractColumn { - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): static { parent::configureOptions($resolver); @@ -36,21 +38,24 @@ class SelectColumn extends AbstractColumn 'label' => '', 'orderable' => false, 'searchable' => false, - 'className' => 'select-checkbox no-colvis', + 'className' => 'dt-select no-colvis', 'visible' => true, ]); return $this; } - public function normalize($value) + /** + * @return mixed + */ + public function normalize($value): mixed { return $value; } - public function render($value, $context) + public function render($value, $context): string { //Return empty string, as it this column is filled by datatables on client side return ''; } -} \ No newline at end of file +} diff --git a/src/DataTables/Column/TagsColumn.php b/src/DataTables/Column/TagsColumn.php index 4d0dee0a..f98a3900 100644 --- a/src/DataTables/Column/TagsColumn.php +++ b/src/DataTables/Column/TagsColumn.php @@ -27,11 +27,8 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class TagsColumn extends AbstractColumn { - protected UrlGeneratorInterface $urlGenerator; - - public function __construct(UrlGeneratorInterface $urlGenerator) + public function __construct(protected UrlGeneratorInterface $urlGenerator) { - $this->urlGenerator = $urlGenerator; } /** @@ -40,17 +37,21 @@ class TagsColumn extends AbstractColumn * @param mixed $value The single value of the column * @return mixed */ - public function normalize($value) + public function normalize(mixed $value): mixed { if (empty($value)) { return []; } - return explode(',', $value); + return explode(',', (string) $value); } public function render($tags, $context): string { + if (!is_iterable($tags)) { + throw new \LogicException('TagsColumn::render() expects an iterable'); + } + $html = ''; $count = 10; foreach ($tags as $tag) { @@ -61,7 +62,7 @@ class TagsColumn extends AbstractColumn $html .= sprintf( '%s', $this->urlGenerator->generate('part_list_tags', ['tag' => $tag]), - htmlspecialchars($tag) + htmlspecialchars((string) $tag) ); } diff --git a/src/DataTables/ErrorDataTable.php b/src/DataTables/ErrorDataTable.php new file mode 100644 index 00000000..833ea934 --- /dev/null +++ b/src/DataTables/ErrorDataTable.php @@ -0,0 +1,87 @@ +. + */ +namespace App\DataTables; + +use App\DataTables\Column\RowClassColumn; +use Omines\DataTablesBundle\Adapter\ArrayAdapter; +use Omines\DataTablesBundle\Column\TextColumn; +use Omines\DataTablesBundle\DataTable; +use Omines\DataTablesBundle\DataTableFactory; +use Omines\DataTablesBundle\DataTableTypeInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class ErrorDataTable implements DataTableTypeInterface +{ + public function configureOptions(OptionsResolver $optionsResolver): void + { + $optionsResolver->setRequired('errors'); + $optionsResolver->setAllowedTypes('errors', ['array', 'string']); + $optionsResolver->setNormalizer('errors', function (OptionsResolver $optionsResolver, $errors) { + if (is_string($errors)) { + $errors = [$errors]; + } + + return $errors; + }); + } + + public function configure(DataTable $dataTable, array $options): void + { + $optionsResolver = new OptionsResolver(); + $this->configureOptions($optionsResolver); + $options = $optionsResolver->resolve($options); + + $dataTable + ->add('dont_matter_we_only_set_color', RowClassColumn::class, [ + 'render' => fn($value, $context): string => 'table-warning', + ]) + + ->add('error', TextColumn::class, [ + 'label' => 'error_table.error', + 'render' => fn($value, $context): string => ' ' . $value, + ]) + ; + + //Build the array containing data + $data = []; + $n = 0; + foreach ($options['errors'] as $error) { + $data['error_' . $n] = ['error' => $error]; + $n++; + } + + $dataTable->createAdapter(ArrayAdapter::class, $data); + } + + /** + * @param string[]|string $errors + */ + public static function errorTable(DataTableFactory $dataTableFactory, Request $request, array|string $errors): Response + { + $error_table = $dataTableFactory->createFromType(self::class, ['errors' => $errors]); + $error_table->handleRequest($request); + return $error_table->getResponse(); + } +} diff --git a/src/DataTables/Filters/AttachmentFilter.php b/src/DataTables/Filters/AttachmentFilter.php index 9325bd60..d41bbe39 100644 --- a/src/DataTables/Filters/AttachmentFilter.php +++ b/src/DataTables/Filters/AttachmentFilter.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters; use App\DataTables\Filters\Constraints\BooleanConstraint; @@ -35,13 +37,16 @@ class AttachmentFilter implements FilterInterface { use CompoundFilterTrait; - protected NumberConstraint $dbId; - protected InstanceOfConstraint $targetType; - protected TextConstraint $name; - protected EntityConstraint $attachmentType; - protected BooleanConstraint $showInTable; - protected DateTimeConstraint $lastModified; - protected DateTimeConstraint $addedDate; + public readonly NumberConstraint $dbId; + public readonly InstanceOfConstraint $targetType; + public readonly TextConstraint $name; + public readonly EntityConstraint $attachmentType; + public readonly BooleanConstraint $showInTable; + public readonly DateTimeConstraint $lastModified; + public readonly DateTimeConstraint $addedDate; + + public readonly TextConstraint $originalFileName; + public readonly TextConstraint $externalLink; public function __construct(NodesListBuilder $nodesListBuilder) @@ -53,74 +58,13 @@ class AttachmentFilter implements FilterInterface $this->lastModified = new DateTimeConstraint('attachment.lastModified'); $this->addedDate = new DateTimeConstraint('attachment.addedDate'); $this->showInTable = new BooleanConstraint('attachment.show_in_table'); + $this->originalFileName = new TextConstraint('attachment.original_filename'); + $this->externalLink = new TextConstraint('attachment.external_path'); + } public function apply(QueryBuilder $queryBuilder): void { $this->applyAllChildFilters($queryBuilder); } - - /** - * @return NumberConstraint - */ - public function getDbId(): NumberConstraint - { - return $this->dbId; - } - - /** - * @return TextConstraint - */ - public function getName(): TextConstraint - { - return $this->name; - } - - /** - * @return DateTimeConstraint - */ - public function getLastModified(): DateTimeConstraint - { - return $this->lastModified; - } - - /** - * @return DateTimeConstraint - */ - public function getAddedDate(): DateTimeConstraint - { - return $this->addedDate; - } - - - /** - * @return BooleanConstraint - */ - public function getShowInTable(): BooleanConstraint - { - return $this->showInTable; - } - - - /** - * @return EntityConstraint - */ - public function getAttachmentType(): EntityConstraint - { - return $this->attachmentType; - } - - /** - * @return InstanceOfConstraint - */ - public function getTargetType(): InstanceOfConstraint - { - return $this->targetType; - } - - - - - - -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/CompoundFilterTrait.php b/src/DataTables/Filters/CompoundFilterTrait.php index ba778aac..5e722841 100644 --- a/src/DataTables/Filters/CompoundFilterTrait.php +++ b/src/DataTables/Filters/CompoundFilterTrait.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters; use Doctrine\Common\Collections\Collection; @@ -37,9 +39,6 @@ trait CompoundFilterTrait $reflection = new \ReflectionClass($this); foreach ($reflection->getProperties() as $property) { - //Set property to accessible (otherwise we run into problems on PHP < 8.1) - $property->setAccessible(true); - $value = $property->getValue($this); //We only want filters (objects implementing FilterInterface) if($value instanceof FilterInterface) { @@ -60,8 +59,6 @@ trait CompoundFilterTrait /** * Applies all children filters that are declared as property of this filter using reflection. - * @param QueryBuilder $queryBuilder - * @return void */ protected function applyAllChildFilters(QueryBuilder $queryBuilder): void { @@ -72,4 +69,4 @@ trait CompoundFilterTrait $filter->apply($queryBuilder); } } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/AbstractConstraint.php b/src/DataTables/Filters/Constraints/AbstractConstraint.php index 0a888214..7f16511e 100644 --- a/src/DataTables/Filters/Constraints/AbstractConstraint.php +++ b/src/DataTables/Filters/Constraints/AbstractConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; use App\DataTables\Filters\FilterInterface; -use Doctrine\ORM\QueryBuilder; abstract class AbstractConstraint implements FilterInterface { use FilterTrait; - /** - * @var string The property where this BooleanConstraint should apply to - */ - protected string $property; - /** * @var string */ @@ -44,9 +40,13 @@ abstract class AbstractConstraint implements FilterInterface */ abstract public function isEnabled(): bool; - public function __construct(string $property, string $identifier = null) + public function __construct( + /** + * @var string The property where this BooleanConstraint should apply to + */ + protected string $property, + ?string $identifier = null) { - $this->property = $property; $this->identifier = $identifier ?? $this->generateParameterIdentifier($property); } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/BooleanConstraint.php b/src/DataTables/Filters/Constraints/BooleanConstraint.php index b180f6aa..8eb4f042 100644 --- a/src/DataTables/Filters/Constraints/BooleanConstraint.php +++ b/src/DataTables/Filters/Constraints/BooleanConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; -use App\DataTables\Filters\FilterInterface; use Doctrine\ORM\QueryBuilder; class BooleanConstraint extends AbstractConstraint { - /** @var bool|null The value of our constraint */ - protected ?bool $value; - - - public function __construct(string $property, string $identifier = null, ?bool $default_value = null) + public function __construct( + string $property, + ?string $identifier = null, + /** @var bool|null The value of our constraint */ + protected ?bool $value = null + ) { parent::__construct($property, $identifier); - $this->value = $default_value; } /** * Gets the value of this constraint. Null means "don't filter", true means "filter for true", false means "filter for false". - * @return bool|null */ public function getValue(): ?bool { @@ -46,7 +46,6 @@ class BooleanConstraint extends AbstractConstraint /** * Sets the value of this constraint. Null means "don't filter", true means "filter for true", false means "filter for false". - * @param bool|null $value */ public function setValue(?bool $value): void { @@ -68,4 +67,4 @@ class BooleanConstraint extends AbstractConstraint $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier, '=', $this->value); } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/ChoiceConstraint.php b/src/DataTables/Filters/Constraints/ChoiceConstraint.php index 52c0739f..cce7ce2c 100644 --- a/src/DataTables/Filters/Constraints/ChoiceConstraint.php +++ b/src/DataTables/Filters/Constraints/ChoiceConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; use Doctrine\ORM\QueryBuilder; class ChoiceConstraint extends AbstractConstraint { - public const ALLOWED_OPERATOR_VALUES = ['ANY', 'NONE']; + final public const ALLOWED_OPERATOR_VALUES = ['ANY', 'NONE']; /** * @var string[]|int[] The values to compare to */ - protected array $value; + protected array $value = []; /** * @var string The operator to use */ - protected string $operator; + protected string $operator = ""; /** * @return string[]|int[] @@ -46,7 +48,6 @@ class ChoiceConstraint extends AbstractConstraint /** * @param string[]|int[] $value - * @return ChoiceConstraint */ public function setValue(array $value): ChoiceConstraint { @@ -54,18 +55,11 @@ class ChoiceConstraint extends AbstractConstraint return $this; } - /** - * @return string - */ public function getOperator(): string { return $this->operator; } - /** - * @param string $operator - * @return ChoiceConstraint - */ public function setOperator(string $operator): ChoiceConstraint { $this->operator = $operator; @@ -76,7 +70,7 @@ class ChoiceConstraint extends AbstractConstraint public function isEnabled(): bool { - return !empty($this->operator); + return $this->operator !== '' && count($this->value) > 0; } public function apply(QueryBuilder $queryBuilder): void @@ -99,4 +93,4 @@ class ChoiceConstraint extends AbstractConstraint throw new \RuntimeException('Unknown operator '. $this->operator . ' provided. Valid operators are '. implode(', ', self::ALLOWED_OPERATOR_VALUES)); } } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/DateTimeConstraint.php b/src/DataTables/Filters/Constraints/DateTimeConstraint.php index 39eaaa1d..a3043170 100644 --- a/src/DataTables/Filters/Constraints/DateTimeConstraint.php +++ b/src/DataTables/Filters/Constraints/DateTimeConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; +use Doctrine\ORM\QueryBuilder; +use RuntimeException; + /** - * An alias of NumberConstraint to use to filter on a DateTime + * Similar to NumberConstraint but for DateTime values */ -class DateTimeConstraint extends NumberConstraint +class DateTimeConstraint extends AbstractConstraint { -} \ No newline at end of file + protected const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN']; + + public function __construct( + string $property, + ?string $identifier = null, + /** + * The value1 used for comparison (this is the main one used for all mono-value comparisons) + */ + protected \DateTimeInterface|null $value1 = null, + protected ?string $operator = null, + /** + * The second value used when operator is RANGE; this is the upper bound of the range + */ + protected \DateTimeInterface|null $value2 = null) + { + parent::__construct($property, $identifier); + } + + public function getValue1(): ?\DateTimeInterface + { + return $this->value1; + } + + public function setValue1(\DateTimeInterface|null $value1): void + { + $this->value1 = $value1; + } + + public function getValue2(): ?\DateTimeInterface + { + return $this->value2; + } + + public function setValue2(?\DateTimeInterface $value2): void + { + $this->value2 = $value2; + } + + public function getOperator(): string|null + { + return $this->operator; + } + + /** + * @param string $operator + */ + public function setOperator(?string $operator): void + { + $this->operator = $operator; + } + + public function isEnabled(): bool + { + return $this->value1 !== null + && ($this->operator !== null && $this->operator !== ''); + } + + public function apply(QueryBuilder $queryBuilder): void + { + //If no value is provided then we do not apply a filter + if (!$this->isEnabled()) { + return; + } + + //Ensure we have an valid operator + if(!in_array($this->operator, self::ALLOWED_OPERATOR_VALUES, true)) { + throw new \RuntimeException('Invalid operator '. $this->operator . ' provided. Valid operators are '. implode(', ', self::ALLOWED_OPERATOR_VALUES)); + } + + if ($this->operator !== 'BETWEEN') { + $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier, $this->operator, $this->value1); + } else { + if ($this->value2 === null) { + throw new RuntimeException("Cannot use operator BETWEEN without value2!"); + } + + $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier . '1', '>=', $this->value1); + $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier . '2', '<=', $this->value2); + } + } +} diff --git a/src/DataTables/Filters/Constraints/EntityConstraint.php b/src/DataTables/Filters/Constraints/EntityConstraint.php index facbbfea..c75da80d 100644 --- a/src/DataTables/Filters/Constraints/EntityConstraint.php +++ b/src/DataTables/Filters/Constraints/EntityConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; use App\Entity\Base\AbstractDBElement; @@ -33,46 +35,26 @@ class EntityConstraint extends AbstractConstraint private const ALLOWED_OPERATOR_VALUES_BASE = ['=', '!=']; private const ALLOWED_OPERATOR_VALUES_STRUCTURAL = ['INCLUDING_CHILDREN', 'EXCLUDING_CHILDREN']; - /** - * @var NodesListBuilder - */ - protected ?NodesListBuilder $nodesListBuilder; - - /** - * @var class-string The class to use for the comparison - */ - protected string $class; - - /** - * @var string|null The operator to use - */ - protected ?string $operator; - - /** - * @var T The value to compare to - */ - protected $value; - /** * @param NodesListBuilder|null $nodesListBuilder * @param class-string $class * @param string $property * @param string|null $identifier - * @param null $value - * @param string $operator + * @param T|null $value + * @param string|null $operator */ - public function __construct(?NodesListBuilder $nodesListBuilder, string $class, string $property, string $identifier = null, $value = null, string $operator = '') + public function __construct(protected ?NodesListBuilder $nodesListBuilder, + protected string $class, + string $property, + ?string $identifier = null, + protected ?AbstractDBElement $value = null, + protected ?string $operator = null) { - $this->nodesListBuilder = $nodesListBuilder; - $this->class = $class; - - if ($nodesListBuilder === null && $this->isStructural()) { + if (!$nodesListBuilder instanceof NodesListBuilder && $this->isStructural()) { throw new \InvalidArgumentException('NodesListBuilder must be provided for structural entities'); } parent::__construct($property, $identifier); - $this->value = $value; - $this->operator = $operator; } public function getClass(): string @@ -80,17 +62,11 @@ class EntityConstraint extends AbstractConstraint return $this->class; } - /** - * @return string|null - */ public function getOperator(): ?string { return $this->operator; } - /** - * @param string|null $operator - */ public function setOperator(?string $operator): self { $this->operator = $operator; @@ -106,9 +82,10 @@ class EntityConstraint extends AbstractConstraint } /** - * @param T|null $value + * @param AbstractDBElement|null $value + * @phpstan-param T|null $value */ - public function setValue(?AbstractDBElement $value): void + public function setValue(AbstractDBElement|null $value): void { if (!$value instanceof $this->class) { throw new \InvalidArgumentException('The value must be an instance of ' . $this->class); @@ -119,7 +96,7 @@ class EntityConstraint extends AbstractConstraint /** * Checks whether the constraints apply to a structural type or not - * @return bool + * @phpstan-assert-if-true AbstractStructuralDBElement $this->value */ public function isStructural(): bool { @@ -136,7 +113,7 @@ class EntityConstraint extends AbstractConstraint $tmp = self::ALLOWED_OPERATOR_VALUES_BASE; if ($this->isStructural()) { - $tmp = array_merge($tmp, self::ALLOWED_OPERATOR_VALUES_STRUCTURAL); + $tmp = [...$tmp, ...self::ALLOWED_OPERATOR_VALUES_STRUCTURAL]; } return $tmp; @@ -144,7 +121,7 @@ class EntityConstraint extends AbstractConstraint public function isEnabled(): bool { - return !empty($this->operator); + return $this->operator !== null && $this->operator !== ''; } public function apply(QueryBuilder $queryBuilder): void @@ -175,8 +152,9 @@ class EntityConstraint extends AbstractConstraint } if($this->operator === '=' || $this->operator === '!=') { - $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier, $this->operator, $this->value); - return; + //Include null values on != operator, so that really all values are returned that are not equal to the given value + $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier, $this->operator, $this->value, $this->operator === '!='); + return; } //Otherwise retrieve the children list and apply the operator to it @@ -191,7 +169,8 @@ class EntityConstraint extends AbstractConstraint } if ($this->operator === 'EXCLUDING_CHILDREN') { - $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier, 'NOT IN', $list); + //Include null values in the result, so that all elements that are not in the list are returned + $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier, 'NOT IN', $list, true); return; } } else { @@ -199,4 +178,4 @@ class EntityConstraint extends AbstractConstraint } } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/FilterTrait.php b/src/DataTables/Filters/Constraints/FilterTrait.php index 988890ba..3260e4e3 100644 --- a/src/DataTables/Filters/Constraints/FilterTrait.php +++ b/src/DataTables/Filters/Constraints/FilterTrait.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; -use Doctrine\DBAL\ParameterType; use Doctrine\ORM\QueryBuilder; trait FilterTrait @@ -28,7 +29,7 @@ trait FilterTrait protected bool $useHaving = false; - public function useHaving($value = true): self + public function useHaving($value = true): static { $this->useHaving = $value; return $this; @@ -36,7 +37,6 @@ trait FilterTrait /** * Checks if the given input is an aggregateFunction like COUNT(part.partsLot) or so - * @return bool */ protected function isAggregateFunctionString(string $input): bool { @@ -45,26 +45,25 @@ trait FilterTrait /** * Generates a parameter identifier that can be used for the given property. It gives random results, to be unique, so you have to cache it. - * @param string $property - * @return string */ protected function generateParameterIdentifier(string $property): string { //Replace all special characters with underscores - $property = preg_replace('/[^a-zA-Z0-9_]/', '_', $property); + $property = preg_replace('/\W/', '_', $property); //Add a random number to the end of the property name for uniqueness return $property . '_' . uniqid("", false); } /** * Adds a simple constraint in the form of (property OPERATOR value) (e.g. "part.name = :name") to the given query builder. - * @param QueryBuilder $queryBuilder - * @param string $property - * @param string $comparison_operator - * @param mixed $value - * @return void + * @param QueryBuilder $queryBuilder The query builder to add the constraint to + * @param string $property The property to compare + * @param string $parameterIdentifier The identifier for the parameter + * @param string $comparison_operator The comparison operator to use + * @param mixed $value The value to compare to + * @param bool $include_null If true, the result of this constraint will also include null values of this property (useful for exclusion filters) */ - protected function addSimpleAndConstraint(QueryBuilder $queryBuilder, string $property, string $parameterIdentifier, string $comparison_operator, $value): void + protected function addSimpleAndConstraint(QueryBuilder $queryBuilder, string $property, string $parameterIdentifier, string $comparison_operator, mixed $value, bool $include_null = false): void { if ($comparison_operator === 'IN' || $comparison_operator === 'NOT IN') { $expression = sprintf("%s %s (:%s)", $property, $comparison_operator, $parameterIdentifier); @@ -72,6 +71,10 @@ trait FilterTrait $expression = sprintf("%s %s :%s", $property, $comparison_operator, $parameterIdentifier); } + if ($include_null) { + $expression = sprintf("(%s OR %s IS NULL)", $expression, $property); + } + if($this->useHaving || $this->isAggregateFunctionString($property)) { //If the property is an aggregate function, we have to use the "having" instead of the "where" $queryBuilder->andHaving($expression); } else { @@ -80,4 +83,4 @@ trait FilterTrait $queryBuilder->setParameter($parameterIdentifier, $value); } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/InstanceOfConstraint.php b/src/DataTables/Filters/Constraints/InstanceOfConstraint.php index 6efe8cce..7fc242d7 100644 --- a/src/DataTables/Filters/Constraints/InstanceOfConstraint.php +++ b/src/DataTables/Filters/Constraints/InstanceOfConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; use Doctrine\ORM\QueryBuilder; @@ -27,17 +29,17 @@ use Doctrine\ORM\QueryBuilder; */ class InstanceOfConstraint extends AbstractConstraint { - public const ALLOWED_OPERATOR_VALUES = ['ANY', 'NONE']; + final public const ALLOWED_OPERATOR_VALUES = ['ANY', 'NONE']; /** * @var string[] The values to compare to (fully qualified class names) */ - protected array $value; + protected array $value = []; /** * @var string The operator to use */ - protected string $operator; + protected string $operator = ""; /** * @return string[] @@ -57,16 +59,12 @@ class InstanceOfConstraint extends AbstractConstraint return $this; } - /** - * @return string - */ public function getOperator(): string { return $this->operator; } /** - * @param string $operator * @return $this */ public function setOperator(string $operator): self @@ -79,7 +77,7 @@ class InstanceOfConstraint extends AbstractConstraint public function isEnabled(): bool { - return !empty($this->operator); + return $this->operator !== '' && count($this->value) > 0; } public function apply(QueryBuilder $queryBuilder): void @@ -96,9 +94,10 @@ class InstanceOfConstraint extends AbstractConstraint $expressions = []; + /** @phpstan-ignore-next-line */ if ($this->operator === 'ANY' || $this->operator === 'NONE') { foreach($this->value as $value) { - //We cannnot use an paramater here, as this is the only way to pass the FCQN to the query (via binded params, we would need to use ClassMetaData). See: https://github.com/doctrine/orm/issues/4462 + //We can not use a parameter here, as this is the only way to pass the FCQN to the query (via binded params, we would need to use ClassMetaData). See: https://github.com/doctrine/orm/issues/4462 $expressions[] = ($queryBuilder->expr()->isInstanceOf($this->property, $value)); } @@ -111,4 +110,4 @@ class InstanceOfConstraint extends AbstractConstraint throw new \RuntimeException('Unknown operator '. $this->operator . ' provided. Valid operators are '. implode(', ', self::ALLOWED_OPERATOR_VALUES)); } } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/IntConstraint.php b/src/DataTables/Filters/Constraints/IntConstraint.php index 601f6aa8..3fc5cce5 100644 --- a/src/DataTables/Filters/Constraints/IntConstraint.php +++ b/src/DataTables/Filters/Constraints/IntConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; use Doctrine\ORM\QueryBuilder; @@ -35,4 +37,4 @@ class IntConstraint extends NumberConstraint parent::apply($queryBuilder); } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/NumberConstraint.php b/src/DataTables/Filters/Constraints/NumberConstraint.php index e8382723..dc7cf733 100644 --- a/src/DataTables/Filters/Constraints/NumberConstraint.php +++ b/src/DataTables/Filters/Constraints/NumberConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; -use Doctrine\DBAL\ParameterType; use Doctrine\ORM\QueryBuilder; use RuntimeException; class NumberConstraint extends AbstractConstraint { - public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN']; + protected const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN']; + public function __construct( + string $property, + ?string $identifier = null, + /** + * The value1 used for comparison (this is the main one used for all mono-value comparisons) + */ + protected float|int|null $value1 = null, + protected ?string $operator = null, + /** + * The second value used when operator is RANGE; this is the upper bound of the range + */ + protected float|int|null $value2 = null) + { + parent::__construct($property, $identifier); + } - /** - * The value1 used for comparison (this is the main one used for all mono-value comparisons) - * @var float|null|int|\DateTimeInterface - */ - protected $value1; - - /** - * The second value used when operator is RANGE; this is the upper bound of the range - * @var float|null|int|\DateTimeInterface - */ - protected $value2; - - /** - * @var string The operator to use - */ - protected ?string $operator; - - /** - * @return float|int|null|\DateTimeInterface - */ - public function getValue1() + public function getValue1(): float|int|null { return $this->value1; } - /** - * @param float|int|\DateTimeInterface|null $value1 - */ - public function setValue1($value1): void + public function setValue1(float|int|null $value1): void { $this->value1 = $value1; } - /** - * @return float|int|null - */ - public function getValue2() + public function getValue2(): float|int|null { return $this->value2; } - /** - * @param float|int|null $value2 - */ - public function setValue2($value2): void + public function setValue2(float|int|null $value2): void { $this->value2 = $value2; } - /** - * @return string - */ - public function getOperator(): string + public function getOperator(): string|null { return $this->operator; } @@ -95,18 +79,10 @@ class NumberConstraint extends AbstractConstraint } - public function __construct(string $property, string $identifier = null, $value1 = null, string $operator = null, $value2 = null) - { - parent::__construct($property, $identifier); - $this->value1 = $value1; - $this->value2 = $value2; - $this->operator = $operator; - } - public function isEnabled(): bool { return $this->value1 !== null - && !empty($this->operator); + && ($this->operator !== null && $this->operator !== ''); } public function apply(QueryBuilder $queryBuilder): void @@ -129,7 +105,13 @@ class NumberConstraint extends AbstractConstraint } $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier . '1', '>=', $this->value1); - $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier . '2', '<=', $this->value2); + + //Workaround for the amountSum which we need to add twice on postgres. Replace one of the __ with __2 to make it work + //Otherwise we get an error, that __partLot was already defined + + $property2 = str_replace('__', '__2', $this->property); + + $this->addSimpleAndConstraint($queryBuilder, $property2, $this->identifier . '2', '<=', $this->value2); } } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php b/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php new file mode 100644 index 00000000..011824e5 --- /dev/null +++ b/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php @@ -0,0 +1,56 @@ +. + */ +namespace App\DataTables\Filters\Constraints\Part; + +use App\DataTables\Filters\Constraints\BooleanConstraint; +use App\Entity\Parts\PartLot; +use Doctrine\ORM\QueryBuilder; + +class LessThanDesiredConstraint extends BooleanConstraint +{ + public function __construct(?string $property = null, ?string $identifier = null, ?bool $default_value = null) + { + parent::__construct($property ?? '( + SELECT COALESCE(SUM(ld_partLot.amount), 0.0) + FROM '.PartLot::class.' ld_partLot + WHERE ld_partLot.part = part.id + AND ld_partLot.instock_unknown = false + AND (ld_partLot.expiration_date IS NULL OR ld_partLot.expiration_date > CURRENT_DATE()) + )', $identifier ?? 'amountSumLessThanDesired', $default_value); + } + + public function apply(QueryBuilder $queryBuilder): void + { + //Do not apply a filter if value is null (filter is set to ignore) + if(!$this->isEnabled()) { + return; + } + + //If value is true, we want to filter for parts with stock < desired stock + if ($this->value) { + $queryBuilder->andHaving( $this->property . ' < part.minamount'); + } else { + $queryBuilder->andHaving($this->property . ' >= part.minamount'); + } + } +} diff --git a/src/DataTables/Filters/Constraints/Part/ParameterConstraint.php b/src/DataTables/Filters/Constraints/Part/ParameterConstraint.php index 76e39cdf..e68dd989 100644 --- a/src/DataTables/Filters/Constraints/Part/ParameterConstraint.php +++ b/src/DataTables/Filters/Constraints/Part/ParameterConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints\Part; use App\DataTables\Filters\Constraints\AbstractConstraint; use App\DataTables\Filters\Constraints\TextConstraint; use App\Entity\Parameters\PartParameter; use Doctrine\ORM\QueryBuilder; -use Svg\Tag\Text; class ParameterConstraint extends AbstractConstraint { - /** @var string */ - protected string $name; + protected string $name = ''; - /** @var string */ - protected string $symbol; + protected string $symbol = ''; - /** @var string */ - protected string $unit; + protected string $unit = ''; - /** @var TextConstraint */ protected TextConstraint $value_text; - - /** @var ParameterValueConstraint */ protected ParameterValueConstraint $value; /** @var string The alias to use for the subquery */ @@ -73,19 +68,19 @@ class ParameterConstraint extends AbstractConstraint ->from(PartParameter::class, $this->alias) ->where($this->alias . '.element = part'); - if (!empty($this->name)) { + if ($this->name !== '') { $paramName = $this->generateParameterIdentifier('params.name'); $subqb->andWhere($this->alias . '.name = :' . $paramName); $queryBuilder->setParameter($paramName, $this->name); } - if (!empty($this->symbol)) { + if ($this->symbol !== '') { $paramName = $this->generateParameterIdentifier('params.symbol'); $subqb->andWhere($this->alias . '.symbol = :' . $paramName); $queryBuilder->setParameter($paramName, $this->symbol); } - if (!empty($this->unit)) { + if ($this->unit !== '') { $paramName = $this->generateParameterIdentifier('params.unit'); $subqb->andWhere($this->alias . '.unit = :' . $paramName); $queryBuilder->setParameter($paramName, $this->unit); @@ -104,75 +99,48 @@ class ParameterConstraint extends AbstractConstraint $queryBuilder->andWhere('(' . $subqb->getDQL() . ') > 0'); } - /** - * @return string - */ public function getName(): string { return $this->name; } - /** - * @param string $name - * @return ParameterConstraint - */ public function setName(string $name): ParameterConstraint { $this->name = $name; return $this; } - /** - * @return string - */ public function getSymbol(): string { return $this->symbol; } - /** - * @param string $symbol - * @return ParameterConstraint - */ public function setSymbol(string $symbol): ParameterConstraint { $this->symbol = $symbol; return $this; } - /** - * @return string - */ public function getUnit(): string { return $this->unit; } - /** - * @param string $unit - * @return ParameterConstraint - */ public function setUnit(string $unit): ParameterConstraint { $this->unit = $unit; return $this; } - /** - * @return TextConstraint - */ public function getValueText(): TextConstraint { return $this->value_text; } - /** - * @return ParameterValueConstraint - */ public function getValue(): ParameterValueConstraint { return $this->value; } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/Part/ParameterValueConstraint.php b/src/DataTables/Filters/Constraints/Part/ParameterValueConstraint.php index 5da64098..469c18c6 100644 --- a/src/DataTables/Filters/Constraints/Part/ParameterValueConstraint.php +++ b/src/DataTables/Filters/Constraints/Part/ParameterValueConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints\Part; use App\DataTables\Filters\Constraints\NumberConstraint; @@ -25,18 +27,14 @@ use Doctrine\ORM\QueryBuilder; class ParameterValueConstraint extends NumberConstraint { - protected string $alias; - - public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN', + protected const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN', //Additional operators 'IN_RANGE', 'NOT_IN_RANGE', 'GREATER_THAN_RANGE', 'GREATER_EQUAL_RANGE', 'LESS_THAN_RANGE', 'LESS_EQUAL_RANGE', 'RANGE_IN_RANGE', 'RANGE_INTERSECT_RANGE']; /** * @param string $alias The alias which is used in the sub query of ParameterConstraint */ - public function __construct(string $alias) { - $this->alias = $alias; - + public function __construct(protected string $alias) { parent::__construct($alias . '.value_typical'); } @@ -145,4 +143,4 @@ class ParameterValueConstraint extends NumberConstraint //For all other cases use the default implementation parent::apply($queryBuilder); } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/Part/TagsConstraint.php b/src/DataTables/Filters/Constraints/Part/TagsConstraint.php index 92c78f6a..02eab7a1 100644 --- a/src/DataTables/Filters/Constraints/Part/TagsConstraint.php +++ b/src/DataTables/Filters/Constraints/Part/TagsConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints\Part; +use Doctrine\ORM\Query\Expr\Orx; use App\DataTables\Filters\Constraints\AbstractConstraint; -use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; class TagsConstraint extends AbstractConstraint { - public const ALLOWED_OPERATOR_VALUES = ['ANY', 'ALL', 'NONE']; + final public const ALLOWED_OPERATOR_VALUES = ['ANY', 'ALL', 'NONE']; - /** - * @var string|null The operator to use - */ - protected ?string $operator; - - /** - * @var string The value to compare to - */ - protected $value; - - public function __construct(string $property, string $identifier = null, $value = null, string $operator = '') + public function __construct(string $property, ?string $identifier = null, + protected ?string $value = null, + protected ?string $operator = '') { parent::__construct($property, $identifier); - $this->value = $value; - $this->operator = $operator; } /** @@ -62,18 +54,12 @@ class TagsConstraint extends AbstractConstraint return $this; } - /** - * @return string - */ - public function getValue(): string + public function getValue(): ?string { return $this->value; } - /** - * @param string $value - */ - public function setValue(string $value): self + public function setValue(?string $value): self { $this->value = $value; return $this; @@ -82,7 +68,7 @@ class TagsConstraint extends AbstractConstraint public function isEnabled(): bool { return $this->value !== null - && !empty($this->operator); + && ($this->operator !== null && $this->operator !== ''); } /** @@ -96,21 +82,21 @@ class TagsConstraint extends AbstractConstraint /** * Builds an expression to query for a single tag - * @param QueryBuilder $queryBuilder - * @param string $tag - * @return Expr\Orx */ - protected function getExpressionForTag(QueryBuilder $queryBuilder, string $tag): Expr\Orx + protected function getExpressionForTag(QueryBuilder $queryBuilder, string $tag): Orx { + //Escape any %, _ or \ in the tag + $tag = addcslashes($tag, '%_\\'); + $tag_identifier_prefix = uniqid($this->identifier . '_', false); $expr = $queryBuilder->expr(); $tmp = $expr->orX( - $expr->like($this->property, ':' . $tag_identifier_prefix . '_1'), - $expr->like($this->property, ':' . $tag_identifier_prefix . '_2'), - $expr->like($this->property, ':' . $tag_identifier_prefix . '_3'), - $expr->eq($this->property, ':' . $tag_identifier_prefix . '_4'), + 'ILIKE(' . $this->property . ', :' . $tag_identifier_prefix . '_1) = TRUE', + 'ILIKE(' . $this->property . ', :' . $tag_identifier_prefix . '_2) = TRUE', + 'ILIKE(' . $this->property . ', :' . $tag_identifier_prefix . '_3) = TRUE', + 'ILIKE(' . $this->property . ', :' . $tag_identifier_prefix . '_4) = TRUE', ); //Set the parameters for the LIKE expression, in each variation of the tag (so with a comma, at the end, at the beginning, and on both ends, and equaling the tag) @@ -147,9 +133,10 @@ class TagsConstraint extends AbstractConstraint return; } + //@phpstan-ignore-next-line Keep this check to ensure that everything has the same structure even if we add a new operator if ($this->operator === 'NONE') { $queryBuilder->andWhere($queryBuilder->expr()->not($queryBuilder->expr()->orX(...$tagsExpressions))); return; } } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/Constraints/TextConstraint.php b/src/DataTables/Filters/Constraints/TextConstraint.php index 3cd53973..31b12a5e 100644 --- a/src/DataTables/Filters/Constraints/TextConstraint.php +++ b/src/DataTables/Filters/Constraints/TextConstraint.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters\Constraints; use Doctrine\ORM\QueryBuilder; @@ -25,23 +27,20 @@ use Doctrine\ORM\QueryBuilder; class TextConstraint extends AbstractConstraint { - public const ALLOWED_OPERATOR_VALUES = ['=', '!=', 'STARTS', 'ENDS', 'CONTAINS', 'LIKE', 'REGEX']; + final public const ALLOWED_OPERATOR_VALUES = ['=', '!=', 'STARTS', 'ENDS', 'CONTAINS', 'LIKE', 'REGEX']; /** + * @param string $value + */ + public function __construct(string $property, ?string $identifier = null, /** + * @var string|null The value to compare to + */ + protected ?string $value = null, /** * @var string|null The operator to use */ - protected ?string $operator; - - /** - * @var string The value to compare to - */ - protected $value; - - public function __construct(string $property, string $identifier = null, $value = null, string $operator = '') + protected ?string $operator = '') { parent::__construct($property, $identifier); - $this->value = $value; - $this->operator = $operator; } /** @@ -61,18 +60,12 @@ class TextConstraint extends AbstractConstraint return $this; } - /** - * @return string - */ - public function getValue(): string + public function getValue(): ?string { return $this->value; } - /** - * @param string $value - */ - public function setValue(string $value): self + public function setValue(?string $value): self { $this->value = $value; return $this; @@ -81,7 +74,7 @@ class TextConstraint extends AbstractConstraint public function isEnabled(): bool { return $this->value !== null - && !empty($this->operator); + && ($this->operator !== null && $this->operator !== ''); } public function apply(QueryBuilder $queryBuilder): void @@ -101,27 +94,28 @@ class TextConstraint extends AbstractConstraint return; } - //The CONTAINS, LIKE, STARTS and ENDS operators use the LIKE operator but we have to build the value string differently + //The CONTAINS, LIKE, STARTS and ENDS operators use the LIKE operator, but we have to build the value string differently $like_value = null; if ($this->operator === 'LIKE') { $like_value = $this->value; - } else if ($this->operator === 'STARTS') { + } elseif ($this->operator === 'STARTS') { $like_value = $this->value . '%'; - } else if ($this->operator === 'ENDS') { + } elseif ($this->operator === 'ENDS') { $like_value = '%' . $this->value; - } else if ($this->operator === 'CONTAINS') { + } elseif ($this->operator === 'CONTAINS') { $like_value = '%' . $this->value . '%'; } if ($like_value !== null) { - $this->addSimpleAndConstraint($queryBuilder, $this->property, $this->identifier, 'LIKE', $like_value); + $queryBuilder->andWhere(sprintf('ILIKE(%s, :%s) = TRUE', $this->property, $this->identifier)); + $queryBuilder->setParameter($this->identifier, $like_value); return; } //Regex is only supported on MySQL and needs a special function if ($this->operator === 'REGEX') { - $queryBuilder->andWhere(sprintf('REGEXP(%s, :%s) = 1', $this->property, $this->identifier)); + $queryBuilder->andWhere(sprintf('REGEXP(%s, :%s) = TRUE', $this->property, $this->identifier)); $queryBuilder->setParameter($this->identifier, $this->value); } } -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/FilterInterface.php b/src/DataTables/Filters/FilterInterface.php index 1abbdc30..bead3fda 100644 --- a/src/DataTables/Filters/FilterInterface.php +++ b/src/DataTables/Filters/FilterInterface.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters; use Doctrine\ORM\QueryBuilder; @@ -31,4 +33,4 @@ interface FilterInterface * @return void */ public function apply(QueryBuilder $queryBuilder): void; -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/LogFilter.php b/src/DataTables/Filters/LogFilter.php index 86a600b0..35d32e74 100644 --- a/src/DataTables/Filters/LogFilter.php +++ b/src/DataTables/Filters/LogFilter.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters; use App\DataTables\Filters\Constraints\ChoiceConstraint; @@ -25,7 +27,6 @@ use App\DataTables\Filters\Constraints\DateTimeConstraint; use App\DataTables\Filters\Constraints\EntityConstraint; use App\DataTables\Filters\Constraints\InstanceOfConstraint; use App\DataTables\Filters\Constraints\IntConstraint; -use App\DataTables\Filters\Constraints\NumberConstraint; use App\Entity\UserSystem\User; use Doctrine\ORM\QueryBuilder; @@ -33,13 +34,13 @@ class LogFilter implements FilterInterface { use CompoundFilterTrait; - protected DateTimeConstraint $timestamp; - protected IntConstraint $dbId; - protected ChoiceConstraint $level; - protected InstanceOfConstraint $eventType; - protected ChoiceConstraint $targetType; - protected IntConstraint $targetId; - protected EntityConstraint $user; + public readonly DateTimeConstraint $timestamp; + public readonly IntConstraint $dbId; + public readonly ChoiceConstraint $level; + public readonly InstanceOfConstraint $eventType; + public readonly ChoiceConstraint $targetType; + public readonly IntConstraint $targetId; + public readonly EntityConstraint $user; public function __construct() { @@ -57,59 +58,4 @@ class LogFilter implements FilterInterface { $this->applyAllChildFilters($queryBuilder); } - - /** - * @return DateTimeConstraint - */ - public function getTimestamp(): DateTimeConstraint - { - return $this->timestamp; - } - - /** - * @return IntConstraint|NumberConstraint - */ - public function getDbId() - { - return $this->dbId; - } - - /** - * @return ChoiceConstraint - */ - public function getLevel(): ChoiceConstraint - { - return $this->level; - } - - /** - * @return InstanceOfConstraint - */ - public function getEventType(): InstanceOfConstraint - { - return $this->eventType; - } - - /** - * @return ChoiceConstraint - */ - public function getTargetType(): ChoiceConstraint - { - return $this->targetType; - } - - /** - * @return IntConstraint - */ - public function getTargetId(): IntConstraint - { - return $this->targetId; - } - - public function getUser(): EntityConstraint - { - return $this->user; - } - - -} \ No newline at end of file +} diff --git a/src/DataTables/Filters/PartFilter.php b/src/DataTables/Filters/PartFilter.php index 2a689a8b..ff98c76f 100644 --- a/src/DataTables/Filters/PartFilter.php +++ b/src/DataTables/Filters/PartFilter.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters; use App\DataTables\Filters\Constraints\BooleanConstraint; @@ -26,6 +28,7 @@ use App\DataTables\Filters\Constraints\DateTimeConstraint; use App\DataTables\Filters\Constraints\EntityConstraint; use App\DataTables\Filters\Constraints\IntConstraint; use App\DataTables\Filters\Constraints\NumberConstraint; +use App\DataTables\Filters\Constraints\Part\LessThanDesiredConstraint; use App\DataTables\Filters\Constraints\Part\ParameterConstraint; use App\DataTables\Filters\Constraints\Part\TagsConstraint; use App\DataTables\Filters\Constraints\TextConstraint; @@ -34,53 +37,69 @@ use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\PartLot; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; +use App\Entity\ProjectSystem\Project; +use App\Entity\UserSystem\User; use App\Services\Trees\NodesListBuilder; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\QueryBuilder; -use Svg\Tag\Text; class PartFilter implements FilterInterface { use CompoundFilterTrait; - protected IntConstraint $dbId; - protected TextConstraint $ipn; - protected TextConstraint $name; - protected TextConstraint $description; - protected TextConstraint $comment; - protected TagsConstraint $tags; - protected NumberConstraint $minAmount; - protected BooleanConstraint $favorite; - protected BooleanConstraint $needsReview; - protected NumberConstraint $mass; - protected DateTimeConstraint $lastModified; - protected DateTimeConstraint $addedDate; - protected EntityConstraint $category; - protected EntityConstraint $footprint; - protected EntityConstraint $manufacturer; - protected ChoiceConstraint $manufacturing_status; - protected EntityConstraint $supplier; - protected IntConstraint $orderdetailsCount; - protected BooleanConstraint $obsolete; - protected EntityConstraint $storelocation; - protected IntConstraint $lotCount; - protected IntConstraint $amountSum; - protected BooleanConstraint $lotNeedsRefill; - protected TextConstraint $lotDescription; - protected BooleanConstraint $lotUnknownAmount; - protected DateTimeConstraint $lotExpirationDate; - protected EntityConstraint $measurementUnit; - protected TextConstraint $manufacturer_product_url; - protected TextConstraint $manufacturer_product_number; - protected IntConstraint $attachmentsCount; - protected EntityConstraint $attachmentType; - protected TextConstraint $attachmentName; + public readonly IntConstraint $dbId; + public readonly TextConstraint $ipn; + public readonly TextConstraint $name; + public readonly TextConstraint $description; + public readonly TextConstraint $comment; + public readonly TagsConstraint $tags; + public readonly NumberConstraint $minAmount; + public readonly BooleanConstraint $favorite; + public readonly BooleanConstraint $needsReview; + public readonly NumberConstraint $mass; + public readonly DateTimeConstraint $lastModified; + public readonly DateTimeConstraint $addedDate; + public readonly EntityConstraint $category; + public readonly EntityConstraint $footprint; + public readonly EntityConstraint $manufacturer; + public readonly ChoiceConstraint $manufacturing_status; + public readonly EntityConstraint $supplier; + public readonly IntConstraint $orderdetailsCount; + public readonly BooleanConstraint $obsolete; + public readonly EntityConstraint $storelocation; + public readonly IntConstraint $lotCount; + public readonly IntConstraint $amountSum; + public readonly LessThanDesiredConstraint $lessThanDesired; + + public readonly BooleanConstraint $lotNeedsRefill; + public readonly TextConstraint $lotDescription; + public readonly BooleanConstraint $lotUnknownAmount; + public readonly DateTimeConstraint $lotExpirationDate; + public readonly EntityConstraint $lotOwner; + + public readonly EntityConstraint $measurementUnit; + public readonly TextConstraint $manufacturer_product_url; + public readonly TextConstraint $manufacturer_product_number; + public readonly IntConstraint $attachmentsCount; + public readonly EntityConstraint $attachmentType; + public readonly TextConstraint $attachmentName; + /** @var ArrayCollection */ - protected ArrayCollection $parameters; - protected IntConstraint $parametersCount; + public readonly ArrayCollection $parameters; + public readonly IntConstraint $parametersCount; + + /************************************************* + * Project tab + *************************************************/ + + public readonly EntityConstraint $project; + public readonly NumberConstraint $bomQuantity; + public readonly TextConstraint $bomName; + public readonly TextConstraint $bomComment; public function __construct(NodesListBuilder $nodesListBuilder) { @@ -105,284 +124,48 @@ class PartFilter implements FilterInterface This seems to be related to the fact, that PDO does not have an float parameter type and using string type does not work in this situation (at least in SQLite) TODO: Find a better solution here */ - $this->amountSum = new IntConstraint('amountSum'); - $this->lotCount = new IntConstraint('COUNT(partLots)'); + $this->amountSum = (new IntConstraint('( + SELECT COALESCE(SUM(__partLot.amount), 0.0) + FROM '.PartLot::class.' __partLot + WHERE __partLot.part = part.id + AND __partLot.instock_unknown = false + AND (__partLot.expiration_date IS NULL OR __partLot.expiration_date > CURRENT_DATE()) + )', identifier: "amountSumWhere")); + $this->lotCount = new IntConstraint('COUNT(_partLots)'); + $this->lessThanDesired = new LessThanDesiredConstraint(); - $this->storelocation = new EntityConstraint($nodesListBuilder, Storelocation::class, 'partLots.storage_location'); - $this->lotNeedsRefill = new BooleanConstraint('partLots.needs_refill'); - $this->lotUnknownAmount = new BooleanConstraint('partLots.instock_unknown'); - $this->lotExpirationDate = new DateTimeConstraint('partLots.expiration_date'); - $this->lotDescription = new TextConstraint('partLots.description'); + $this->storelocation = new EntityConstraint($nodesListBuilder, StorageLocation::class, '_partLots.storage_location'); + $this->lotNeedsRefill = new BooleanConstraint('_partLots.needs_refill'); + $this->lotUnknownAmount = new BooleanConstraint('_partLots.instock_unknown'); + $this->lotExpirationDate = new DateTimeConstraint('_partLots.expiration_date'); + $this->lotDescription = new TextConstraint('_partLots.description'); + $this->lotOwner = new EntityConstraint($nodesListBuilder, User::class, '_partLots.owner'); $this->manufacturer = new EntityConstraint($nodesListBuilder, Manufacturer::class, 'part.manufacturer'); $this->manufacturer_product_number = new TextConstraint('part.manufacturer_product_number'); $this->manufacturer_product_url = new TextConstraint('part.manufacturer_product_url'); $this->manufacturing_status = new ChoiceConstraint('part.manufacturing_status'); - $this->attachmentsCount = new IntConstraint('COUNT(attachments)'); - $this->attachmentType = new EntityConstraint($nodesListBuilder, AttachmentType::class, 'attachments.attachment_type'); - $this->attachmentName = new TextConstraint('attachments.name'); + $this->attachmentsCount = new IntConstraint('COUNT(_attachments)'); + $this->attachmentType = new EntityConstraint($nodesListBuilder, AttachmentType::class, '_attachments.attachment_type'); + $this->attachmentName = new TextConstraint('_attachments.name'); - $this->supplier = new EntityConstraint($nodesListBuilder, Supplier::class, 'orderdetails.supplier'); - $this->orderdetailsCount = new IntConstraint('COUNT(orderdetails)'); - $this->obsolete = new BooleanConstraint('orderdetails.obsolete'); + $this->supplier = new EntityConstraint($nodesListBuilder, Supplier::class, '_orderdetails.supplier'); + $this->orderdetailsCount = new IntConstraint('COUNT(_orderdetails)'); + $this->obsolete = new BooleanConstraint('_orderdetails.obsolete'); $this->parameters = new ArrayCollection(); - $this->parametersCount = new IntConstraint('COUNT(parameters)'); + $this->parametersCount = new IntConstraint('COUNT(_parameters)'); + + $this->project = new EntityConstraint($nodesListBuilder, Project::class, '_projectBomEntries.project'); + $this->bomQuantity = new NumberConstraint('_projectBomEntries.quantity'); + $this->bomName = new TextConstraint('_projectBomEntries.name'); + $this->bomComment = new TextConstraint('_projectBomEntries.comment'); + } public function apply(QueryBuilder $queryBuilder): void { $this->applyAllChildFilters($queryBuilder); } - - - /** - * @return BooleanConstraint|false - */ - public function getFavorite() - { - return $this->favorite; - } - - /** - * @return BooleanConstraint - */ - public function getNeedsReview(): BooleanConstraint - { - return $this->needsReview; - } - - public function getMass(): NumberConstraint - { - return $this->mass; - } - - public function getName(): TextConstraint - { - return $this->name; - } - - public function getDescription(): TextConstraint - { - return $this->description; - } - - /** - * @return DateTimeConstraint - */ - public function getLastModified(): DateTimeConstraint - { - return $this->lastModified; - } - - /** - * @return DateTimeConstraint - */ - public function getAddedDate(): DateTimeConstraint - { - return $this->addedDate; - } - - public function getCategory(): EntityConstraint - { - return $this->category; - } - - /** - * @return EntityConstraint - */ - public function getFootprint(): EntityConstraint - { - return $this->footprint; - } - - /** - * @return EntityConstraint - */ - public function getManufacturer(): EntityConstraint - { - return $this->manufacturer; - } - - /** - * @return EntityConstraint - */ - public function getSupplier(): EntityConstraint - { - return $this->supplier; - } - - /** - * @return EntityConstraint - */ - public function getStorelocation(): EntityConstraint - { - return $this->storelocation; - } - - /** - * @return EntityConstraint - */ - public function getMeasurementUnit(): EntityConstraint - { - return $this->measurementUnit; - } - - /** - * @return NumberConstraint - */ - public function getDbId(): NumberConstraint - { - return $this->dbId; - } - - public function getIpn(): TextConstraint - { - return $this->ipn; - } - - /** - * @return TextConstraint - */ - public function getComment(): TextConstraint - { - return $this->comment; - } - - /** - * @return NumberConstraint - */ - public function getMinAmount(): NumberConstraint - { - return $this->minAmount; - } - - /** - * @return TextConstraint - */ - public function getManufacturerProductUrl(): TextConstraint - { - return $this->manufacturer_product_url; - } - - /** - * @return TextConstraint - */ - public function getManufacturerProductNumber(): TextConstraint - { - return $this->manufacturer_product_number; - } - - public function getLotCount(): NumberConstraint - { - return $this->lotCount; - } - - /** - * @return TagsConstraint - */ - public function getTags(): TagsConstraint - { - return $this->tags; - } - - /** - * @return IntConstraint - */ - public function getOrderdetailsCount(): IntConstraint - { - return $this->orderdetailsCount; - } - - /** - * @return IntConstraint - */ - public function getAttachmentsCount(): IntConstraint - { - return $this->attachmentsCount; - } - - /** - * @return BooleanConstraint - */ - public function getLotNeedsRefill(): BooleanConstraint - { - return $this->lotNeedsRefill; - } - - /** - * @return BooleanConstraint - */ - public function getLotUnknownAmount(): BooleanConstraint - { - return $this->lotUnknownAmount; - } - - /** - * @return DateTimeConstraint - */ - public function getLotExpirationDate(): DateTimeConstraint - { - return $this->lotExpirationDate; - } - - /** - * @return EntityConstraint - */ - public function getAttachmentType(): EntityConstraint - { - return $this->attachmentType; - } - - /** - * @return TextConstraint - */ - public function getAttachmentName(): TextConstraint - { - return $this->attachmentName; - } - - public function getManufacturingStatus(): ChoiceConstraint - { - return $this->manufacturing_status; - } - - public function getAmountSum(): NumberConstraint - { - return $this->amountSum; - } - - /** - * @return ArrayCollection - */ - public function getParameters(): ArrayCollection - { - return $this->parameters; - } - - public function getParametersCount(): IntConstraint - { - return $this->parametersCount; - } - - /** - * @return TextConstraint - */ - public function getLotDescription(): TextConstraint - { - return $this->lotDescription; - } - - /** - * @return BooleanConstraint - */ - public function getObsolete(): BooleanConstraint - { - return $this->obsolete; - } - - - - } diff --git a/src/DataTables/Filters/PartSearchFilter.php b/src/DataTables/Filters/PartSearchFilter.php index 6dfe2a62..6e2e5894 100644 --- a/src/DataTables/Filters/PartSearchFilter.php +++ b/src/DataTables/Filters/PartSearchFilter.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables\Filters; - -use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; class PartSearchFilter implements FilterInterface { - /** @var string The string to query for */ - protected string $keyword; - /** @var boolean Whether to use regex for searching */ protected bool $regex = false; @@ -65,9 +62,14 @@ class PartSearchFilter implements FilterInterface /** @var bool Use footprint name for searching */ protected bool $footprint = false; - public function __construct(string $query) + /** @var bool Use Internal Part number for searching */ + protected bool $ipn = true; + + public function __construct( + /** @var string The string to query for */ + protected string $keyword + ) { - $this->keyword = $query; } protected function getFieldsToSearch(): array @@ -78,31 +80,37 @@ class PartSearchFilter implements FilterInterface $fields_to_search[] = 'part.name'; } if($this->category) { - $fields_to_search[] = 'category.name'; + $fields_to_search[] = '_category.name'; } if($this->description) { $fields_to_search[] = 'part.description'; } + if ($this->comment) { + $fields_to_search[] = 'part.comment'; + } if($this->tags) { $fields_to_search[] = 'part.tags'; } if($this->storelocation) { - $fields_to_search[] = 'storelocations.name'; + $fields_to_search[] = '_storelocations.name'; } if($this->ordernr) { - $fields_to_search[] = 'orderdetails.supplierpartnr'; + $fields_to_search[] = '_orderdetails.supplierpartnr'; } if($this->mpn) { - $fields_to_search[] = 'part.manufacturer_product_url'; + $fields_to_search[] = 'part.manufacturer_product_number'; } if($this->supplier) { - $fields_to_search[] = 'suppliers.name'; + $fields_to_search[] = '_suppliers.name'; } if($this->manufacturer) { - $fields_to_search[] = 'manufacturer.name'; + $fields_to_search[] = '_manufacturer.name'; } if($this->footprint) { - $fields_to_search[] = 'footprint.name'; + $fields_to_search[] = '_footprint.name'; + } + if ($this->ipn) { + $fields_to_search[] = 'part.ipn'; } return $fields_to_search; @@ -113,25 +121,25 @@ class PartSearchFilter implements FilterInterface $fields_to_search = $this->getFieldsToSearch(); //If we have nothing to search for, do nothing - if (empty($fields_to_search) || empty($this->keyword)) { + if ($fields_to_search === [] || $this->keyword === '') { return; } //Convert the fields to search to a list of expressions - $expressions = array_map(function (string $field) { + $expressions = array_map(function (string $field): string { if ($this->regex) { - return sprintf("REGEXP(%s, :search_query) = 1", $field); + return sprintf("REGEXP(%s, :search_query) = TRUE", $field); } - return sprintf("%s LIKE :search_query", $field); + return sprintf("ILIKE(%s, :search_query) = TRUE", $field); }, $fields_to_search); - //Add Or concatation of the expressions to our query + //Add Or concatenation of the expressions to our query $queryBuilder->andWhere( $queryBuilder->expr()->orX(...$expressions) ); - //For regex we pass the query as is, for like we add % to the start and end as wildcards + //For regex, we pass the query as is, for like we add % to the start and end as wildcards if ($this->regex) { $queryBuilder->setParameter('search_query', $this->keyword); } else { @@ -139,234 +147,154 @@ class PartSearchFilter implements FilterInterface } } - /** - * @return string - */ public function getKeyword(): string { return $this->keyword; } - /** - * @param string $keyword - * @return PartSearchFilter - */ public function setKeyword(string $keyword): PartSearchFilter { $this->keyword = $keyword; return $this; } - /** - * @return bool - */ public function isRegex(): bool { return $this->regex; } - /** - * @param bool $regex - * @return PartSearchFilter - */ public function setRegex(bool $regex): PartSearchFilter { $this->regex = $regex; return $this; } - /** - * @return bool - */ public function isName(): bool { return $this->name; } - /** - * @param bool $name - * @return PartSearchFilter - */ public function setName(bool $name): PartSearchFilter { $this->name = $name; return $this; } - /** - * @return bool - */ public function isCategory(): bool { return $this->category; } - /** - * @param bool $category - * @return PartSearchFilter - */ public function setCategory(bool $category): PartSearchFilter { $this->category = $category; return $this; } - /** - * @return bool - */ public function isDescription(): bool { return $this->description; } - /** - * @param bool $description - * @return PartSearchFilter - */ public function setDescription(bool $description): PartSearchFilter { $this->description = $description; return $this; } - /** - * @return bool - */ public function isTags(): bool { return $this->tags; } - /** - * @param bool $tags - * @return PartSearchFilter - */ public function setTags(bool $tags): PartSearchFilter { $this->tags = $tags; return $this; } - /** - * @return bool - */ public function isStorelocation(): bool { return $this->storelocation; } - /** - * @param bool $storelocation - * @return PartSearchFilter - */ public function setStorelocation(bool $storelocation): PartSearchFilter { $this->storelocation = $storelocation; return $this; } - /** - * @return bool - */ public function isOrdernr(): bool { return $this->ordernr; } - /** - * @param bool $ordernr - * @return PartSearchFilter - */ public function setOrdernr(bool $ordernr): PartSearchFilter { $this->ordernr = $ordernr; return $this; } - /** - * @return bool - */ public function isMpn(): bool { return $this->mpn; } - /** - * @param bool $mpn - * @return PartSearchFilter - */ public function setMpn(bool $mpn): PartSearchFilter { $this->mpn = $mpn; return $this; } - /** - * @return bool - */ + public function isIPN(): bool + { + return $this->ipn; + } + + public function setIPN(bool $ipn): PartSearchFilter + { + $this->ipn = $ipn; + return $this; + } + public function isSupplier(): bool { return $this->supplier; } - /** - * @param bool $supplier - * @return PartSearchFilter - */ public function setSupplier(bool $supplier): PartSearchFilter { $this->supplier = $supplier; return $this; } - /** - * @return bool - */ public function isManufacturer(): bool { return $this->manufacturer; } - /** - * @param bool $manufacturer - * @return PartSearchFilter - */ public function setManufacturer(bool $manufacturer): PartSearchFilter { $this->manufacturer = $manufacturer; return $this; } - /** - * @return bool - */ public function isFootprint(): bool { return $this->footprint; } - /** - * @param bool $footprint - * @return PartSearchFilter - */ public function setFootprint(bool $footprint): PartSearchFilter { $this->footprint = $footprint; return $this; } - /** - * @return bool - */ public function isComment(): bool { return $this->comment; } - /** - * @param bool $comment - * @return PartSearchFilter - */ public function setComment(bool $comment): PartSearchFilter { $this->comment = $comment; @@ -374,4 +302,4 @@ class PartSearchFilter implements FilterInterface } -} \ No newline at end of file +} diff --git a/src/DataTables/Helpers/ColumnSortHelper.php b/src/DataTables/Helpers/ColumnSortHelper.php new file mode 100644 index 00000000..05bd8182 --- /dev/null +++ b/src/DataTables/Helpers/ColumnSortHelper.php @@ -0,0 +1,130 @@ +. + */ + +declare(strict_types=1); + + +namespace App\DataTables\Helpers; + +use Omines\DataTablesBundle\DataTable; +use Psr\Log\LoggerInterface; + +class ColumnSortHelper +{ + private array $columns = []; + + public function __construct(private readonly LoggerInterface $logger) + { + } + + /** + * Add a new column which can be sorted and visibility controlled by the user. The basic syntax is similar to + * the DataTable add method, but with additional options. + * @param string $name + * @param string $type + * @param array $options + * @param string|null $alias If an alias is set here, the column will be available under this alias in the config + * string instead of the name. + * @param bool $visibility_configurable If set to false, this column can not be visibility controlled by the user + * @return $this + */ + public function add(string $name, string $type, array $options = [], ?string $alias = null, + bool $visibility_configurable = true): self + { + //Alias allows us to override the name of the column in the env variable + $this->columns[$alias ?? $name] = [ + 'name' => $name, + 'type' => $type, + 'options' => $options, + 'visibility_configurable' => $visibility_configurable + ]; + + return $this; + } + + /** + * Remove all columns saved inside this helper + * @return void + */ + public function reset(): void + { + $this->columns = []; + } + + /** + * 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. + * @return void + */ + public function applyVisibilityAndConfigureColumns(DataTable $dataTable, string|array $visible_columns, + string $config_var_name): void + { + //If the config is given as a string, convert it to an array first + if (!is_array($visible_columns)) { + $visible_columns = array_map(trim(...), explode(",", $visible_columns)); + } + + $processed_columns = []; + + //First add all columns which visibility is not configurable + foreach ($this->columns as $col_id => $col_data) { + if (!$col_data['visibility_configurable']) { + $this->addColumnEntry($dataTable, $this->columns[$col_id], null); + $processed_columns[] = $col_id; + } + } + + //Afterwards the columns, which should be visible by default + foreach ($visible_columns as $col_id) { + if (!isset($this->columns[$col_id]) || !$this->columns[$col_id]['visibility_configurable']) { + $this->logger->warning("Configuration option $config_var_name specify invalid column '$col_id'. Column is skipped."); + continue; + } + + if (in_array($col_id, $processed_columns, true)) { + $this->logger->warning("Configuration option $config_var_name specify column '$col_id' multiple time. Only first occurrence is used."); + continue; + } + $this->addColumnEntry($dataTable, $this->columns[$col_id], true); + $processed_columns[] = $col_id; + } + + //and the remaining non-visible columns + foreach (array_keys($this->columns) as $col_id) { + if (in_array($col_id, $processed_columns, true)) { + // column already processed + continue; + } + $this->addColumnEntry($dataTable, $this->columns[$col_id], false); + $processed_columns[] = $col_id; + } + } + + private function addColumnEntry(DataTable $dataTable, array $entry, ?bool $visible): void + { + $options = $entry['options'] ?? []; + if (!is_null($visible)) { + $options["visible"] = $visible; + } + $dataTable->add($entry['name'], $entry['type'], $options); + } +} \ No newline at end of file diff --git a/src/DataTables/Helpers/PartDataTableHelper.php b/src/DataTables/Helpers/PartDataTableHelper.php index 90255835..c33c3a82 100644 --- a/src/DataTables/Helpers/PartDataTableHelper.php +++ b/src/DataTables/Helpers/PartDataTableHelper.php @@ -1,4 +1,7 @@ previewGenerator = $previewGenerator; - $this->attachmentURLGenerator = $attachmentURLGenerator; - $this->translator = $translator; - $this->entityURLGenerator = $entityURLGenerator; + public function __construct( + private readonly PartPreviewGenerator $previewGenerator, + private readonly AttachmentURLGenerator $attachmentURLGenerator, + private readonly EntityURLGenerator $entityURLGenerator, + private readonly TranslatorInterface $translator, + private readonly AmountFormatter $amountFormatter, + ) { } public function renderName(Part $context): string @@ -52,14 +53,16 @@ class PartDataTableHelper //Depending on the part status we show a different icon (the later conditions have higher priority) if ($context->isFavorite()) { - $icon = sprintf('', $this->translator->trans('part.favorite.badge')); + $icon = sprintf('', + $this->translator->trans('part.favorite.badge')); } if ($context->isNeedsReview()) { - $icon = sprintf('', $this->translator->trans('part.needs_review.badge')); + $icon = sprintf('', + $this->translator->trans('part.needs_review.badge')); } - if ($context->getBuiltProject() !== null) { + if ($context->getBuiltProject() instanceof Project) { $icon = sprintf('', - $this->translator->trans('part.info.projectBuildPart.hint') . ': ' . $context->getBuiltProject()->getName()); + $this->translator->trans('part.info.projectBuildPart.hint').': '.$context->getBuiltProject()->getName()); } @@ -67,14 +70,14 @@ class PartDataTableHelper '%s%s', $this->entityURLGenerator->infoURL($context), $icon, - htmlentities($context->getName()) + htmlspecialchars($context->getName()) ); } public function renderPicture(Part $context): string { $preview_attachment = $this->previewGenerator->getTablePreviewAttachment($context); - if (null === $preview_attachment) { + if (!$preview_attachment instanceof Attachment) { return ''; } @@ -88,8 +91,66 @@ class PartDataTableHelper 'Part image', $this->attachmentURLGenerator->getThumbnailURL($preview_attachment), $this->attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_md'), - 'img-fluid hoverpic', + 'hoverpic part-table-image', $title ); } -} \ No newline at end of file + + public function renderStorageLocations(Part $context): string + { + $tmp = []; + foreach ($context->getPartLots() as $lot) { + //Ignore lots without storelocation + if (!$lot->getStorageLocation() instanceof StorageLocation) { + continue; + } + $tmp[] = sprintf( + '%s', + $this->entityURLGenerator->listPartsURL($lot->getStorageLocation()), + htmlspecialchars($lot->getStorageLocation()->getFullPath()), + htmlspecialchars($lot->getStorageLocation()->getName()) + ); + } + + return implode('
', $tmp); + } + + public function renderAmount(Part $context): string + { + $amount = $context->getAmountSum(); + $expiredAmount = $context->getExpiredAmountSum(); + + $ret = ''; + + if ($context->isAmountUnknown()) { + //When all amounts are unknown, we show a question mark + if ($amount === 0.0) { + $ret .= sprintf('?', + $this->translator->trans('part_lots.instock_unknown')); + } else { //Otherwise mark it with greater equal and the (known) amount + $ret .= sprintf('', + $this->translator->trans('part_lots.instock_unknown') + ); + $ret .= htmlspecialchars($this->amountFormatter->format($amount, $context->getPartUnit())); + } + } else { + $ret .= htmlspecialchars($this->amountFormatter->format($amount, $context->getPartUnit())); + } + + //If we have expired lots, we show them in parentheses behind + if ($expiredAmount > 0) { + $ret .= sprintf(' (+%s)', + $this->translator->trans('part_lots.is_expired'), + htmlspecialchars($this->amountFormatter->format($expiredAmount, $context->getPartUnit()))); + } + + //When the amount is below the minimum amount, we highlight the number red + if ($context->isNotEnoughInstock()) { + $ret = sprintf('%s', + $this->translator->trans('part.info.amount.less_than_desired'), + $ret); + } + + return $ret; + } +} diff --git a/src/DataTables/LogDataTable.php b/src/DataTables/LogDataTable.php index 5d23446a..f6604279 100644 --- a/src/DataTables/LogDataTable.php +++ b/src/DataTables/LogDataTable.php @@ -22,13 +22,15 @@ declare(strict_types=1); namespace App\DataTables; +use App\DataTables\Column\EnumColumn; +use App\Entity\LogSystem\LogTargetType; +use Symfony\Bundle\SecurityBundle\Security; use App\DataTables\Column\IconLinkColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\LogEntryExtraColumn; use App\DataTables\Column\LogEntryTargetColumn; use App\DataTables\Column\RevertLogColumn; use App\DataTables\Column\RowClassColumn; -use App\DataTables\Filters\AttachmentFilter; use App\DataTables\Filters\LogFilter; use App\Entity\Base\AbstractDBElement; use App\Entity\Contracts\TimeTravelInterface; @@ -38,12 +40,12 @@ use App\Entity\LogSystem\ElementCreatedLogEntry; use App\Entity\LogSystem\ElementDeletedLogEntry; use App\Entity\LogSystem\ElementEditedLogEntry; use App\Entity\LogSystem\PartStockChangedLogEntry; -use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use App\Exceptions\EntityNotSupportedException; use App\Repository\LogEntryRepository; use App\Services\ElementTypeNameGenerator; use App\Services\EntityURLGenerator; +use App\Services\LogSystem\LogLevelHelper; use App\Services\UserSystem\UserAvatarHelper; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; @@ -52,36 +54,20 @@ use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter; use Omines\DataTablesBundle\Column\TextColumn; use Omines\DataTablesBundle\DataTable; use Omines\DataTablesBundle\DataTableTypeInterface; -use Psr\Log\LogLevel; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; use Symfony\Contracts\Translation\TranslatorInterface; -use function Symfony\Component\Translation\t; - class LogDataTable implements DataTableTypeInterface { - protected ElementTypeNameGenerator $elementTypeNameGenerator; - protected TranslatorInterface $translator; - protected UrlGeneratorInterface $urlGenerator; - protected EntityURLGenerator $entityURLGenerator; protected LogEntryRepository $logRepo; - protected Security $security; - protected UserAvatarHelper $userAvatarHelper; - public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator, - UrlGeneratorInterface $urlGenerator, EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager, - Security $security, UserAvatarHelper $userAvatarHelper) + public function __construct(protected ElementTypeNameGenerator $elementTypeNameGenerator, protected TranslatorInterface $translator, + protected UrlGeneratorInterface $urlGenerator, protected EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager, + protected Security $security, protected UserAvatarHelper $userAvatarHelper, protected LogLevelHelper $logLevelHelper) { - $this->elementTypeNameGenerator = $elementTypeNameGenerator; - $this->translator = $translator; - $this->urlGenerator = $urlGenerator; - $this->entityURLGenerator = $entityURLGenerator; $this->logRepo = $entityManager->getRepository(AbstractLogEntry::class); - $this->security = $security; - $this->userAvatarHelper = $userAvatarHelper; } public function configureOptions(OptionsResolver $optionsResolver): void @@ -115,72 +101,17 @@ class LogDataTable implements DataTableTypeInterface //This special $$rowClass column is used to set the row class depending on the log level. The class gets set by the frontend controller $dataTable->add('dont_matter', RowClassColumn::class, [ - 'render' => static function ($value, AbstractLogEntry $context) { - switch ($context->getLevel()) { - case AbstractLogEntry::LEVEL_EMERGENCY: - case AbstractLogEntry::LEVEL_ALERT: - case AbstractLogEntry::LEVEL_CRITICAL: - case AbstractLogEntry::LEVEL_ERROR: - return 'table-danger'; - case AbstractLogEntry::LEVEL_WARNING: - return 'table-warning'; - case AbstractLogEntry::LEVEL_NOTICE: - return 'table-info'; - default: - return ''; - } - }, + 'render' => fn($value, AbstractLogEntry $context) => $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString()), ]); $dataTable->add('symbol', TextColumn::class, [ 'label' => '', 'className' => 'no-colvis', - 'render' => static function ($value, AbstractLogEntry $context) { - switch ($context->getLevelString()) { - case LogLevel::DEBUG: - $symbol = 'fa-bug'; - - break; - case LogLevel::INFO: - $symbol = 'fa-info'; - - break; - case LogLevel::NOTICE: - $symbol = 'fa-flag'; - - break; - case LogLevel::WARNING: - $symbol = 'fa-exclamation-circle'; - - break; - case LogLevel::ERROR: - $symbol = 'fa-exclamation-triangle'; - - break; - case LogLevel::CRITICAL: - $symbol = 'fa-bolt'; - - break; - case LogLevel::ALERT: - $symbol = 'fa-radiation'; - - break; - case LogLevel::EMERGENCY: - $symbol = 'fa-skull-crossbones'; - - break; - default: - $symbol = 'fa-question-circle'; - - break; - } - - return sprintf( - '', - $symbol, - $context->getLevelString() - ); - }, + 'render' => fn($value, AbstractLogEntry $context): string => sprintf( + '', + $this->logLevelHelper->logLevelToIconClass($context->getLevelString()), + $context->getLevelString() + ), ]); $dataTable->add('id', TextColumn::class, [ @@ -191,6 +122,10 @@ class LogDataTable implements DataTableTypeInterface $dataTable->add('timestamp', LocaleDateTimeColumn::class, [ 'label' => 'log.timestamp', 'timeFormat' => 'medium', + 'render' => fn(string $value, AbstractLogEntry $context): string => sprintf('%s', + $this->urlGenerator->generate('log_details', ['id' => $context->getID()]), + $value + ) ]); $dataTable->add('type', TextColumn::class, [ @@ -202,7 +137,7 @@ class LogDataTable implements DataTableTypeInterface if ($context instanceof PartStockChangedLogEntry) { $text .= sprintf( ' (%s)', - $this->translator->trans('log.part_stock_changed.' . $context->getInstockChangeType()) + $this->translator->trans($context->getInstockChangeType()->toTranslationKey()) ); } @@ -214,18 +149,25 @@ class LogDataTable implements DataTableTypeInterface 'label' => 'log.level', 'visible' => 'system_log' === $options['mode'], 'propertyPath' => 'levelString', - 'render' => function (string $value, AbstractLogEntry $context) { - return $this->translator->trans('log.level.'.$value); - }, + 'render' => fn(string $value, AbstractLogEntry $context) => $this->translator->trans('log.level.'.$value), ]); $dataTable->add('user', TextColumn::class, [ 'label' => 'log.user', - 'render' => function ($value, AbstractLogEntry $context) { + 'orderField' => 'NATSORT(user.name)', + 'render' => function ($value, AbstractLogEntry $context): string { $user = $context->getUser(); //If user was deleted, show the info from the username field - if ($user === null) { + if (!$user instanceof User) { + if ($context->isCLIEntry()) { + return sprintf('%s [%s]', + htmlentities((string) $context->getCLIUsername()), + $this->translator->trans('log.cli_user') + ); + } + + //Else we just deal with a deleted user return sprintf( '@%s [%s]', htmlentities($context->getUsername()), @@ -245,11 +187,12 @@ class LogDataTable implements DataTableTypeInterface }, ]); - $dataTable->add('target_type', TextColumn::class, [ + $dataTable->add('target_type', EnumColumn::class, [ 'label' => 'log.target_type', 'visible' => false, - 'render' => function ($value, AbstractLogEntry $context) { - $class = $context->getTargetClass(); + 'class' => LogTargetType::class, + 'render' => function (LogTargetType $value, AbstractLogEntry $context) { + $class = $value->toClass(); if (null !== $class) { return $this->elementTypeNameGenerator->getLocalizedTypeLabel($class); } @@ -273,24 +216,22 @@ class LogDataTable implements DataTableTypeInterface 'href' => function ($value, AbstractLogEntry $context) { if ( ($context instanceof TimeTravelInterface - && $context->hasOldDataInformations()) + && $context->hasOldDataInformation()) || $context instanceof CollectionElementDeleted ) { try { $target = $this->logRepo->getTargetElement($context); - if (null !== $target) { + if ($target instanceof AbstractDBElement) { return $this->entityURLGenerator->timeTravelURL($target, $context->getTimestamp()); } - } catch (EntityNotSupportedException $exception) { + } catch (EntityNotSupportedException) { return null; } } return null; }, - 'disabled' => function ($value, AbstractLogEntry $context) { - return !$this->security->isGranted('show_history', $context->getTargetClass()); - }, + 'disabled' => fn($value, AbstractLogEntry $context) => !$this->security->isGranted('show_history', $context->getTargetClass()), ]); $dataTable->add('actionRevert', RevertLogColumn::class, [ @@ -338,8 +279,8 @@ class LogDataTable implements DataTableTypeInterface ->andWhere('log.target_type NOT IN (:disallowed)'); $builder->setParameter('disallowed', [ - AbstractLogEntry::targetTypeClassToID(User::class), - AbstractLogEntry::targetTypeClassToID(Group::class), + LogTargetType::USER, + LogTargetType::GROUP, ]); } @@ -347,9 +288,16 @@ class LogDataTable implements DataTableTypeInterface foreach ($options['filter_elements'] as $element) { /** @var AbstractDBElement $element */ - $target_type = AbstractLogEntry::targetTypeClassToID(get_class($element)); + $target_type = LogTargetType::fromElementClass($element); $target_id = $element->getID(); - $builder->orWhere("log.target_type = ${target_type} AND log.target_id = ${target_id}"); + + //We have to create unique parameter names for each element + $target_type_var = 'filter_target_type_' . uniqid('', false); + $target_id_var = 'filter_target_id_' . uniqid('', false); + + $builder->orWhere("log.target_type = :$target_type_var AND log.target_id = :$target_id_var"); + $builder->setParameter($target_type_var, $target_type); + $builder->setParameter($target_id_var, $target_id); } } } diff --git a/src/DataTables/PartsDataTable.php b/src/DataTables/PartsDataTable.php index f38215e3..3163a38b 100644 --- a/src/DataTables/PartsDataTable.php +++ b/src/DataTables/PartsDataTable.php @@ -22,7 +22,9 @@ declare(strict_types=1); namespace App\DataTables; +use App\DataTables\Adapters\TwoStepORMAdapter; use App\DataTables\Column\EntityColumn; +use App\DataTables\Column\EnumColumn; use App\DataTables\Column\IconLinkColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\MarkdownColumn; @@ -34,57 +36,38 @@ use App\DataTables\Column\SIUnitNumberColumn; use App\DataTables\Column\TagsColumn; use App\DataTables\Filters\PartFilter; use App\DataTables\Filters\PartSearchFilter; +use App\DataTables\Helpers\ColumnSortHelper; use App\DataTables\Helpers\PartDataTableHelper; -use App\Entity\Parts\Category; -use App\Entity\Parts\Footprint; -use App\Entity\Parts\Manufacturer; +use App\Doctrine\Helpers\FieldHelper; +use App\Entity\Parts\ManufacturingStatus; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; -use App\Entity\Parts\Supplier; -use App\Services\Formatters\AmountFormatter; -use App\Services\Attachments\AttachmentURLGenerator; -use App\Services\Attachments\PartPreviewGenerator; +use App\Entity\ProjectSystem\Project; use App\Services\EntityURLGenerator; -use App\Services\Trees\NodesListBuilder; +use App\Services\Formatters\AmountFormatter; +use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\QueryBuilder; -use Omines\DataTablesBundle\Adapter\Doctrine\FetchJoinORMAdapter; use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider; -use Omines\DataTablesBundle\Column\BoolColumn; -use Omines\DataTablesBundle\Column\MapColumn; use Omines\DataTablesBundle\Column\TextColumn; use Omines\DataTablesBundle\DataTable; use Omines\DataTablesBundle\DataTableTypeInterface; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; use Symfony\Contracts\Translation\TranslatorInterface; final class PartsDataTable implements DataTableTypeInterface { - private TranslatorInterface $translator; - private NodesListBuilder $treeBuilder; - private AmountFormatter $amountFormatter; - private AttachmentURLGenerator $attachmentURLGenerator; - private Security $security; + const LENGTH_MENU = [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]]; - private PartDataTableHelper $partDataTableHelper; - - /** - * @var EntityURLGenerator - */ - private $urlGenerator; - - public function __construct(EntityURLGenerator $urlGenerator, TranslatorInterface $translator, - NodesListBuilder $treeBuilder, AmountFormatter $amountFormatter,PartDataTableHelper $partDataTableHelper, - AttachmentURLGenerator $attachmentURLGenerator, Security $security) - { - $this->urlGenerator = $urlGenerator; - $this->translator = $translator; - $this->treeBuilder = $treeBuilder; - $this->amountFormatter = $amountFormatter; - $this->attachmentURLGenerator = $attachmentURLGenerator; - $this->security = $security; - $this->partDataTableHelper = $partDataTableHelper; + public function __construct( + private readonly EntityURLGenerator $urlGenerator, + private readonly TranslatorInterface $translator, + private readonly AmountFormatter $amountFormatter, + private readonly PartDataTableHelper $partDataTableHelper, + private readonly Security $security, + private readonly string $visible_columns, + private readonly ColumnSortHelper $csh, + ) { } public function configureOptions(OptionsResolver $optionsResolver): void @@ -104,10 +87,10 @@ final class PartsDataTable implements DataTableTypeInterface $this->configureOptions($resolver); $options = $resolver->resolve($options); - $dataTable + $this->csh //Color the table rows depending on the review and favorite status - ->add('dont_matter', RowClassColumn::class, [ - 'render' => function ($value, Part $context) { + ->add('row_color', RowClassColumn::class, [ + 'render' => function ($value, Part $context): string { if ($context->isNeedsReview()) { return 'table-secondary'; } @@ -117,189 +100,208 @@ final class PartsDataTable implements DataTableTypeInterface return ''; //Default coloring otherwise }, - ]) - - ->add('select', SelectColumn::class) + ], visibility_configurable: false) + ->add('select', SelectColumn::class, visibility_configurable: false) ->add('picture', TextColumn::class, [ 'label' => '', 'className' => 'no-colvis', - 'render' => function ($value, Part $context) { - return $this->partDataTableHelper->renderPicture($context); - }, - ]) + 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderPicture($context), + ], visibility_configurable: false) ->add('name', TextColumn::class, [ 'label' => $this->translator->trans('part.table.name'), - 'render' => function ($value, Part $context) { - return $this->partDataTableHelper->renderName($context); - }, + 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderName($context), + 'orderField' => 'NATSORT(part.name)' ]) ->add('id', TextColumn::class, [ 'label' => $this->translator->trans('part.table.id'), - 'visible' => false, ]) ->add('ipn', TextColumn::class, [ 'label' => $this->translator->trans('part.table.ipn'), - 'visible' => false, + 'orderField' => 'NATSORT(part.ipn)' ]) ->add('description', MarkdownColumn::class, [ 'label' => $this->translator->trans('part.table.description'), - ]); - - if ($this->security->isGranted('@categories.read')) { - $dataTable->add('category', EntityColumn::class, [ + ]) + ->add('category', EntityColumn::class, [ 'label' => $this->translator->trans('part.table.category'), 'property' => 'category', - ]); - } - - if ($this->security->isGranted('@footprints.read')) { - $dataTable->add('footprint', EntityColumn::class, [ + 'orderField' => 'NATSORT(_category.name)' + ]) + ->add('footprint', EntityColumn::class, [ 'property' => 'footprint', 'label' => $this->translator->trans('part.table.footprint'), - ]); - } - if ($this->security->isGranted('@manufacturers.read')) { - $dataTable->add('manufacturer', EntityColumn::class, [ + 'orderField' => 'NATSORT(_footprint.name)' + ]) + ->add('manufacturer', EntityColumn::class, [ 'property' => 'manufacturer', 'label' => $this->translator->trans('part.table.manufacturer'), - ]); - } - if ($this->security->isGranted('@storelocations.read')) { - $dataTable->add('storelocation', TextColumn::class, [ + 'orderField' => 'NATSORT(_manufacturer.name)' + ]) + ->add('storelocation', TextColumn::class, [ 'label' => $this->translator->trans('part.table.storeLocations'), - 'render' => function ($value, Part $context) { - $tmp = []; - foreach ($context->getPartLots() as $lot) { - //Ignore lots without storelocation - if (null === $lot->getStorageLocation()) { - continue; - } - $tmp[] = sprintf( - '%s', - $this->urlGenerator->listPartsURL($lot->getStorageLocation()), - $lot->getStorageLocation()->getName() - ); - } + //We need to use a aggregate function to get the first store location, as we have a one-to-many relation + 'orderField' => 'NATSORT(MIN(_storelocations.name))', + 'render' => fn ($value, Part $context) => $this->partDataTableHelper->renderStorageLocations($context), + ], alias: 'storage_location') - return implode('
', $tmp); - }, - ]); - } - - $dataTable->add('amount', TextColumn::class, [ - 'label' => $this->translator->trans('part.table.amount'), - 'render' => function ($value, Part $context) { - $amount = $context->getAmountSum(); - $expiredAmount = $context->getExpiredAmountSum(); - - $ret = $this->amountFormatter->format($amount, $context->getPartUnit()); - - //If we have expired lots, we show them in parentheses behind - if ($expiredAmount > 0) { - $ret .= sprintf(' (+%s)', - $this->translator->trans('part_lots.is_expired'), - $this->amountFormatter->format($expiredAmount, $context->getPartUnit())); - } - - - return $ret; - }, - 'orderField' => 'amountSum' - ]) + ->add('amount', TextColumn::class, [ + 'label' => $this->translator->trans('part.table.amount'), + 'render' => fn ($value, Part $context) => $this->partDataTableHelper->renderAmount($context), + 'orderField' => 'amountSum' + ]) ->add('minamount', TextColumn::class, [ 'label' => $this->translator->trans('part.table.minamount'), - 'visible' => false, - 'render' => function ($value, Part $context) { - return $this->amountFormatter->format($value, $context->getPartUnit()); - }, - ]); - - if ($this->security->isGranted('@footprints.read')) { - $dataTable->add('partUnit', TextColumn::class, [ - 'field' => 'partUnit.name', + 'render' => fn($value, Part $context): string => htmlspecialchars($this->amountFormatter->format($value, + $context->getPartUnit())), + ]) + ->add('partUnit', TextColumn::class, [ 'label' => $this->translator->trans('part.table.partUnit'), - 'visible' => false, - ]); - } + 'orderField' => 'NATSORT(_partUnit.name)', + 'render' => function($value, Part $context): string { + $partUnit = $context->getPartUnit(); + if ($partUnit === null) { + return ''; + } - $dataTable->add('addedDate', LocaleDateTimeColumn::class, [ - 'label' => $this->translator->trans('part.table.addedDate'), - 'visible' => false, - ]) + $tmp = htmlspecialchars($partUnit->getName()); + + if ($partUnit->getUnit()) { + $tmp .= ' ('.htmlspecialchars($partUnit->getUnit()).')'; + } + return $tmp; + } + ]) + ->add('addedDate', LocaleDateTimeColumn::class, [ + 'label' => $this->translator->trans('part.table.addedDate'), + ]) ->add('lastModified', LocaleDateTimeColumn::class, [ 'label' => $this->translator->trans('part.table.lastModified'), - 'visible' => false, ]) ->add('needs_review', PrettyBoolColumn::class, [ 'label' => $this->translator->trans('part.table.needsReview'), - 'visible' => false, ]) ->add('favorite', PrettyBoolColumn::class, [ 'label' => $this->translator->trans('part.table.favorite'), - 'visible' => false, ]) - ->add('manufacturing_status', MapColumn::class, [ + ->add('manufacturing_status', EnumColumn::class, [ 'label' => $this->translator->trans('part.table.manufacturingStatus'), - 'visible' => false, - 'default' => $this->translator->trans('m_status.unknown'), - 'map' => [ - '' => $this->translator->trans('m_status.unknown'), - 'announced' => $this->translator->trans('m_status.announced'), - 'active' => $this->translator->trans('m_status.active'), - 'nrfnd' => $this->translator->trans('m_status.nrfnd'), - 'eol' => $this->translator->trans('m_status.eol'), - 'discontinued' => $this->translator->trans('m_status.discontinued'), - ], + 'class' => ManufacturingStatus::class, + 'render' => function (?ManufacturingStatus $status, Part $context): string { + if ($status === null) { + return ''; + } + + return $this->translator->trans($status->toTranslationKey()); + }, ]) ->add('manufacturer_product_number', TextColumn::class, [ 'label' => $this->translator->trans('part.table.mpn'), - 'visible' => false, + 'orderField' => 'NATSORT(part.manufacturer_product_number)' ]) ->add('mass', SIUnitNumberColumn::class, [ 'label' => $this->translator->trans('part.table.mass'), - 'visible' => false, 'unit' => 'g' ]) ->add('tags', TagsColumn::class, [ 'label' => $this->translator->trans('part.table.tags'), - 'visible' => false, ]) ->add('attachments', PartAttachmentsColumn::class, [ 'label' => $this->translator->trans('part.table.attachments'), - 'visible' => false, - ]) + ]); + + //Add a column to list the projects where the part is used, when the user has the permission to see the projects + if ($this->security->isGranted('read', Project::class)) { + $this->csh->add('projects', TextColumn::class, [ + 'label' => $this->translator->trans('project.labelp'), + 'render' => function ($value, Part $context): string { + //Only show the first 5 projects names + $projects = $context->getProjects(); + $tmp = ""; + + $max = 5; + + for ($i = 0; $i < min($max, count($projects)); $i++) { + $url = $this->urlGenerator->infoURL($projects[$i]); + $tmp .= sprintf('%s', $url, htmlspecialchars($projects[$i]->getName())); + if ($i < count($projects) - 1) { + $tmp .= ", "; + } + } + + if (count($projects) > $max) { + $tmp .= ", + ".(count($projects) - $max); + } + + return $tmp; + } + ]); + } + + $this->csh ->add('edit', IconLinkColumn::class, [ 'label' => $this->translator->trans('part.table.edit'), - 'visible' => false, - 'href' => function ($value, Part $context) { - return $this->urlGenerator->editURL($context); - }, - 'disabled' => function ($value, Part $context) { - return !$this->security->isGranted('edit', $context); - }, + 'href' => fn($value, Part $context) => $this->urlGenerator->editURL($context), + 'disabled' => fn($value, Part $context) => !$this->security->isGranted('edit', $context), 'title' => $this->translator->trans('part.table.edit.title'), - ]) + ]); - ->addOrderBy('name') - ->createAdapter(FetchJoinORMAdapter::class, [ - 'simple_total_query' => true, - 'query' => function (QueryBuilder $builder): void { - $this->getQuery($builder); - }, + //Apply the user configured order and visibility and add the columns to the table + $this->csh->applyVisibilityAndConfigureColumns($dataTable, $this->visible_columns, + "TABLE_PARTS_DEFAULT_COLUMNS"); + + $dataTable->addOrderBy('name') + ->createAdapter(TwoStepORMAdapter::class, [ + 'filter_query' => $this->getFilterQuery(...), + 'detail_query' => $this->getDetailQuery(...), 'entity' => Part::class, + 'hydrate' => AbstractQuery::HYDRATE_OBJECT, + //Use the simple total query, as we just want to get the total number of parts without any conditions + //For this the normal query would be pretty slow + 'simple_total_query' => true, 'criteria' => [ function (QueryBuilder $builder) use ($options): void { $this->buildCriteria($builder, $options); }, new SearchCriteriaProvider(), ], + 'query_modifier' => $this->addJoins(...), ]); } - private function getQuery(QueryBuilder $builder): void + + private function getFilterQuery(QueryBuilder $builder): void { - //Distinct is very slow here, do not add this here (also I think this is not needed here, as the id column is always distinct) - $builder->select('part') + /* In the filter query we only select the IDs. The fetching of the full entities is done in the detail query. + * We only need to join the entities here, so we can filter by them. + * The filter conditions are added to this QB in the buildCriteria method. + * + * The amountSum field and the joins are dynmically added by the addJoins method, if the fields are used in the query. + * This improves the performance, as we do not need to join all tables, if we do not need them. + */ + $builder + ->select('part.id') + ->addSelect('part.minamount AS HIDDEN minamount') + ->from(Part::class, 'part') + + //The other group by fields, are dynamically added by the addJoins method + ->addGroupBy('part'); + } + + private function getDetailQuery(QueryBuilder $builder, array $filter_results): void + { + $ids = array_map(static fn($row) => $row['id'], $filter_results); + + /* + * In this query we take the IDs which were filtered, paginated and sorted in the filter query, and fetch the + * full entities. + * We can do complex fetch joins, as we do not need to filter or sort here (which would kill the performance). + * The only condition should be for the IDs. + * It is important that elements are ordered the same way, as the IDs are passed, or ordering will be wrong. + * + * We do not require the subqueries like amountSum here, as it is not used to render the table (and only for sorting) + */ + $builder + ->select('part') ->addSelect('category') ->addSelect('footprint') ->addSelect('manufacturer') @@ -310,16 +312,6 @@ final class PartsDataTable implements DataTableTypeInterface ->addSelect('orderdetails') ->addSelect('attachments') ->addSelect('storelocations') - //Calculate amount sum using a subquery, so we can filter and sort by it - ->addSelect( - '( - SELECT IFNULL(SUM(partLot.amount), 0.0) - FROM '. PartLot::class. ' partLot - WHERE partLot.part = part.id - AND partLot.instock_unknown = false - AND (partLot.expiration_date IS NULL OR partLot.expiration_date > CURRENT_DATE()) - ) AS HIDDEN amountSum' - ) ->from(Part::class, 'part') ->leftJoin('part.category', 'category') ->leftJoin('part.master_picture_attachment', 'master_picture_attachment') @@ -333,6 +325,8 @@ final class PartsDataTable implements DataTableTypeInterface ->leftJoin('part.attachments', 'attachments') ->leftJoin('part.partUnit', 'partUnit') ->leftJoin('part.parameters', 'parameters') + ->where('part.id IN (:ids)') + ->setParameter('ids', $ids) //We have to group by all elements, or only the first sub elements of an association is fetched! (caused issue #190) ->addGroupBy('part') @@ -347,8 +341,89 @@ final class PartsDataTable implements DataTableTypeInterface ->addGroupBy('suppliers') ->addGroupBy('attachments') ->addGroupBy('partUnit') - ->addGroupBy('parameters') - ; + ->addGroupBy('parameters'); + + //Get the results in the same order as the IDs were passed + FieldHelper::addOrderByFieldParam($builder, 'part.id', 'ids'); + } + + /** + * This function is called right before the filter query is executed. + * We use it to dynamically add joins to the query, if the fields are used in the query. + * @param QueryBuilder $builder + * @return QueryBuilder + */ + private function addJoins(QueryBuilder $builder): QueryBuilder + { + //Check if the query contains certain conditions, for which we need to add additional joins + //The join fields get prefixed with an underscore, so we can check if they are used in the query easy without confusing them for a part subfield + $dql = $builder->getDQL(); + + //Add the amountSum field, if it is used in the query + if (str_contains($dql, 'amountSum')) { + //Calculate amount sum using a subquery, so we can filter and sort by it + $builder->addSelect( + '( + SELECT COALESCE(SUM(partLot.amount), 0.0) + FROM '.PartLot::class.' partLot + WHERE partLot.part = part.id + AND partLot.instock_unknown = false + AND (partLot.expiration_date IS NULL OR partLot.expiration_date > CURRENT_DATE()) + ) AS HIDDEN amountSum' + ); + } + + if (str_contains($dql, '_category')) { + $builder->leftJoin('part.category', '_category'); + $builder->addGroupBy('_category'); + } + if (str_contains($dql, '_master_picture_attachment')) { + $builder->leftJoin('part.master_picture_attachment', '_master_picture_attachment'); + $builder->addGroupBy('_master_picture_attachment'); + } + if (str_contains($dql, '_partLots') || str_contains($dql, '_storelocations')) { + $builder->leftJoin('part.partLots', '_partLots'); + $builder->leftJoin('_partLots.storage_location', '_storelocations'); + //Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1 + //$builder->addGroupBy('_partLots'); + //$builder->addGroupBy('_storelocations'); + } + if (str_contains($dql, '_footprint')) { + $builder->leftJoin('part.footprint', '_footprint'); + $builder->addGroupBy('_footprint'); + } + if (str_contains($dql, '_manufacturer')) { + $builder->leftJoin('part.manufacturer', '_manufacturer'); + $builder->addGroupBy('_manufacturer'); + } + if (str_contains($dql, '_orderdetails') || str_contains($dql, '_suppliers')) { + $builder->leftJoin('part.orderdetails', '_orderdetails'); + $builder->leftJoin('_orderdetails.supplier', '_suppliers'); + //Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1 + //$builder->addGroupBy('_orderdetails'); + //$builder->addGroupBy('_suppliers'); + } + if (str_contains($dql, '_attachments')) { + $builder->leftJoin('part.attachments', '_attachments'); + //Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1 + //$builder->addGroupBy('_attachments'); + } + if (str_contains($dql, '_partUnit')) { + $builder->leftJoin('part.partUnit', '_partUnit'); + $builder->addGroupBy('_partUnit'); + } + if (str_contains($dql, '_parameters')) { + $builder->leftJoin('part.parameters', '_parameters'); + //Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1 + //$builder->addGroupBy('_parameters'); + } + if (str_contains($dql, '_projectBomEntries')) { + $builder->leftJoin('part.project_bom_entries', '_projectBomEntries'); + //Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1 + //$builder->addGroupBy('_projectBomEntries'); + } + + return $builder; } private function buildCriteria(QueryBuilder $builder, array $options): void @@ -364,6 +439,5 @@ final class PartsDataTable implements DataTableTypeInterface $filter = $options['filter']; $filter->apply($builder); } - } } diff --git a/src/DataTables/ProjectBomEntriesDataTable.php b/src/DataTables/ProjectBomEntriesDataTable.php index 8d7c839d..fcb06984 100644 --- a/src/DataTables/ProjectBomEntriesDataTable.php +++ b/src/DataTables/ProjectBomEntriesDataTable.php @@ -1,4 +1,7 @@ . */ - namespace App\DataTables; use App\DataTables\Column\EntityColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\MarkdownColumn; -use App\DataTables\Column\SelectColumn; use App\DataTables\Helpers\PartDataTableHelper; use App\Entity\Attachments\Attachment; use App\Entity\Parts\Part; @@ -40,22 +41,12 @@ use Symfony\Contracts\Translation\TranslatorInterface; class ProjectBomEntriesDataTable implements DataTableTypeInterface { - protected TranslatorInterface $translator; - protected PartDataTableHelper $partDataTableHelper; - protected EntityURLGenerator $entityURLGenerator; - protected AmountFormatter $amountFormatter; - - public function __construct(TranslatorInterface $translator, PartDataTableHelper $partDataTableHelper, - EntityURLGenerator $entityURLGenerator, AmountFormatter $amountFormatter) + public function __construct(protected TranslatorInterface $translator, protected PartDataTableHelper $partDataTableHelper, protected EntityURLGenerator $entityURLGenerator, protected AmountFormatter $amountFormatter) { - $this->translator = $translator; - $this->partDataTableHelper = $partDataTableHelper; - $this->entityURLGenerator = $entityURLGenerator; - $this->amountFormatter = $amountFormatter; } - public function configure(DataTable $dataTable, array $options) + public function configure(DataTable $dataTable, array $options): void { $dataTable //->add('select', SelectColumn::class) @@ -63,7 +54,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface 'label' => '', 'className' => 'no-colvis', 'render' => function ($value, ProjectBOMEntry $context) { - if($context->getPart() === null) { + if(!$context->getPart() instanceof Part) { return ''; } return $this->partDataTableHelper->renderPicture($context->getPart()); @@ -78,38 +69,48 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ->add('quantity', TextColumn::class, [ 'label' => $this->translator->trans('project.bom.quantity'), 'className' => 'text-center', - 'render' => function ($value, ProjectBOMEntry $context) { + 'orderField' => 'bom_entry.quantity', + 'render' => function ($value, ProjectBOMEntry $context): float|string { //If we have a non-part entry, only show the rounded quantity - if ($context->getPart() === null) { + if (!$context->getPart() instanceof Part) { return round($context->getQuantity()); } //Otherwise use the unit of the part to format the quantity - return $this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit()); + return htmlspecialchars($this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit())); }, ]) ->add('name', TextColumn::class, [ 'label' => $this->translator->trans('part.table.name'), - 'orderable' => false, + 'orderField' => 'NATSORT(part.name)', 'render' => function ($value, ProjectBOMEntry $context) { - if($context->getPart() === null) { - return $context->getName(); + if(!$context->getPart() instanceof Part) { + return htmlspecialchars((string) $context->getName()); } - if($context->getPart() !== null) { - $tmp = $this->partDataTableHelper->renderName($context->getPart()); - if(!empty($context->getName())) { - $tmp .= '
'.htmlspecialchars($context->getName()).''; - } - return $tmp; + + //Part exists if we reach this point + + $tmp = $this->partDataTableHelper->renderName($context->getPart()); + if($context->getName() !== null && $context->getName() !== '') { + $tmp .= '
'.htmlspecialchars($context->getName()).''; } - throw new \Exception('This should never happen!'); + return $tmp; }, ]) - + ->add('ipn', TextColumn::class, [ + 'label' => $this->translator->trans('part.table.ipn'), + 'orderField' => 'NATSORT(part.ipn)', + 'visible' => false, + 'render' => function ($value, ProjectBOMEntry $context) { + if($context->getPart() instanceof Part) { + return $context->getPart()->getIpn(); + } + } + ]) ->add('description', MarkdownColumn::class, [ 'label' => $this->translator->trans('part.table.description'), 'data' => function (ProjectBOMEntry $context) { - if($context->getPart() !== null) { + if($context->getPart() instanceof Part) { return $context->getPart()->getDescription(); } //For non-part BOM entries show the comment field @@ -121,15 +122,18 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ->add('category', EntityColumn::class, [ 'label' => $this->translator->trans('part.table.category'), 'property' => 'part.category', + 'orderField' => 'NATSORT(category.name)', ]) ->add('footprint', EntityColumn::class, [ 'property' => 'part.footprint', 'label' => $this->translator->trans('part.table.footprint'), + 'orderField' => 'NATSORT(footprint.name)', ]) ->add('manufacturer', EntityColumn::class, [ 'property' => 'part.manufacturer', 'label' => $this->translator->trans('part.table.manufacturer'), + 'orderField' => 'NATSORT(manufacturer.name)', ]) ->add('mountnames', TextColumn::class, [ @@ -144,6 +148,28 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface }, ]) + ->add('instockAmount', TextColumn::class, [ + 'label' => 'project.bom.instockAmount', + 'visible' => false, + 'render' => function ($value, ProjectBOMEntry $context) { + if ($context->getPart() !== null) { + return $this->partDataTableHelper->renderAmount($context->getPart()); + } + + return ''; + } + ]) + ->add('storageLocations', TextColumn::class, [ + 'label' => 'part.table.storeLocations', + 'visible' => false, + 'render' => function ($value, ProjectBOMEntry $context) { + if ($context->getPart() !== null) { + return $this->partDataTableHelper->renderStorageLocations($context->getPart()); + } + + return ''; + } + ]) ->add('addedDate', LocaleDateTimeColumn::class, [ 'label' => $this->translator->trans('part.table.addedDate'), @@ -155,6 +181,8 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ]) ; + $dataTable->addOrderBy('name', DataTable::SORT_ASCENDING); + $dataTable->createAdapter(ORMAdapter::class, [ 'entity' => Attachment::class, 'query' => function (QueryBuilder $builder) use ($options): void { @@ -175,6 +203,9 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ->addSelect('part') ->from(ProjectBOMEntry::class, 'bom_entry') ->leftJoin('bom_entry.part', 'part') + ->leftJoin('part.category', 'category') + ->leftJoin('part.footprint', 'footprint') + ->leftJoin('part.manufacturer', 'manufacturer') ->where('bom_entry.project = :project') ->setParameter('project', $options['project']) ; @@ -184,4 +215,4 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface { } -} \ No newline at end of file +} diff --git a/src/Doctrine/Functions/ArrayPosition.php b/src/Doctrine/Functions/ArrayPosition.php new file mode 100644 index 00000000..39276912 --- /dev/null +++ b/src/Doctrine/Functions/ArrayPosition.php @@ -0,0 +1,59 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Functions; + +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\AST\Node; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\TokenType; + +class ArrayPosition extends FunctionNode +{ + private ?Node $array = null; + + private ?Node $field = null; + + public function parse(Parser $parser): void + { + $parser->match(TokenType::T_IDENTIFIER); + $parser->match(TokenType::T_OPEN_PARENTHESIS); + + $this->array = $parser->InParameter(); + + $parser->match(TokenType::T_COMMA); + + $this->field = $parser->ArithmeticPrimary(); + + $parser->match(TokenType::T_CLOSE_PARENTHESIS); + } + + public function getSql(SqlWalker $sqlWalker): string + { + return 'ARRAY_POSITION(' . + $this->array->dispatch($sqlWalker) . ', ' . + $this->field->dispatch($sqlWalker) . + ')'; + } +} \ No newline at end of file diff --git a/src/Doctrine/Functions/Field2.php b/src/Doctrine/Functions/Field2.php new file mode 100644 index 00000000..57f55653 --- /dev/null +++ b/src/Doctrine/Functions/Field2.php @@ -0,0 +1,82 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Functions; + +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\TokenType; + +/** + * Basically the same as the original Field function, but uses FIELD2 for the SQL query. + */ +class Field2 extends FunctionNode +{ + + private $field = null; + + private $values = []; + + public function parse(Parser $parser): void + { + $parser->match(TokenType::T_IDENTIFIER); + $parser->match(TokenType::T_OPEN_PARENTHESIS); + + // Do the field. + $this->field = $parser->ArithmeticPrimary(); + + // Add the strings to the values array. FIELD must + // be used with at least 1 string not including the field. + + $lexer = $parser->getLexer(); + + while (count($this->values) < 1 || + $lexer->lookahead->type !== TokenType::T_CLOSE_PARENTHESIS) { + $parser->match(TokenType::T_COMMA); + $this->values[] = $parser->ArithmeticPrimary(); + } + + $parser->match(TokenType::T_CLOSE_PARENTHESIS); + } + + public function getSql(SqlWalker $sqlWalker): string + { + $query = 'FIELD2('; + + $query .= $this->field->dispatch($sqlWalker); + + $query .= ', '; + $counter = count($this->values); + + for ($i = 0; $i < $counter; $i++) { + if ($i > 0) { + $query .= ', '; + } + + $query .= $this->values[$i]->dispatch($sqlWalker); + } + + return $query . ')'; + } +} \ No newline at end of file diff --git a/src/Doctrine/Functions/ILike.php b/src/Doctrine/Functions/ILike.php new file mode 100644 index 00000000..5246220a --- /dev/null +++ b/src/Doctrine/Functions/ILike.php @@ -0,0 +1,71 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Functions; + +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\TokenType; + +/** + * A platform invariant version of the case-insensitive LIKE operation. + * On MySQL and SQLite this is the normal LIKE, but on PostgreSQL it is the ILIKE operator. + */ +class ILike extends FunctionNode +{ + + public $value = null; + + public $expr = null; + + public function parse(Parser $parser): void + { + $parser->match(TokenType::T_IDENTIFIER); + $parser->match(TokenType::T_OPEN_PARENTHESIS); + $this->value = $parser->StringPrimary(); + $parser->match(TokenType::T_COMMA); + $this->expr = $parser->StringExpression(); + $parser->match(TokenType::T_CLOSE_PARENTHESIS); + } + + public function getSql(SqlWalker $sqlWalker): string + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + + // + if ($platform instanceof AbstractMySQLPlatform || $platform instanceof SQLitePlatform) { + $operator = 'LIKE'; + } elseif ($platform instanceof PostgreSQLPlatform) { + //Use the case-insensitive operator, to have the same behavior as MySQL + $operator = 'ILIKE'; + } else { + throw new \RuntimeException('Platform ' . gettype($platform) . ' does not support case insensitive like expressions.'); + } + + return '(' . $this->value->dispatch($sqlWalker) . ' ' . $operator . ' ' . $this->expr->dispatch($sqlWalker) . ')'; + } +} \ No newline at end of file diff --git a/src/Doctrine/Functions/Natsort.php b/src/Doctrine/Functions/Natsort.php new file mode 100644 index 00000000..bd05e0d6 --- /dev/null +++ b/src/Doctrine/Functions/Natsort.php @@ -0,0 +1,151 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Functions; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Platforms\MariaDBPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\AST\Node; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\TokenType; + +class Natsort extends FunctionNode +{ + private ?Node $field = null; + + private static ?bool $supportsNaturalSort = null; + + private static bool $allowSlowNaturalSort = false; + + /** + * As we can not inject parameters into the function, we use an event listener, to call the value on the static function. + * This is the only way to inject the value into the function. + * @param bool $allow + * @return void + */ + public static function allowSlowNaturalSort(bool $allow = true): void + { + self::$allowSlowNaturalSort = $allow; + } + + /** + * Check if the slow natural sort is allowed + * @return bool + */ + public static function isSlowNaturalSortAllowed(): bool + { + return self::$allowSlowNaturalSort; + } + + /** + * Check if the MariaDB version which is connected to supports the natural sort (meaning it has a version of 10.7.0 or higher) + * The result is cached in memory. + * @param Connection $connection + * @return bool + * @throws Exception + */ + private function mariaDBSupportsNaturalSort(Connection $connection): bool + { + if (self::$supportsNaturalSort !== null) { + return self::$supportsNaturalSort; + } + + $version = $connection->getServerVersion(); + + //Get the effective MariaDB version number + $version = $this->getMariaDbMysqlVersionNumber($version); + + //We need at least MariaDB 10.7.0 to support the natural sort + self::$supportsNaturalSort = version_compare($version, '10.7.0', '>='); + return self::$supportsNaturalSort; + } + + /** + * Taken from Doctrine\DBAL\Driver\AbstractMySQLDriver + * + * Detect MariaDB server version, including hack for some mariadb distributions + * that starts with the prefix '5.5.5-' + * + * @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial' + */ + private function getMariaDbMysqlVersionNumber(string $versionString) : string + { + if ( ! preg_match( + '/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', + $versionString, + $versionParts + )) { + throw new \RuntimeException('Could not detect MariaDB version from version string ' . $versionString); + } + + return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; + } + + public function parse(Parser $parser): void + { + $parser->match(TokenType::T_IDENTIFIER); + $parser->match(TokenType::T_OPEN_PARENTHESIS); + + $this->field = $parser->ArithmeticExpression(); + + $parser->match(TokenType::T_CLOSE_PARENTHESIS); + } + + public function getSql(SqlWalker $sqlWalker): string + { + assert($this->field !== null, 'Field is not set'); + + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + + if ($platform instanceof PostgreSQLPlatform) { + return $this->field->dispatch($sqlWalker) . ' COLLATE numeric'; + } + + if ($platform instanceof MariaDBPlatform && $this->mariaDBSupportsNaturalSort($sqlWalker->getConnection())) { + return 'NATURAL_SORT_KEY(' . $this->field->dispatch($sqlWalker) . ')'; + } + + //Do the following operations only if we allow slow natural sort + if (self::$allowSlowNaturalSort) { + if ($platform instanceof SQLitePlatform) { + return $this->field->dispatch($sqlWalker).' COLLATE NATURAL_CMP'; + } + + if ($platform instanceof AbstractMySQLPlatform) { + return 'NatSortKey(' . $this->field->dispatch($sqlWalker) . ', 0)'; + } + } + + //For every other platform, return the field as is + return $this->field->dispatch($sqlWalker); + } + + +} \ No newline at end of file diff --git a/src/Doctrine/Functions/Regexp.php b/src/Doctrine/Functions/Regexp.php new file mode 100644 index 00000000..d7c6f1e7 --- /dev/null +++ b/src/Doctrine/Functions/Regexp.php @@ -0,0 +1,52 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Functions; + +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; +use Doctrine\ORM\Query\SqlWalker; + +/** + * Similar to the regexp function, but with support for multi platform. + */ +class Regexp extends \DoctrineExtensions\Query\Mysql\Regexp +{ + public function getSql(SqlWalker $sqlWalker): string + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + + // + if ($platform instanceof AbstractMySQLPlatform || $platform instanceof SQLitePlatform) { + $operator = 'REGEXP'; + } elseif ($platform instanceof PostgreSQLPlatform) { + //Use the case-insensitive operator, to have the same behavior as MySQL + $operator = '~*'; + } else { + throw new \RuntimeException('Platform ' . gettype($platform) . ' does not support regular expressions.'); + } + + return '(' . $this->value->dispatch($sqlWalker) . ' ' . $operator . ' ' . $this->regexp->dispatch($sqlWalker) . ')'; + } +} \ No newline at end of file diff --git a/src/Doctrine/Helpers/FieldHelper.php b/src/Doctrine/Helpers/FieldHelper.php new file mode 100644 index 00000000..11300db3 --- /dev/null +++ b/src/Doctrine/Helpers/FieldHelper.php @@ -0,0 +1,124 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Helpers; + +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\ORM\QueryBuilder; + +/** + * The purpose of this class is to provide help with using the FIELD functions in Doctrine, which depends on the database platform. + */ +final class FieldHelper +{ + /** + * Add an ORDER BY FIELD expression to the query builder. The correct FIELD function is used depending on the database platform. + * In this function an already bound paramater is used. If you want to a not already bound value, use the addOrderByFieldValues function. + * @param QueryBuilder $qb The query builder to apply the order by to + * @param string $field_expr The expression to compare with the values + * @param string|int $bound_param The already bound parameter to use for the values + * @param string|null $order The order direction (ASC or DESC) + * @return QueryBuilder + */ + public static function addOrderByFieldParam(QueryBuilder $qb, string $field_expr, string|int $bound_param, ?string $order = null): QueryBuilder + { + $db_platform = $qb->getEntityManager()->getConnection()->getDatabasePlatform(); + + //If we are on MySQL, we can just use the FIELD function + if ($db_platform instanceof AbstractMySQLPlatform ) { + $param = (is_numeric($bound_param) ? '?' : ":").(string)$bound_param; + $qb->orderBy("FIELD($field_expr, $param)", $order); + } else { //Use the sqlite/portable version or postgresql + //Retrieve the values from the bound parameter + $param = $qb->getParameter($bound_param); + if ($param === null) { + throw new \InvalidArgumentException("The bound parameter $bound_param does not exist."); + } + + //Generate a unique key from the field_expr + $key = 'field2_' . (string) $bound_param; + + if ($db_platform instanceof PostgreSQLPlatform) { + self::addPostgresOrderBy($qb, $field_expr, $key, $param->getValue(), $order); + } else { + self::addSqliteOrderBy($qb, $field_expr, $key, $param->getValue(), $order); + } + } + + return $qb; + } + + + private static function addPostgresOrderBy(QueryBuilder $qb, string $field_expr, string $key, array $values, ?string $order = null): void + { + //Use postgres native array_position function, to get the index of the value in the array + //In the end it gives a similar result as the FIELD function + $qb->orderBy("array_position(:$key, $field_expr)", $order); + + //Convert the values to a literal array, to overcome the problem of passing more than 100 parameters + $values = array_map(fn($value) => is_string($value) ? "'$value'" : $value, $values); + $literalArray = '{' . implode(',', $values) . '}'; + + $qb->setParameter($key, $literalArray); + } + + private static function addSqliteOrderBy(QueryBuilder $qb, string $field_expr, string $key, array $values, ?string $order = null): void + { + //Otherwise we emulate it using + $qb->orderBy("LOCATE(CONCAT(',', $field_expr, ','), :$key)", $order); + //The string must be padded with a comma on both sides, otherwise the search using INSTR will fail + $qb->setParameter($key, ',' .implode(',', $values) . ','); + } + + /** + * Add an ORDER BY FIELD expression to the query builder. The correct FIELD function is used depending on the database platform. + * In this function the values are passed as an array. If you want to reuse an existing bound parameter, use the addOrderByFieldParam function. + * @param QueryBuilder $qb The query builder to apply the order by to + * @param string $field_expr The expression to compare with the values + * @param array $values The values to compare with the expression as array + * @param string|null $order The order direction (ASC or DESC) + * @return QueryBuilder + */ + public static function addOrderByFieldValues(QueryBuilder $qb, string $field_expr, array $values, ?string $order = null): QueryBuilder + { + $db_platform = $qb->getEntityManager()->getConnection()->getDatabasePlatform(); + + $key = 'field2_' . md5($field_expr); + + //If we are on MySQL, we can just use the FIELD function + if ($db_platform instanceof AbstractMySQLPlatform) { + $qb->orderBy("FIELD2($field_expr, :field_arr)", $order); + } elseif ($db_platform instanceof PostgreSQLPlatform) { + //Use the postgres native array_position function + self::addPostgresOrderBy($qb, $field_expr, $key, $values, $order); + } else { + //Otherwise use the portable version using string concatenation + self::addSqliteOrderBy($qb, $field_expr, $key, $values, $order); + } + + $qb->setParameter($key, $values); + + return $qb; + } +} \ No newline at end of file diff --git a/src/Doctrine/Middleware/MySQLSSLConnectionMiddlewareDriver.php b/src/Doctrine/Middleware/MySQLSSLConnectionMiddlewareDriver.php new file mode 100644 index 00000000..2a707e1f --- /dev/null +++ b/src/Doctrine/Middleware/MySQLSSLConnectionMiddlewareDriver.php @@ -0,0 +1,51 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Middleware; + +use Composer\CaBundle\CaBundle; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; + +/** + * This middleware sets SSL options for MySQL connections + */ +class MySQLSSLConnectionMiddlewareDriver extends AbstractDriverMiddleware +{ + public function __construct(Driver $wrappedDriver, private readonly bool $enabled, private readonly bool $verify = true) + { + parent::__construct($wrappedDriver); + } + + public function connect(array $params): Connection + { + //Only set this on MySQL connections, as other databases don't support this parameter + if($this->enabled && $params['driver'] === 'pdo_mysql') { + $params['driverOptions'][\PDO::MYSQL_ATTR_SSL_CA] = CaBundle::getSystemCaRootBundlePath(); + $params['driverOptions'][\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $this->verify; + } + + return parent::connect($params); + } +} \ No newline at end of file diff --git a/src/Doctrine/Middleware/MySQLSSLConnectionMiddlewareWrapper.php b/src/Doctrine/Middleware/MySQLSSLConnectionMiddlewareWrapper.php new file mode 100644 index 00000000..8bd25971 --- /dev/null +++ b/src/Doctrine/Middleware/MySQLSSLConnectionMiddlewareWrapper.php @@ -0,0 +1,39 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Middleware; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\Middleware; + +class MySQLSSLConnectionMiddlewareWrapper implements Middleware +{ + public function __construct(private readonly bool $enabled, private readonly bool $verify = true) + { + } + + public function wrap(Driver $driver): Driver + { + return new MySQLSSLConnectionMiddlewareDriver($driver, $this->enabled, $this->verify); + } +} \ No newline at end of file diff --git a/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php new file mode 100644 index 00000000..ad572d4c --- /dev/null +++ b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php @@ -0,0 +1,117 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Middleware; + +use App\Exceptions\InvalidRegexException; +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; + +/** + * This middleware is used to add the regexp operator to the SQLite platform. + * As a PHP callback is called for every entry to compare it is most likely much slower than using regex on MySQL. + * But as regex is not often used, this should be fine for most use cases, also it is almost impossible to implement a better solution. + */ +class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware +{ + public function connect(#[\SensitiveParameter] array $params): Connection + { + //Do connect process first + $connection = parent::connect($params); // TODO: Change the autogenerated stub + + //Then add the functions if we are on SQLite + if ($params['driver'] === 'pdo_sqlite') { + $native_connection = $connection->getNativeConnection(); + + //Ensure that the function really exists on the connection, as it is marked as experimental according to PHP documentation + if($native_connection instanceof \PDO) { + $native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC); + $native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC); + $native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC); + + //Create a new collation for natural sorting + $native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...)); + } + } + + + return $connection; + } + + /** + * This function emulates the MySQL regexp function for SQLite + * @param string $pattern + * @param string $value + * @return int + */ + final public static function regexp(string $pattern, ?string $value): int + { + if ($value === null) { + return 0; + } + + try { + return (mb_ereg($pattern, $value)) ? 1 : 0; + + } catch (\ErrorException $e) { + throw InvalidRegexException::fromMBRegexError($e); + } + } + + /** + * Very similar to the field function, but takes the array values as a comma separated string. + * This is needed as SQLite has a pretty low argument count limit. + * @param string|int|null $value + * @param string $imploded_array + * @return int + */ + final public static function field2(string|int|null $value, string $imploded_array): int + { + $array = explode(',', $imploded_array); + return self::field($value, ...$array); + } + + /** + * This function emulates the MySQL field function for SQLite + * This function returns the index (position) of the first argument in the subsequent arguments. + * If the first argument is not found or is NULL, 0 is returned. + * @param string|int|null $value + * @return int + */ + final public static function field(string|int|null $value, mixed ...$array): int + { + if ($value === null) { + return 0; + } + + //We are loose with the types here + //@phpstan-ignore-next-line + $index = array_search($value, $array, false); + + if ($index === false) { + return 0; + } + + return $index + 1; + } +} \ No newline at end of file diff --git a/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareWrapper.php b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareWrapper.php new file mode 100644 index 00000000..42aafaad --- /dev/null +++ b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareWrapper.php @@ -0,0 +1,35 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Middleware; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\Middleware; + +class SQLiteRegexExtensionMiddlewareWrapper implements Middleware +{ + public function wrap(Driver $driver): Driver + { + return new SQLiteRegexExtensionMiddlewareDriver($driver); + } +} \ No newline at end of file diff --git a/src/Doctrine/SetSQLMode/SetSQLModeMiddlewareDriver.php b/src/Doctrine/Middleware/SetSQLModeMiddlewareDriver.php similarity index 79% rename from src/Doctrine/SetSQLMode/SetSQLModeMiddlewareDriver.php rename to src/Doctrine/Middleware/SetSQLModeMiddlewareDriver.php index 4b91ab57..d05b6b9c 100644 --- a/src/Doctrine/SetSQLMode/SetSQLModeMiddlewareDriver.php +++ b/src/Doctrine/Middleware/SetSQLModeMiddlewareDriver.php @@ -1,4 +1,7 @@ . */ +namespace App\Doctrine\Middleware; -namespace App\Doctrine\SetSQLMode; - +use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; -use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; /** * This command sets the initial command parameter for MySQL connections, so we can set the SQL mode @@ -29,14 +31,14 @@ use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; */ class SetSQLModeMiddlewareDriver extends AbstractDriverMiddleware { - public function connect(array $params): \Doctrine\DBAL\Driver\Connection + public function connect(array $params): Connection { //Only set this on MySQL connections, as other databases don't support this parameter - if($this->getDatabasePlatform() instanceof AbstractMySQLPlatform) { + if($params['driver'] === 'pdo_mysql') { //1002 is \PDO::MYSQL_ATTR_INIT_COMMAND constant value - $params['driverOptions'][1002] = 'SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode, \'ONLY_FULL_GROUP_BY\', \'\'))'; + $params['driverOptions'][\PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode, \'ONLY_FULL_GROUP_BY\', \'\'))'; } return parent::connect($params); } -} \ No newline at end of file +} diff --git a/src/Doctrine/SetSQLMode/SetSQLModeMiddlewareWrapper.php b/src/Doctrine/Middleware/SetSQLModeMiddlewareWrapper.php similarity index 91% rename from src/Doctrine/SetSQLMode/SetSQLModeMiddlewareWrapper.php rename to src/Doctrine/Middleware/SetSQLModeMiddlewareWrapper.php index 0ff670ba..3307bc7f 100644 --- a/src/Doctrine/SetSQLMode/SetSQLModeMiddlewareWrapper.php +++ b/src/Doctrine/Middleware/SetSQLModeMiddlewareWrapper.php @@ -1,4 +1,7 @@ . */ - -namespace App\Doctrine\SetSQLMode; +namespace App\Doctrine\Middleware; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Middleware; /** - * This class wraps the Doctrine DBAL driver and wraps it into an Midleware driver so we can change the SQL mode + * This class wraps the Doctrine DBAL driver and wraps it into a Midleware driver, so we can change the SQL mode */ class SetSQLModeMiddlewareWrapper implements Middleware { - public function wrap(Driver $driver): Driver { return new SetSQLModeMiddlewareDriver($driver); } -} \ No newline at end of file +} diff --git a/src/Doctrine/Purger/DoNotUsePurgerFactory.php b/src/Doctrine/Purger/DoNotUsePurgerFactory.php new file mode 100644 index 00000000..6d487573 --- /dev/null +++ b/src/Doctrine/Purger/DoNotUsePurgerFactory.php @@ -0,0 +1,53 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Doctrine\Purger; + +use Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory; +use Doctrine\Common\DataFixtures\Purger\ORMPurgerInterface; +use Doctrine\Common\DataFixtures\Purger\PurgerInterface; +use Doctrine\ORM\EntityManagerInterface; + +class DoNotUsePurgerFactory implements PurgerFactory +{ + + public function createForEntityManager( + ?string $emName, + EntityManagerInterface $em, + array $excluded = [], + bool $purgeWithTruncate = false + ): PurgerInterface { + return new class() implements ORMPurgerInterface { + + public function purge(): void + { + throw new \LogicException('Do not use doctrine:fixtures:load directly. Use partdb:fixtures:load instead!'); + } + + public function setEntityManager(EntityManagerInterface $em): void + { + // TODO: Implement setEntityManager() method. + } + }; + } +} \ No newline at end of file diff --git a/src/Doctrine/Purger/ResetAutoIncrementORMPurger.php b/src/Doctrine/Purger/ResetAutoIncrementORMPurger.php index a38045d4..0fbf6cdb 100644 --- a/src/Doctrine/Purger/ResetAutoIncrementORMPurger.php +++ b/src/Doctrine/Purger/ResetAutoIncrementORMPurger.php @@ -27,15 +27,11 @@ use Doctrine\Common\DataFixtures\Purger\PurgerInterface; use Doctrine\Common\DataFixtures\Sorter\TopologicalSorter; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Schema\Identifier; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\ClassMetadataInfo; - use function array_reverse; -use function array_search; use function assert; use function count; use function is_callable; @@ -49,25 +45,15 @@ use function preg_match; */ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface { - public const PURGE_MODE_DELETE = 1; - public const PURGE_MODE_TRUNCATE = 2; - - /** @var EntityManagerInterface|null */ - private $em; + final public const PURGE_MODE_DELETE = 1; + final public const PURGE_MODE_TRUNCATE = 2; /** * If the purge should be done through DELETE or TRUNCATE statements * * @var int */ - private $purgeMode = self::PURGE_MODE_DELETE; - - /** - * Table/view names to be excluded from purge - * - * @var string[] - */ - private $excluded; + private int $purgeMode = self::PURGE_MODE_DELETE; /** * Construct new purger instance. @@ -75,18 +61,20 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface * @param EntityManagerInterface|null $em EntityManagerInterface instance used for persistence. * @param string[] $excluded array of table/view names to be excluded from purge */ - public function __construct(?EntityManagerInterface $em = null, array $excluded = []) + public function __construct( + private ?EntityManagerInterface $em = null, + /** + * Table/view names to be excluded from purge + */ + private readonly array $excluded = [] + ) { - $this->em = $em; - $this->excluded = $excluded; } /** * Set the purge mode * - * @param int $mode * - * @return void */ public function setPurgeMode(int $mode): void { @@ -95,8 +83,6 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface /** * Get the purge mode - * - * @return int */ public function getPurgeMode(): int { @@ -125,7 +111,7 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface $classes = []; foreach ($this->em->getMetadataFactory()->getAllMetadata() as $metadata) { - if ($metadata->isMappedSuperclass || (isset($metadata->isEmbeddedClass) && $metadata->isEmbeddedClass)) { + if ($metadata->isMappedSuperclass || ($metadata->isEmbeddedClass !== null && $metadata->isEmbeddedClass)) { continue; } @@ -145,7 +131,7 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface $class = $commitOrder[$i]; if ( - (isset($class->isEmbeddedClass) && $class->isEmbeddedClass) || + ($class->isEmbeddedClass !== null && $class->isEmbeddedClass) || $class->isMappedSuperclass || ($class->isInheritanceTypeSingleTable() && $class->name !== $class->rootEntityName) ) { @@ -174,12 +160,17 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface foreach ($orderedTables as $tbl) { // If we have a filter expression, check it and skip if necessary - if (! $emptyFilterExpression && ! preg_match($filterExpr, $tbl)) { + if (! $emptyFilterExpression && ! preg_match($filterExpr, (string) $tbl)) { continue; } + // The table name might be quoted, we have to trim it + // See https://github.com/Part-DB/Part-DB-server/issues/299 + $tbl = trim((string) $tbl, '"'); + $tbl = trim($tbl, '`'); + // If the table is excluded, skip it as well - if (array_search($tbl, $this->excluded) !== false) { + if (in_array($tbl, $this->excluded, true)) { continue; } @@ -195,8 +186,7 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface } //Reseting autoincrement is only supported on MySQL platforms - if ($platform instanceof AbstractMySQLPlatform) { - $connection->beginTransaction(); + if ($platform instanceof AbstractMySQLPlatform ) { //|| $platform instanceof SqlitePlatform) { $connection->executeQuery($this->getResetAutoIncrementSQL($tbl, $platform)); } } @@ -211,7 +201,16 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface { $tableIdentifier = new Identifier($tableName); - return 'ALTER TABLE '. $tableIdentifier->getQuotedName($platform) .' AUTO_INCREMENT = 1;'; + if ($platform instanceof AbstractMySQLPlatform) { + return 'ALTER TABLE '.$tableIdentifier->getQuotedName($platform).' AUTO_INCREMENT = 1;'; + } + + throw new \RuntimeException("Resetting autoincrement is not supported on this platform!"); + + //This seems to cause problems somehow + /*if ($platform instanceof SqlitePlatform) { + return 'DELETE FROM `sqlite_sequence` WHERE name = \''.$tableIdentifier->getQuotedName($platform).'\';'; + }*/ } /** @@ -273,18 +272,13 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface return array_reverse($sorter->sort()); } - /** - * @param array $classes - * - * @return array - */ private function getAssociationTables(array $classes, AbstractPlatform $platform): array { $associationTables = []; foreach ($classes as $class) { foreach ($class->associationMappings as $assoc) { - if (! $assoc['isOwningSide'] || $assoc['type'] !== ClassMetadataInfo::MANY_TO_MANY) { + if (! $assoc['isOwningSide'] || $assoc['type'] !== ClassMetadata::MANY_TO_MANY) { continue; } @@ -307,9 +301,6 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface return $this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform); } - /** - * @param array $assoc - */ private function getJoinTableName( array $assoc, ClassMetadata $class, diff --git a/src/Doctrine/Purger/ResetAutoIncrementPurgerFactory.php b/src/Doctrine/Purger/ResetAutoIncrementPurgerFactory.php index 00152718..4d78e0f0 100644 --- a/src/Doctrine/Purger/ResetAutoIncrementPurgerFactory.php +++ b/src/Doctrine/Purger/ResetAutoIncrementPurgerFactory.php @@ -1,4 +1,7 @@ . */ - namespace App\Doctrine\Purger; use Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory; @@ -35,4 +37,4 @@ class ResetAutoIncrementPurgerFactory implements PurgerFactory return $purger; } -} \ No newline at end of file +} diff --git a/src/Doctrine/SQLiteRegexExtension.php b/src/Doctrine/SQLiteRegexExtension.php deleted file mode 100644 index f1bca465..00000000 --- a/src/Doctrine/SQLiteRegexExtension.php +++ /dev/null @@ -1,58 +0,0 @@ -. - */ - -namespace App\Doctrine; - -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; -use Doctrine\DBAL\Event\ConnectionEventArgs; -use Doctrine\DBAL\Events; -use Doctrine\DBAL\Platforms\SqlitePlatform; - -/** - * This subscriber is used to add the regexp operator to the SQLite platform. - * As a PHP callback is called for every entry to compare it is most likely much slower than using regex on MySQL. - * But as regex is not often used, this should be fine for most use cases, also it is almost impossible to implement a better solution. - */ -class SQLiteRegexExtension implements EventSubscriberInterface -{ - public function postConnect(ConnectionEventArgs $eventArgs): void - { - $connection = $eventArgs->getConnection(); - - //We only execute this on SQLite databases - if ($connection->getDatabasePlatform() instanceof SqlitePlatform) { - $native_connection = $connection->getNativeConnection(); - - //Ensure that the function really exists on the connection, as it is marked as experimental according to PHP documentation - if($native_connection instanceof \PDO && method_exists($native_connection, 'sqliteCreateFunction' )) { - $native_connection->sqliteCreateFunction('REGEXP', function ($pattern, $value) { - return (false !== mb_ereg($pattern, $value)) ? 1 : 0; - }); - } - } - } - - public function getSubscribedEvents() - { - return[ - Events::postConnect - ]; - } -} \ No newline at end of file diff --git a/src/Doctrine/Types/ArrayType.php b/src/Doctrine/Types/ArrayType.php new file mode 100644 index 00000000..daab9b75 --- /dev/null +++ b/src/Doctrine/Types/ArrayType.php @@ -0,0 +1,116 @@ +. + */ + +declare(strict_types=1); + +namespace App\Doctrine\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\Exception\SerializationFailed; +use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; + +use function is_resource; +use function restore_error_handler; +use function serialize; +use function set_error_handler; +use function stream_get_contents; +use function unserialize; + +use const E_DEPRECATED; +use const E_USER_DEPRECATED; + +/** + * This class is taken from doctrine ORM 3.8. https://github.com/doctrine/dbal/blob/3.8.x/src/Types/ArrayType.php + * + * It was removed in doctrine ORM 4.0. However, we require it for backward compatibility with WebauthnKey. + * Therefore, we manually added it here as a custom type as a forward compatibility layer. + */ +class ArrayType extends Type +{ + /** + * {@inheritDoc} + */ + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string + { + return $platform->getClobTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): string + { + return serialize($value); + } + + /** + * {@inheritDoc} + */ + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed + { + if ($value === null) { + return null; + } + + $value = is_resource($value) ? stream_get_contents($value) : $value; + + set_error_handler(function (int $code, string $message): bool { + if ($code === E_DEPRECATED || $code === E_USER_DEPRECATED) { + return false; + } + + //Change to original code. Use SerializationFailed instead of ConversionException. + throw new SerializationFailed("Serialization failed (Code $code): " . $message); + }); + + try { + //Change to original code. Use false for allowed_classes, to avoid unsafe unserialization of objects. + return unserialize($value, ['allowed_classes' => false]); + } finally { + restore_error_handler(); + } + } + + /** + * {@inheritDoc} + */ + public function getName(): string + { + return "array"; + } + + /** + * {@inheritDoc} + * + * @deprecated + */ + public function requiresSQLCommentHint(AbstractPlatform $platform): bool + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + + return true; + } +} \ No newline at end of file diff --git a/src/Doctrine/Types/BigDecimalType.php b/src/Doctrine/Types/BigDecimalType.php index f1522857..a9f796dd 100644 --- a/src/Doctrine/Types/BigDecimalType.php +++ b/src/Doctrine/Types/BigDecimalType.php @@ -1,4 +1,7 @@ . */ - namespace App\Doctrine\Types; use Brick\Math\BigDecimal; @@ -27,24 +29,21 @@ use Doctrine\DBAL\Types\Type; class BigDecimalType extends Type { - public const BIG_DECIMAL = 'big_decimal'; + final public const BIG_DECIMAL = 'big_decimal'; public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string { return $platform->getDecimalTypeDeclarationSQL($fieldDeclaration); } - /** - * @param string|null $value - * - * @return BigDecimal|BigNumber|mixed - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?BigNumber { if (null === $value) { return null; } + + return BigDecimal::of($value); } @@ -53,7 +52,7 @@ class BigDecimalType extends Type */ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { - if (null === $value) { + if (!$value instanceof BigDecimal) { return null; } diff --git a/src/Doctrine/Types/TinyIntType.php b/src/Doctrine/Types/TinyIntType.php new file mode 100644 index 00000000..c2daeeca --- /dev/null +++ b/src/Doctrine/Types/TinyIntType.php @@ -0,0 +1,73 @@ +. + */ +namespace App\Doctrine\Types; + +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; +use Doctrine\DBAL\Types\Type; + +/** + * A type to use for tinyint columns in MySQL + */ +class TinyIntType extends Type +{ + + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string + { + //MySQL knows the TINYINT type directly + //We do not use the TINYINT for sqlite, as it will be resolved to a BOOL type and bring problems with migrations + if ($platform instanceof AbstractMySQLPlatform ) { + //Use TINYINT(1) to allow for proper migration diffs + return 'TINYINT(1)'; + } + + //For other platforms, we use the smallest integer type available + return $platform->getSmallIntTypeDeclarationSQL($column); + } + + public function getName(): string + { + return 'tinyint'; + } + + /** + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : int) + * + * @template T + */ + public function convertToPHPValue($value, AbstractPlatform $platform): ?int + { + return $value === null ? null : (int) $value; + } + + public function requiresSQLCommentHint(AbstractPlatform $platform): bool + { + //We use the comment, so that doctrine migrations can properly detect, that nothing has changed and no migration is needed. + return true; + } +} diff --git a/src/Doctrine/Types/UTCDateTimeImmutableType.php b/src/Doctrine/Types/UTCDateTimeImmutableType.php new file mode 100644 index 00000000..c0d6659d --- /dev/null +++ b/src/Doctrine/Types/UTCDateTimeImmutableType.php @@ -0,0 +1,97 @@ +. + */ + +declare(strict_types=1); + +namespace App\Doctrine\Types; + +use DateTime; +use DateTimeInterface; +use DateTimeZone; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\ConversionException; +use Doctrine\DBAL\Types\DateTimeImmutableType; +use Doctrine\DBAL\Types\DateTimeType; +use Doctrine\DBAL\Types\Exception\InvalidFormat; + +/** + * This DateTimeImmutableType all dates to UTC, so it can be later used with the timezones. + * Taken (and adapted) from here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/cookbook/working-with-datetime.html. + */ +class UTCDateTimeImmutableType extends DateTimeImmutableType +{ + private static ?DateTimeZone $utc_timezone = null; + + /** + * {@inheritdoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string + { + if (!self::$utc_timezone instanceof \DateTimeZone) { + self::$utc_timezone = new DateTimeZone('UTC'); + } + + if ($value instanceof \DateTimeImmutable) { + $value = $value->setTimezone(self::$utc_timezone); + } + + return parent::convertToDatabaseValue($value, $platform); + } + + /** + * {@inheritDoc} + * + * @param T $value + * + * @template T + */ + public function convertToPHPValue($value, AbstractPlatform $platform): ?\DateTimeImmutable + { + if (!self::$utc_timezone instanceof \DateTimeZone) { + self::$utc_timezone = new DateTimeZone('UTC'); + } + + if (null === $value || $value instanceof \DateTimeImmutable) { + return $value; + } + + $converted = \DateTimeImmutable::createFromFormat( + $platform->getDateTimeFormatString(), + $value, + self::$utc_timezone + ); + + if (!$converted) { + throw InvalidFormat::new( + $value, + static::class, + $platform->getDateTimeFormatString(), + ); + } + + return $converted; + } +} diff --git a/src/Doctrine/Types/UTCDateTimeType.php b/src/Doctrine/Types/UTCDateTimeType.php index c9fe215b..a6fda747 100644 --- a/src/Doctrine/Types/UTCDateTimeType.php +++ b/src/Doctrine/Types/UTCDateTimeType.php @@ -23,10 +23,12 @@ declare(strict_types=1); namespace App\Doctrine\Types; use DateTime; +use DateTimeInterface; use DateTimeZone; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\DateTimeType; +use Doctrine\DBAL\Types\Exception\InvalidFormat; /** * This DateTimeType all dates to UTC, so it can be later used with the timezones. @@ -47,7 +49,7 @@ class UTCDateTimeType extends DateTimeType */ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { - if (!self::$utc_timezone) { + if (!self::$utc_timezone instanceof \DateTimeZone) { self::$utc_timezone = new DateTimeZone('UTC'); } @@ -58,9 +60,16 @@ class UTCDateTimeType extends DateTimeType return parent::convertToDatabaseValue($value, $platform); } + /** + * {@inheritDoc} + * + * @param T $value + * + * @template T + */ public function convertToPHPValue($value, AbstractPlatform $platform): ?DateTime { - if (!self::$utc_timezone) { + if (!self::$utc_timezone instanceof \DateTimeZone) { self::$utc_timezone = new DateTimeZone('UTC'); } @@ -75,7 +84,11 @@ class UTCDateTimeType extends DateTimeType ); if (!$converted) { - throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeFormatString()); + throw InvalidFormat::new( + $value, + static::class, + $platform->getDateTimeFormatString(), + ); } return $converted; diff --git a/src/Entity/Attachments/Attachment.php b/src/Entity/Attachments/Attachment.php index a4807a21..00cf581a 100644 --- a/src/Entity/Attachments/Attachment.php +++ b/src/Entity/Attachments/Attachment.php @@ -22,103 +22,191 @@ declare(strict_types=1); namespace App\Entity\Attachments; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use App\ApiPlatform\DocumentedAPIProperties\DocumentedAPIProperty; +use App\ApiPlatform\Filter\EntityFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\ApiPlatform\HandleAttachmentsUploadsProcessor; use App\Entity\Base\AbstractNamedDBElement; +use App\EntityListeners\AttachmentDeleteListener; +use App\Repository\AttachmentRepository; use App\Validator\Constraints\Selectable; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Validator\Constraints as Assert; -use function in_array; use InvalidArgumentException; use LogicException; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Serializer\Attribute\DiscriminatorMap; +use Symfony\Component\Validator\Constraints as Assert; + +use function in_array; /** * Class Attachment. - * - * @ORM\Entity(repositoryClass="App\Repository\AttachmentRepository") - * @ORM\Table(name="`attachments`", indexes={ - * @ORM\Index(name="attachments_idx_id_element_id_class_name", columns={"id", "element_id", "class_name"}), - * @ORM\Index(name="attachments_idx_class_name_id", columns={"class_name", "id"}), - * @ORM\Index(name="attachment_name_idx", columns={"name"}), - * @ORM\Index(name="attachment_element_idx", columns={"class_name", "element_id"}) - * }) - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="class_name", type="string") - * @ORM\DiscriminatorMap({ - * "PartDB\Part" = "PartAttachment", "Part" = "PartAttachment", - * "PartDB\Device" = "ProjectAttachment", "Device" = "ProjectAttachment", - * "AttachmentType" = "AttachmentTypeAttachment", "Category" = "CategoryAttachment", - * "Footprint" = "FootprintAttachment", "Manufacturer" = "ManufacturerAttachment", - * "Currency" = "CurrencyAttachment", "Group" = "GroupAttachment", - * "MeasurementUnit" = "MeasurementUnitAttachment", "Storelocation" = "StorelocationAttachment", - * "Supplier" = "SupplierAttachment", "User" = "UserAttachment", "LabelProfile" = "LabelAttachment", - * }) - * @ORM\EntityListeners({"App\EntityListeners\AttachmentDeleteListener"}) + * @see \App\Tests\Entity\Attachments\AttachmentTest + * @template-covariant T of AttachmentContainingDBElement */ +#[ORM\Entity(repositoryClass: AttachmentRepository::class)] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'class_name', type: 'string')] +#[ORM\DiscriminatorMap(self::ORM_DISCRIMINATOR_MAP)] +#[ORM\EntityListeners([AttachmentDeleteListener::class])] +#[ORM\Table(name: '`attachments`')] +#[ORM\Index(columns: ['id', 'element_id', 'class_name'], name: 'attachments_idx_id_element_id_class_name')] +#[ORM\Index(columns: ['class_name', 'id'], name: 'attachments_idx_class_name_id')] +#[ORM\Index(columns: ['name'], name: 'attachment_name_idx')] +#[ORM\Index(columns: ['class_name', 'element_id'], name: 'attachment_element_idx')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@attachments.list_attachments")'), + new Post(securityPostDenormalize: 'is_granted("create", object)', ), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['attachment:read', 'attachment:read:standalone', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['attachment:write', 'attachment:write:standalone', 'api:basic:write'], 'openapi_definition_name' => 'Write'], + processor: HandleAttachmentsUploadsProcessor::class, +)] +//This property is added by the denormalizer in order to resolve the placeholder +#[DocumentedAPIProperty( + schemaName: 'Attachment-Read', property: 'internal_path', type: 'string', nullable: false, + description: 'The URL to the internally saved copy of the file, if one exists', + example: '/media/part/2/bc547-6508afa5a79c8.pdf' +)] +#[DocumentedAPIProperty( + schemaName: 'Attachment-Read', property: 'thumbnail_url', type: 'string', nullable: true, + description: 'The URL to a thumbnail version of this file. This only exists for internal picture attachments.' +)] +#[ApiFilter(LikeFilter::class, properties: ["name"])] +#[ApiFilter(EntityFilter::class, properties: ["attachment_type"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] +//This discriminator map is required for API platform to know which class to use for deserialization, when creating a new attachment. +#[DiscriminatorMap(typeProperty: '_type', mapping: self::API_DISCRIMINATOR_MAP)] abstract class Attachment extends AbstractNamedDBElement { + private const ORM_DISCRIMINATOR_MAP = ['Part' => PartAttachment::class, 'Device' => ProjectAttachment::class, + 'AttachmentType' => AttachmentTypeAttachment::class, + 'Category' => CategoryAttachment::class, 'Footprint' => FootprintAttachment::class, 'Manufacturer' => ManufacturerAttachment::class, + 'Currency' => CurrencyAttachment::class, 'Group' => GroupAttachment::class, 'MeasurementUnit' => MeasurementUnitAttachment::class, + 'Storelocation' => StorageLocationAttachment::class, 'Supplier' => SupplierAttachment::class, + 'User' => UserAttachment::class, 'LabelProfile' => LabelAttachment::class]; + + /* + * The discriminator map used for API platform. The key should be the same as the api platform short type (the @type JSONLD field). + */ + private const API_DISCRIMINATOR_MAP = ["Part" => PartAttachment::class, "Project" => ProjectAttachment::class, "AttachmentType" => AttachmentTypeAttachment::class, + "Category" => CategoryAttachment::class, "Footprint" => FootprintAttachment::class, "Manufacturer" => ManufacturerAttachment::class, + "Currency" => CurrencyAttachment::class, "Group" => GroupAttachment::class, "MeasurementUnit" => MeasurementUnitAttachment::class, + "StorageLocation" => StorageLocationAttachment::class, "Supplier" => SupplierAttachment::class, "User" => UserAttachment::class, "LabelProfile" => LabelAttachment::class]; + /** * A list of file extensions, that browsers can show directly as image. * Based on: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types - * It will be used to determine if a attachment is a picture and therefore will be shown to user as preview. + * It will be used to determine if an attachment is a picture and therefore will be shown to user as preview. */ - public const PICTURE_EXTS = ['apng', 'bmp', 'gif', 'ico', 'cur', 'jpg', 'jpeg', 'jfif', 'pjpeg', 'pjp', 'png', + final public const PICTURE_EXTS = ['apng', 'bmp', 'gif', 'ico', 'cur', 'jpg', 'jpeg', 'jfif', 'pjpeg', 'pjp', 'png', 'svg', 'webp', ]; /** * A list of extensions that will be treated as a 3D Model that can be shown to user directly in Part-DB. */ - public const MODEL_EXTS = ['x3d']; + final public const MODEL_EXTS = ['x3d']; - /** - * When the path begins with one of this placeholders. - */ - public const INTERNAL_PLACEHOLDER = ['%BASE%', '%MEDIA%', '%SECURE%']; /** * @var array placeholders for attachments which using built in files */ - public const BUILTIN_PLACEHOLDER = ['%FOOTPRINTS%', '%FOOTPRINTS3D%']; + final public const BUILTIN_PLACEHOLDER = ['%FOOTPRINTS%', '%FOOTPRINTS3D%']; /** * @var string The class of the element that can be passed to this attachment. Must be overridden in subclasses. + * @phpstan-var class-string */ - public const ALLOWED_ELEMENT_CLASS = ''; + protected const ALLOWED_ELEMENT_CLASS = AttachmentContainingDBElement::class; + + /** + * @var AttachmentUpload|null The options used for uploading a file to this attachment or modify it. + * This value is not persisted in the database, but is just used to pass options to the upload manager. + * If it is null, no upload process is started. + */ + #[Groups(['attachment:write'])] + protected ?AttachmentUpload $upload = null; /** * @var string|null the original filename the file had, when the user uploaded it - * @ORM\Column(type="string", nullable=true) */ + #[ORM\Column(type: Types::STRING, nullable: true)] + #[Groups(['attachment:read', 'import'])] + #[Assert\Length(max: 255)] protected ?string $original_filename = null; /** - * @var string The path to the file relative to a placeholder path like %MEDIA% - * @ORM\Column(type="string", name="path") + * @var string|null If a copy of the file is stored internally, the path to the file relative to a placeholder + * path like %MEDIA% */ - protected string $path = ''; + #[ORM\Column(type: Types::STRING, nullable: true)] + protected ?string $internal_path = null; + /** - * ORM mapping is done in sub classes (like PartAttachment). + * @var string|null The path to the external source if the file is stored externally or was downloaded from an + * external source. Null if there is no external source. */ + #[ORM\Column(type: Types::STRING, nullable: true)] + #[Groups(['attachment:read'])] + #[ApiProperty(example: 'http://example.com/image.jpg')] + protected ?string $external_path = null; + + /** + * @var string the name of this element + */ + #[Assert\NotBlank(message: 'validator.attachment.name_not_blank')] + #[Groups(['simple', 'extended', 'full', 'attachment:read', 'attachment:write', 'import'])] + protected string $name = ''; + + /** + * ORM mapping is done in subclasses (like PartAttachment). + * @phpstan-param T|null $element + */ + #[Groups(['attachment:read:standalone', 'attachment:write:standalone'])] + #[ApiProperty(writableLink: false)] protected ?AttachmentContainingDBElement $element = null; - /** - * @var bool - * @ORM\Column(type="boolean") - */ + #[ORM\Column(type: Types::BOOLEAN)] + #[Groups(['attachment:read', 'attachment_write', 'full', 'import'])] protected bool $show_in_table = false; - /** - * @var AttachmentType - * @ORM\ManyToOne(targetEntity="AttachmentType", inversedBy="attachments_with_type") - * @ORM\JoinColumn(name="type_id", referencedColumnName="id", nullable=false) - * @Selectable() - * @Assert\NotNull(message="validator.attachment.must_not_be_null") - */ + #[Assert\NotNull(message: 'validator.attachment.must_not_be_null')] + #[ORM\ManyToOne(targetEntity: AttachmentType::class, inversedBy: 'attachments_with_type')] + #[ORM\JoinColumn(name: 'type_id', nullable: false)] + #[Selectable] + #[Groups(['attachment:read', 'attachment:write', 'import', 'full'])] + #[ApiProperty(readableLink: false)] protected ?AttachmentType $attachment_type = null; + #[Groups(['attachment:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['attachment:read'])] + protected ?\DateTimeImmutable $lastModified = null; + + public function __construct() { //parent::__construct(); - if ('' === static::ALLOWED_ELEMENT_CLASS) { + if (AttachmentContainingDBElement::class === static::ALLOWED_ELEMENT_CLASS) { throw new LogicException('An *Attachment class must override the ALLOWED_ELEMENT_CLASS const!'); } } @@ -131,61 +219,106 @@ abstract class Attachment extends AbstractNamedDBElement } } + /** + * Gets the upload currently associated with this attachment. + * This is only temporary and not persisted directly in the database. + * @internal This function should only be used by the Attachment Submit handler service + * @return AttachmentUpload|null + */ + public function getUpload(): ?AttachmentUpload + { + return $this->upload; + } + + /** + * Sets the current upload for this attachment. + * It will be processed as the attachment is persisted/flushed. + * @param AttachmentUpload|null $upload + * @return $this + */ + public function setUpload(?AttachmentUpload $upload): Attachment + { + $this->upload = $upload; + return $this; + } + + + /*********************************************************** * Various function ***********************************************************/ /** * Check if this attachment is a picture (analyse the file's extension). - * If the link is external, it is assumed that this is true. + * If the link is only external and doesn't contain an extension, it is assumed that this is true. * * @return bool * true if the file extension is a picture extension * * otherwise false */ + #[Groups(['attachment:read'])] public function isPicture(): bool { - //We can not check if a external link is a picture, so just assume this is false - if ($this->isExternal()) { - return true; + if($this->hasInternal()){ + + $extension = pathinfo($this->getInternalPath(), PATHINFO_EXTENSION); + + return in_array(strtolower($extension), static::PICTURE_EXTS, true); + } + if ($this->hasExternal()) { + //Check if we can extract a file extension from the URL + $extension = pathinfo(parse_url($this->getExternalPath(), PHP_URL_PATH) ?? '', PATHINFO_EXTENSION); - $extension = pathinfo($this->getPath(), PATHINFO_EXTENSION); - - return in_array(strtolower($extension), static::PICTURE_EXTS, true); + //If no extension is found or it is known picture extension, we assume that this is a picture extension + return $extension === '' || in_array(strtolower($extension), static::PICTURE_EXTS, true); + } + //File doesn't have an internal, nor an external copy. This shouldn't happen, but it certainly isn't a picture... + return false; } /** * Check if this attachment is a 3D model and therefore can be directly shown to user. - * If the attachment is external, false is returned (3D Models must be internal). + * If no internal copy exists, false is returned (3D Models must be internal). */ + #[Groups(['attachment:read'])] + #[SerializedName('3d_model')] public function is3DModel(): bool { //We just assume that 3D Models are internally saved, otherwise we get problems loading them. - if ($this->isExternal()) { + if (!$this->hasInternal()) { return false; } - $extension = pathinfo($this->getPath(), PATHINFO_EXTENSION); + $extension = pathinfo($this->getInternalPath(), PATHINFO_EXTENSION); return in_array(strtolower($extension), static::MODEL_EXTS, true); } /** - * Checks if the attachment file is externally saved (the database saves an URL). + * Checks if this attachment has a path to an external file * - * @return bool true, if the file is saved externally + * @return bool true, if there is a path to an external file + * @phpstan-assert-if-true non-empty-string $this->external_path + * @phpstan-assert-if-true non-empty-string $this->getExternalPath()) */ - public function isExternal(): bool + #[Groups(['attachment:read'])] + public function hasExternal(): bool { - //When path is empty, this attachment can not be external - if (empty($this->path)) { - return false; - } + return $this->external_path !== null && $this->external_path !== ''; + } - //After the %PLACEHOLDER% comes a slash, so we can check if we have a placeholder via explode - $tmp = explode('/', $this->path); - - return !in_array($tmp[0], array_merge(static::INTERNAL_PLACEHOLDER, static::BUILTIN_PLACEHOLDER), true); + /** + * Checks if this attachment has a path to an internal file. + * Does not check if the file exists. + * + * @return bool true, if there is a path to an internal file + * @phpstan-assert-if-true non-empty-string $this->internal_path + * @phpstan-assert-if-true non-empty-string $this->getInternalPath()) + */ + #[Groups(['attachment:read'])] + public function hasInternal(): bool + { + return $this->internal_path !== null && $this->internal_path !== ''; } /** @@ -194,10 +327,16 @@ abstract class Attachment extends AbstractNamedDBElement * * @return bool true, if the file is secure */ + #[Groups(['attachment:read'])] + #[SerializedName('private')] public function isSecure(): bool { + if ($this->internal_path === null) { + return false; + } + //After the %PLACEHOLDER% comes a slash, so we can check if we have a placeholder via explode - $tmp = explode('/', $this->path); + $tmp = explode('/', $this->internal_path); return '%SECURE%' === $tmp[0]; } @@ -206,11 +345,16 @@ abstract class Attachment extends AbstractNamedDBElement * Checks if the attachment file is using a builtin file. (see BUILTIN_PLACEHOLDERS const for possible placeholders) * If a file is built in, the path is shown to user in url field (no sensitive infos are provided). * - * @return bool true if the attachment is using an builtin file + * @return bool true if the attachment is using a builtin file */ + #[Groups(['attachment:read'])] public function isBuiltIn(): bool { - return static::checkIfBuiltin($this->path); + if ($this->internal_path === null) { + return false; + } + + return static::checkIfBuiltin($this->internal_path); } /******************************************************************************** @@ -222,27 +366,28 @@ abstract class Attachment extends AbstractNamedDBElement /** * Returns the extension of the file referenced via the attachment. * For a path like %BASE/path/foo.bar, bar will be returned. - * If this attachment is external null is returned. + * If this attachment is only external null is returned. * * @return string|null the file extension in lower case */ public function getExtension(): ?string { - if ($this->isExternal()) { + if (!$this->hasInternal()) { return null; } - if (!empty($this->original_filename)) { + if ($this->original_filename !== null && $this->original_filename !== '') { return strtolower(pathinfo($this->original_filename, PATHINFO_EXTENSION)); } - return strtolower(pathinfo($this->getPath(), PATHINFO_EXTENSION)); + return strtolower(pathinfo($this->getInternalPath(), PATHINFO_EXTENSION)); } /** * Get the element, associated with this Attachment (for example a "Part" object). * - * @return AttachmentContainingDBElement the associated Element + * @return AttachmentContainingDBElement|null the associated Element + * @phpstan-return T|null */ public function getElement(): ?AttachmentContainingDBElement { @@ -250,59 +395,63 @@ abstract class Attachment extends AbstractNamedDBElement } /** - * The URL to the external file, or the path to the built in file. + * The URL to the external file, or the path to the built-in file, but not paths to uploaded files. * Returns null, if the file is not external (and not builtin). + * The output of this function is such, that no changes occur when it is fed back into setURL(). + * Required for the Attachment form field. */ public function getURL(): ?string { - if (!$this->isExternal() && !$this->isBuiltIn()) { - return null; + if($this->hasExternal()){ + return $this->getExternalPath(); } - - return $this->path; + if($this->isBuiltIn()){ + return $this->getInternalPath(); + } + return null; } /** * Returns the hostname where the external file is stored. - * Returns null, if the file is not external. + * Returns null, if there is no external path. */ public function getHost(): ?string { - if (!$this->isExternal()) { + if (!$this->hasExternal()) { return null; } - return parse_url($this->getURL(), PHP_URL_HOST); + return parse_url($this->getExternalPath(), PHP_URL_HOST); } - /** - * Get the filepath, relative to %BASE%. - * - * @return string A string like %BASE/path/foo.bar - */ - public function getPath(): string + public function getInternalPath(): ?string { - return $this->path; + return $this->internal_path; + } + + public function getExternalPath(): ?string + { + return $this->external_path; } /** * Returns the filename of the attachment. * For a path like %BASE/path/foo.bar, foo.bar will be returned. * - * If the path is a URL (can be checked via isExternal()), null will be returned. + * If there is no internal copy of the file, null will be returned. */ public function getFilename(): ?string { - if ($this->isExternal()) { + if (!$this->hasInternal()) { return null; } //If we have a stored original filename, then use it - if (!empty($this->original_filename)) { + if ($this->original_filename !== null && $this->original_filename !== '') { return $this->original_filename; } - return pathinfo($this->getPath(), PATHINFO_BASENAME); + return pathinfo($this->getInternalPath(), PATHINFO_BASENAME); } /** @@ -360,12 +509,12 @@ abstract class Attachment extends AbstractNamedDBElement /** * Sets the element that is associated with this attachment. - * * @return $this */ public function setElement(AttachmentContainingDBElement $element): self { - if (!is_a($element, static::ALLOWED_ELEMENT_CLASS)) { + //Do not allow Rector to replace this check with a instanceof. It will not work!! + if (!is_a($element, static::ALLOWED_ELEMENT_CLASS, true)) { throw new InvalidArgumentException(sprintf('The element associated with a %s must be a %s!', static::class, static::ALLOWED_ELEMENT_CLASS)); } @@ -375,15 +524,12 @@ abstract class Attachment extends AbstractNamedDBElement } /** - * Sets the filepath (with relative placeholder) for this attachment. - * - * @param string $path the new filepath of the attachment - * - * @return Attachment + * Sets the path to a file hosted internally. If you set this path to a file that was not downloaded from the + * external source in external_path, make sure to reset external_path. */ - public function setPath(string $path): self + public function setInternalPath(?string $internal_path): self { - $this->path = $path; + $this->internal_path = $internal_path; return $this; } @@ -399,24 +545,61 @@ abstract class Attachment extends AbstractNamedDBElement } /** - * Sets the url associated with this attachment. - * If the url is empty nothing is changed, to not override the file path. - * - * @return Attachment + * Sets up the paths using a user provided string which might contain an external path or a builtin path. Allows + * resetting the external path if an internal path exists. Resets any other paths if a (nonempty) new path is set. */ + #[Groups(['attachment:write'])] + #[SerializedName('url')] + #[ApiProperty(description: 'Set the path of the attachment here. + Provide either an external URL, a path to a builtin file (like %FOOTPRINTS%/Active/ICs/IC_DFS.png) or an empty + string if the attachment has an internal file associated and you\'d like to reset the external source. + If you set a new (nonempty) file path any associated internal file will be removed!')] public function setURL(?string $url): self { - //Only set if the URL is not empty - if (!empty($url)) { - if (false !== strpos($url, '%BASE%') || false !== strpos($url, '%MEDIA%')) { - throw new InvalidArgumentException('You can not reference internal files via the url field! But nice try!'); - } - - $this->path = $url; - //Reset internal filename - $this->original_filename = null; + //Don't allow the user to set an empty external path if the internal path is empty already + if (($url === null || $url === "") && !$this->hasInternal()) { + return $this; } + //The URL field can also contain the special builtin internal paths, so we need to distinguish here + if ($this::checkIfBuiltin($url)) { + $this->setInternalPath($url); + //make sure the external path isn't still pointing to something unrelated + $this->setExternalPath(null); + } else { + $this->setExternalPath($url); + } + return $this; + } + + + /** + * Sets the path to a file hosted on an external server. Setting the external path to a (nonempty) value different + * from the the old one _clears_ the internal path, so that the external path reflects where any associated internal + * file came from. + */ + public function setExternalPath(?string $external_path): self + { + //If we only clear the external path, don't reset the internal path, since that could be confusing + if($external_path === null || $external_path === '') { + $this->external_path = null; + return $this; + } + + $external_path = trim($external_path); + //Escape spaces in URL + $external_path = str_replace(' ', '%20', $external_path); + + if($this->external_path === $external_path) { + //Nothing changed, nothing to do + return $this; + } + + $this->external_path = $external_path; + $this->internal_path = null; + //Reset internal filename + $this->original_filename = null; + return $this; } @@ -427,17 +610,22 @@ abstract class Attachment extends AbstractNamedDBElement /** * Checks if the given path is a path to a builtin resource. * - * @param string $path The path that should be checked + * @param string|null $path The path that should be checked * * @return bool true if the path is pointing to a builtin resource */ - public static function checkIfBuiltin(string $path): bool + public static function checkIfBuiltin(?string $path): bool { + //An empty path can't be a builtin + if ($path === null) { + return false; + } + //After the %PLACEHOLDER% comes a slash, so we can check if we have a placeholder via explode $tmp = explode('/', $path); //Builtins must have a %PLACEHOLDER% construction - return in_array($tmp[0], static::BUILTIN_PLACEHOLDER, false); + return in_array($tmp[0], static::BUILTIN_PLACEHOLDER, true); } /** @@ -446,9 +634,9 @@ abstract class Attachment extends AbstractNamedDBElement * @param string $string The string which should be checked * @param bool $path_required If true, the string must contain a path to be valid. (e.g. foo.bar would be invalid, foo.bar/test.php would be valid). * @param bool $only_http Set this to true, if only HTTPS or HTTP schemata should be allowed. - * *Caution: When this is set to false, a attacker could use the file:// schema, to get internal server files, like /etc/passwd.* + * *Caution: When this is set to false, an attacker could use the file:// schema, to get internal server files, like /etc/passwd.* * - * @return bool True if the string is a valid URL. False, if the string is not an URL or invalid. + * @return bool True if the string is a valid URL. False, if the string is not a URL or invalid. */ public static function isValidURL(string $string, bool $path_required = true, bool $only_http = true): bool { @@ -464,4 +652,13 @@ abstract class Attachment extends AbstractNamedDBElement return (bool) filter_var($string, FILTER_VALIDATE_URL); } + + /** + * Returns the class of the element that is allowed to be associated with this attachment. + * @return string + */ + public function getElementClass(): string + { + return static::ALLOWED_ELEMENT_CLASS; + } } diff --git a/src/Entity/Attachments/AttachmentContainingDBElement.php b/src/Entity/Attachments/AttachmentContainingDBElement.php index 02aabadc..a78cb1f4 100644 --- a/src/Entity/Attachments/AttachmentContainingDBElement.php +++ b/src/Entity/Attachments/AttachmentContainingDBElement.php @@ -26,25 +26,27 @@ use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\MasterAttachmentTrait; use App\Entity\Contracts\HasAttachmentsInterface; use App\Entity\Contracts\HasMasterAttachmentInterface; +use App\Repository\AttachmentContainingDBElementRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; /** - * @ORM\MappedSuperclass() + * @template AT of Attachment */ +#[ORM\MappedSuperclass(repositoryClass: AttachmentContainingDBElementRepository::class)] abstract class AttachmentContainingDBElement extends AbstractNamedDBElement implements HasMasterAttachmentInterface, HasAttachmentsInterface { use MasterAttachmentTrait; /** - * @var Attachment[]|Collection - * //TODO - * //@ORM\OneToMany(targetEntity="Attachment", mappedBy="element") - * - * Mapping is done in sub classes like part + * @var Collection + * @phpstan-var Collection + * ORM Mapping is done in subclasses (e.g. Part) */ - protected $attachments; + #[Groups(['full', 'import'])] + protected Collection $attachments; public function __construct() { @@ -77,9 +79,7 @@ abstract class AttachmentContainingDBElement extends AbstractNamedDBElement impl *********************************************************************************/ /** - * Gets all attachments associated with this element. - * - * @return Attachment[]|Collection + * Gets all attachments associated with this element. */ public function getAttachments(): Collection { diff --git a/src/Entity/Attachments/AttachmentType.php b/src/Entity/Attachments/AttachmentType.php index ad7a0e61..22333c16 100644 --- a/src/Entity/Attachments/AttachmentType.php +++ b/src/Entity/Attachments/AttachmentType.php @@ -22,66 +22,128 @@ declare(strict_types=1); namespace App\Entity\Attachments; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Repository\StructuralDBElementRepository; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Parameters\AttachmentTypeParameter; use App\Validator\Constraints\ValidFileFilter; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; /** * Class AttachmentType. - * - * @ORM\Entity(repositoryClass="App\Repository\StructuralDBElementRepository") - * @ORM\Table(name="`attachment_types`", indexes={ - * @ORM\Index(name="attachment_types_idx_name", columns={"name"}), - * @ORM\Index(name="attachment_types_idx_parent_name", columns={"parent_id", "name"}), - * }) + * @see \App\Tests\Entity\Attachments\AttachmentTypeTest + * @extends AbstractStructuralDBElement */ +#[ORM\Entity(repositoryClass: StructuralDBElementRepository::class)] +#[ORM\Table(name: '`attachment_types`')] +#[ORM\Index(columns: ['name'], name: 'attachment_types_idx_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'attachment_types_idx_parent_name')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@attachment_types.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['attachment_type:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['attachment_type:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/attachment_types/{id}/children.{_format}', + operations: [ + new GetCollection(openapi: new Operation(summary: 'Retrieves the children elements of an attachment type.'), + security: 'is_granted("@attachment_types.read")') + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: AttachmentType::class) + ], + normalizationContext: ['groups' => ['attachment_type:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class AttachmentType extends AbstractStructuralDBElement { - /** - * @ORM\OneToMany(targetEntity="AttachmentType", mappedBy="parent", cascade={"persist"}) - * @ORM\OrderBy({"name" = "ASC"}) - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: AttachmentType::class, cascade: ['persist'])] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; + + #[ORM\ManyToOne(targetEntity: AttachmentType::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['attachment_type:read', 'attachment_type:write'])] + #[ApiProperty(readableLink: true, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; /** - * @ORM\ManyToOne(targetEntity="AttachmentType", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") - */ - protected $parent; - - /** - * @var string - * @ORM\Column(type="text") - * @ValidFileFilter + * @var string A comma separated list of file types, which are allowed for attachment files. + * Must be in the format of
accept attribute + * (See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers). */ + #[ORM\Column(type: Types::TEXT)] + #[ValidFileFilter] + #[Groups(['attachment_type:read', 'attachment_type:write', 'import', 'extended'])] protected string $filetype_filter = ''; + /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\AttachmentTypeAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: AttachmentTypeAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + #[Groups(['attachment_type:read', 'attachment_type:write', 'import', 'full'])] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: AttachmentTypeAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['attachment_type:read', 'attachment_type:write', 'full'])] + protected ?Attachment $master_picture_attachment = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\AttachmentTypeParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() */ - protected $parameters; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: AttachmentTypeParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[Groups(['attachment_type:read', 'attachment_type:write', 'import', 'full'])] + protected Collection $parameters; /** - * @var Collection - * @ORM\OneToMany(targetEntity="Attachment", mappedBy="attachment_type") + * @var Collection */ - protected $attachments_with_type; + #[ORM\OneToMany(mappedBy: 'attachment_type', targetEntity: Attachment::class)] + protected Collection $attachments_with_type; + + #[Groups(['attachment_type:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['attachment_type:read'])] + protected ?\DateTimeImmutable $lastModified = null; + public function __construct() { + $this->children = new ArrayCollection(); + $this->parameters = new ArrayCollection(); parent::__construct(); $this->attachments = new ArrayCollection(); $this->attachments_with_type = new ArrayCollection(); @@ -90,8 +152,9 @@ class AttachmentType extends AbstractStructuralDBElement /** * Get all attachments ("Attachment" objects) with this type. * - * @return Collection|Attachment[] all attachments with this type, as a one-dimensional array of Attachments + * @return Collection all attachments with this type, as a one-dimensional array of Attachments * (sorted by their names) + * @phpstan-return Collection */ public function getAttachmentsForType(): Collection { @@ -99,7 +162,7 @@ class AttachmentType extends AbstractStructuralDBElement } /** - * Gets an filter, which file types are allowed for attachment files. + * Gets a filter, which file types are allowed for attachment files. * Must be in the format of accept attribute * (See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers). */ diff --git a/src/Entity/Attachments/AttachmentTypeAttachment.php b/src/Entity/Attachments/AttachmentTypeAttachment.php index 2a21405c..1e677ff0 100644 --- a/src/Entity/Attachments/AttachmentTypeAttachment.php +++ b/src/Entity/Attachments/AttachmentTypeAttachment.php @@ -22,22 +22,25 @@ declare(strict_types=1); namespace App\Entity\Attachments; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** * A attachment attached to an attachmentType element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class AttachmentTypeAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = AttachmentType::class; + final public const ALLOWED_ELEMENT_CLASS = AttachmentType::class; /** - * @var AttachmentType the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Attachments\AttachmentType", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var AttachmentContainingDBElement|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: AttachmentType::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/AttachmentUpload.php b/src/Entity/Attachments/AttachmentUpload.php new file mode 100644 index 00000000..f2b042b7 --- /dev/null +++ b/src/Entity/Attachments/AttachmentUpload.php @@ -0,0 +1,77 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\Attachments; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\Serializer\Attribute\Groups; + +/** + * This is a DTO representing a file upload for an attachment and which is used to pass data to the Attachment + * submit handler service. + */ +class AttachmentUpload +{ + public function __construct( + /** @var UploadedFile|null The file which was uploaded, or null if the file should not be changed */ + public readonly ?UploadedFile $file, + /** @var string|null The base64 encoded data of the file which should be uploaded. */ + #[Groups(['attachment:write'])] + public readonly ?string $data = null, + /** @vaar string|null The original filename of the file passed in data. */ + #[Groups(['attachment:write'])] + public readonly ?string $filename = null, + /** @var bool True, if the URL in the attachment should be downloaded by Part-DB */ + #[Groups(['attachment:write'])] + public readonly bool $downloadUrl = false, + /** @var bool If true the file will be moved to private attachment storage, + * if false it will be moved to public attachment storage. On null file is not moved + */ + #[Groups(['attachment:write'])] + public readonly ?bool $private = null, + /** @var bool If true and no preview image was set yet, the new uploaded file will become the preview image */ + #[Groups(['attachment:write'])] + public readonly ?bool $becomePreviewIfEmpty = true, + ) { + } + + /** + * Creates an AttachmentUpload object from an Attachment FormInterface + * @param FormInterface $form + * @return AttachmentUpload + */ + public static function fromAttachmentForm(FormInterface $form): AttachmentUpload + { + if (!$form->has('file')) { + throw new \InvalidArgumentException('The form does not have a file field. Is it an attachment form?'); + } + + return new self( + file: $form->get('file')->getData(), + downloadUrl: $form->get('downloadURL')->getData(), + private: $form->get('secureFile')->getData() + ); + + } +} \ No newline at end of file diff --git a/src/Entity/Attachments/CategoryAttachment.php b/src/Entity/Attachments/CategoryAttachment.php index e4d38137..3bea265e 100644 --- a/src/Entity/Attachments/CategoryAttachment.php +++ b/src/Entity/Attachments/CategoryAttachment.php @@ -23,22 +23,25 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\Parts\Category; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * A attachment attached to a category element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * An attachment attached to a category element. + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class CategoryAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Category::class; + final public const ALLOWED_ELEMENT_CLASS = Category::class; /** - * @var Category the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Category", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var AttachmentContainingDBElement|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: Category::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/CurrencyAttachment.php b/src/Entity/Attachments/CurrencyAttachment.php index 73ad1145..a5d6e061 100644 --- a/src/Entity/Attachments/CurrencyAttachment.php +++ b/src/Entity/Attachments/CurrencyAttachment.php @@ -23,22 +23,26 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\PriceInformations\Currency; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** * An attachment attached to a currency element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class CurrencyAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Currency::class; + final public const ALLOWED_ELEMENT_CLASS = Currency::class; + /** - * @var Currency the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\PriceInformations\Currency", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var Currency|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: Currency::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/FootprintAttachment.php b/src/Entity/Attachments/FootprintAttachment.php index 84ba3fb7..4a9b866c 100644 --- a/src/Entity/Attachments/FootprintAttachment.php +++ b/src/Entity/Attachments/FootprintAttachment.php @@ -23,22 +23,26 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\Parts\Footprint; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * A attachment attached to a footprint element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * An attachment attached to a footprint element. + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class FootprintAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Footprint::class; + final public const ALLOWED_ELEMENT_CLASS = Footprint::class; + /** - * @var Footprint the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Footprint", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var Footprint|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: Footprint::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/GroupAttachment.php b/src/Entity/Attachments/GroupAttachment.php index e64c5745..e9bf947f 100644 --- a/src/Entity/Attachments/GroupAttachment.php +++ b/src/Entity/Attachments/GroupAttachment.php @@ -23,22 +23,26 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\UserSystem\Group; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * A attachment attached to a Group element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * An attachment attached to a Group element. + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class GroupAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Group::class; + final public const ALLOWED_ELEMENT_CLASS = Group::class; + /** - * @var Group the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\Group", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var Group|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: Group::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/LabelAttachment.php b/src/Entity/Attachments/LabelAttachment.php index f0f70b79..b8891ced 100644 --- a/src/Entity/Attachments/LabelAttachment.php +++ b/src/Entity/Attachments/LabelAttachment.php @@ -42,23 +42,26 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\LabelSystem\LabelProfile; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** * A attachment attached to a user element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class LabelAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = LabelProfile::class; + final public const ALLOWED_ELEMENT_CLASS = LabelProfile::class; /** * @var LabelProfile the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\LabelSystem\LabelProfile", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ + #[ORM\ManyToOne(targetEntity: LabelProfile::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/ManufacturerAttachment.php b/src/Entity/Attachments/ManufacturerAttachment.php index 0d113977..1b8891e5 100644 --- a/src/Entity/Attachments/ManufacturerAttachment.php +++ b/src/Entity/Attachments/ManufacturerAttachment.php @@ -23,22 +23,26 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\Parts\Manufacturer; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * A attachment attached to a manufacturer element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * An attachment attached to a manufacturer element. + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class ManufacturerAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Manufacturer::class; + final public const ALLOWED_ELEMENT_CLASS = Manufacturer::class; + /** - * @var Manufacturer the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Manufacturer", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var Manufacturer|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: Manufacturer::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/MeasurementUnitAttachment.php b/src/Entity/Attachments/MeasurementUnitAttachment.php index 0c24f649..dbc8829e 100644 --- a/src/Entity/Attachments/MeasurementUnitAttachment.php +++ b/src/Entity/Attachments/MeasurementUnitAttachment.php @@ -22,24 +22,25 @@ declare(strict_types=1); namespace App\Entity\Attachments; -use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * A attachment attached to a measurement unit element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * An attachment attached to a measurement unit element. + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class MeasurementUnitAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = MeasurementUnit::class; - /** - * @var Manufacturer the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\MeasurementUnit", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). - */ + final public const ALLOWED_ELEMENT_CLASS = MeasurementUnit::class; + + + #[ORM\ManyToOne(targetEntity: MeasurementUnit::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/PartAttachment.php b/src/Entity/Attachments/PartAttachment.php index 3aa2d05c..0873b3c5 100644 --- a/src/Entity/Attachments/PartAttachment.php +++ b/src/Entity/Attachments/PartAttachment.php @@ -23,22 +23,25 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\Parts\Part; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** * A attachment attached to a part element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class PartAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Part::class; + final public const ALLOWED_ELEMENT_CLASS = Part::class; /** * @var Part the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Part", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ + #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/ProjectAttachment.php b/src/Entity/Attachments/ProjectAttachment.php index 1fab6bc8..f3e96292 100644 --- a/src/Entity/Attachments/ProjectAttachment.php +++ b/src/Entity/Attachments/ProjectAttachment.php @@ -23,22 +23,25 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\ProjectSystem\Project; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** * A attachment attached to a device element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class ProjectAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Project::class; + final public const ALLOWED_ELEMENT_CLASS = Project::class; /** - * @var Project the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\ProjectSystem\Project", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var Project|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: Project::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/StorelocationAttachment.php b/src/Entity/Attachments/StorageLocationAttachment.php similarity index 57% rename from src/Entity/Attachments/StorelocationAttachment.php rename to src/Entity/Attachments/StorageLocationAttachment.php index e2b9025a..3cd82d0c 100644 --- a/src/Entity/Attachments/StorelocationAttachment.php +++ b/src/Entity/Attachments/StorageLocationAttachment.php @@ -22,23 +22,27 @@ declare(strict_types=1); namespace App\Entity\Attachments; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * A attachment attached to a measurement unit element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * An attachment attached to a measurement unit element. + * @extends Attachment */ -class StorelocationAttachment extends Attachment +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] +class StorageLocationAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Storelocation::class; + final public const ALLOWED_ELEMENT_CLASS = StorageLocation::class; + /** - * @var Storelocation the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Storelocation", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var StorageLocation|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: StorageLocation::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/SupplierAttachment.php b/src/Entity/Attachments/SupplierAttachment.php index 59893b74..c3adc438 100644 --- a/src/Entity/Attachments/SupplierAttachment.php +++ b/src/Entity/Attachments/SupplierAttachment.php @@ -23,22 +23,26 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\Parts\Supplier; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** * A attachment attached to a supplier element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class SupplierAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = Supplier::class; + final public const ALLOWED_ELEMENT_CLASS = Supplier::class; + /** - * @var Supplier the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Supplier", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var Supplier|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: Supplier::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Attachments/UserAttachment.php b/src/Entity/Attachments/UserAttachment.php index 84a04c17..b031d419 100644 --- a/src/Entity/Attachments/UserAttachment.php +++ b/src/Entity/Attachments/UserAttachment.php @@ -23,22 +23,26 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\UserSystem\User; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * A attachment attached to a user element. - * - * @ORM\Entity() - * @UniqueEntity({"name", "attachment_type", "element"}) + * An attachment attached to a user element. + * @extends Attachment */ +#[UniqueEntity(['name', 'attachment_type', 'element'])] +#[ORM\Entity] class UserAttachment extends Attachment { - public const ALLOWED_ELEMENT_CLASS = User::class; + final public const ALLOWED_ELEMENT_CLASS = User::class; + /** - * @var User the element this attachment is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User", inversedBy="attachments") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var User|null the element this attachment is associated with */ + #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'attachments')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Entity/Base/AbstractCompany.php b/src/Entity/Base/AbstractCompany.php index 7325bab6..947d1339 100644 --- a/src/Entity/Base/AbstractCompany.php +++ b/src/Entity/Base/AbstractCompany.php @@ -22,53 +22,80 @@ declare(strict_types=1); namespace App\Entity\Base; +use App\Entity\Attachments\Attachment; +use App\Entity\Parameters\AbstractParameter; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use function is_string; use Symfony\Component\Validator\Constraints as Assert; /** * This abstract class is used for companies like suppliers or manufacturers. * - * @ORM\MappedSuperclass() + * @template AT of Attachment + * @template PT of AbstractParameter + * @extends AbstractPartsContainingDBElement */ +#[ORM\MappedSuperclass] abstract class AbstractCompany extends AbstractPartsContainingDBElement { + #[Groups(['company:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['company:read'])] + protected ?\DateTimeImmutable $lastModified = null; + /** * @var string The address of the company - * @ORM\Column(type="string") */ + #[Groups(['full', 'company:read', 'company:write', 'import', 'extended'])] + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 255)] protected string $address = ''; /** * @var string The phone number of the company - * @ORM\Column(type="string") */ + #[Groups(['full', 'company:read', 'company:write', 'import', 'extended'])] + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 255)] protected string $phone_number = ''; /** * @var string The fax number of the company - * @ORM\Column(type="string") */ + #[Groups(['full', 'company:read', 'company:write', 'import', 'extended'])] + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 255)] protected string $fax_number = ''; /** * @var string The email address of the company - * @ORM\Column(type="string") - * @Assert\Email() */ + #[Assert\Email] + #[Groups(['full', 'company:read', 'company:write', 'import', 'extended'])] + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 255)] protected string $email_address = ''; /** * @var string The website of the company - * @ORM\Column(type="string") - * @Assert\Url() */ + #[Assert\Url] + #[Groups(['full', 'company:read', 'company:write', 'import', 'extended'])] + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 255)] protected string $website = ''; + #[Groups(['company:read', 'company:write', 'import', 'full', 'extended'])] + protected string $comment = ''; + /** - * @var string - * @ORM\Column(type="string") + * @var string The link to the website of an article. Use %PARTNUMBER% as placeholder for the part number. */ + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 255)] + #[Groups(['full', 'company:read', 'company:write', 'import', 'extended'])] protected string $auto_product_url = ''; /******************************************************************************** @@ -135,7 +162,7 @@ abstract class AbstractCompany extends AbstractPartsContainingDBElement * * @return string the link to the article */ - public function getAutoProductUrl(string $partnr = null): string + public function getAutoProductUrl(?string $partnr = null): string { if (is_string($partnr)) { return str_replace('%PARTNUMBER%', $partnr, $this->auto_product_url); diff --git a/src/Entity/Base/AbstractDBElement.php b/src/Entity/Base/AbstractDBElement.php index dd736eac..871a22d0 100644 --- a/src/Entity/Base/AbstractDBElement.php +++ b/src/Entity/Base/AbstractDBElement.php @@ -22,6 +22,38 @@ declare(strict_types=1); namespace App\Entity\Base; +use App\Entity\Attachments\AttachmentType; +use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\AttachmentTypeAttachment; +use App\Entity\Attachments\CategoryAttachment; +use App\Entity\Attachments\CurrencyAttachment; +use App\Entity\Attachments\FootprintAttachment; +use App\Entity\Attachments\GroupAttachment; +use App\Entity\Attachments\LabelAttachment; +use App\Entity\Attachments\ManufacturerAttachment; +use App\Entity\Attachments\MeasurementUnitAttachment; +use App\Entity\Attachments\PartAttachment; +use App\Entity\Attachments\ProjectAttachment; +use App\Entity\Attachments\StorageLocationAttachment; +use App\Entity\Attachments\SupplierAttachment; +use App\Entity\Attachments\UserAttachment; +use App\Entity\Parameters\AbstractParameter; +use App\Entity\Parts\Category; +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use App\Entity\Parts\Footprint; +use App\Entity\UserSystem\Group; +use App\Entity\Parts\Manufacturer; +use App\Entity\PriceInformations\Orderdetail; +use App\Entity\Parts\Part; +use App\Entity\Parts\StorageLocation; +use App\Entity\Parts\PartLot; +use App\Entity\PriceInformations\Currency; +use App\Entity\Parts\MeasurementUnit; +use App\Entity\Parts\Supplier; +use App\Entity\UserSystem\User; +use App\Repository\DBElementRepository; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use JsonSerializable; use Symfony\Component\Serializer\Annotation\DiscriminatorMap; @@ -34,35 +66,18 @@ use Symfony\Component\Serializer\Annotation\Groups; * (except special tables like "internal"...) * Every database table which are managed with this class (or a subclass of it) * must have the table row "id"!! The ID is the unique key to identify the elements. - * - * @ORM\MappedSuperclass(repositoryClass="App\Repository\DBElementRepository") - * - * @DiscriminatorMap(typeProperty="type", mapping={ - * "attachment_type" = "App\Entity\AttachmentType", - * "attachment" = "App\Entity\Attachment", - * "category" = "App\Entity\Attachment", - * "project" = "App\Entity\ProjectSystem\Project", - * "project_bom_entry" = "App\Entity\ProjectSystem\ProjectBOMEntry", - * "footprint" = "App\Entity\Footprint", - * "group" = "App\Entity\Group", - * "manufacturer" = "App\Entity\Manufacturer", - * "orderdetail" = "App\Entity\Orderdetail", - * "part" = "App\Entity\Part", - * "pricedetail" = "App\Entity\Pricedetail", - * "storelocation" = "App\Entity\Storelocation", - * "supplier" = "App\Entity\Supplier", - * "user" = "App\Entity\User" - * }) */ +#[DiscriminatorMap(typeProperty: 'type', mapping: ['attachment_type' => AttachmentType::class, 'attachment' => Attachment::class, 'attachment_type_attachment' => AttachmentTypeAttachment::class, 'category_attachment' => CategoryAttachment::class, 'currency_attachment' => CurrencyAttachment::class, 'footprint_attachment' => FootprintAttachment::class, 'group_attachment' => GroupAttachment::class, 'label_attachment' => LabelAttachment::class, 'manufacturer_attachment' => ManufacturerAttachment::class, 'measurement_unit_attachment' => MeasurementUnitAttachment::class, 'part_attachment' => PartAttachment::class, 'project_attachment' => ProjectAttachment::class, 'storelocation_attachment' => StorageLocationAttachment::class, 'supplier_attachment' => SupplierAttachment::class, 'user_attachment' => UserAttachment::class, 'category' => Category::class, 'project' => Project::class, 'project_bom_entry' => ProjectBOMEntry::class, 'footprint' => Footprint::class, 'group' => Group::class, 'manufacturer' => Manufacturer::class, 'orderdetail' => Orderdetail::class, 'part' => Part::class, 'pricedetail' => 'App\Entity\PriceInformation\Pricedetail', 'storelocation' => StorageLocation::class, 'part_lot' => PartLot::class, 'currency' => Currency::class, 'measurement_unit' => MeasurementUnit::class, 'parameter' => AbstractParameter::class, 'supplier' => Supplier::class, 'user' => User::class])] +#[ORM\MappedSuperclass(repositoryClass: DBElementRepository::class)] abstract class AbstractDBElement implements JsonSerializable { /** @var int|null The Identification number for this part. This value is unique for the element in this table. * Null if the element is not saved to DB yet. - * @ORM\Column(type="integer") - * @ORM\Id() - * @ORM\GeneratedValue() - * @Groups({"full"}) */ + #[Groups(['full', 'api:basic:read'])] + #[ORM\Column(type: Types::INTEGER)] + #[ORM\Id] + #[ORM\GeneratedValue] protected ?int $id = null; public function __clone() diff --git a/src/Entity/Base/AbstractNamedDBElement.php b/src/Entity/Base/AbstractNamedDBElement.php index 50c78f41..f7939589 100644 --- a/src/Entity/Base/AbstractNamedDBElement.php +++ b/src/Entity/Base/AbstractNamedDBElement.php @@ -22,6 +22,8 @@ declare(strict_types=1); namespace App\Entity\Base; +use App\Repository\NamedDBElementRepository; +use Doctrine\DBAL\Types\Types; use App\Entity\Contracts\NamedElementInterface; use App\Entity\Contracts\TimeStampableInterface; use Doctrine\ORM\Mapping as ORM; @@ -30,20 +32,20 @@ use Symfony\Component\Validator\Constraints as Assert; /** * All subclasses of this class have an attribute "name". - * - * @ORM\MappedSuperclass(repositoryClass="App\Repository\NamedDBElement") - * @ORM\HasLifecycleCallbacks() */ -abstract class AbstractNamedDBElement extends AbstractDBElement implements NamedElementInterface, TimeStampableInterface +#[ORM\MappedSuperclass(repositoryClass: NamedDBElementRepository::class)] +#[ORM\HasLifecycleCallbacks] +abstract class AbstractNamedDBElement extends AbstractDBElement implements NamedElementInterface, TimeStampableInterface, \Stringable { use TimestampTrait; /** - * @var string the name of this element - * @ORM\Column(type="string") - * @Assert\NotBlank() - * @Groups({"simple", "extended", "full"}) + * @var string The name of this element */ + #[Assert\NotBlank] + #[Groups(['simple', 'extended', 'full', 'import', 'api:basic:read', 'api:basic:write'])] + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 255)] protected string $name = ''; /****************************************************************************** @@ -52,7 +54,7 @@ abstract class AbstractNamedDBElement extends AbstractDBElement implements Named * ******************************************************************************/ - public function __toString() + public function __toString(): string { return $this->getName(); } diff --git a/src/Entity/Base/AbstractPartsContainingDBElement.php b/src/Entity/Base/AbstractPartsContainingDBElement.php index f30819f5..70d88fa9 100644 --- a/src/Entity/Base/AbstractPartsContainingDBElement.php +++ b/src/Entity/Base/AbstractPartsContainingDBElement.php @@ -22,14 +22,28 @@ declare(strict_types=1); namespace App\Entity\Base; +use App\Entity\Attachments\Attachment; +use App\Entity\Parameters\AbstractParameter; +use App\Repository\AbstractPartsContainingRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; /** - * Class PartsContainingDBElement. - * - * @ORM\MappedSuperclass(repositoryClass="App\Repository\AbstractPartsContainingRepository") + * @template AT of Attachment + * @template PT of AbstractParameter + * @extends AbstractStructuralDBElement */ -abstract class -AbstractPartsContainingDBElement extends AbstractStructuralDBElement +#[ORM\MappedSuperclass(repositoryClass: AbstractPartsContainingRepository::class)] +abstract class AbstractPartsContainingDBElement extends AbstractStructuralDBElement { + #[Groups(['full', 'import'])] + protected Collection $parameters; + + public function __construct() + { + parent::__construct(); + $this->parameters = new ArrayCollection(); + } } diff --git a/src/Entity/Base/AbstractStructuralDBElement.php b/src/Entity/Base/AbstractStructuralDBElement.php index cbe2c7be..660710db 100644 --- a/src/Entity/Base/AbstractStructuralDBElement.php +++ b/src/Entity/Base/AbstractStructuralDBElement.php @@ -22,14 +22,21 @@ declare(strict_types=1); namespace App\Entity\Base; +use App\Entity\Attachments\Attachment; +use App\Entity\Parameters\AbstractParameter; +use App\Repository\StructuralDBElementRepository; +use App\EntityListeners\TreeCacheInvalidationListener; +use App\Validator\Constraints\UniqueObjectCollection; +use Doctrine\DBAL\Types\Types; use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Parameters\ParametersTrait; use App\Validator\Constraints\NoneOfItsChildren; +use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Validator\Constraints as Assert; use function count; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use function get_class; use InvalidArgumentException; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; @@ -43,35 +50,40 @@ use Symfony\Component\Serializer\Annotation\Groups; * It's allowed to have instances of root elements, but if you try to change * an attribute of a root element, you will get an exception! * - * @ORM\MappedSuperclass(repositoryClass="App\Repository\StructuralDBElementRepository") * - * @ORM\EntityListeners({"App\EntityListeners\TreeCacheInvalidationListener"}) + * @see \App\Tests\Entity\Base\AbstractStructuralDBElementTest * - * @UniqueEntity(fields={"name", "parent"}, ignoreNull=false, message="structural.entity.unique_name") + * @template AT of Attachment + * @template PT of AbstractParameter + * @template-use ParametersTrait + * @extends AttachmentContainingDBElement + * @uses ParametersTrait */ +#[UniqueEntity(fields: ['name', 'parent'], message: 'structural.entity.unique_name', ignoreNull: false)] +#[ORM\MappedSuperclass(repositoryClass: StructuralDBElementRepository::class)] +#[ORM\EntityListeners([TreeCacheInvalidationListener::class])] abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement { use ParametersTrait; - public const ID_ROOT_ELEMENT = 0; - /** * This is a not standard character, so build a const, so a dev can easily use it. */ - public const PATH_DELIMITER_ARROW = ' → '; + final public const PATH_DELIMITER_ARROW = ' → '; /** - * @var string The comment info for this element - * @ORM\Column(type="text") - * @Groups({"simple", "extended", "full"}) + * @var string The comment info for this element as markdown */ + #[Groups(['full', 'import'])] + #[ORM\Column(type: Types::TEXT)] protected string $comment = ''; /** * @var bool If this property is set, this element can not be selected for part properties. * Useful if this element should be used only for grouping, sorting. - * @ORM\Column(type="boolean") */ + #[Groups(['full', 'import'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $not_selectable = false; /** @@ -80,26 +92,44 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement protected int $level = 0; /** - * We can not define the mapping here or we will get an exception. Unfortunately we have to do the mapping in the + * We can not define the mapping here, or we will get an exception. Unfortunately we have to do the mapping in the * subclasses. * - * @var AbstractStructuralDBElement[]|Collection - * @Groups({"include_children"}) + * @var Collection + * @phpstan-var Collection */ - protected $children; + #[Groups(['include_children'])] + protected Collection $children; /** - * @var AbstractStructuralDBElement - * @NoneOfItsChildren() - * @Groups({"include_parents"}) + * @var AbstractStructuralDBElement|null + * @phpstan-var static|null */ - protected $parent = null; + #[Groups(['include_parents', 'import'])] + #[NoneOfItsChildren] + protected ?AbstractStructuralDBElement $parent = null; - /** @var string[] all names of all parent elements as a array of strings, + /** + * Mapping done in subclasses. + * + * @var Collection + * @phpstan-var Collection + */ + #[Assert\Valid] + #[UniqueObjectCollection(fields: ['name', 'group', 'element'])] + protected Collection $parameters; + + /** @var string[] all names of all parent elements as an array of strings, * the last array element is the name of the element itself */ private array $full_path_strings = []; + /** + * Alternative names (semicolon-separated) for this element, which can be used for searching (especially for info provider system) + */ + #[ORM\Column(type: Types::TEXT, nullable: true, options: ['default' => null])] + private ?string $alternative_names = ""; + public function __construct() { parent::__construct(); @@ -140,11 +170,11 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement //Check if both elements compared, are from the same type // (we have to check inheritance, or we get exceptions when using doctrine entities (they have a proxy type): - if (!is_a($another_element, $class_name) && !is_a($this, get_class($another_element))) { + if (!$another_element instanceof $class_name && !is_a($this, $another_element::class)) { throw new InvalidArgumentException('isChildOf() only works for objects of the same type!'); } - if (null === $this->getParent()) { // this is the root node + if (!$this->getParent() instanceof self) { // this is the root node return false; } @@ -154,10 +184,8 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement if ($this->getParent() === $another_element) { return true; } - } else { //If the IDs are defined, we can compare the IDs - if ($this->getParent()->getID() === $another_element->getID()) { - return true; - } + } elseif ($this->getParent()->getID() === $another_element->getID()) { + return true; } //Otherwise, check recursively @@ -167,11 +195,11 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement /** * Checks if this element is an root element (has no parent). * - * @return bool true if the this element is an root element + * @return bool true if this element is a root element */ public function isRoot(): bool { - return null === $this->parent; + return $this->parent === null; } /****************************************************************************** @@ -183,7 +211,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement /** * Get the parent of this element. * - * @return AbstractStructuralDBElement|null The parent element. Null if this element, does not have a parent. + * @return static|null The parent element. Null if this element, does not have a parent. */ public function getParent(): ?self { @@ -191,7 +219,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement } /** - * Get the comment of the element. + * Get the comment of the element as markdown encoded string. * * @return string the comment @@ -214,9 +242,9 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement /* * Only check for nodes that have a parent. In the other cases zero is correct. */ - if (0 === $this->level && null !== $this->parent) { + if (0 === $this->level && $this->parent instanceof self) { $element = $this->parent; - while (null !== $element) { + while ($element instanceof self) { /** @var AbstractStructuralDBElement $element */ $element = $element->parent; ++$this->level; @@ -233,16 +261,18 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement * * @return string the full path (incl. the name of this element), delimited by $delimiter */ + #[Groups(['api:basic:read'])] + #[SerializedName('full_path')] public function getFullPath(string $delimiter = self::PATH_DELIMITER_ARROW): string { - if (empty($this->full_path_strings)) { + if ($this->full_path_strings === []) { $this->full_path_strings = []; $this->full_path_strings[] = $this->getName(); $element = $this; $overflow = 20; //We only allow 20 levels depth - while (null !== $element->parent && $overflow >= 0) { + while ($element->parent instanceof self && $overflow >= 0) { $element = $element->parent; $this->full_path_strings[] = $element->getName(); //Decrement to prevent mem overflow. @@ -282,6 +312,13 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement */ public function getSubelements(): iterable { + //If the parent is equal to this object, we would get an endless loop, so just return an empty array + //This is just a workaround, as validator should prevent this behaviour, before it gets written to the database + if ($this->parent === $this) { + return new ArrayCollection(); + } + + //@phpstan-ignore-next-line return $this->children ?? new ArrayCollection(); } @@ -309,9 +346,8 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement /** * Sets the new parent object. * - * @param AbstractStructuralDBElement|null $new_parent The new parent object - * - * @return AbstractStructuralDBElement + * @param static|null $new_parent The new parent object + * @return $this */ public function setParent(?self $new_parent): self { @@ -323,7 +359,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement $this->parent = $new_parent; //Add this element as child to the new parent - if (null !== $new_parent) { + if ($new_parent instanceof self) { $new_parent->getChildren()->add($this); } @@ -333,11 +369,11 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement /** * Set the comment. * - * @param string|null $new_comment the new comment + * @param string $new_comment the new comment * - * @return AbstractStructuralDBElement + * @return $this */ - public function setComment(?string $new_comment): self + public function setComment(string $new_comment): self { $this->comment = $new_comment; @@ -386,4 +422,34 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement return $this; } + + /** + * Returns a comma separated list of alternative names. + * @return string|null + */ + public function getAlternativeNames(): ?string + { + if ($this->alternative_names === null) { + return null; + } + + //Remove trailing comma + return rtrim($this->alternative_names, ','); + } + + /** + * Sets a comma separated list of alternative names. + * @return $this + */ + public function setAlternativeNames(?string $new_value): self + { + //Add a trailing comma, if not already there (makes it easier to find in the database) + if (is_string($new_value) && !str_ends_with($new_value, ',')) { + $new_value .= ','; + } + + $this->alternative_names = $new_value; + + return $this; + } } diff --git a/src/Entity/Base/MasterAttachmentTrait.php b/src/Entity/Base/MasterAttachmentTrait.php index 42461f65..723bab07 100644 --- a/src/Entity/Base/MasterAttachmentTrait.php +++ b/src/Entity/Base/MasterAttachmentTrait.php @@ -23,20 +23,21 @@ declare(strict_types=1); namespace App\Entity\Base; use App\Entity\Attachments\Attachment; -use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /** - * A entity with this class has a master attachment, which is used as a preview image for this object. + * An entity with this class has a master attachment, which is used as a preview image for this object. */ trait MasterAttachmentTrait { /** - * @var Attachment - * @ORM\ManyToOne(targetEntity="App\Entity\Attachments\Attachment") - * @ORM\JoinColumn(name="id_preview_attachement", referencedColumnName="id") - * @Assert\Expression("value == null or value.isPicture()", message="part.master_attachment.must_be_picture") + * @var Attachment|null + * Mapping is done in the subclasses (e.g. Part), like with the attachments. + * If this is done here (which is possible in theory), the attachment is not lazy loaded anymore, which causes unnecessary overhead. + * + * !!! If you change this name, you have to change it in the fetchHint in the AttachmentContainingDBElementRepository (getElementsAndPreviewAttachmentByIDs()) too !!! */ + #[Assert\Expression('value == null or value.isPicture()', message: 'part.master_attachment.must_be_picture')] protected ?Attachment $master_picture_attachment = null; /** diff --git a/src/Entity/Base/PartsContainingRepositoryInterface.php b/src/Entity/Base/PartsContainingRepositoryInterface.php index 16932677..89e7e5f6 100644 --- a/src/Entity/Base/PartsContainingRepositoryInterface.php +++ b/src/Entity/Base/PartsContainingRepositoryInterface.php @@ -1,4 +1,7 @@ . */ - namespace App\Entity\Base; use App\Entity\Parts\Part; @@ -28,11 +30,11 @@ interface PartsContainingRepositoryInterface * Returns all parts associated with this element. * * @param object $element the element for which the parts should be determined - * @param array $order_by The order of the parts. Format ['name' => 'ASC'] + * @param string $nameOrderDirection the direction in which the parts should be ordered by name, either ASC or DESC * * @return Part[] */ - public function getParts(object $element, array $order_by = ['name' => 'ASC']): array; + public function getParts(object $element, string $nameOrderDirection = "ASC"): array; /** * Gets the count of the parts associated with this element. diff --git a/src/Entity/Base/TimestampTrait.php b/src/Entity/Base/TimestampTrait.php index 3caf3358..77506b18 100644 --- a/src/Entity/Base/TimestampTrait.php +++ b/src/Entity/Base/TimestampTrait.php @@ -22,6 +22,8 @@ declare(strict_types=1); namespace App\Entity\Base; +use ApiPlatform\Metadata\ApiProperty; +use Doctrine\DBAL\Types\Types; use DateTime; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; @@ -32,26 +34,28 @@ use Symfony\Component\Serializer\Annotation\Groups; trait TimestampTrait { /** - * @var DateTime|null the date when this element was modified the last time - * @ORM\Column(type="datetime", name="last_modified", options={"default"="CURRENT_TIMESTAMP"}) - * @Groups({"extended", "full"}) + * @var \DateTimeImmutable|null the date when this element was modified the last time */ - protected ?DateTime $lastModified = null; + #[Groups(['extended', 'full'])] + #[ApiProperty(writable: false)] + #[ORM\Column(name: 'last_modified', type: Types::DATETIME_IMMUTABLE, options: ['default' => 'CURRENT_TIMESTAMP'])] + protected ?\DateTimeImmutable $lastModified = null; /** - * @var DateTime|null the date when this element was created - * @ORM\Column(type="datetime", name="datetime_added", options={"default"="CURRENT_TIMESTAMP"}) - * @Groups({"extended", "full"}) + * @var \DateTimeImmutable|null the date when this element was created */ - protected ?DateTime $addedDate = null; + #[Groups(['extended', 'full'])] + #[ApiProperty(writable: false)] + #[ORM\Column(name: 'datetime_added', type: Types::DATETIME_IMMUTABLE, options: ['default' => 'CURRENT_TIMESTAMP'])] + protected ?\DateTimeImmutable $addedDate = null; /** * Returns the last time when the element was modified. * Returns null if the element was not yet saved to DB yet. * - * @return DateTime|null the time of the last edit + * @return \DateTimeImmutable|null the time of the last edit */ - public function getLastModified(): ?DateTime + public function getLastModified(): ?\DateTimeImmutable { return $this->lastModified; } @@ -60,24 +64,23 @@ trait TimestampTrait * Returns the date/time when the element was created. * Returns null if the element was not yet saved to DB yet. * - * @return DateTime|null the creation time of the part + * @return \DateTimeImmutable|null the creation time of the part */ - public function getAddedDate(): ?DateTime + public function getAddedDate(): ?\DateTimeImmutable { return $this->addedDate; } /** * Helper for updating the timestamp. It is automatically called by doctrine before persisting. - * - * @ORM\PrePersist - * @ORM\PreUpdate */ + #[ORM\PrePersist] + #[ORM\PreUpdate] public function updateTimestamps(): void { - $this->lastModified = new DateTime('now'); + $this->lastModified = new \DateTimeImmutable('now'); if (null === $this->addedDate) { - $this->addedDate = new DateTime('now'); + $this->addedDate = new \DateTimeImmutable('now'); } } } diff --git a/src/Entity/Contracts/LogWithEventUndoInterface.php b/src/Entity/Contracts/LogWithEventUndoInterface.php index fecc6eaa..1e8db0a1 100644 --- a/src/Entity/Contracts/LogWithEventUndoInterface.php +++ b/src/Entity/Contracts/LogWithEventUndoInterface.php @@ -42,6 +42,7 @@ declare(strict_types=1); namespace App\Entity\Contracts; use App\Entity\LogSystem\AbstractLogEntry; +use App\Services\LogSystem\EventUndoMode; interface LogWithEventUndoInterface { @@ -60,12 +61,12 @@ interface LogWithEventUndoInterface * * @return $this */ - public function setUndoneEvent(AbstractLogEntry $event, string $mode = 'undo'): self; + public function setUndoneEvent(AbstractLogEntry $event, EventUndoMode $mode = EventUndoMode::UNDO): self; /** * Returns the mode how the event was undone: * "undo" = Only a single event was applied to element * "revert" = Element was reverted to the state it was to the timestamp of the log. */ - public function getUndoMode(): string; + public function getUndoMode(): EventUndoMode; } diff --git a/src/Entity/Contracts/LogWithNewDataInterface.php b/src/Entity/Contracts/LogWithNewDataInterface.php new file mode 100644 index 00000000..c4128cb7 --- /dev/null +++ b/src/Entity/Contracts/LogWithNewDataInterface.php @@ -0,0 +1,43 @@ +. + */ +namespace App\Entity\Contracts; + +interface LogWithNewDataInterface +{ + /** + * Checks if this entry has information about the new data. + * @return bool + */ + public function hasNewDataInformation(): bool; + + /** + * Returns the new data for this entry. + */ + public function getNewData(): array; + + /** + * Sets the new data for this entry. + * @return $this + */ + public function setNewData(array $new_data): self; +} diff --git a/src/Entity/Contracts/TimeStampableInterface.php b/src/Entity/Contracts/TimeStampableInterface.php index e198ae7c..c99c0a1c 100644 --- a/src/Entity/Contracts/TimeStampableInterface.php +++ b/src/Entity/Contracts/TimeStampableInterface.php @@ -22,23 +22,21 @@ declare(strict_types=1); namespace App\Entity\Contracts; -use DateTime; - interface TimeStampableInterface { /** * Returns the last time when the element was modified. * Returns null if the element was not yet saved to DB yet. * - * @return DateTime|null the time of the last edit + * @return \DateTimeInterface|null the time of the last edit */ - public function getLastModified(): ?DateTime; + public function getLastModified(): ?\DateTimeInterface; /** * Returns the date/time when the element was created. * Returns null if the element was not yet saved to DB yet. * - * @return DateTime|null the creation time of the part + * @return \DateTimeInterface|null the creation time of the part */ - public function getAddedDate(): ?DateTime; + public function getAddedDate(): ?\DateTimeInterface; } diff --git a/src/Entity/Contracts/TimeTravelInterface.php b/src/Entity/Contracts/TimeTravelInterface.php index 6324d75f..2b0f4571 100644 --- a/src/Entity/Contracts/TimeTravelInterface.php +++ b/src/Entity/Contracts/TimeTravelInterface.php @@ -22,16 +22,14 @@ declare(strict_types=1); namespace App\Entity\Contracts; -use DateTime; - interface TimeTravelInterface { /** - * Checks if this entry has informations which data has changed. + * Checks if this entry has information which data has changed. * - * @return bool true if this entry has informations about the changed data + * @return bool true if this entry has information about the changed data */ - public function hasOldDataInformations(): bool; + public function hasOldDataInformation(): bool; /** * Returns the data the entity had before this log entry. @@ -39,7 +37,7 @@ interface TimeTravelInterface public function getOldData(): array; /** - * Returns the the timestamp associated with this change. + * Returns the timestamp associated with this change. */ - public function getTimestamp(): DateTime; + public function getTimestamp(): \DateTimeInterface; } diff --git a/src/Entity/EDA/EDACategoryInfo.php b/src/Entity/EDA/EDACategoryInfo.php new file mode 100644 index 00000000..0163dfb3 --- /dev/null +++ b/src/Entity/EDA/EDACategoryInfo.php @@ -0,0 +1,135 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\EDA; + +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Embeddable; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints\Length; + +#[Embeddable] +class EDACategoryInfo +{ + /** + * @var string|null The reference prefix of the Part in the schematic. E.g. "R" for resistors, or "C" for capacitors. + */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['full', 'category:read', 'category:write', 'import'])] + #[Length(max: 255)] + private ?string $reference_prefix = null; + + /** @var bool|null Visibility of this part to EDA software in trinary logic. True=Visible, False=Invisible, Null=Auto */ + #[Column(name: 'invisible', type: Types::BOOLEAN, nullable: true)] //TODO: Rename column to visibility + #[Groups(['full', 'category:read', 'category:write', 'import'])] + private ?bool $visibility = null; + + /** @var bool|null If this is set to true, then this part will be excluded from the BOM */ + #[Column(type: Types::BOOLEAN, nullable: true)] + #[Groups(['full', 'category:read', 'category:write', 'import'])] + private ?bool $exclude_from_bom = null; + + /** @var bool|null If this is set to true, then this part will be excluded from the board/the PCB */ + #[Column(type: Types::BOOLEAN, nullable: true)] + #[Groups(['full', 'category:read', 'category:write', 'import'])] + private ?bool $exclude_from_board = null; + + /** @var bool|null If this is set to true, then this part will be excluded in the simulation */ + #[Column(type: Types::BOOLEAN, nullable: true)] + #[Groups(['full', 'category:read', 'category:write', 'import'])] + private ?bool $exclude_from_sim = true; + + /** @var string|null The KiCAD schematic symbol, which should be used (the path to the library) */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['full', 'category:read', 'category:write', 'import'])] + #[Length(max: 255)] + private ?string $kicad_symbol = null; + + public function getReferencePrefix(): ?string + { + return $this->reference_prefix; + } + + public function setReferencePrefix(?string $reference_prefix): EDACategoryInfo + { + $this->reference_prefix = $reference_prefix; + return $this; + } + + public function getVisibility(): ?bool + { + return $this->visibility; + } + + public function setVisibility(?bool $visibility): EDACategoryInfo + { + $this->visibility = $visibility; + return $this; + } + + public function getExcludeFromBom(): ?bool + { + return $this->exclude_from_bom; + } + + public function setExcludeFromBom(?bool $exclude_from_bom): EDACategoryInfo + { + $this->exclude_from_bom = $exclude_from_bom; + return $this; + } + + public function getExcludeFromBoard(): ?bool + { + return $this->exclude_from_board; + } + + public function setExcludeFromBoard(?bool $exclude_from_board): EDACategoryInfo + { + $this->exclude_from_board = $exclude_from_board; + return $this; + } + + public function getExcludeFromSim(): ?bool + { + return $this->exclude_from_sim; + } + + public function setExcludeFromSim(?bool $exclude_from_sim): EDACategoryInfo + { + $this->exclude_from_sim = $exclude_from_sim; + return $this; + } + + public function getKicadSymbol(): ?string + { + return $this->kicad_symbol; + } + + public function setKicadSymbol(?string $kicad_symbol): EDACategoryInfo + { + $this->kicad_symbol = $kicad_symbol; + return $this; + } + +} \ No newline at end of file diff --git a/src/Entity/EDA/EDAFootprintInfo.php b/src/Entity/EDA/EDAFootprintInfo.php new file mode 100644 index 00000000..9c5ef1c1 --- /dev/null +++ b/src/Entity/EDA/EDAFootprintInfo.php @@ -0,0 +1,51 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\EDA; + +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Embeddable; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints\Length; + +#[Embeddable] +class EDAFootprintInfo +{ + /** @var string|null The KiCAD footprint, which should be used (the path to the library) */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['full', 'footprint:read', 'footprint:write', 'import'])] + #[Length(max: 255)] + private ?string $kicad_footprint = null; + + public function getKicadFootprint(): ?string + { + return $this->kicad_footprint; + } + + public function setKicadFootprint(?string $kicad_footprint): EDAFootprintInfo + { + $this->kicad_footprint = $kicad_footprint; + return $this; + } +} \ No newline at end of file diff --git a/src/Entity/EDA/EDAPartInfo.php b/src/Entity/EDA/EDAPartInfo.php new file mode 100644 index 00000000..b4fc3588 --- /dev/null +++ b/src/Entity/EDA/EDAPartInfo.php @@ -0,0 +1,175 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\EDA; + +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Embeddable; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints\Length; + +#[Embeddable] +class EDAPartInfo +{ + /** + * @var string|null The reference prefix of the Part in the schematic. E.g. "R" for resistors, or "C" for capacitors. + */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['full', 'eda_info:read', 'eda_info:write', 'import'])] + #[Length(max: 255)] + private ?string $reference_prefix = null; + + /** @var string|null The value, which should be shown together with the part (e.g. 470 for a 470 Ohm resistor) */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['full', 'eda_info:read', 'eda_info:write', 'import'])] + #[Length(max: 255)] + private ?string $value = null; + + /** @var bool|null Visibility of this part to EDA software in trinary logic. True=Visible, False=Invisible, Null=Auto */ + #[Column(name: 'invisible', type: Types::BOOLEAN, nullable: true)] //TODO: Rename column to visibility + #[Groups(['full', 'eda_info:read', 'eda_info:write', 'import'])] + private ?bool $visibility = null; + + /** @var bool|null If this is set to true, then this part will be excluded from the BOM */ + #[Column(type: Types::BOOLEAN, nullable: true)] + #[Groups(['full', 'eda_info:read', 'eda_info:write', 'import'])] + private ?bool $exclude_from_bom = null; + + /** @var bool|null If this is set to true, then this part will be excluded from the board/the PCB */ + #[Column(type: Types::BOOLEAN, nullable: true)] + #[Groups(['full', 'eda_info:read', 'eda_info:write', 'import'])] + private ?bool $exclude_from_board = null; + + /** @var bool|null If this is set to true, then this part will be excluded in the simulation */ + #[Column(type: Types::BOOLEAN, nullable: true)] + #[Groups(['full', 'eda_info:read', 'eda_info:write', 'import'])] + private ?bool $exclude_from_sim = null; + + /** @var string|null The KiCAD schematic symbol, which should be used (the path to the library) */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['full', 'eda_info:read', 'eda_info:write', 'import'])] + #[Length(max: 255)] + private ?string $kicad_symbol = null; + + /** @var string|null The KiCAD footprint, which should be used (the path to the library) */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['full', 'eda_info:read', 'eda_info:write', 'import'])] + #[Length(max: 255)] + private ?string $kicad_footprint = null; + + public function __construct() + { + + } + + public function getReferencePrefix(): ?string + { + return $this->reference_prefix; + } + + public function setReferencePrefix(?string $reference_prefix): EDAPartInfo + { + $this->reference_prefix = $reference_prefix; + return $this; + } + + public function getValue(): ?string + { + return $this->value; + } + + public function setValue(?string $value): EDAPartInfo + { + $this->value = $value; + return $this; + } + + public function getVisibility(): ?bool + { + return $this->visibility; + } + + public function setVisibility(?bool $visibility): EDAPartInfo + { + $this->visibility = $visibility; + return $this; + } + + public function getExcludeFromBom(): ?bool + { + return $this->exclude_from_bom; + } + + public function setExcludeFromBom(?bool $exclude_from_bom): EDAPartInfo + { + $this->exclude_from_bom = $exclude_from_bom; + return $this; + } + + public function getExcludeFromBoard(): ?bool + { + return $this->exclude_from_board; + } + + public function setExcludeFromBoard(?bool $exclude_from_board): EDAPartInfo + { + $this->exclude_from_board = $exclude_from_board; + return $this; + } + + public function getExcludeFromSim(): ?bool + { + return $this->exclude_from_sim; + } + + public function setExcludeFromSim(?bool $exclude_from_sim): EDAPartInfo + { + $this->exclude_from_sim = $exclude_from_sim; + return $this; + } + + public function getKicadSymbol(): ?string + { + return $this->kicad_symbol; + } + + public function setKicadSymbol(?string $kicad_symbol): EDAPartInfo + { + $this->kicad_symbol = $kicad_symbol; + return $this; + } + + public function getKicadFootprint(): ?string + { + return $this->kicad_footprint; + } + + public function setKicadFootprint(?string $kicad_footprint): EDAPartInfo + { + $this->kicad_footprint = $kicad_footprint; + return $this; + } + + +} \ No newline at end of file diff --git a/src/Entity/LabelSystem/BarcodeType.php b/src/Entity/LabelSystem/BarcodeType.php new file mode 100644 index 00000000..daf7d401 --- /dev/null +++ b/src/Entity/LabelSystem/BarcodeType.php @@ -0,0 +1,66 @@ +. + */ +namespace App\Entity\LabelSystem; + +enum BarcodeType: string +{ + case NONE = 'none'; + case QR = 'qr'; + case CODE39 = 'code39'; + case DATAMATRIX = 'datamatrix'; + case CODE93 = 'code93'; + case CODE128 = 'code128'; + + /** + * Returns true if the barcode is none. (Useful for twig templates) + * @return bool + */ + public function isNone(): bool + { + return $this === self::NONE; + } + + /** + * Returns true if the barcode is a 1D barcode (Code39, etc.). + * @return bool + */ + public function is1D(): bool + { + return match ($this) { + self::CODE39, self::CODE93, self::CODE128 => true, + default => false, + }; + } + + /** + * Returns true if the barcode is a 2D barcode (QR code, datamatrix). + * @return bool + */ + public function is2D(): bool + { + return match ($this) { + self::QR, self::DATAMATRIX => true, + default => false, + }; + } +} diff --git a/src/Entity/LabelSystem/LabelOptions.php b/src/Entity/LabelSystem/LabelOptions.php index f3f448ad..ee1a5414 100644 --- a/src/Entity/LabelSystem/LabelOptions.php +++ b/src/Entity/LabelSystem/LabelOptions.php @@ -41,71 +41,66 @@ declare(strict_types=1); namespace App\Entity\LabelSystem; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Validator\Constraints as Assert; -/** - * @ORM\Embeddable() - */ +#[ORM\Embeddable] class LabelOptions { - public const BARCODE_TYPES = ['none', /*'ean8',*/ 'qr', 'code39', 'datamatrix', 'code93', 'code128']; - public const SUPPORTED_ELEMENTS = ['part', 'part_lot', 'storelocation']; - public const PICTURE_TYPES = ['none', 'element_picture', 'main_attachment']; - - public const LINES_MODES = ['html', 'twig']; - /** * @var float The page size of the label in mm - * @Assert\Positive() - * @ORM\Column(type="float") */ + #[Assert\Positive] + #[ORM\Column(type: Types::FLOAT)] + #[Groups(["extended", "full", "import"])] protected float $width = 50.0; /** * @var float The page size of the label in mm - * @Assert\Positive() - * @ORM\Column(type="float") */ + #[Assert\Positive] + #[ORM\Column(type: Types::FLOAT)] + #[Groups(["extended", "full", "import"])] protected float $height = 30.0; /** - * @var string The type of the barcode that should be used in the label (e.g. 'qr') - * @Assert\Choice(choices=LabelOptions::BARCODE_TYPES) - * @ORM\Column(type="string") + * @var BarcodeType The type of the barcode that should be used in the label (e.g. 'qr') */ - protected string $barcode_type = 'none'; + #[ORM\Column(type: Types::STRING, enumType: BarcodeType::class)] + #[Groups(["extended", "full", "import"])] + protected BarcodeType $barcode_type = BarcodeType::NONE; /** - * @var string What image should be shown along the - * @Assert\Choice(choices=LabelOptions::PICTURE_TYPES) - * @ORM\Column(type="string") + * @var LabelPictureType What image should be shown along the label */ - protected string $picture_type = 'none'; + #[ORM\Column(type: Types::STRING, enumType: LabelPictureType::class)] + #[Groups(["extended", "full", "import"])] + protected LabelPictureType $picture_type = LabelPictureType::NONE; - /** - * @var string - * @Assert\Choice(choices=LabelOptions::SUPPORTED_ELEMENTS) - * @ORM\Column(type="string") - */ - protected string $supported_element = 'part'; + #[ORM\Column(type: Types::STRING, enumType: LabelSupportedElement::class)] + #[Groups(["extended", "full", "import"])] + protected LabelSupportedElement $supported_element = LabelSupportedElement::PART; /** * @var string any additional CSS for the label - * @ORM\Column(type="text") */ + #[ORM\Column(type: Types::TEXT)] + #[Groups([ "full", "import"])] protected string $additional_css = ''; - /** @var string The mode that will be used to interpret the lines - * @Assert\Choice(choices=LabelOptions::LINES_MODES) - * @ORM\Column(type="string") + /** @var LabelProcessMode The mode that will be used to interpret the lines */ - protected string $lines_mode = 'html'; + #[ORM\Column(name: 'lines_mode', type: Types::STRING, enumType: LabelProcessMode::class)] + #[Groups(["extended", "full", "import"])] + protected LabelProcessMode $process_mode = LabelProcessMode::PLACEHOLDER; /** * @var string - * @ORM\Column(type="text") */ + #[ORM\Column(type: Types::TEXT)] + #[Groups(["extended", "full", "import"])] protected string $lines = ''; public function getWidth(): float @@ -113,9 +108,6 @@ class LabelOptions return $this->width; } - /** - * @return LabelOptions - */ public function setWidth(float $width): self { $this->width = $width; @@ -128,9 +120,6 @@ class LabelOptions return $this->height; } - /** - * @return LabelOptions - */ public function setHeight(float $height): self { $this->height = $height; @@ -138,45 +127,36 @@ class LabelOptions return $this; } - public function getBarcodeType(): string + public function getBarcodeType(): BarcodeType { return $this->barcode_type; } - /** - * @return LabelOptions - */ - public function setBarcodeType(string $barcode_type): self + public function setBarcodeType(BarcodeType $barcode_type): self { $this->barcode_type = $barcode_type; return $this; } - public function getPictureType(): string + public function getPictureType(): LabelPictureType { return $this->picture_type; } - /** - * @return LabelOptions - */ - public function setPictureType(string $picture_type): self + public function setPictureType(LabelPictureType $picture_type): self { $this->picture_type = $picture_type; return $this; } - public function getSupportedElement(): string + public function getSupportedElement(): LabelSupportedElement { return $this->supported_element; } - /** - * @return LabelOptions - */ - public function setSupportedElement(string $supported_element): self + public function setSupportedElement(LabelSupportedElement $supported_element): self { $this->supported_element = $supported_element; @@ -188,9 +168,6 @@ class LabelOptions return $this->lines; } - /** - * @return LabelOptions - */ public function setLines(string $lines): self { $this->lines = $lines; @@ -206,9 +183,6 @@ class LabelOptions return $this->additional_css; } - /** - * @return LabelOptions - */ public function setAdditionalCss(string $additional_css): self { $this->additional_css = $additional_css; @@ -216,17 +190,14 @@ class LabelOptions return $this; } - public function getLinesMode(): string + public function getProcessMode(): LabelProcessMode { - return $this->lines_mode; + return $this->process_mode; } - /** - * @return LabelOptions - */ - public function setLinesMode(string $lines_mode): self + public function setProcessMode(LabelProcessMode $process_mode): self { - $this->lines_mode = $lines_mode; + $this->process_mode = $process_mode; return $this; } diff --git a/src/Entity/LabelSystem/LabelPictureType.php b/src/Entity/LabelSystem/LabelPictureType.php new file mode 100644 index 00000000..c1f90fe2 --- /dev/null +++ b/src/Entity/LabelSystem/LabelPictureType.php @@ -0,0 +1,39 @@ +. + */ +namespace App\Entity\LabelSystem; + +enum LabelPictureType: string +{ + /** + * Show no picture on the label + */ + case NONE = 'none'; + /** + * Show the preview picture of the element on the label + */ + case ELEMENT_PICTURE = 'element_picture'; + /** + * Show the main attachment of the element on the label + */ + case MAIN_ATTACHMENT = 'main_attachment'; +} diff --git a/assets/css/app/darkmode.css b/src/Entity/LabelSystem/LabelProcessMode.php similarity index 68% rename from assets/css/app/darkmode.css rename to src/Entity/LabelSystem/LabelProcessMode.php index 4368ed64..d5967b49 100644 --- a/assets/css/app/darkmode.css +++ b/src/Entity/LabelSystem/LabelProcessMode.php @@ -1,7 +1,11 @@ +. */ +namespace App\Entity\LabelSystem; -.darkmode-layer { - z-index: 2020; -} - -/** If darkmode is enabled revert the blening for images and videos, as these should be shown not inverted */ -.darkmode--activated img, -.darkmode--activated video { - mix-blend-mode: difference; -} - -.darkmode--activated .hoverpic:hover { - background: black; +enum LabelProcessMode: string +{ + /** Use placeholders like [[PLACEHOLDER]] which gets replaced with content */ + case PLACEHOLDER = 'html'; + /** Interpret the given lines as twig template */ + case TWIG = 'twig'; } diff --git a/src/Entity/LabelSystem/LabelProfile.php b/src/Entity/LabelSystem/LabelProfile.php index 46c478ee..d3616c34 100644 --- a/src/Entity/LabelSystem/LabelProfile.php +++ b/src/Entity/LabelSystem/LabelProfile.php @@ -41,49 +41,64 @@ declare(strict_types=1); namespace App\Entity\LabelSystem; +use Doctrine\Common\Collections\Criteria; +use App\Entity\Attachments\Attachment; +use App\Repository\LabelProfileRepository; +use App\EntityListeners\TreeCacheInvalidationListener; +use Doctrine\DBAL\Types\Types; +use Doctrine\Common\Collections\ArrayCollection; use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\LabelAttachment; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Validator\Constraints as Assert; /** - * @ORM\Entity(repositoryClass="App\Repository\LabelProfileRepository") - * @ORM\Table(name="label_profiles") - * @ORM\EntityListeners({"App\EntityListeners\TreeCacheInvalidationListener"}) - * @UniqueEntity({"name", "options.supported_element"}) + * @extends AttachmentContainingDBElement */ +#[UniqueEntity(['name', 'options.supported_element'])] +#[ORM\Entity(repositoryClass: LabelProfileRepository::class)] +#[ORM\EntityListeners([TreeCacheInvalidationListener::class])] +#[ORM\Table(name: 'label_profiles')] class LabelProfile extends AttachmentContainingDBElement { /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\LabelAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) */ - protected $attachments; + #[ORM\OneToMany(mappedBy: 'element', targetEntity: LabelAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: LabelAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + protected ?Attachment $master_picture_attachment = null; /** * @var LabelOptions - * @ORM\Embedded(class="LabelOptions") - * @Assert\Valid() */ + #[Assert\Valid] + #[ORM\Embedded(class: 'LabelOptions')] + #[Groups(["extended", "full", "import"])] protected LabelOptions $options; /** * @var string The comment info for this element - * @ORM\Column(type="text") */ + #[ORM\Column(type: Types::TEXT)] protected string $comment = ''; /** * @var bool determines, if this label profile should be shown in the dropdown quick menu - * @ORM\Column(type="boolean") */ + #[ORM\Column(type: Types::BOOLEAN)] + #[Groups(["extended", "full", "import"])] protected bool $show_in_dropdown = true; public function __construct() { + $this->attachments = new ArrayCollection(); parent::__construct(); $this->options = new LabelOptions(); } @@ -127,8 +142,6 @@ class LabelProfile extends AttachmentContainingDBElement /** * Sets the show in dropdown menu. - * - * @return LabelProfile */ public function setShowInDropdown(bool $show_in_dropdown): self { diff --git a/src/Entity/LabelSystem/LabelSupportedElement.php b/src/Entity/LabelSystem/LabelSupportedElement.php new file mode 100644 index 00000000..7649e586 --- /dev/null +++ b/src/Entity/LabelSystem/LabelSupportedElement.php @@ -0,0 +1,49 @@ +. + */ +namespace App\Entity\LabelSystem; + +use App\Entity\Base\AbstractDBElement; +use App\Entity\Base\AbstractNamedDBElement; +use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; +use App\Entity\Parts\StorageLocation; + +enum LabelSupportedElement: string +{ + case PART = 'part'; + case PART_LOT = 'part_lot'; + case STORELOCATION = 'storelocation'; + + /** + * Returns the entity class for the given element type + * @return class-string + */ + public function getEntityClass(): string + { + return match ($this) { + self::PART => Part::class, + self::PART_LOT => PartLot::class, + self::STORELOCATION => StorageLocation::class, + }; + } +} diff --git a/src/Entity/LogSystem/AbstractLogEntry.php b/src/Entity/LogSystem/AbstractLogEntry.php index e2dca513..aa795613 100644 --- a/src/Entity/LogSystem/AbstractLogEntry.php +++ b/src/Entity/LogSystem/AbstractLogEntry.php @@ -22,157 +22,60 @@ declare(strict_types=1); namespace App\Entity\LogSystem; -use App\Entity\Attachments\Attachment; -use App\Entity\Attachments\AttachmentType; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\AbstractDBElement; -use App\Entity\ProjectSystem\Project; -use App\Entity\ProjectSystem\ProjectBOMEntry; -use App\Entity\LabelSystem\LabelProfile; -use App\Entity\Parameters\AbstractParameter; -use App\Entity\Parts\Category; -use App\Entity\Parts\Footprint; -use App\Entity\Parts\Manufacturer; -use App\Entity\Parts\MeasurementUnit; -use App\Entity\Parts\Part; -use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; -use App\Entity\Parts\Supplier; -use App\Entity\PriceInformations\Currency; -use App\Entity\PriceInformations\Orderdetail; -use App\Entity\PriceInformations\Pricedetail; -use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; -use DateTime; + use Doctrine\ORM\Mapping as ORM; -use InvalidArgumentException; -use Psr\Log\LogLevel; +use App\Repository\LogEntryRepository; /** - * This entity describes a entry in the event log. - * - * @ORM\Entity(repositoryClass="App\Repository\LogEntryRepository") - * @ORM\Table("log", indexes={ - * @ORM\Index(name="log_idx_type", columns={"type"}), - * @ORM\Index(name="log_idx_type_target", columns={"type", "target_type", "target_id"}), - * @ORM\Index(name="log_idx_datetime", columns={"datetime"}), - * }) - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="type", type="smallint") - * @ORM\DiscriminatorMap({ - * 1 = "UserLoginLogEntry", - * 2 = "UserLogoutLogEntry", - * 3 = "UserNotAllowedLogEntry", - * 4 = "ExceptionLogEntry", - * 5 = "ElementDeletedLogEntry", - * 6 = "ElementCreatedLogEntry", - * 7 = "ElementEditedLogEntry", - * 8 = "ConfigChangedLogEntry", - * 9 = "LegacyInstockChangedLogEntry", - * 10 = "DatabaseUpdatedLogEntry", - * 11 = "CollectionElementDeleted", - * 12 = "SecurityEventLogEntry", - * 13 = "PartStockChangedLogEntry", - * }) + * This entity describes an entry in the event log. + * @see \App\Tests\Entity\LogSystem\AbstractLogEntryTest */ +#[ORM\Entity(repositoryClass: LogEntryRepository::class)] +#[ORM\Table('log')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'smallint')] +#[ORM\DiscriminatorMap([1 => 'UserLoginLogEntry', 2 => 'UserLogoutLogEntry', 3 => 'UserNotAllowedLogEntry', 4 => 'ExceptionLogEntry', 5 => 'ElementDeletedLogEntry', 6 => 'ElementCreatedLogEntry', 7 => 'ElementEditedLogEntry', 8 => 'ConfigChangedLogEntry', 9 => 'LegacyInstockChangedLogEntry', 10 => 'DatabaseUpdatedLogEntry', 11 => 'CollectionElementDeleted', 12 => 'SecurityEventLogEntry', 13 => 'PartStockChangedLogEntry'])] +#[ORM\Index(columns: ['type'], name: 'log_idx_type')] +#[ORM\Index(columns: ['type', 'target_type', 'target_id'], name: 'log_idx_type_target')] +#[ORM\Index(columns: ['datetime'], name: 'log_idx_datetime')] abstract class AbstractLogEntry extends AbstractDBElement { - public const LEVEL_EMERGENCY = 0; - public const LEVEL_ALERT = 1; - public const LEVEL_CRITICAL = 2; - public const LEVEL_ERROR = 3; - public const LEVEL_WARNING = 4; - public const LEVEL_NOTICE = 5; - public const LEVEL_INFO = 6; - public const LEVEL_DEBUG = 7; - - protected const TARGET_TYPE_NONE = 0; - protected const TARGET_TYPE_USER = 1; - protected const TARGET_TYPE_ATTACHEMENT = 2; - protected const TARGET_TYPE_ATTACHEMENTTYPE = 3; - protected const TARGET_TYPE_CATEGORY = 4; - protected const TARGET_TYPE_DEVICE = 5; - protected const TARGET_TYPE_DEVICEPART = 6; - protected const TARGET_TYPE_FOOTPRINT = 7; - protected const TARGET_TYPE_GROUP = 8; - protected const TARGET_TYPE_MANUFACTURER = 9; - protected const TARGET_TYPE_PART = 10; - protected const TARGET_TYPE_STORELOCATION = 11; - protected const TARGET_TYPE_SUPPLIER = 12; - protected const TARGET_TYPE_PARTLOT = 13; - protected const TARGET_TYPE_CURRENCY = 14; - protected const TARGET_TYPE_ORDERDETAIL = 15; - protected const TARGET_TYPE_PRICEDETAIL = 16; - protected const TARGET_TYPE_MEASUREMENTUNIT = 17; - protected const TARGET_TYPE_PARAMETER = 18; - protected const TARGET_TYPE_LABEL_PROFILE = 19; - - /** - * @var array This const is used to convert the numeric level to a PSR-3 compatible log level - */ - protected const LEVEL_ID_TO_STRING = [ - self::LEVEL_EMERGENCY => LogLevel::EMERGENCY, - self::LEVEL_ALERT => LogLevel::ALERT, - self::LEVEL_CRITICAL => LogLevel::CRITICAL, - self::LEVEL_ERROR => LogLevel::ERROR, - self::LEVEL_WARNING => LogLevel::WARNING, - self::LEVEL_NOTICE => LogLevel::NOTICE, - self::LEVEL_INFO => LogLevel::INFO, - self::LEVEL_DEBUG => LogLevel::DEBUG, - ]; - - protected const TARGET_CLASS_MAPPING = [ - self::TARGET_TYPE_USER => User::class, - self::TARGET_TYPE_ATTACHEMENT => Attachment::class, - self::TARGET_TYPE_ATTACHEMENTTYPE => AttachmentType::class, - self::TARGET_TYPE_CATEGORY => Category::class, - self::TARGET_TYPE_DEVICE => Project::class, - self::TARGET_TYPE_DEVICEPART => ProjectBOMEntry::class, - self::TARGET_TYPE_FOOTPRINT => Footprint::class, - self::TARGET_TYPE_GROUP => Group::class, - self::TARGET_TYPE_MANUFACTURER => Manufacturer::class, - self::TARGET_TYPE_PART => Part::class, - self::TARGET_TYPE_STORELOCATION => Storelocation::class, - self::TARGET_TYPE_SUPPLIER => Supplier::class, - self::TARGET_TYPE_PARTLOT => PartLot::class, - self::TARGET_TYPE_CURRENCY => Currency::class, - self::TARGET_TYPE_ORDERDETAIL => Orderdetail::class, - self::TARGET_TYPE_PRICEDETAIL => Pricedetail::class, - self::TARGET_TYPE_MEASUREMENTUNIT => MeasurementUnit::class, - self::TARGET_TYPE_PARAMETER => AbstractParameter::class, - self::TARGET_TYPE_LABEL_PROFILE => LabelProfile::class, - ]; - - /** @var User The user which has caused this log entry - * @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User", fetch="EAGER") - * @ORM\JoinColumn(name="id_user", nullable=true, onDelete="SET NULL") + /** @var User|null The user which has caused this log entry */ + #[ORM\ManyToOne(targetEntity: User::class, fetch: 'EAGER')] + #[ORM\JoinColumn(name: 'id_user', onDelete: 'SET NULL')] protected ?User $user = null; /** * @var string The username of the user which has caused this log entry (shown if the user is deleted) - * @ORM\Column(type="string", nullable=false) */ + #[ORM\Column(type: Types::STRING)] protected string $username = ''; - /** @var DateTime The datetime the event associated with this log entry has occured - * @ORM\Column(type="datetime", name="datetime") + /** + * @var \DateTimeImmutable The datetime the event associated with this log entry has occured */ - protected ?DateTime $timestamp = null; + #[ORM\Column(name: 'datetime', type: Types::DATETIME_IMMUTABLE)] + protected \DateTimeImmutable $timestamp; - /** @var int The priority level of the associated level. 0 is highest, 7 lowest - * @ORM\Column(type="integer", name="level", columnDefinition="TINYINT(4) NOT NULL") + /** + * @var LogLevel The priority level of the associated level. 0 is highest, 7 lowest */ - protected int $level; + #[ORM\Column(name: 'level', type: 'tinyint', enumType: LogLevel::class)] + protected LogLevel $level = LogLevel::WARNING; /** @var int The ID of the element targeted by this event - * @ORM\Column(name="target_id", type="integer", nullable=false) */ + #[ORM\Column(name: 'target_id', type: Types::INTEGER)] protected int $target_id = 0; - /** @var int The Type of the targeted element - * @ORM\Column(name="target_type", type="smallint", nullable=false) + /** @var LogTargetType The Type of the targeted element */ - protected int $target_type = 0; + #[ORM\Column(name: 'target_type', type: Types::SMALLINT, enumType: LogTargetType::class)] + protected LogTargetType $target_type = LogTargetType::NONE; /** @var string The type of this log entry, aka the description what has happened. * The mapping between the log entry class and the discriminator column is done by doctrine. @@ -181,14 +84,13 @@ abstract class AbstractLogEntry extends AbstractDBElement protected string $typeString = 'unknown'; /** @var array The extra data in raw (short form) saved in the DB - * @ORM\Column(name="extra", type="json") */ - protected $extra = []; + #[ORM\Column(name: 'extra', type: Types::JSON)] + protected array $extra = []; public function __construct() { - $this->timestamp = new DateTime(); - $this->level = self::LEVEL_WARNING; + $this->timestamp = new \DateTimeImmutable(); } /** @@ -216,6 +118,40 @@ abstract class AbstractLogEntry extends AbstractDBElement return $this; } + /** + * Returns true if this log entry was created by a CLI command, false otherwise. + * @return bool + */ + public function isCLIEntry(): bool + { + return str_starts_with($this->username, '!!!CLI '); + } + + /** + * Marks this log entry as a CLI entry, and set the username of the CLI user. + * This removes the association to a user object in database, as CLI users are not really related to logged in + * Part-DB users. + * @return $this + */ + public function setCLIUsername(string $cli_username): self + { + $this->user = null; + $this->username = '!!!CLI ' . $cli_username; + return $this; + } + + /** + * Retrieves the username of the CLI user that caused the event. + * @return string|null The username of the CLI user, or null if this log entry was not created by a CLI command. + */ + public function getCLIUsername(): ?string + { + if ($this->isCLIEntry()) { + return substr($this->username, 7); + } + return null; + } + /** * Retuns the username of the user that caused the event (useful if the user was deleted). * @@ -227,9 +163,9 @@ abstract class AbstractLogEntry extends AbstractDBElement } /** - * Returns the timestamp when the event that caused this log entry happened. + * Returns the timestamp when the event that caused this log entry happened. */ - public function getTimestamp(): DateTime + public function getTimestamp(): \DateTimeImmutable { return $this->timestamp; } @@ -239,7 +175,7 @@ abstract class AbstractLogEntry extends AbstractDBElement * * @return $this */ - public function setTimestamp(DateTime $timestamp): self + public function setTimestamp(\DateTimeImmutable $timestamp): self { $this->timestamp = $timestamp; @@ -247,16 +183,10 @@ abstract class AbstractLogEntry extends AbstractDBElement } /** - * Get the priority level of this log entry. 0 is highest and 7 lowest level. - * See LEVEL_* consts in this class for more info. + * Get the priority level of this log entry. */ - public function getLevel(): int + public function getLevel(): LogLevel { - //It is always alerting when a wrong int is saved in DB... - if ($this->level < 0 || $this->level > 7) { - return self::LEVEL_ALERT; - } - return $this->level; } @@ -265,13 +195,9 @@ abstract class AbstractLogEntry extends AbstractDBElement * * @return $this */ - public function setLevel(int $level): self + public function setLevel(LogLevel $level): self { - if ($level < 0 || $this->level > 7) { - throw new InvalidArgumentException(sprintf('$level must be between 0 and 7! %d given!', $level)); - } $this->level = $level; - return $this; } @@ -280,7 +206,7 @@ abstract class AbstractLogEntry extends AbstractDBElement */ public function getLevelString(): string { - return self::levelIntToString($this->getLevel()); + return $this->level->toPSR3LevelString(); } /** @@ -290,8 +216,7 @@ abstract class AbstractLogEntry extends AbstractDBElement */ public function setLevelString(string $level): self { - $this->setLevel(self::levelStringToInt($level)); - + LogLevel::fromPSR3LevelString($level); return $this; } @@ -305,22 +230,27 @@ abstract class AbstractLogEntry extends AbstractDBElement /** * Returns the class name of the target element associated with this log entry. - * Returns null, if this log entry is not associated with an log entry. + * Returns null, if this log entry is not associated with a log entry. * * @return string|null the class name of the target class */ public function getTargetClass(): ?string { - if (self::TARGET_TYPE_NONE === $this->target_type) { - return null; - } + return $this->target_type->toClass(); + } - return self::targetTypeIdToClass($this->target_type); + /** + * Returns the type of the target element associated with this log entry. + * @return LogTargetType + */ + public function getTargetType(): LogTargetType + { + return $this->target_type; } /** * Returns the ID of the target element associated with this log entry. - * Returns null, if this log entry is not associated with an log entry. + * Returns null, if this log entry is not associated with a log entry. * * @return int|null the ID of the associated element */ @@ -352,14 +282,14 @@ abstract class AbstractLogEntry extends AbstractDBElement */ public function setTargetElement(?AbstractDBElement $element): self { - if (null === $element) { + if ($element === null) { $this->target_id = 0; - $this->target_type = self::TARGET_TYPE_NONE; + $this->target_type = LogTargetType::NONE; return $this; } - $this->target_type = static::targetTypeClassToID(get_class($element)); + $this->target_type = LogTargetType::fromElementClass($element); $this->target_id = $element->getID(); return $this; @@ -382,75 +312,4 @@ abstract class AbstractLogEntry extends AbstractDBElement return $this->extra; } - /** - * This function converts the internal numeric log level into an PSR3 compatible level string. - * - * @param int $level The numerical log level - * - * @return string The PSR3 compatible level string - */ - final public static function levelIntToString(int $level): string - { - if (!isset(self::LEVEL_ID_TO_STRING[$level])) { - throw new InvalidArgumentException('No level with this int is existing!'); - } - - return self::LEVEL_ID_TO_STRING[$level]; - } - - /** - * This function converts a PSR3 compatible string to the internal numeric level string. - * - * @param string $level the PSR3 compatible string that should be converted - * - * @return int the internal int representation - */ - final public static function levelStringToInt(string $level): int - { - $tmp = array_flip(self::LEVEL_ID_TO_STRING); - if (!isset($tmp[$level])) { - throw new InvalidArgumentException('No level with this string is existing!'); - } - - return $tmp[$level]; - } - - /** - * Converts an target type id to an full qualified class name. - * - * @param int $type_id The target type ID - */ - final public static function targetTypeIdToClass(int $type_id): string - { - if (!isset(self::TARGET_CLASS_MAPPING[$type_id])) { - throw new InvalidArgumentException('No target type with this ID is existing!'); - } - - return self::TARGET_CLASS_MAPPING[$type_id]; - } - - /** - * Convert a class name to a target type ID. - * - * @param string $class The name of the class (FQN) that should be converted to id - * - * @return int the ID of the associated target type ID - */ - final public static function targetTypeClassToID(string $class): int - { - $tmp = array_flip(self::TARGET_CLASS_MAPPING); - //Check if we can use a key directly - if (isset($tmp[$class])) { - return $tmp[$class]; - } - - //Otherwise we have to iterate over everything and check for inheritance - foreach ($tmp as $compare_class => $class_id) { - if (is_a($class, $compare_class, true)) { - return $class_id; - } - } - - throw new InvalidArgumentException('No target ID for this class is existing! (Class: '.$class.')'); - } } diff --git a/src/Entity/LogSystem/CollectionElementDeleted.php b/src/Entity/LogSystem/CollectionElementDeleted.php index 5b12119a..16bf33f5 100644 --- a/src/Entity/LogSystem/CollectionElementDeleted.php +++ b/src/Entity/LogSystem/CollectionElementDeleted.php @@ -52,7 +52,7 @@ use App\Entity\Attachments\GroupAttachment; use App\Entity\Attachments\ManufacturerAttachment; use App\Entity\Attachments\MeasurementUnitAttachment; use App\Entity\Attachments\PartAttachment; -use App\Entity\Attachments\StorelocationAttachment; +use App\Entity\Attachments\StorageLocationAttachment; use App\Entity\Attachments\SupplierAttachment; use App\Entity\Attachments\UserAttachment; use App\Entity\Base\AbstractDBElement; @@ -69,40 +69,36 @@ use App\Entity\Parameters\GroupParameter; use App\Entity\Parameters\ManufacturerParameter; use App\Entity\Parameters\MeasurementUnitParameter; use App\Entity\Parameters\PartParameter; -use App\Entity\Parameters\StorelocationParameter; +use App\Entity\Parameters\StorageLocationParameter; use App\Entity\Parameters\SupplierParameter; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; -use App\Repository\Parts\ManufacturerRepository; use Doctrine\ORM\Mapping as ORM; -use InvalidArgumentException; -/** - * @ORM\Entity() - * This log entry is created when an element is deleted, that is used in a collection of an other entity. - * This is needed to signal time travel, that it has to undelete the deleted entity. - */ +#[ORM\Entity] class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventUndoInterface { + use LogWithEventUndoTrait; + protected string $typeString = 'collection_element_deleted'; - protected int $level = self::LEVEL_INFO; public function __construct(AbstractDBElement $changed_element, string $collection_name, AbstractDBElement $deletedElement) { parent::__construct(); - $this->level = self::LEVEL_INFO; + $this->level = LogLevel::INFO; + $this->setTargetElement($changed_element); $this->extra['n'] = $collection_name; - $this->extra['c'] = self::targetTypeClassToID(get_class($deletedElement)); + $this->extra['c'] = LogTargetType::fromElementClass($deletedElement)->value; $this->extra['i'] = $deletedElement->getID(); if ($deletedElement instanceof NamedElementInterface) { $this->extra['o'] = $deletedElement->getName(); @@ -132,7 +128,7 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU public function getDeletedElementClass(): string { //The class name of our target element - $tmp = self::targetTypeIdToClass($this->extra['c']); + $tmp = LogTargetType::from($this->extra['c'])->toClass(); $reflection_class = new \ReflectionClass($tmp); //If the class is abstract, we have to map it to an instantiable class @@ -146,71 +142,42 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU /** * This functions maps an abstract class name derived from the extra c element to an instantiable class name (based on the target element of this log entry). * For example if the target element is a part and the extra c element is "App\Entity\Attachments\Attachment", this function will return "App\Entity\Attachments\PartAttachment". - * @param string $abstract_class - * @return string */ private function resolveAbstractClassToInstantiableClass(string $abstract_class): string { if (is_a($abstract_class, AbstractParameter::class, true)) { - switch ($this->getTargetClass()) { - case AttachmentType::class: - return AttachmentTypeParameter::class; - case Category::class: - return CategoryParameter::class; - case Currency::class: - return CurrencyParameter::class; - case Project::class: - return ProjectParameter::class; - case Footprint::class: - return FootprintParameter::class; - case Group::class: - return GroupParameter::class; - case Manufacturer::class: - return ManufacturerParameter::class; - case MeasurementUnit::class: - return MeasurementUnitParameter::class; - case Part::class: - return PartParameter::class; - case Storelocation::class: - return StorelocationParameter::class; - case Supplier::class: - return SupplierParameter::class; - - default: - throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass()); - } + return match ($this->getTargetClass()) { + AttachmentType::class => AttachmentTypeParameter::class, + Category::class => CategoryParameter::class, + Currency::class => CurrencyParameter::class, + Project::class => ProjectParameter::class, + Footprint::class => FootprintParameter::class, + Group::class => GroupParameter::class, + Manufacturer::class => ManufacturerParameter::class, + MeasurementUnit::class => MeasurementUnitParameter::class, + Part::class => PartParameter::class, + StorageLocation::class => StorageLocationParameter::class, + Supplier::class => SupplierParameter::class, + default => throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass()), + }; } if (is_a($abstract_class, Attachment::class, true)) { - switch ($this->getTargetClass()) { - case AttachmentType::class: - return AttachmentTypeAttachment::class; - case Category::class: - return CategoryAttachment::class; - case Currency::class: - return CurrencyAttachment::class; - case Project::class: - return ProjectAttachment::class; - case Footprint::class: - return FootprintAttachment::class; - case Group::class: - return GroupAttachment::class; - case Manufacturer::class: - return ManufacturerAttachment::class; - case MeasurementUnit::class: - return MeasurementUnitAttachment::class; - case Part::class: - return PartAttachment::class; - case Storelocation::class: - return StorelocationAttachment::class; - case Supplier::class: - return SupplierAttachment::class; - case User::class: - return UserAttachment::class; - - default: - throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass()); - } + return match ($this->getTargetClass()) { + AttachmentType::class => AttachmentTypeAttachment::class, + Category::class => CategoryAttachment::class, + Currency::class => CurrencyAttachment::class, + Project::class => ProjectAttachment::class, + Footprint::class => FootprintAttachment::class, + Group::class => GroupAttachment::class, + Manufacturer::class => ManufacturerAttachment::class, + MeasurementUnit::class => MeasurementUnitAttachment::class, + Part::class => PartAttachment::class, + StorageLocation::class => StorageLocationAttachment::class, + Supplier::class => SupplierAttachment::class, + User::class => UserAttachment::class, + default => throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass()), + }; } throw new \RuntimeException('The class '.$abstract_class.' is abstract and no explicit resolving to an concrete type is defined!'); @@ -223,39 +190,4 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU { return $this->extra['i']; } - - public function isUndoEvent(): bool - { - return isset($this->extra['u']); - } - - public function getUndoEventID(): ?int - { - return $this->extra['u'] ?? null; - } - - public function setUndoneEvent(AbstractLogEntry $event, string $mode = 'undo'): LogWithEventUndoInterface - { - $this->extra['u'] = $event->getID(); - - if ('undo' === $mode) { - $this->extra['um'] = 1; - } elseif ('revert' === $mode) { - $this->extra['um'] = 2; - } else { - throw new InvalidArgumentException('Passed invalid $mode!'); - } - - return $this; - } - - public function getUndoMode(): string - { - $mode_int = $this->extra['um'] ?? 1; - if (1 === $mode_int) { - return 'undo'; - } - - return 'revert'; - } } diff --git a/src/Entity/LogSystem/ConfigChangedLogEntry.php b/src/Entity/LogSystem/ConfigChangedLogEntry.php index 543886bf..68f8edf3 100644 --- a/src/Entity/LogSystem/ConfigChangedLogEntry.php +++ b/src/Entity/LogSystem/ConfigChangedLogEntry.php @@ -25,9 +25,7 @@ namespace App\Entity\LogSystem; use App\Exceptions\LogEntryObsoleteException; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class ConfigChangedLogEntry extends AbstractLogEntry { protected string $typeString = 'config_changed'; diff --git a/src/Entity/LogSystem/DatabaseUpdatedLogEntry.php b/src/Entity/LogSystem/DatabaseUpdatedLogEntry.php index 9c600365..6e137373 100644 --- a/src/Entity/LogSystem/DatabaseUpdatedLogEntry.php +++ b/src/Entity/LogSystem/DatabaseUpdatedLogEntry.php @@ -24,9 +24,7 @@ namespace App\Entity\LogSystem; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class DatabaseUpdatedLogEntry extends AbstractLogEntry { protected string $typeString = 'database_updated'; @@ -43,7 +41,7 @@ class DatabaseUpdatedLogEntry extends AbstractLogEntry */ public function isSuccessful(): bool { - //We dont save unsuccessful updates now, so just assume it to save space. + //We don't save unsuccessful updates now, so just assume it to save space. return $this->extra['s'] ?? true; } diff --git a/src/Entity/LogSystem/ElementCreatedLogEntry.php b/src/Entity/LogSystem/ElementCreatedLogEntry.php index 2a327e73..8364974c 100644 --- a/src/Entity/LogSystem/ElementCreatedLogEntry.php +++ b/src/Entity/LogSystem/ElementCreatedLogEntry.php @@ -28,24 +28,23 @@ use App\Entity\Contracts\LogWithEventUndoInterface; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use Doctrine\ORM\Mapping as ORM; -use InvalidArgumentException; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class ElementCreatedLogEntry extends AbstractLogEntry implements LogWithCommentInterface, LogWithEventUndoInterface { + use LogWithEventUndoTrait; + protected string $typeString = 'element_created'; public function __construct(AbstractDBElement $new_element) { parent::__construct(); - $this->level = self::LEVEL_INFO; + $this->level = LogLevel::INFO; $this->setTargetElement($new_element); //Creation of new users is maybe more interesting... if ($new_element instanceof User || $new_element instanceof Group) { - $this->level = self::LEVEL_NOTICE; + $this->level = LogLevel::NOTICE; } } @@ -54,7 +53,7 @@ class ElementCreatedLogEntry extends AbstractLogEntry implements LogWithCommentI */ public function getCreationInstockValue(): ?string { - return $this->extra['i'] ?? null; + return isset($this->extra['i']) ? (string)$this->extra['i'] : null; } /** @@ -81,39 +80,4 @@ class ElementCreatedLogEntry extends AbstractLogEntry implements LogWithCommentI return $this; } - - public function isUndoEvent(): bool - { - return isset($this->extra['u']); - } - - public function getUndoEventID(): ?int - { - return $this->extra['u'] ?? null; - } - - public function setUndoneEvent(AbstractLogEntry $event, string $mode = 'undo'): LogWithEventUndoInterface - { - $this->extra['u'] = $event->getID(); - - if ('undo' === $mode) { - $this->extra['um'] = 1; - } elseif ('revert' === $mode) { - $this->extra['um'] = 2; - } else { - throw new InvalidArgumentException('Passed invalid $mode!'); - } - - return $this; - } - - public function getUndoMode(): string - { - $mode_int = $this->extra['um'] ?? 1; - if (1 === $mode_int) { - return 'undo'; - } - - return 'revert'; - } } diff --git a/src/Entity/LogSystem/ElementDeletedLogEntry.php b/src/Entity/LogSystem/ElementDeletedLogEntry.php index a25f9bf7..e3dd2ac7 100644 --- a/src/Entity/LogSystem/ElementDeletedLogEntry.php +++ b/src/Entity/LogSystem/ElementDeletedLogEntry.php @@ -30,24 +30,23 @@ use App\Entity\Contracts\TimeTravelInterface; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use Doctrine\ORM\Mapping as ORM; -use InvalidArgumentException; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class ElementDeletedLogEntry extends AbstractLogEntry implements TimeTravelInterface, LogWithCommentInterface, LogWithEventUndoInterface { protected string $typeString = 'element_deleted'; + use LogWithEventUndoTrait; + public function __construct(AbstractDBElement $deleted_element) { parent::__construct(); - $this->level = self::LEVEL_INFO; + $this->level = LogLevel::INFO; $this->setTargetElement($deleted_element); //Deletion of a user is maybe more interesting... if ($deleted_element instanceof User || $deleted_element instanceof Group) { - $this->level = self::LEVEL_NOTICE; + $this->level = LogLevel::NOTICE; } } @@ -88,7 +87,7 @@ class ElementDeletedLogEntry extends AbstractLogEntry implements TimeTravelInter return $this; } - public function hasOldDataInformations(): bool + public function hasOldDataInformation(): bool { return !empty($this->extra['o']); } @@ -114,39 +113,4 @@ class ElementDeletedLogEntry extends AbstractLogEntry implements TimeTravelInter return $this; } - - public function isUndoEvent(): bool - { - return isset($this->extra['u']); - } - - public function getUndoEventID(): ?int - { - return $this->extra['u'] ?? null; - } - - public function setUndoneEvent(AbstractLogEntry $event, string $mode = 'undo'): LogWithEventUndoInterface - { - $this->extra['u'] = $event->getID(); - - if ('undo' === $mode) { - $this->extra['um'] = 1; - } elseif ('revert' === $mode) { - $this->extra['um'] = 2; - } else { - throw new InvalidArgumentException('Passed invalid $mode!'); - } - - return $this; - } - - public function getUndoMode(): string - { - $mode_int = $this->extra['um'] ?? 1; - if (1 === $mode_int) { - return 'undo'; - } - - return 'revert'; - } } diff --git a/src/Entity/LogSystem/ElementEditedLogEntry.php b/src/Entity/LogSystem/ElementEditedLogEntry.php index ec5b9f80..8d4b7b9d 100644 --- a/src/Entity/LogSystem/ElementEditedLogEntry.php +++ b/src/Entity/LogSystem/ElementEditedLogEntry.php @@ -25,21 +25,21 @@ namespace App\Entity\LogSystem; use App\Entity\Base\AbstractDBElement; use App\Entity\Contracts\LogWithCommentInterface; use App\Entity\Contracts\LogWithEventUndoInterface; +use App\Entity\Contracts\LogWithNewDataInterface; use App\Entity\Contracts\TimeTravelInterface; use Doctrine\ORM\Mapping as ORM; -use InvalidArgumentException; -/** - * @ORM\Entity() - */ -class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterface, LogWithCommentInterface, LogWithEventUndoInterface +#[ORM\Entity] +class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterface, LogWithCommentInterface, LogWithEventUndoInterface, LogWithNewDataInterface { + use LogWithEventUndoTrait; + protected string $typeString = 'element_edited'; public function __construct(AbstractDBElement $changed_element) { parent::__construct(); - $this->level = self::LEVEL_INFO; + $this->level = LogLevel::INFO; $this->setTargetElement($changed_element); } @@ -49,7 +49,7 @@ class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterf */ public function hasChangedFieldsInfo(): bool { - return isset($this->extra['f']) || $this->hasOldDataInformations(); + return isset($this->extra['f']) || $this->hasOldDataInformation(); } /** @@ -59,7 +59,7 @@ class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterf */ public function getChangedFields(): array { - if ($this->hasOldDataInformations()) { + if ($this->hasOldDataInformation()) { return array_keys($this->getOldData()); } @@ -92,7 +92,29 @@ class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterf return $this; } - public function hasOldDataInformations(): bool + public function hasNewDataInformation(): bool + { + return !empty($this->extra['n']); + } + + public function getNewData(): array + { + return $this->extra['n'] ?? []; + } + + /** + * Sets the old data for this entry. + * + * @return $this + */ + public function setNewData(array $new_data): self + { + $this->extra['n'] = $new_data; + + return $this; + } + + public function hasOldDataInformation(): bool { return !empty($this->extra['d']); } @@ -118,39 +140,4 @@ class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterf return $this; } - - public function isUndoEvent(): bool - { - return isset($this->extra['u']); - } - - public function getUndoEventID(): ?int - { - return $this->extra['u'] ?? null; - } - - public function setUndoneEvent(AbstractLogEntry $event, string $mode = 'undo'): LogWithEventUndoInterface - { - $this->extra['u'] = $event->getID(); - - if ('undo' === $mode) { - $this->extra['um'] = 1; - } elseif ('revert' === $mode) { - $this->extra['um'] = 2; - } else { - throw new InvalidArgumentException('Passed invalid $mode!'); - } - - return $this; - } - - public function getUndoMode(): string - { - $mode_int = $this->extra['um'] ?? 1; - if (1 === $mode_int) { - return 'undo'; - } - - return 'revert'; - } } diff --git a/src/Entity/LogSystem/ExceptionLogEntry.php b/src/Entity/LogSystem/ExceptionLogEntry.php index dc9a7f32..e8fb06f9 100644 --- a/src/Entity/LogSystem/ExceptionLogEntry.php +++ b/src/Entity/LogSystem/ExceptionLogEntry.php @@ -25,9 +25,7 @@ namespace App\Entity\LogSystem; use App\Exceptions\LogEntryObsoleteException; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class ExceptionLogEntry extends AbstractLogEntry { protected string $typeString = 'exception'; diff --git a/src/Entity/LogSystem/LegacyInstockChangedLogEntry.php b/src/Entity/LogSystem/LegacyInstockChangedLogEntry.php index 35d58592..27f7afe4 100644 --- a/src/Entity/LogSystem/LegacyInstockChangedLogEntry.php +++ b/src/Entity/LogSystem/LegacyInstockChangedLogEntry.php @@ -24,9 +24,7 @@ namespace App\Entity\LogSystem; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class LegacyInstockChangedLogEntry extends AbstractLogEntry { protected string $typeString = 'instock_changed'; @@ -56,7 +54,7 @@ class LegacyInstockChangedLogEntry extends AbstractLogEntry } /** - * Returns the price that has to be payed for the change (in the base currency). + * Returns the price that has to be paid for the change (in the base currency). * * @param bool $absolute Set this to true, if you want only get the absolute value of the price (without minus) */ @@ -92,9 +90,9 @@ class LegacyInstockChangedLogEntry extends AbstractLogEntry } /** - * Checks if the Change was an withdrawal of parts. + * Checks if the Change was a withdrawal of parts. * - * @return bool true if the change was an withdrawal, false if not + * @return bool true if the change was a withdrawal, false if not */ public function isWithdrawal(): bool { diff --git a/src/Entity/LogSystem/LogLevel.php b/src/Entity/LogSystem/LogLevel.php new file mode 100644 index 00000000..435c5468 --- /dev/null +++ b/src/Entity/LogSystem/LogLevel.php @@ -0,0 +1,119 @@ +. + */ +namespace App\Entity\LogSystem; + +use Psr\Log\LogLevel as PSRLogLevel; + +enum LogLevel: int +{ + case EMERGENCY = 0; + case ALERT = 1; + case CRITICAL = 2; + case ERROR = 3; + case WARNING = 4; + case NOTICE = 5; + case INFO = 6; + case DEBUG = 7; + + /** + * Converts the current log level to a PSR-3 log level string. + * @return string + */ + public function toPSR3LevelString(): string + { + return match ($this) { + self::EMERGENCY => PSRLogLevel::EMERGENCY, + self::ALERT => PSRLogLevel::ALERT, + self::CRITICAL => PSRLogLevel::CRITICAL, + self::ERROR => PSRLogLevel::ERROR, + self::WARNING => PSRLogLevel::WARNING, + self::NOTICE => PSRLogLevel::NOTICE, + self::INFO => PSRLogLevel::INFO, + self::DEBUG => PSRLogLevel::DEBUG, + }; + } + + /** + * Creates a log level (enum) from a PSR-3 log level string. + * @param string $level + * @return self + */ + public static function fromPSR3LevelString(string $level): self + { + return match ($level) { + PSRLogLevel::EMERGENCY => self::EMERGENCY, + PSRLogLevel::ALERT => self::ALERT, + PSRLogLevel::CRITICAL => self::CRITICAL, + PSRLogLevel::ERROR => self::ERROR, + PSRLogLevel::WARNING => self::WARNING, + PSRLogLevel::NOTICE => self::NOTICE, + PSRLogLevel::INFO => self::INFO, + PSRLogLevel::DEBUG => self::DEBUG, + default => throw new \InvalidArgumentException("Invalid log level: $level"), + }; + } + + /** + * Checks if the current log level is more important than the given one. + * @param LogLevel $other + * @return bool + */ + public function moreImportThan(self $other): bool + { + //Smaller values are more important + return $this->value < $other->value; + } + + /** + * Checks if the current log level is more important or equal than the given one. + * @param LogLevel $other + * @return bool + */ + public function moreImportOrEqualThan(self $other): bool + { + //Smaller values are more important + return $this->value <= $other->value; + } + + /** + * Checks if the current log level is less important than the given one. + * @param LogLevel $other + * @return bool + */ + public function lessImportThan(self $other): bool + { + //Bigger values are less important + return $this->value > $other->value; + } + + /** + * Checks if the current log level is less important or equal than the given one. + * @param LogLevel $other + * @return bool + */ + public function lessImportOrEqualThan(self $other): bool + { + //Bigger values are less important + return $this->value >= $other->value; + } +} diff --git a/src/Entity/LogSystem/LogTargetType.php b/src/Entity/LogSystem/LogTargetType.php new file mode 100644 index 00000000..1c6e4f8c --- /dev/null +++ b/src/Entity/LogSystem/LogTargetType.php @@ -0,0 +1,129 @@ +. + */ +namespace App\Entity\LogSystem; + +use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\AttachmentType; +use App\Entity\LabelSystem\LabelProfile; +use App\Entity\Parameters\AbstractParameter; +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; +use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\MeasurementUnit; +use App\Entity\Parts\Part; +use App\Entity\Parts\PartAssociation; +use App\Entity\Parts\PartLot; +use App\Entity\Parts\StorageLocation; +use App\Entity\Parts\Supplier; +use App\Entity\PriceInformations\Currency; +use App\Entity\PriceInformations\Orderdetail; +use App\Entity\PriceInformations\Pricedetail; +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use App\Entity\UserSystem\Group; +use App\Entity\UserSystem\User; + +enum LogTargetType: int +{ + case NONE = 0; + case USER = 1; + case ATTACHMENT = 2; + case ATTACHMENT_TYPE = 3; + case CATEGORY = 4; + case PROJECT = 5; + case BOM_ENTRY = 6; + case FOOTPRINT = 7; + case GROUP = 8; + case MANUFACTURER = 9; + case PART = 10; + case STORELOCATION = 11; + case SUPPLIER = 12; + case PART_LOT = 13; + case CURRENCY = 14; + case ORDERDETAIL = 15; + case PRICEDETAIL = 16; + case MEASUREMENT_UNIT = 17; + case PARAMETER = 18; + case LABEL_PROFILE = 19; + + case PART_ASSOCIATION = 20; + + /** + * Returns the class name of the target type or null if the target type is NONE. + * @return string|null + */ + public function toClass(): ?string + { + return match ($this) { + self::NONE => null, + self::USER => User::class, + self::ATTACHMENT => Attachment::class, + self::ATTACHMENT_TYPE => AttachmentType::class, + self::CATEGORY => Category::class, + self::PROJECT => Project::class, + self::BOM_ENTRY => ProjectBOMEntry::class, + self::FOOTPRINT => Footprint::class, + self::GROUP => Group::class, + self::MANUFACTURER => Manufacturer::class, + self::PART => Part::class, + self::STORELOCATION => StorageLocation::class, + self::SUPPLIER => Supplier::class, + self::PART_LOT => PartLot::class, + self::CURRENCY => Currency::class, + self::ORDERDETAIL => Orderdetail::class, + self::PRICEDETAIL => Pricedetail::class, + self::MEASUREMENT_UNIT => MeasurementUnit::class, + self::PARAMETER => AbstractParameter::class, + self::LABEL_PROFILE => LabelProfile::class, + self::PART_ASSOCIATION => PartAssociation::class, + }; + } + + /** + * Determines the target type from the given class name or object. + * @param object|string $element + * @phpstan-param object|class-string $element + * @return self + */ + public static function fromElementClass(object|string $element): self + { + //Iterate over all possible types + foreach (self::cases() as $case) { + $class = $case->toClass(); + + //Skip NONE + if ($class === null) { + continue; + } + + //Check if the given element is a instance of the class + if (is_a($element, $class, true)) { + return $case; + } + } + + $elementClass = is_object($element) ? $element::class : $element; + //If no matching type was found, throw an exception + throw new \InvalidArgumentException("The given class $elementClass is not a valid log target type."); + } +} diff --git a/src/Entity/LogSystem/LogWithEventUndoTrait.php b/src/Entity/LogSystem/LogWithEventUndoTrait.php new file mode 100644 index 00000000..ed8629dc --- /dev/null +++ b/src/Entity/LogSystem/LogWithEventUndoTrait.php @@ -0,0 +1,53 @@ +. + */ +namespace App\Entity\LogSystem; + +use App\Entity\Contracts\LogWithEventUndoInterface; +use App\Services\LogSystem\EventUndoMode; + +trait LogWithEventUndoTrait +{ + public function isUndoEvent(): bool + { + return isset($this->extra['u']); + } + + public function getUndoEventID(): ?int + { + return $this->extra['u'] ?? null; + } + + public function setUndoneEvent(AbstractLogEntry $event, EventUndoMode $mode = EventUndoMode::UNDO): LogWithEventUndoInterface + { + $this->extra['u'] = $event->getID(); + $this->extra['um'] = $mode->toExtraInt(); + + return $this; + } + + public function getUndoMode(): EventUndoMode + { + $mode_int = $this->extra['um'] ?? 1; + return EventUndoMode::fromExtraInt($mode_int); + } +} diff --git a/src/Entity/LogSystem/PartStockChangeType.php b/src/Entity/LogSystem/PartStockChangeType.php new file mode 100644 index 00000000..f69fe95f --- /dev/null +++ b/src/Entity/LogSystem/PartStockChangeType.php @@ -0,0 +1,58 @@ +. + */ +namespace App\Entity\LogSystem; + +enum PartStockChangeType: string +{ + case ADD = "add"; + case WITHDRAW = "withdraw"; + case MOVE = "move"; + + /** + * Converts the type to a short representation usable in the extra field of the log entry. + * @return string + */ + public function toExtraShortType(): string + { + return match ($this) { + self::ADD => 'a', + self::WITHDRAW => 'w', + self::MOVE => 'm', + }; + } + + public function toTranslationKey(): string + { + return 'log.part_stock_changed.' . $this->value; + } + + public static function fromExtraShortType(string $value): self + { + return match ($value) { + 'a' => self::ADD, + 'w' => self::WITHDRAW, + 'm' => self::MOVE, + default => throw new \InvalidArgumentException("Invalid short type: $value"), + }; + } +} diff --git a/src/Entity/LogSystem/PartStockChangedLogEntry.php b/src/Entity/LogSystem/PartStockChangedLogEntry.php index 44852076..1bac9e9f 100644 --- a/src/Entity/LogSystem/PartStockChangedLogEntry.php +++ b/src/Entity/LogSystem/PartStockChangedLogEntry.php @@ -1,4 +1,7 @@ . */ - namespace App\Entity\LogSystem; use App\Entity\Parts\PartLot; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class PartStockChangedLogEntry extends AbstractLogEntry { - public const TYPE_ADD = "add"; - public const TYPE_WITHDRAW = "withdraw"; - public const TYPE_MOVE = "move"; - protected string $typeString = 'part_stock_changed'; protected const COMMENT_MAX_LENGTH = 300; /** * Creates a new part stock changed log entry. - * @param string $type The type of the log entry. One of the TYPE_* constants. + * @param PartStockChangeType $type The type of the log entry. * @param PartLot $lot The part lot which has been changed. * @param float $old_stock The old stock of the lot. * @param float $new_stock The new stock of the lot. * @param float $new_total_part_instock The new total instock of the part. * @param string $comment The comment associated with the change. * @param PartLot|null $move_to_target The target lot if the type is TYPE_MOVE. + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. */ - protected function __construct(string $type, PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?PartLot $move_to_target = null) + protected function __construct(PartStockChangeType $type, PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?PartLot $move_to_target = null, + ?\DateTimeInterface $action_timestamp = null) { parent::__construct(); - if (!in_array($type, [self::TYPE_ADD, self::TYPE_WITHDRAW, self::TYPE_MOVE], true)) { - throw new \InvalidArgumentException('Invalid type for PartStockChangedLogEntry!'); - } - //Same as every other element change log entry - $this->level = self::LEVEL_INFO; + $this->level = LogLevel::INFO; $this->setTargetElement($lot); - - $this->typeString = 'part_stock_changed'; $this->extra = array_merge($this->extra, [ - 't' => $this->typeToShortType($type), + 't' => $type->toExtraShortType(), 'o' => $old_stock, 'n' => $new_stock, 'p' => $new_total_part_instock, ]); - if (!empty($comment)) { + if ($comment !== '') { $this->extra['c'] = mb_strimwidth($comment, 0, self::COMMENT_MAX_LENGTH, '...'); } - if ($move_to_target) { - if ($type !== self::TYPE_MOVE) { + if ($action_timestamp instanceof \DateTimeInterface) { + //The action timestamp is saved as an ISO 8601 string + $this->extra['a'] = $action_timestamp->format(\DateTimeInterface::ATOM); + } + + if ($move_to_target instanceof PartLot) { + if ($type !== PartStockChangeType::MOVE) { throw new \InvalidArgumentException('The move_to_target parameter can only be set if the type is "move"!'); } @@ -86,11 +83,12 @@ class PartStockChangedLogEntry extends AbstractLogEntry * @param float $new_stock The new stock of the lot. * @param float $new_total_part_instock The new total instock of the part. * @param string $comment The comment associated with the change. - * @return static + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. + * @return self */ - public static function add(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment): self + public static function add(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?\DateTimeInterface $action_timestamp = null): self { - return new self(self::TYPE_ADD, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment); + return new self(PartStockChangeType::ADD, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, action_timestamp: $action_timestamp); } /** @@ -100,11 +98,12 @@ class PartStockChangedLogEntry extends AbstractLogEntry * @param float $new_stock The new stock of the lot. * @param float $new_total_part_instock The new total instock of the part. * @param string $comment The comment associated with the change. - * @return static + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. + * @return self */ - public static function withdraw(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment): self + public static function withdraw(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?\DateTimeInterface $action_timestamp = null): self { - return new self(self::TYPE_WITHDRAW, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment); + return new self(PartStockChangeType::WITHDRAW, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, action_timestamp: $action_timestamp); } /** @@ -115,24 +114,25 @@ class PartStockChangedLogEntry extends AbstractLogEntry * @param float $new_total_part_instock The new total instock of the part. * @param string $comment The comment associated with the change. * @param PartLot $move_to_target The target lot. + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. + * @return self */ - public static function move(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, PartLot $move_to_target): self + public static function move(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, PartLot $move_to_target, ?\DateTimeInterface $action_timestamp = null): self { - return new self(self::TYPE_MOVE, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, $move_to_target); + return new self(PartStockChangeType::MOVE, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, $move_to_target, action_timestamp: $action_timestamp); } /** * Returns the instock change type of this entry - * @return string One of the TYPE_* constants. + * @return PartStockChangeType */ - public function getInstockChangeType(): string + public function getInstockChangeType(): PartStockChangeType { - return $this->shortTypeToType($this->extra['t']); + return PartStockChangeType::fromExtraShortType($this->extra['t']); } /** * Returns the old stock of the lot. - * @return float */ public function getOldStock(): float { @@ -141,7 +141,6 @@ class PartStockChangedLogEntry extends AbstractLogEntry /** * Returns the new stock of the lot. - * @return float */ public function getNewStock(): float { @@ -150,7 +149,6 @@ class PartStockChangedLogEntry extends AbstractLogEntry /** * Returns the new total instock of the part. - * @return float */ public function getNewTotalPartInstock(): float { @@ -159,7 +157,6 @@ class PartStockChangedLogEntry extends AbstractLogEntry /** * Returns the comment associated with the change. - * @return string */ public function getComment(): string { @@ -168,7 +165,6 @@ class PartStockChangedLogEntry extends AbstractLogEntry /** * Gets the difference between the old and the new stock value of the lot as a positive number. - * @return float */ public function getChangeAmount(): float { @@ -177,7 +173,6 @@ class PartStockChangedLogEntry extends AbstractLogEntry /** * Returns the target lot ID (where the instock was moved to) if the type is TYPE_MOVE. - * @return int|null */ public function getMoveToTargetID(): ?int { @@ -185,40 +180,16 @@ class PartStockChangedLogEntry extends AbstractLogEntry } /** - * Converts the human-readable type (TYPE_* consts) to the version stored in DB - * @param string $type - * @return string + * Returns the timestamp when this action was performed and not when the log entry was created. + * This is useful if the action happened in the past, and the log entry is created afterwards. + * If the timestamp is not set, null is returned. + * @return \DateTimeInterface|null */ - protected function typeToShortType(string $type): string + public function getActionTimestamp(): ?\DateTimeInterface { - switch ($type) { - case self::TYPE_ADD: - return 'a'; - case self::TYPE_WITHDRAW: - return 'w'; - case self::TYPE_MOVE: - return 'm'; - default: - throw new \InvalidArgumentException('Invalid type: '.$type); + if (!empty($this->extra['a'])) { + return \DateTimeImmutable::createFromFormat(\DateTimeInterface::ATOM, $this->extra['a']); } + return null; } - - /** - * Converts the short type stored in DB to the human-readable type (TYPE_* consts). - * @param string $short_type - * @return string - */ - protected function shortTypeToType(string $short_type): string - { - switch ($short_type) { - case 'a': - return self::TYPE_ADD; - case 'w': - return self::TYPE_WITHDRAW; - case 'm': - return self::TYPE_MOVE; - default: - throw new \InvalidArgumentException('Invalid short type: '.$short_type); - } - } -} \ No newline at end of file +} diff --git a/src/Entity/LogSystem/SecurityEventLogEntry.php b/src/Entity/LogSystem/SecurityEventLogEntry.php index 095996c2..12e8e65e 100644 --- a/src/Entity/LogSystem/SecurityEventLogEntry.php +++ b/src/Entity/LogSystem/SecurityEventLogEntry.php @@ -44,18 +44,17 @@ namespace App\Entity\LogSystem; use App\Entity\Base\AbstractDBElement; use App\Entity\UserSystem\User; use App\Events\SecurityEvents; +use App\Helpers\IPAnonymizer; use Doctrine\ORM\Mapping as ORM; use InvalidArgumentException; -use Symfony\Component\HttpFoundation\IpUtils; /** * This log entry is created when something security related to a user happens. - * - * @ORM\Entity() */ +#[ORM\Entity] class SecurityEventLogEntry extends AbstractLogEntry { - public const SECURITY_TYPE_MAPPING = [ + final public const SECURITY_TYPE_MAPPING = [ 0 => SecurityEvents::PASSWORD_CHANGED, 1 => SecurityEvents::PASSWORD_RESET, 2 => SecurityEvents::BACKUP_KEYS_RESET, @@ -65,15 +64,15 @@ class SecurityEventLogEntry extends AbstractLogEntry 6 => SecurityEvents::GOOGLE_DISABLED, 7 => SecurityEvents::TRUSTED_DEVICE_RESET, 8 => SecurityEvents::TFA_ADMIN_RESET, + 9 => SecurityEvents::USER_IMPERSONATED, ]; public function __construct(string $type, string $ip_address, bool $anonymize = true) { parent::__construct(); - $this->level = self::LEVEL_INFO; $this->setIPAddress($ip_address, $anonymize); $this->setEventType($type); - $this->level = self::LEVEL_NOTICE; + $this->level = LogLevel::NOTICE; } public function setTargetElement(?AbstractDBElement $element): AbstractLogEntry @@ -113,11 +112,11 @@ class SecurityEventLogEntry extends AbstractLogEntry { $key = $this->extra['e']; - return static::SECURITY_TYPE_MAPPING[$key] ?? 'unkown'; + return static::SECURITY_TYPE_MAPPING[$key] ?? 'unknown'; } /** - * Return the (anonymized) IP address used to login the user. + * Return the (anonymized) IP address used to log in the user. */ public function getIPAddress(): string { @@ -125,17 +124,17 @@ class SecurityEventLogEntry extends AbstractLogEntry } /** - * Sets the IP address used to login the user. + * Sets the IP address used to log in the user. * - * @param string $ip the IP address used to login the user - * @param bool $anonymize Anonymize the IP address (remove last block) to be GPDR compliant + * @param string $ip the IP address used to log in the user + * @param bool $anonymize Anonymize the IP address (remove last block) to be GDPR compliant * * @return $this */ public function setIPAddress(string $ip, bool $anonymize = true): self { if ($anonymize) { - $ip = IpUtils::anonymize($ip); + $ip = IPAnonymizer::anonymize($ip); } $this->extra['i'] = $ip; diff --git a/src/Entity/LogSystem/UserLoginLogEntry.php b/src/Entity/LogSystem/UserLoginLogEntry.php index 5d1733ac..0719a740 100644 --- a/src/Entity/LogSystem/UserLoginLogEntry.php +++ b/src/Entity/LogSystem/UserLoginLogEntry.php @@ -22,14 +22,14 @@ declare(strict_types=1); namespace App\Entity\LogSystem; +use App\Helpers\IPAnonymizer; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\HttpFoundation\IpUtils; + /** * This log entry is created when a user logs in. - * - * @ORM\Entity() */ +#[ORM\Entity] class UserLoginLogEntry extends AbstractLogEntry { protected string $typeString = 'user_login'; @@ -37,12 +37,12 @@ class UserLoginLogEntry extends AbstractLogEntry public function __construct(string $ip_address, bool $anonymize = true) { parent::__construct(); - $this->level = self::LEVEL_INFO; + $this->level = LogLevel::INFO; $this->setIPAddress($ip_address, $anonymize); } /** - * Return the (anonymized) IP address used to login the user. + * Return the (anonymized) IP address used to log in the user. */ public function getIPAddress(): string { @@ -50,17 +50,17 @@ class UserLoginLogEntry extends AbstractLogEntry } /** - * Sets the IP address used to login the user. + * Sets the IP address used to log in the user. * - * @param string $ip the IP address used to login the user - * @param bool $anonymize Anonymize the IP address (remove last block) to be GPDR compliant + * @param string $ip the IP address used to log in the user + * @param bool $anonymize Anonymize the IP address (remove last block) to be GDPR compliant * * @return $this */ public function setIPAddress(string $ip, bool $anonymize = true): self { if ($anonymize) { - $ip = IpUtils::anonymize($ip); + $ip = IPAnonymizer::anonymize($ip); } $this->extra['i'] = $ip; diff --git a/src/Entity/LogSystem/UserLogoutLogEntry.php b/src/Entity/LogSystem/UserLogoutLogEntry.php index d3abb931..f9f9a3dc 100644 --- a/src/Entity/LogSystem/UserLogoutLogEntry.php +++ b/src/Entity/LogSystem/UserLogoutLogEntry.php @@ -22,12 +22,10 @@ declare(strict_types=1); namespace App\Entity\LogSystem; +use App\Helpers\IPAnonymizer; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\HttpFoundation\IpUtils; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class UserLogoutLogEntry extends AbstractLogEntry { protected string $typeString = 'user_logout'; @@ -35,12 +33,12 @@ class UserLogoutLogEntry extends AbstractLogEntry public function __construct(string $ip_address, bool $anonymize = true) { parent::__construct(); - $this->level = self::LEVEL_INFO; + $this->level = LogLevel::INFO; $this->setIPAddress($ip_address, $anonymize); } /** - * Return the (anonymized) IP address used to login the user. + * Return the (anonymized) IP address used to log in the user. */ public function getIPAddress(): string { @@ -48,17 +46,17 @@ class UserLogoutLogEntry extends AbstractLogEntry } /** - * Sets the IP address used to login the user. + * Sets the IP address used to log in the user. * - * @param string $ip the IP address used to login the user - * @param bool $anonymize Anonymize the IP address (remove last block) to be GPDR compliant + * @param string $ip the IP address used to log in the user + * @param bool $anonymize Anonymize the IP address (remove last block) to be GDPR compliant * * @return $this */ public function setIPAddress(string $ip, bool $anonymize = true): self { if ($anonymize) { - $ip = IpUtils::anonymize($ip); + $ip = IPAnonymizer::anonymize($ip); } $this->extra['i'] = $ip; diff --git a/src/Entity/LogSystem/UserNotAllowedLogEntry.php b/src/Entity/LogSystem/UserNotAllowedLogEntry.php index 5d4e3acd..c570a012 100644 --- a/src/Entity/LogSystem/UserNotAllowedLogEntry.php +++ b/src/Entity/LogSystem/UserNotAllowedLogEntry.php @@ -24,9 +24,7 @@ namespace App\Entity\LogSystem; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class UserNotAllowedLogEntry extends AbstractLogEntry { protected string $typeString = 'user_not_allowed'; @@ -34,7 +32,7 @@ class UserNotAllowedLogEntry extends AbstractLogEntry public function __construct(string $path) { parent::__construct(); - $this->level = static::LEVEL_WARNING; + $this->level = LogLevel::WARNING; $this->extra['a'] = $path; } diff --git a/src/Entity/OAuthToken.php b/src/Entity/OAuthToken.php new file mode 100644 index 00000000..bc692369 --- /dev/null +++ b/src/Entity/OAuthToken.php @@ -0,0 +1,151 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity; + +use App\Entity\Base\AbstractNamedDBElement; +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping as ORM; +use League\OAuth2\Client\Token\AccessTokenInterface; + +/** + * This entity represents a OAuth token pair (access and refresh token), for an application + */ +#[ORM\Entity] +#[ORM\Table(name: 'oauth_tokens')] +#[ORM\UniqueConstraint(name: 'oauth_tokens_unique_name', columns: ['name'])] +#[ORM\Index(columns: ['name'], name: 'oauth_tokens_name_idx')] +class OAuthToken extends AbstractNamedDBElement implements AccessTokenInterface +{ + /** @var string|null The short-term usable OAuth2 token */ + #[ORM\Column(type: Types::TEXT, nullable: true)] + private ?string $token = null; + + /** @var \DateTimeImmutable|null The date when the token expires */ + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + private ?\DateTimeImmutable $expires_at = null; + + /** @var string|null The refresh token for the OAuth2 auth */ + #[ORM\Column(type: Types::TEXT, nullable: true)] + private ?string $refresh_token = null; + + /** + * The default expiration time for a authorization token, if no expiration time is given + */ + private const DEFAULT_EXPIRATION_TIME = 3600; + + public function __construct(string $name, ?string $refresh_token, ?string $token = null, ?\DateTimeImmutable $expires_at = null) + { + //If token is given, you also have to give the expires_at date + if ($token !== null && $expires_at === null) { + throw new \InvalidArgumentException('If you give a token, you also have to give the expires_at date'); + } + + //If no refresh_token is given, the token is a client credentials grant token, which must have a token + if ($refresh_token === null && $token === null) { + throw new \InvalidArgumentException('If you give no refresh_token, you have to give a token!'); + } + + $this->name = $name; + $this->refresh_token = $refresh_token; + $this->expires_at = $expires_at; + $this->token = $token; + } + + public static function fromAccessToken(AccessTokenInterface $accessToken, string $name): self + { + return new self( + $name, + $accessToken->getRefreshToken(), + $accessToken->getToken(), + self::unixTimestampToDatetime($accessToken->getExpires() ?? time() + self::DEFAULT_EXPIRATION_TIME) + ); + } + + private static function unixTimestampToDatetime(int $timestamp): \DateTimeImmutable + { + return \DateTimeImmutable::createFromFormat('U', (string)$timestamp); + } + + public function getToken(): ?string + { + return $this->token; + } + + public function getExpirationDate(): ?\DateTimeImmutable + { + return $this->expires_at; + } + + public function getRefreshToken(): string + { + return $this->refresh_token; + } + + public function isExpired(): bool + { + //null token is always expired + if ($this->token === null) { + return true; + } + + if ($this->expires_at === null) { + return false; + } + + return $this->expires_at->getTimestamp() < time(); + } + + /** + * Returns true if this token is a client credentials grant token (meaning it has no refresh token), and + * needs to be refreshed via the client credentials grant. + * @return bool + */ + public function isClientCredentialsGrant(): bool + { + return $this->refresh_token === null; + } + + public function replaceWithNewToken(AccessTokenInterface $accessToken): void + { + $this->token = $accessToken->getToken(); + $this->refresh_token = $accessToken->getRefreshToken(); + //If no expiration date is given, we set it to the default expiration time + $this->expires_at = self::unixTimestampToDatetime($accessToken->getExpires() ?? time() + self::DEFAULT_EXPIRATION_TIME); + } + + public function getExpires(): ?int + { + return $this->expires_at->getTimestamp(); + } + + public function hasExpired(): bool + { + return $this->isExpired(); + } + + public function getValues(): array + { + return []; + } +} \ No newline at end of file diff --git a/src/Entity/Parameters/AbstractParameter.php b/src/Entity/Parameters/AbstractParameter.php index 5a3f00e3..edcedc3e 100644 --- a/src/Entity/Parameters/AbstractParameter.php +++ b/src/Entity/Parameters/AbstractParameter.php @@ -41,100 +41,143 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Doctrine\Orm\Filter\RangeFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use App\ApiPlatform\Filter\LikeFilter; +use App\Repository\ParameterRepository; +use App\Validator\UniqueValidatableInterface; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractNamedDBElement; use Doctrine\ORM\Mapping as ORM; use InvalidArgumentException; use LogicException; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Serializer\Attribute\DiscriminatorMap; use Symfony\Component\Validator\Constraints as Assert; use function sprintf; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @ORM\Table("parameters", indexes={ - * @ORM\Index(name="parameter_name_idx", columns={"name"}), - * @ORM\Index(name="parameter_group_idx", columns={"param_group"}), - * @ORM\Index(name="parameter_type_element_idx", columns={"type", "element_id"}) - * }) - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="type", type="smallint") - * @ORM\DiscriminatorMap({ - * 0 = "CategoryParameter", - * 1 = "CurrencyParameter", - * 2 = "ProjectParameter", - * 3 = "FootprintParameter", - * 4 = "GroupParameter", - * 5 = "ManufacturerParameter", - * 6 = "MeasurementUnitParameter", - * 7 = "PartParameter", - * 8 = "StorelocationParameter", - * 9 = "SupplierParameter", - * 10 = "AttachmentTypeParameter" - * }) - */ -abstract class AbstractParameter extends AbstractNamedDBElement +#[ORM\Entity(repositoryClass: ParameterRepository::class)] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'smallint')] +#[ORM\DiscriminatorMap([0 => CategoryParameter::class, 1 => CurrencyParameter::class, 2 => ProjectParameter::class, + 3 => FootprintParameter::class, 4 => GroupParameter::class, 5 => ManufacturerParameter::class, + 6 => MeasurementUnitParameter::class, 7 => PartParameter::class, 8 => StorageLocationParameter::class, + 9 => SupplierParameter::class, 10 => AttachmentTypeParameter::class])] +#[ORM\Table('parameters')] +#[ORM\Index(columns: ['name'], name: 'parameter_name_idx')] +#[ORM\Index(columns: ['param_group'], name: 'parameter_group_idx')] +#[ORM\Index(columns: ['type', 'element_id'], name: 'parameter_type_element_idx')] +#[ApiResource( + shortName: 'Parameter', + operations: [ + new Get(security: 'is_granted("read", object)'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['parameter:read', 'parameter:read:standalone', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['parameter:write', 'parameter:write:standalone', 'api:basic:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiFilter(LikeFilter::class, properties: ["name", "symbol", "unit", "group", "value_text"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(RangeFilter::class, properties: ["value_min", "value_typical", "value_max"])] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] +//This discriminator map is required for API platform to know which class to use for deserialization, when creating a new parameter. +#[DiscriminatorMap(typeProperty: '_type', mapping: self::API_DISCRIMINATOR_MAP)] +abstract class AbstractParameter extends AbstractNamedDBElement implements UniqueValidatableInterface { + + /* + * The discriminator map used for API platform. The key should be the same as the api platform short type (the @type JSONLD field). + */ + private const API_DISCRIMINATOR_MAP = ["Part" => PartParameter::class, + "AttachmentType" => AttachmentTypeParameter::class, "Category" => CategoryParameter::class, "Currency" => CurrencyParameter::class, + "Project" => ProjectParameter::class, "Footprint" => FootprintParameter::class, "Group" => GroupParameter::class, + "Manufacturer" => ManufacturerParameter::class, "MeasurementUnit" => MeasurementUnitParameter::class, + "StorageLocation" => StorageLocationParameter::class, "Supplier" => SupplierParameter::class]; + /** * @var string The class of the element that can be passed to this attachment. Must be overridden in subclasses. */ - public const ALLOWED_ELEMENT_CLASS = ''; + protected const ALLOWED_ELEMENT_CLASS = ''; /** * @var string The mathematical symbol for this specification. Can be rendered pretty later. Should be short - * @Assert\Length(max=20) - * @ORM\Column(type="string", nullable=false) */ + #[Assert\Length(max: 20)] + #[Groups(['full', 'parameter:read', 'parameter:write', 'import'])] + #[ORM\Column(type: Types::STRING)] protected string $symbol = ''; /** * @var float|null the guaranteed minimum value of this property - * @Assert\Type({"float","null"}) - * @Assert\LessThanOrEqual(propertyPath="value_typical", message="parameters.validator.min_lesser_typical") - * @Assert\LessThan(propertyPath="value_max", message="parameters.validator.min_lesser_max") - * @ORM\Column(type="float", nullable=true) */ + #[Assert\Type(['float', null])] + #[Assert\LessThanOrEqual(propertyPath: 'value_typical', message: 'parameters.validator.min_lesser_typical')] + #[Assert\LessThan(propertyPath: 'value_max', message: 'parameters.validator.min_lesser_max')] + #[Groups(['full', 'parameter:read', 'parameter:write', 'import'])] + #[ORM\Column(type: Types::FLOAT, nullable: true)] protected ?float $value_min = null; /** * @var float|null the typical value of this property - * @Assert\Type({"null", "float"}) - * @ORM\Column(type="float", nullable=true) */ + #[Assert\Type([null, 'float'])] + #[Groups(['full', 'parameter:read', 'parameter:write', 'import'])] + #[ORM\Column(type: Types::FLOAT, nullable: true)] protected ?float $value_typical = null; /** * @var float|null the maximum value of this property - * @Assert\Type({"float", "null"}) - * @Assert\GreaterThanOrEqual(propertyPath="value_typical", message="parameters.validator.max_greater_typical") - * @ORM\Column(type="float", nullable=true) */ + #[Assert\Type(['float', null])] + #[Assert\GreaterThanOrEqual(propertyPath: 'value_typical', message: 'parameters.validator.max_greater_typical')] + #[Groups(['full', 'parameter:read', 'parameter:write', 'import'])] + #[ORM\Column(type: Types::FLOAT, nullable: true)] protected ?float $value_max = null; /** * @var string The unit in which the value values are given (e.g. V) - * @ORM\Column(type="string", nullable=false) */ + #[Groups(['full', 'parameter:read', 'parameter:write', 'import'])] + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 50)] protected string $unit = ''; /** * @var string a text value for the given property - * @ORM\Column(type="string", nullable=false) */ + #[Groups(['full', 'parameter:read', 'parameter:write', 'import'])] + #[ORM\Column(type: Types::STRING)] + #[Assert\Length(max: 255)] protected string $value_text = ''; /** * @var string the group this parameter belongs to - * @ORM\Column(type="string", nullable=false, name="param_group") */ + #[Groups(['full', 'parameter:read', 'parameter:write', 'import'])] + #[ORM\Column(name: 'param_group', type: Types::STRING)] + #[Assert\Length(max: 255)] protected string $group = ''; /** - * Mapping is done in sub classes. + * Mapping is done in subclasses. * * @var AbstractDBElement|null the element to which this parameter belongs to */ - protected $element; + #[Groups(['parameter:read:standalone', 'parameter:write:standalone'])] + protected ?AbstractDBElement $element = null; public function __construct() { @@ -163,7 +206,9 @@ abstract class AbstractParameter extends AbstractNamedDBElement * Return a formatted string version of the values of the string. * Based on the set values it can return something like this: 34 V (12 V ... 50 V) [Text]. */ - public function getFormattedValue(): string + #[Groups(['parameter:read', 'full'])] + #[SerializedName('formatted')] + public function getFormattedValue(bool $latex_formatted = false): string { //If we just only have text value, return early if (null === $this->value_typical && null === $this->value_min && null === $this->value_max) { @@ -173,7 +218,7 @@ abstract class AbstractParameter extends AbstractNamedDBElement $str = ''; $bracket_opened = false; if ($this->value_typical) { - $str .= $this->getValueTypicalWithUnit(); + $str .= $this->getValueTypicalWithUnit($latex_formatted); if ($this->value_min || $this->value_max) { $bracket_opened = true; $str .= ' ('; @@ -181,11 +226,11 @@ abstract class AbstractParameter extends AbstractNamedDBElement } if ($this->value_max && $this->value_min) { - $str .= $this->getValueMinWithUnit().' ... '.$this->getValueMaxWithUnit(); + $str .= $this->getValueMinWithUnit($latex_formatted).' ... '.$this->getValueMaxWithUnit($latex_formatted); } elseif ($this->value_max) { - $str .= 'max. '.$this->getValueMaxWithUnit(); + $str .= 'max. '.$this->getValueMaxWithUnit($latex_formatted); } elseif ($this->value_min) { - $str .= 'min. '.$this->getValueMinWithUnit(); + $str .= 'min. '.$this->getValueMinWithUnit($latex_formatted); } //Add closing bracket @@ -193,7 +238,7 @@ abstract class AbstractParameter extends AbstractNamedDBElement $str .= ')'; } - if ($this->value_text) { + if ($this->value_text !== '' && $this->value_text !== '0') { $str .= ' ['.$this->value_text.']'; } @@ -299,31 +344,30 @@ abstract class AbstractParameter extends AbstractNamedDBElement /** * Return a formatted version with the minimum value with the unit of this parameter. */ - public function getValueTypicalWithUnit(): string + public function getValueTypicalWithUnit(bool $with_latex = false): string { - return $this->formatWithUnit($this->value_typical); + return $this->formatWithUnit($this->value_typical, with_latex: $with_latex); } /** * Return a formatted version with the maximum value with the unit of this parameter. */ - public function getValueMaxWithUnit(): string + public function getValueMaxWithUnit(bool $with_latex = false): string { - return $this->formatWithUnit($this->value_max); + return $this->formatWithUnit($this->value_max, with_latex: $with_latex); } /** * Return a formatted version with the typical value with the unit of this parameter. */ - public function getValueMinWithUnit(): string + public function getValueMinWithUnit(bool $with_latex = false): string { - return $this->formatWithUnit($this->value_min); + return $this->formatWithUnit($this->value_min, with_latex: $with_latex); } /** * Sets the typical value of this property. * - * @param float|null $value_typical * * @return $this */ @@ -397,13 +441,34 @@ abstract class AbstractParameter extends AbstractNamedDBElement /** * Return a string representation and (if possible) with its unit. */ - protected function formatWithUnit(float $value, string $format = '%g'): string + protected function formatWithUnit(float $value, string $format = '%g', bool $with_latex = false): string { $str = sprintf($format, $value); - if (!empty($this->unit)) { - return $str.' '.$this->unit; + if ($this->unit !== '') { + + if (!$with_latex) { + $unit = $this->unit; + } else { + $unit = '$\mathrm{'.$this->unit.'}$'; + } + + return $str.' '.$unit; } return $str; } + + /** + * Returns the class of the element that is allowed to be associated with this attachment. + * @return string + */ + public function getElementClass(): string + { + return static::ALLOWED_ELEMENT_CLASS; + } + + public function getComparableFields(): array + { + return ['name' => $this->name, 'group' => $this->group, 'element' => $this->element?->getId()]; + } } diff --git a/src/Entity/Parameters/AttachmentTypeParameter.php b/src/Entity/Parameters/AttachmentTypeParameter.php index aa39a9a6..9a272a7d 100644 --- a/src/Entity/Parameters/AttachmentTypeParameter.php +++ b/src/Entity/Parameters/AttachmentTypeParameter.php @@ -42,20 +42,23 @@ declare(strict_types=1); namespace App\Entity\Parameters; use App\Entity\Attachments\AttachmentType; +use App\Entity\Base\AbstractDBElement; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class AttachmentTypeParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = AttachmentType::class; + final public const ALLOWED_ELEMENT_CLASS = AttachmentType::class; /** * @var AttachmentType the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Attachments\AttachmentType", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: AttachmentType::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/CategoryParameter.php b/src/Entity/Parameters/CategoryParameter.php index f2ce3807..ecab1740 100644 --- a/src/Entity/Parameters/CategoryParameter.php +++ b/src/Entity/Parameters/CategoryParameter.php @@ -41,21 +41,24 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\Parts\Category; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class CategoryParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Category::class; + final public const ALLOWED_ELEMENT_CLASS = Category::class; /** * @var Category the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Category", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: Category::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/CurrencyParameter.php b/src/Entity/Parameters/CurrencyParameter.php index c5fa1e2a..9ab09bed 100644 --- a/src/Entity/Parameters/CurrencyParameter.php +++ b/src/Entity/Parameters/CurrencyParameter.php @@ -41,24 +41,28 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\PriceInformations\Currency; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * A attachment attached to a category element. - * - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) + * An attachment attached to a category element. */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class CurrencyParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Currency::class; + final public const ALLOWED_ELEMENT_CLASS = Currency::class; /** * @var Currency the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\PriceInformations\Currency", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: Currency::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/FootprintParameter.php b/src/Entity/Parameters/FootprintParameter.php index 9c682720..578ddef3 100644 --- a/src/Entity/Parameters/FootprintParameter.php +++ b/src/Entity/Parameters/FootprintParameter.php @@ -41,22 +41,25 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\Parts\Footprint; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class FootprintParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Footprint::class; + final public const ALLOWED_ELEMENT_CLASS = Footprint::class; /** * @var Footprint the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Footprint", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: Footprint::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/GroupParameter.php b/src/Entity/Parameters/GroupParameter.php index aa15edb8..7fb5540f 100644 --- a/src/Entity/Parameters/GroupParameter.php +++ b/src/Entity/Parameters/GroupParameter.php @@ -41,22 +41,25 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\UserSystem\Group; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class GroupParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Group::class; + final public const ALLOWED_ELEMENT_CLASS = Group::class; /** * @var Group the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\Group", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: Group::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/ManufacturerParameter.php b/src/Entity/Parameters/ManufacturerParameter.php index 1f01ce4d..883a78f4 100644 --- a/src/Entity/Parameters/ManufacturerParameter.php +++ b/src/Entity/Parameters/ManufacturerParameter.php @@ -41,22 +41,25 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\Parts\Manufacturer; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class ManufacturerParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Manufacturer::class; + final public const ALLOWED_ELEMENT_CLASS = Manufacturer::class; /** * @var Manufacturer the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Manufacturer", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: Manufacturer::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/MeasurementUnitParameter.php b/src/Entity/Parameters/MeasurementUnitParameter.php index 7ca4b1c5..09ff81ec 100644 --- a/src/Entity/Parameters/MeasurementUnitParameter.php +++ b/src/Entity/Parameters/MeasurementUnitParameter.php @@ -41,22 +41,25 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\Parts\MeasurementUnit; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class MeasurementUnitParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = MeasurementUnit::class; + final public const ALLOWED_ELEMENT_CLASS = MeasurementUnit::class; /** * @var MeasurementUnit the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\MeasurementUnit", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: MeasurementUnit::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/ParametersTrait.php b/src/Entity/Parameters/ParametersTrait.php index c7ccbddf..2ccaa763 100644 --- a/src/Entity/Parameters/ParametersTrait.php +++ b/src/Entity/Parameters/ParametersTrait.php @@ -44,20 +44,25 @@ namespace App\Entity\Parameters; use Doctrine\Common\Collections\Collection; use Symfony\Component\Validator\Constraints as Assert; +/** + * @template-covariant T of AbstractParameter + */ trait ParametersTrait { /** * Mapping done in subclasses. * * @var Collection - * @Assert\Valid() + * @phpstan-var Collection */ - protected $parameters; + #[Assert\Valid] + protected Collection $parameters; /** * Return all associated specifications. + * @return Collection + * @phpstan-return Collection * - * @psalm-return Collection */ public function getParameters(): Collection { @@ -66,7 +71,7 @@ trait ParametersTrait /** * Add a new parameter information. - * + * @phpstan-param T $parameter * @return $this */ public function addParameter(AbstractParameter $parameter): self @@ -77,6 +82,9 @@ trait ParametersTrait return $this; } + /** + * @phpstan-param T $parameter + */ public function removeParameter(AbstractParameter $parameter): self { $this->parameters->removeElement($parameter); @@ -84,6 +92,10 @@ trait ParametersTrait return $this; } + /** + * @return array> + * @phpstan-return array> + */ public function getGroupedParameters(): array { $tmp = []; diff --git a/src/Entity/Parameters/PartParameter.php b/src/Entity/Parameters/PartParameter.php index 45c566d9..91b51c00 100644 --- a/src/Entity/Parameters/PartParameter.php +++ b/src/Entity/Parameters/PartParameter.php @@ -41,22 +41,28 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\Parts\Part; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; /** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) + * @see \App\Tests\Entity\Parameters\PartParameterTest */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class PartParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Part::class; + final public const ALLOWED_ELEMENT_CLASS = Part::class; /** * @var Part the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Part", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/ProjectParameter.php b/src/Entity/Parameters/ProjectParameter.php index 2961a843..7c3907cd 100644 --- a/src/Entity/Parameters/ProjectParameter.php +++ b/src/Entity/Parameters/ProjectParameter.php @@ -41,22 +41,25 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\ProjectSystem\Project; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class ProjectParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Project::class; + final public const ALLOWED_ELEMENT_CLASS = Project::class; /** * @var Project the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\ProjectSystem\Project", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). */ - protected $element; + #[ORM\ManyToOne(targetEntity: Project::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/StorelocationParameter.php b/src/Entity/Parameters/StorageLocationParameter.php similarity index 68% rename from src/Entity/Parameters/StorelocationParameter.php rename to src/Entity/Parameters/StorageLocationParameter.php index 2a7aa202..f5cc6415 100644 --- a/src/Entity/Parameters/StorelocationParameter.php +++ b/src/Entity/Parameters/StorageLocationParameter.php @@ -41,22 +41,25 @@ declare(strict_types=1); namespace App\Entity\Parameters; -use App\Entity\Parts\Storelocation; +use App\Entity\Base\AbstractDBElement; +use App\Entity\Parts\StorageLocation; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ -class StorelocationParameter extends AbstractParameter +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] +class StorageLocationParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Storelocation::class; + final public const ALLOWED_ELEMENT_CLASS = StorageLocation::class; /** - * @var Storelocation the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Storelocation", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var StorageLocation the element this para is associated with */ - protected $element; + #[ORM\ManyToOne(targetEntity: StorageLocation::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parameters/SupplierParameter.php b/src/Entity/Parameters/SupplierParameter.php index a40e3e92..6e42206f 100644 --- a/src/Entity/Parameters/SupplierParameter.php +++ b/src/Entity/Parameters/SupplierParameter.php @@ -41,22 +41,25 @@ declare(strict_types=1); namespace App\Entity\Parameters; +use App\Entity\Base\AbstractDBElement; use App\Entity\Parts\Supplier; +use App\Repository\ParameterRepository; +use App\Serializer\APIPlatform\OverrideClassDenormalizer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Attribute\Context; -/** - * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository") - * @UniqueEntity(fields={"name", "group", "element"}) - */ +#[UniqueEntity(fields: ['name', 'group', 'element'])] +#[ORM\Entity(repositoryClass: ParameterRepository::class)] class SupplierParameter extends AbstractParameter { - public const ALLOWED_ELEMENT_CLASS = Supplier::class; + final public const ALLOWED_ELEMENT_CLASS = Supplier::class; /** - * @var Supplier the element this para is associated with - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Supplier", inversedBy="parameters") - * @ORM\JoinColumn(name="element_id", referencedColumnName="id", nullable=false, onDelete="CASCADE"). + * @var Supplier the element this parameter is associated with */ - protected $element; + #[ORM\ManyToOne(targetEntity: Supplier::class, inversedBy: 'parameters')] + #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])] + protected ?AbstractDBElement $element = null; } diff --git a/src/Entity/Parts/AssociationType.php b/src/Entity/Parts/AssociationType.php new file mode 100644 index 00000000..52a56af2 --- /dev/null +++ b/src/Entity/Parts/AssociationType.php @@ -0,0 +1,46 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\Parts; + +/** + * The values of this enums are used to describe how two parts are associated with each other. + */ +enum AssociationType: int +{ + /** A user definable association type, which can be described in the comment field */ + case OTHER = 0; + /** The owning part is compatible with the other part */ + case COMPATIBLE = 1; + /** The owning part supersedes the other part (owner is newer version) */ + case SUPERSEDES = 2; + + /** + * Returns the translation key for this association type. + * @return string + */ + public function getTranslationKey(): string + { + return 'part_association.type.' . strtolower($this->name); + } +} diff --git a/src/Entity/Parts/Category.php b/src/Entity/Parts/Category.php index eac47877..99ed3c6d 100644 --- a/src/Entity/Parts/Category.php +++ b/src/Entity/Parts/Category.php @@ -22,107 +22,190 @@ declare(strict_types=1); namespace App\Entity\Parts; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Entity\EDA\EDACategoryInfo; +use App\Repository\Parts\CategoryRepository; +use Doctrine\DBAL\Types\Types; +use Doctrine\Common\Collections\ArrayCollection; use App\Entity\Attachments\CategoryAttachment; use App\Entity\Base\AbstractPartsContainingDBElement; +use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Parameters\CategoryParameter; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; /** - * Class AttachmentType. + * This entity describes a category, a part can belong to, which is used to group parts by their function. * - * @ORM\Entity(repositoryClass="App\Repository\Parts\CategoryRepository") - * @ORM\Table(name="`categories`", indexes={ - * @ORM\Index(name="category_idx_name", columns={"name"}), - * @ORM\Index(name="category_idx_parent_name", columns={"parent_id", "name"}), - * }) + * @extends AbstractPartsContainingDBElement */ +#[ORM\Entity(repositoryClass: CategoryRepository::class)] +#[ORM\Table(name: '`categories`')] +#[ORM\Index(columns: ['name'], name: 'category_idx_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'category_idx_parent_name')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@categories.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['category:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['category:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/categories/{id}/children.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the children elements of a category.'), + security: 'is_granted("@categories.read")' + ) + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: Category::class) + ], + normalizationContext: ['groups' => ['category:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class Category extends AbstractPartsContainingDBElement { - /** - * @ORM\OneToMany(targetEntity="Category", mappedBy="parent") - * @ORM\OrderBy({"name" = "ASC"}) - * @var Collection - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; + + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['category:read', 'category:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; + + #[Groups(['category:read', 'category:write'])] + protected string $comment = ''; /** - * @ORM\ManyToOne(targetEntity="Category", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") - */ - protected $parent; - - /** - * @var string - * @ORM\Column(type="text") + * @var string The hint which is shown as hint under the partname field, when a part is created in this category. */ + #[Groups(['full', 'import', 'category:read', 'category:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $partname_hint = ''; /** - * @var string - * @ORM\Column(type="text") + * @var string The regular expression which is used to validate the partname of a part in this category. */ + #[Groups(['full', 'import', 'category:read', 'category:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $partname_regex = ''; /** - * @var bool - * @ORM\Column(type="boolean") + * @var bool Set to true, if the footprints should be disabled for parts this category (not implemented yet). */ + #[Groups(['full', 'import', 'category:read', 'category:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $disable_footprints = false; /** - * @var bool - * @ORM\Column(type="boolean") + * @var bool Set to true, if the manufacturers should be disabled for parts this category (not implemented yet). */ + #[Groups(['full', 'import', 'category:read', 'category:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $disable_manufacturers = false; /** - * @var bool - * @ORM\Column(type="boolean") + * @var bool Set to true, if the autodatasheets should be disabled for parts this category (not implemented yet). */ + #[Groups(['full', 'import', 'category:read', 'category:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $disable_autodatasheets = false; /** - * @var bool - * @ORM\Column(type="boolean") + * @var bool Set to true, if the properties should be disabled for parts this category (not implemented yet). */ + #[Groups(['full', 'import', 'category:read', 'category:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $disable_properties = false; /** - * @var string - * @ORM\Column(type="text") + * @var string The default description for parts in this category. */ + #[Groups(['full', 'import', 'category:read', 'category:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $default_description = ''; /** - * @var string - * @ORM\Column(type="text") + * @var string The default comment for parts in this category. */ + #[Groups(['full', 'import', 'category:read', 'category:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $default_comment = ''; + /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\CategoryAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[Groups(['full', 'category:read', 'category:write'])] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: CategoryAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: CategoryAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['category:read', 'category:write'])] + protected ?Attachment $master_picture_attachment = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\CategoryParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() */ - protected $parameters; + #[Assert\Valid] + #[Groups(['full', 'category:read', 'category:write'])] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: CategoryParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + protected Collection $parameters; + + #[Groups(['category:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['category:read'])] + protected ?\DateTimeImmutable $lastModified = null; + + #[Assert\Valid] + #[ORM\Embedded(class: EDACategoryInfo::class)] + #[Groups(['full', 'category:read', 'category:write'])] + protected EDACategoryInfo $eda_info; + + public function __construct() + { + parent::__construct(); + $this->children = new ArrayCollection(); + $this->attachments = new ArrayCollection(); + $this->parameters = new ArrayCollection(); + $this->eda_info = new EDACategoryInfo(); + } public function getPartnameHint(): string { return $this->partname_hint; } - /** - * @return Category - */ public function setPartnameHint(string $partname_hint): self { $this->partname_hint = $partname_hint; @@ -135,9 +218,6 @@ class Category extends AbstractPartsContainingDBElement return $this->partname_regex; } - /** - * @return Category - */ public function setPartnameRegex(string $partname_regex): self { $this->partname_regex = $partname_regex; @@ -150,9 +230,6 @@ class Category extends AbstractPartsContainingDBElement return $this->disable_footprints; } - /** - * @return Category - */ public function setDisableFootprints(bool $disable_footprints): self { $this->disable_footprints = $disable_footprints; @@ -165,9 +242,6 @@ class Category extends AbstractPartsContainingDBElement return $this->disable_manufacturers; } - /** - * @return Category - */ public function setDisableManufacturers(bool $disable_manufacturers): self { $this->disable_manufacturers = $disable_manufacturers; @@ -180,9 +254,6 @@ class Category extends AbstractPartsContainingDBElement return $this->disable_autodatasheets; } - /** - * @return Category - */ public function setDisableAutodatasheets(bool $disable_autodatasheets): self { $this->disable_autodatasheets = $disable_autodatasheets; @@ -195,9 +266,6 @@ class Category extends AbstractPartsContainingDBElement return $this->disable_properties; } - /** - * @return Category - */ public function setDisableProperties(bool $disable_properties): self { $this->disable_properties = $disable_properties; @@ -210,9 +278,6 @@ class Category extends AbstractPartsContainingDBElement return $this->default_description; } - /** - * @return Category - */ public function setDefaultDescription(string $default_description): self { $this->default_description = $default_description; @@ -225,13 +290,20 @@ class Category extends AbstractPartsContainingDBElement return $this->default_comment; } - /** - * @return Category - */ public function setDefaultComment(string $default_comment): self { $this->default_comment = $default_comment; + return $this; + } + public function getEdaInfo(): EDACategoryInfo + { + return $this->eda_info; + } + + public function setEdaInfo(EDACategoryInfo $eda_info): Category + { + $this->eda_info = $eda_info; return $this; } } diff --git a/src/Entity/Parts/Footprint.php b/src/Entity/Parts/Footprint.php index cac63fe2..6b043562 100644 --- a/src/Entity/Parts/Footprint.php +++ b/src/Entity/Parts/Footprint.php @@ -22,58 +22,135 @@ declare(strict_types=1); namespace App\Entity\Parts; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Entity\EDA\EDAFootprintInfo; +use App\Repository\Parts\FootprintRepository; +use App\Entity\Base\AbstractStructuralDBElement; +use Doctrine\Common\Collections\ArrayCollection; use App\Entity\Attachments\FootprintAttachment; use App\Entity\Base\AbstractPartsContainingDBElement; use App\Entity\Parameters\FootprintParameter; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; /** - * Class Footprint. + * This entity represents a footprint of a part (its physical dimensions and shape). * - * @ORM\Entity(repositoryClass="App\Repository\Parts\FootprintRepository") - * @ORM\Table("`footprints`", indexes={ - * @ORM\Index(name="footprint_idx_name", columns={"name"}), - * @ORM\Index(name="footprint_idx_parent_name", columns={"parent_id", "name"}), - * }) + * @extends AbstractPartsContainingDBElement */ +#[ORM\Entity(repositoryClass: FootprintRepository::class)] +#[ORM\Table('`footprints`')] +#[ORM\Index(columns: ['name'], name: 'footprint_idx_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'footprint_idx_parent_name')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@footprints.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['footprint:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['footprint:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/footprints/{id}/children.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the children elements of a footprint.'), + security: 'is_granted("@footprints.read")' + ) + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: Footprint::class) + ], + normalizationContext: ['groups' => ['footprint:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class Footprint extends AbstractPartsContainingDBElement { - /** - * @ORM\ManyToOne(targetEntity="Footprint", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") - */ - protected $parent; + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['footprint:read', 'footprint:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; - /** - * @ORM\OneToMany(targetEntity="Footprint", mappedBy="parent") - * @ORM\OrderBy({"name" = "ASC"}) - * @var Collection - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; + + #[Groups(['footprint:read', 'footprint:write'])] + protected string $comment = ''; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\FootprintAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: FootprintAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + #[Groups(['footprint:read', 'footprint:write'])] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: FootprintAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['footprint:read', 'footprint:write'])] + protected ?Attachment $master_picture_attachment = null; /** * @var FootprintAttachment|null - * @ORM\ManyToOne(targetEntity="App\Entity\Attachments\FootprintAttachment") - * @ORM\JoinColumn(name="id_footprint_3d", referencedColumnName="id") */ + #[ORM\ManyToOne(targetEntity: FootprintAttachment::class)] + #[ORM\JoinColumn(name: 'id_footprint_3d')] + #[Groups(['footprint:read', 'footprint:write'])] protected ?FootprintAttachment $footprint_3d = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\FootprintParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() */ - protected $parameters; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: FootprintParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[Groups(['footprint:read', 'footprint:write'])] + protected Collection $parameters; + + #[Groups(['footprint:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['footprint:read'])] + protected ?\DateTimeImmutable $lastModified = null; + + #[Assert\Valid] + #[ORM\Embedded(class: EDAFootprintInfo::class)] + #[Groups(['full', 'footprint:read', 'footprint:write'])] + protected EDAFootprintInfo $eda_info; + + public function __construct() + { + parent::__construct(); + $this->children = new ArrayCollection(); + $this->attachments = new ArrayCollection(); + $this->parameters = new ArrayCollection(); + $this->eda_info = new EDAFootprintInfo(); + } /**************************************** * Getters @@ -92,13 +169,10 @@ class Footprint extends AbstractPartsContainingDBElement * Setters * *********************************************************************************/ - /** * Sets the 3D Model associated with this footprint. * * @param FootprintAttachment|null $new_attachment The new 3D Model - * - * @return Footprint */ public function setFootprint3d(?FootprintAttachment $new_attachment): self { @@ -106,4 +180,15 @@ class Footprint extends AbstractPartsContainingDBElement return $this; } + + public function getEdaInfo(): EDAFootprintInfo + { + return $this->eda_info; + } + + public function setEdaInfo(EDAFootprintInfo $eda_info): Footprint + { + $this->eda_info = $eda_info; + return $this; + } } diff --git a/src/Entity/Parts/InfoProviderReference.php b/src/Entity/Parts/InfoProviderReference.php new file mode 100644 index 00000000..bfa62f32 --- /dev/null +++ b/src/Entity/Parts/InfoProviderReference.php @@ -0,0 +1,160 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\Parts; + +use App\Services\InfoProviderSystem\DTOs\SearchResultDTO; +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Embeddable; +use Symfony\Component\Serializer\Annotation\Groups; + +/** + * This class represents a reference to a info provider inside a part. + * @see \App\Tests\Entity\Parts\InfoProviderReferenceTest + */ +#[Embeddable] +class InfoProviderReference +{ + + /** @var string|null The key referencing the provider used to get this part, or null if it was not provided by a data provider */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['provider_reference:read', 'full'])] + private ?string $provider_key = null; + + /** @var string|null The id of this part inside the provider system or null if the part was not provided by a data provider */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['provider_reference:read', 'full'])] + private ?string $provider_id = null; + + /** + * @var string|null The url of this part inside the provider system or null if this info is not existing + */ + #[Column(type: Types::STRING, nullable: true)] + #[Groups(['provider_reference:read', 'full'])] + private ?string $provider_url = null; + + #[Column(type: Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])] + #[Groups(['provider_reference:read', 'full'])] + private ?\DateTimeImmutable $last_updated = null; + + /** + * Constructing is forbidden from outside. + */ + private function __construct() + { + + } + + /** + * Returns the key usable to identify the provider, which provided this part. Returns null, if the part was not created by a provider. + * @return string|null + */ + public function getProviderKey(): ?string + { + return $this->provider_key; + } + + /** + * Returns the id of this part inside the provider system or null if the part was not provided by a data provider. + * @return string|null + */ + public function getProviderId(): ?string + { + return $this->provider_id; + } + + /** + * Returns the url of this part inside the provider system or null if this info is not existing. + * @return string|null + */ + public function getProviderUrl(): ?string + { + return $this->provider_url; + } + + /** + * Gets the time, when the part was last time updated by the provider. + */ + public function getLastUpdated(): ?\DateTimeImmutable + { + return $this->last_updated; + } + + /** + * Returns true, if this part was created based on infos from a provider. + * Or false, if this part was created by a user manually. + * @return bool + */ + public function isProviderCreated(): bool + { + return $this->provider_key !== null; + } + + /** + * Creates a new instance, without any provider information. + * Use this for parts, which are created by a user manually. + * @return InfoProviderReference + */ + public static function noProvider(): self + { + $ref = new InfoProviderReference(); + $ref->provider_key = null; + $ref->provider_id = null; + $ref->provider_url = null; + $ref->last_updated = null; + return $ref; + } + + /** + * Creates a reference to an info provider based on the given parameters. + * @param string $provider_key + * @param string $provider_id + * @param string|null $provider_url + * @return self + */ + public static function providerReference(string $provider_key, string $provider_id, ?string $provider_url = null): self + { + $ref = new InfoProviderReference(); + $ref->provider_key = $provider_key; + $ref->provider_id = $provider_id; + $ref->provider_url = $provider_url; + $ref->last_updated = new \DateTimeImmutable(); + return $ref; + } + + /** + * Creates a reference to an info provider based on the given Part DTO + * @param SearchResultDTO $dto + * @return self + */ + public static function fromPartDTO(SearchResultDTO $dto): self + { + $ref = new InfoProviderReference(); + $ref->provider_key = $dto->provider_key; + $ref->provider_id = $dto->provider_id; + $ref->provider_url = $dto->provider_url; + $ref->last_updated = new \DateTimeImmutable(); + return $ref; + } +} \ No newline at end of file diff --git a/src/Entity/Parts/Manufacturer.php b/src/Entity/Parts/Manufacturer.php index 45e4d140..0edf8232 100644 --- a/src/Entity/Parts/Manufacturer.php +++ b/src/Entity/Parts/Manufacturer.php @@ -22,49 +22,112 @@ declare(strict_types=1); namespace App\Entity\Parts; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Repository\Parts\ManufacturerRepository; +use App\Entity\Base\AbstractStructuralDBElement; +use Doctrine\Common\Collections\ArrayCollection; use App\Entity\Attachments\ManufacturerAttachment; use App\Entity\Base\AbstractCompany; use App\Entity\Parameters\ManufacturerParameter; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; /** - * Class Manufacturer. + * This entity represents a manufacturer of a part (The company that produces the part). * - * @ORM\Entity(repositoryClass="App\Repository\Parts\ManufacturerRepository") - * @ORM\Table("`manufacturers`", indexes={ - * @ORM\Index(name="manufacturer_name", columns={"name"}), - * @ORM\Index(name="manufacturer_idx_parent_name", columns={"parent_id", "name"}), - * }) + * @extends AbstractCompany */ +#[ORM\Entity(repositoryClass: ManufacturerRepository::class)] +#[ORM\Table('`manufacturers`')] +#[ORM\Index(columns: ['name'], name: 'manufacturer_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'manufacturer_idx_parent_name')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@manufacturers.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['manufacturer:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['manufacturer:write', 'company:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/manufacturers/{id}/children.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the children elements of a manufacturer.'), + security: 'is_granted("@manufacturers.read")' + ) + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: Manufacturer::class) + ], + normalizationContext: ['groups' => ['manufacturer:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class Manufacturer extends AbstractCompany { - /** - * @ORM\ManyToOne(targetEntity="Manufacturer", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") - */ - protected $parent; + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['manufacturer:read', 'manufacturer:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; - /** - * @ORM\OneToMany(targetEntity="Manufacturer", mappedBy="parent") - * @ORM\OrderBy({"name" = "ASC"}) - * @var Collection - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\ManufacturerAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: ManufacturerAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + #[Groups(['manufacturer:read', 'manufacturer:write'])] + #[ApiProperty(readableLink: false, writableLink: true)] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: ManufacturerAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['manufacturer:read', 'manufacturer:write'])] + #[ApiProperty(readableLink: false, writableLink: true)] + protected ?Attachment $master_picture_attachment = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\ManufacturerParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() */ - protected $parameters; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: ManufacturerParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[Groups(['manufacturer:read', 'manufacturer:write'])] + #[ApiProperty(readableLink: false, writableLink: true)] + protected Collection $parameters; + public function __construct() + { + parent::__construct(); + $this->children = new ArrayCollection(); + $this->attachments = new ArrayCollection(); + $this->parameters = new ArrayCollection(); + } } diff --git a/src/Entity/Parts/ManufacturingStatus.php b/src/Entity/Parts/ManufacturingStatus.php new file mode 100644 index 00000000..2b6de800 --- /dev/null +++ b/src/Entity/Parts/ManufacturingStatus.php @@ -0,0 +1,53 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\Parts; + +enum ManufacturingStatus: string +{ + /** Part has been announced, but is not in production yet */ + case ANNOUNCED = 'announced'; + /** Part is in production and will be for the foreseeable future */ + case ACTIVE = 'active'; + /** Not recommended for new designs. */ + case NRFND = 'nrfnd'; + /** End of life: Part will become discontinued soon */ + case EOL = 'eol'; + /** Part is obsolete/discontinued by the manufacturer. */ + case DISCONTINUED = 'discontinued'; + + /** Status not set */ + case NOT_SET = ''; + + public function toTranslationKey(): string + { + return match ($this) { + self::ANNOUNCED => 'm_status.announced', + self::ACTIVE => 'm_status.active', + self::NRFND => 'm_status.nrfnd', + self::EOL => 'm_status.eol', + self::DISCONTINUED => 'm_status.discontinued', + self::NOT_SET => '', + }; + } +} \ No newline at end of file diff --git a/src/Entity/Parts/MeasurementUnit.php b/src/Entity/Parts/MeasurementUnit.php index 1110f287..6dd0b9f2 100644 --- a/src/Entity/Parts/MeasurementUnit.php +++ b/src/Entity/Parts/MeasurementUnit.php @@ -22,77 +22,144 @@ declare(strict_types=1); namespace App\Entity\Parts; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Repository\Parts\MeasurementUnitRepository; +use Doctrine\DBAL\Types\Types; +use App\Entity\Base\AbstractStructuralDBElement; +use Doctrine\Common\Collections\ArrayCollection; use App\Entity\Attachments\MeasurementUnitAttachment; use App\Entity\Base\AbstractPartsContainingDBElement; use App\Entity\Parameters\MeasurementUnitParameter; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Constraints\Length; /** * This unit represents the unit in which the amount of parts in stock are measured. * This could be something like N, grams, meters, etc... * - * @ORM\Entity(repositoryClass="App\Repository\Parts\MeasurementUnitRepository") - * @ORM\Table(name="`measurement_units`", indexes={ - * @ORM\Index(name="unit_idx_name", columns={"name"}), - * @ORM\Index(name="unit_idx_parent_name", columns={"parent_id", "name"}), - * }) - * @UniqueEntity("unit") + * @extends AbstractPartsContainingDBElement */ +#[UniqueEntity('unit')] +#[ORM\Entity(repositoryClass: MeasurementUnitRepository::class)] +#[ORM\Table(name: '`measurement_units`')] +#[ORM\Index(columns: ['name'], name: 'unit_idx_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'unit_idx_parent_name')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@measurement_units.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['measurement_unit:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['measurement_unit:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/measurement_units/{id}/children.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the children elements of a MeasurementUnit.'), + security: 'is_granted("@measurement_units.read")' + ) + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: MeasurementUnit::class) + ], + normalizationContext: ['groups' => ['measurement_unit:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "unit"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class MeasurementUnit extends AbstractPartsContainingDBElement { /** * @var string The unit symbol that should be used for the Unit. This could be something like "", g (for grams) * or m (for meters). - * @ORM\Column(type="string", name="unit", nullable=true) - * @Assert\Length(max=10) */ + #[Assert\Length(max: 10)] + #[Groups(['simple', 'extended', 'full', 'import', 'measurement_unit:read', 'measurement_unit:write'])] + #[ORM\Column(name: 'unit', type: Types::STRING, nullable: true)] protected ?string $unit = null; + #[Groups(['measurement_unit:read', 'measurement_unit:write'])] + protected string $comment = ''; + /** * @var bool Determines if the amount value associated with this unit should be treated as integer. * Set to false, to measure continuous sizes likes masses or lengths. - * @ORM\Column(type="boolean", name="is_integer") */ + #[Groups(['simple', 'extended', 'full', 'import', 'measurement_unit:read', 'measurement_unit:write'])] + #[ORM\Column(name: 'is_integer', type: Types::BOOLEAN)] protected bool $is_integer = false; /** * @var bool Determines if the unit can be used with SI Prefixes (kilo, giga, milli, etc.). * Useful for sizes like meters. For this the unit must be set - * @ORM\Column(type="boolean", name="use_si_prefix") - * @Assert\Expression("this.isUseSIPrefix() == false or this.getUnit() != null", message="validator.measurement_unit.use_si_prefix_needs_unit") */ + #[Assert\Expression('this.isUseSIPrefix() == false or this.getUnit() != null', message: 'validator.measurement_unit.use_si_prefix_needs_unit')] + #[Groups(['simple', 'full', 'import', 'measurement_unit:read', 'measurement_unit:write'])] + #[ORM\Column(name: 'use_si_prefix', type: Types::BOOLEAN)] protected bool $use_si_prefix = false; - /** - * @ORM\OneToMany(targetEntity="MeasurementUnit", mappedBy="parent", cascade={"persist"}) - * @ORM\OrderBy({"name" = "ASC"}) - * @var Collection - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class, cascade: ['persist'])] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; - /** - * @ORM\ManyToOne(targetEntity="MeasurementUnit", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") - */ - protected $parent; + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['measurement_unit:read', 'measurement_unit:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\MeasurementUnitAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: MeasurementUnitAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + #[Groups(['measurement_unit:read', 'measurement_unit:write'])] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: MeasurementUnitAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['measurement_unit:read', 'measurement_unit:write'])] + protected ?Attachment $master_picture_attachment = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\MeasurementUnitParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() */ - protected $parameters; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: MeasurementUnitParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[Groups(['measurement_unit:read', 'measurement_unit:write'])] + protected Collection $parameters; + + #[Groups(['measurement_unit:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['measurement_unit:read'])] + protected ?\DateTimeImmutable $lastModified = null; + /** * @return string @@ -102,11 +169,6 @@ class MeasurementUnit extends AbstractPartsContainingDBElement return $this->unit; } - /** - * @param string|null $unit - * - * @return MeasurementUnit - */ public function setUnit(?string $unit): self { $this->unit = $unit; @@ -119,9 +181,6 @@ class MeasurementUnit extends AbstractPartsContainingDBElement return $this->is_integer; } - /** - * @return MeasurementUnit - */ public function setIsInteger(bool $isInteger): self { $this->is_integer = $isInteger; @@ -134,13 +193,17 @@ class MeasurementUnit extends AbstractPartsContainingDBElement return $this->use_si_prefix; } - /** - * @return MeasurementUnit - */ public function setUseSIPrefix(bool $usesSIPrefixes): self { $this->use_si_prefix = $usesSIPrefixes; return $this; } + public function __construct() + { + parent::__construct(); + $this->children = new ArrayCollection(); + $this->attachments = new ArrayCollection(); + $this->parameters = new ArrayCollection(); + } } diff --git a/src/Entity/Parts/Part.php b/src/Entity/Parts/Part.php index bcd87e77..14a7903f 100644 --- a/src/Entity/Parts/Part.php +++ b/src/Entity/Parts/Part.php @@ -22,24 +22,46 @@ declare(strict_types=1); namespace App\Entity\Parts; +use App\ApiPlatform\Filter\TagFilter; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Doctrine\Orm\Filter\RangeFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\EntityFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\ApiPlatform\Filter\PartStoragelocationFilter; use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\PartAttachment; -use App\Entity\Parts\PartTraits\ProjectTrait; -use App\Entity\ProjectSystem\Project; +use App\Entity\EDA\EDAPartInfo; use App\Entity\Parameters\ParametersTrait; use App\Entity\Parameters\PartParameter; use App\Entity\Parts\PartTraits\AdvancedPropertyTrait; +use App\Entity\Parts\PartTraits\AssociationTrait; use App\Entity\Parts\PartTraits\BasicPropertyTrait; +use App\Entity\Parts\PartTraits\EDATrait; use App\Entity\Parts\PartTraits\InstockTrait; use App\Entity\Parts\PartTraits\ManufacturerTrait; use App\Entity\Parts\PartTraits\OrderTrait; -use App\Entity\ProjectSystem\ProjectBOMEntry; -use DateTime; +use App\Entity\Parts\PartTraits\ProjectTrait; +use App\EntityListeners\TreeCacheInvalidationListener; +use App\Repository\PartRepository; +use App\Validator\Constraints\UniqueObjectCollection; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; @@ -47,16 +69,41 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface; * Part class. * * The class properties are split over various traits in directory PartTraits. - * Otherwise this class would be too big, to be maintained. - * - * @ORM\Entity(repositoryClass="App\Repository\PartRepository") - * @ORM\Table("`parts`", indexes={ - * @ORM\Index(name="parts_idx_datet_name_last_id_needs", columns={"datetime_added", "name", "last_modified", "id", "needs_review"}), - * @ORM\Index(name="parts_idx_name", columns={"name"}), - * @ORM\Index(name="parts_idx_ipn", columns={"ipn"}), - * }) - * @UniqueEntity(fields={"ipn"}, message="part.ipn.must_be_unique") + * Otherwise, this class would be too big, to be maintained. + * @see \App\Tests\Entity\Parts\PartTest + * @extends AttachmentContainingDBElement + * @template-use ParametersTrait */ +#[UniqueEntity(fields: ['ipn'], message: 'part.ipn.must_be_unique')] +#[ORM\Entity(repositoryClass: PartRepository::class)] +#[ORM\EntityListeners([TreeCacheInvalidationListener::class])] +#[ORM\Table('`parts`')] +#[ORM\Index(columns: ['datetime_added', 'name', 'last_modified', 'id', 'needs_review'], name: 'parts_idx_datet_name_last_id_needs')] +#[ORM\Index(columns: ['name'], name: 'parts_idx_name')] +#[ORM\Index(columns: ['ipn'], name: 'parts_idx_ipn')] +#[ApiResource( + operations: [ + new Get(normalizationContext: ['groups' => ['part:read', 'provider_reference:read', 'api:basic:read', 'part_lot:read', + 'orderdetail:read', 'pricedetail:read', 'parameter:read', 'attachment:read', 'eda_info:read'], + 'openapi_definition_name' => 'Read', + ], security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@parts.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['part:read', 'provider_reference:read', 'api:basic:read', 'part_lot:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['part:write', 'api:basic:write', 'eda_info:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(EntityFilter::class, properties: ["category", "footprint", "manufacturer", "partUnit"])] +#[ApiFilter(PartStoragelocationFilter::class, properties: ["storage_location"])] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "description", "ipn", "manufacturer_product_number"])] +#[ApiFilter(TagFilter::class, properties: ["tags"])] +#[ApiFilter(BooleanFilter::class, properties: ["favorite" , "needs_review"])] +#[ApiFilter(RangeFilter::class, properties: ["mass", "minamount"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class Part extends AttachmentContainingDBElement { use AdvancedPropertyTrait; @@ -67,18 +114,18 @@ class Part extends AttachmentContainingDBElement use OrderTrait; use ParametersTrait; use ProjectTrait; + use AssociationTrait; + use EDATrait; /** @var Collection - * @Assert\Valid() - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\PartParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) */ - protected $parameters; + #[Assert\Valid] + #[Groups(['full', 'part:read', 'part:write', 'import'])] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: PartParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[UniqueObjectCollection(fields: ['name', 'group', 'element'])] + protected Collection $parameters; - /** - * @ORM\Column(type="datetime", name="datetime_added", options={"default"="CURRENT_TIMESTAMP"}) - */ - protected ?DateTime $addedDate = null; /** ************************************************************* * Overridden properties @@ -87,39 +134,48 @@ class Part extends AttachmentContainingDBElement /** * @var string The name of this part - * @ORM\Column(type="string") */ protected string $name = ''; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\PartAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[Groups(['full', 'part:read', 'part:write'])] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: PartAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $attachments; /** - * @var DateTime the date when this element was modified the last time - * @ORM\Column(type="datetime", name="last_modified", options={"default"="CURRENT_TIMESTAMP"}) - */ - protected ?DateTime $lastModified = null; - - /** - * @var Attachment - * @ORM\ManyToOne(targetEntity="App\Entity\Attachments\Attachment") - * @ORM\JoinColumn(name="id_preview_attachement", referencedColumnName="id") - * @Assert\Expression("value == null or value.isPicture()", message="part.master_attachment.must_be_picture") + * @var Attachment|null */ + #[Assert\Expression('value == null or value.isPicture()', message: 'part.master_attachment.must_be_picture')] + #[ORM\ManyToOne(targetEntity: PartAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['part:read', 'part:write'])] protected ?Attachment $master_picture_attachment = null; + #[Groups(['part:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['part:read'])] + protected ?\DateTimeImmutable $lastModified = null; + + public function __construct() { + $this->attachments = new ArrayCollection(); parent::__construct(); $this->partLots = new ArrayCollection(); $this->orderdetails = new ArrayCollection(); $this->parameters = new ArrayCollection(); $this->project_bom_entries = new ArrayCollection(); + + $this->associated_parts_as_owner = new ArrayCollection(); + $this->associated_parts_as_other = new ArrayCollection(); + + //By default, the part has no provider + $this->providerReference = InfoProviderReference::noProvider(); + $this->eda_info = new EDAPartInfo(); } public function __clone() @@ -145,25 +201,32 @@ class Part extends AttachmentContainingDBElement foreach ($parameters as $parameter) { $this->addParameter(clone $parameter); } + + //Deep clone the owned part associations (the owned ones make not much sense without the owner) + $ownedAssociations = $this->associated_parts_as_owner; + $this->associated_parts_as_owner = new ArrayCollection(); + foreach ($ownedAssociations as $association) { + $this->addAssociatedPartsAsOwner(clone $association); + } + + //Deep clone info provider + $this->providerReference = clone $this->providerReference; + $this->eda_info = clone $this->eda_info; } parent::__clone(); } - /** - * @Assert\Callback - */ - public function validate(ExecutionContextInterface $context, $payload) + #[Assert\Callback] + public function validate(ExecutionContextInterface $context, $payload): void { //Ensure that the part name fullfills the regex of the category - if ($this->category) { + if ($this->category instanceof Category) { $regex = $this->category->getPartnameRegex(); - if (!empty($regex)) { - if (!preg_match($regex, $this->name)) { - $context->buildViolation('part.name.must_match_category_regex') - ->atPath('name') - ->setParameter('%regex%', $regex) - ->addViolation(); - } + if ($regex !== '' && !preg_match($regex, $this->name)) { + $context->buildViolation('part.name.must_match_category_regex') + ->atPath('name') + ->setParameter('%regex%', $regex) + ->addViolation(); } } } diff --git a/src/Entity/Parts/PartAssociation.php b/src/Entity/Parts/PartAssociation.php new file mode 100644 index 00000000..32017488 --- /dev/null +++ b/src/Entity/Parts/PartAssociation.php @@ -0,0 +1,235 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\Parts; + +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Contracts\TimeStampableInterface; +use App\Repository\DBElementRepository; +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping as ORM; +use App\Entity\Base\AbstractDBElement; +use App\Entity\Base\TimestampTrait; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Constraints\Length; + +/** + * This entity describes a part association, which is a semantic connection between two parts. + * For example, a part association can be used to describe that a part is a replacement for another part. + * @see \App\Tests\Entity\Parts\PartAssociationTest + */ +#[ORM\Entity(repositoryClass: DBElementRepository::class)] +#[ORM\HasLifecycleCallbacks] +#[UniqueEntity(fields: ['other', 'owner', 'type'], message: 'validator.part_association.already_exists')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@parts.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['part_assoc:read', 'part_assoc:read:standalone', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['part_assoc:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["other_type", "comment"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['comment', 'addedDate', 'lastModified'])] +class PartAssociation extends AbstractDBElement implements TimeStampableInterface +{ + use TimestampTrait; + + /** + * @var AssociationType The type of this association (how the two parts are related) + */ + #[ORM\Column(type: Types::SMALLINT, enumType: AssociationType::class)] + #[Groups(['part_assoc:read', 'part_assoc:write'])] + protected AssociationType $type = AssociationType::OTHER; + + /** + * @var string|null A user definable association type, which can be described in the comment field, which + * is used if the type is OTHER + */ + #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] + #[Assert\Expression("this.getType().value !== 0 or this.getOtherType() !== null", + message: 'validator.part_association.must_set_an_value_if_type_is_other')] + #[Groups(['part_assoc:read', 'part_assoc:write'])] + #[Length(max: 255)] + protected ?string $other_type = null; + + /** + * @var string|null A comment describing this association further. + */ + #[ORM\Column(type: Types::TEXT, nullable: true)] + #[Groups(['part_assoc:read', 'part_assoc:write'])] + protected ?string $comment = null; + + /** + * @var Part|null The part which "owns" this association, e.g. the part which is a replacement for another part + */ + #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'associated_parts_as_owner')] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + #[Assert\NotNull] + #[Groups(['part_assoc:read:standalone', 'part_assoc:write'])] + protected ?Part $owner = null; + + /** + * @var Part|null The part which is "owned" by this association, e.g. the part which is replaced by another part + */ + #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'associated_parts_as_other')] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + #[Assert\NotNull] + #[Assert\Expression("this.getOwner() !== this.getOther()", + message: 'validator.part_association.part_cannot_be_associated_with_itself')] + #[Groups(['part_assoc:read', 'part_assoc:write'])] + protected ?Part $other = null; + + /** + * Returns the (semantic) relation type of this association as an AssociationType enum value. + * If the type is set to OTHER, then the other_type field value is used for the user defined type. + * @return AssociationType + */ + public function getType(): AssociationType + { + return $this->type; + } + + /** + * Sets the (semantic) relation type of this association as an AssociationType enum value. + * @param AssociationType $type + * @return $this + */ + public function setType(AssociationType $type): PartAssociation + { + $this->type = $type; + return $this; + } + + /** + * Returns a comment, which describes this association further. + * @return string|null + */ + public function getComment(): ?string + { + return $this->comment; + } + + /** + * Sets a comment, which describes this association further. + * @param string|null $comment + * @return $this + */ + public function setComment(?string $comment): PartAssociation + { + $this->comment = $comment; + return $this; + } + + /** + * Returns the part which "owns" this association, e.g. the part which is a replacement for another part. + * @return Part|null + */ + public function getOwner(): ?Part + { + return $this->owner; + } + + /** + * Sets the part which "owns" this association, e.g. the part which is a replacement for another part. + * @param Part|null $owner + * @return $this + */ + public function setOwner(?Part $owner): PartAssociation + { + $this->owner = $owner; + return $this; + } + + /** + * Returns the part which is "owned" by this association, e.g. the part which is replaced by another part. + * @return Part|null + */ + public function getOther(): ?Part + { + return $this->other; + } + + /** + * Sets the part which is "owned" by this association, e.g. the part which is replaced by another part. + * @param Part|null $other + * @return $this + */ + public function setOther(?Part $other): PartAssociation + { + $this->other = $other; + return $this; + } + + /** + * Returns the user defined association type, which is used if the type is set to OTHER. + * @return string|null + */ + public function getOtherType(): ?string + { + return $this->other_type; + } + + /** + * Sets the user defined association type, which is used if the type is set to OTHER. + * @param string|null $other_type + * @return $this + */ + public function setOtherType(?string $other_type): PartAssociation + { + $this->other_type = $other_type; + return $this; + } + + /** + * Returns the translation key for the type of this association. + * If the type is set to OTHER, then the other_type field value is used. + * @return string + */ + public function getTypeTranslationKey(): string + { + if ($this->type === AssociationType::OTHER) { + return $this->other_type ?? 'Unknown'; + } + return $this->type->getTranslationKey(); + } + +} \ No newline at end of file diff --git a/src/Entity/Parts/PartLot.php b/src/Entity/Parts/PartLot.php index 1201f254..d893e6de 100644 --- a/src/Entity/Parts/PartLot.php +++ b/src/Entity/Parts/PartLot.php @@ -22,86 +22,154 @@ declare(strict_types=1); namespace App\Entity\Parts; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Doctrine\Orm\Filter\RangeFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Validator\Constraints\Year2038BugWorkaround; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\TimestampTrait; use App\Entity\Contracts\NamedElementInterface; use App\Entity\Contracts\TimeStampableInterface; +use App\Entity\UserSystem\User; use App\Validator\Constraints\Selectable; use App\Validator\Constraints\ValidPartLot; use DateTime; use Doctrine\ORM\Mapping as ORM; use Exception; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Context\ExecutionContextInterface; /** * This entity describes a lot where parts can be stored. * It is the connection between a part and its store locations. * - * @ORM\Entity() - * @ORM\Table(name="part_lots", indexes={ - * @ORM\Index(name="part_lots_idx_instock_un_expiration_id_part", columns={"instock_unknown", "expiration_date", "id_part"}), - * @ORM\Index(name="part_lots_idx_needs_refill", columns={"needs_refill"}), - * }) - * @ORM\HasLifecycleCallbacks() - * @ValidPartLot() + * @see \App\Tests\Entity\Parts\PartLotTest */ +#[ORM\Entity] +#[ORM\HasLifecycleCallbacks] +#[ORM\Table(name: 'part_lots')] +#[ORM\Index(columns: ['instock_unknown', 'expiration_date', 'id_part'], name: 'part_lots_idx_instock_un_expiration_id_part')] +#[ORM\Index(columns: ['needs_refill'], name: 'part_lots_idx_needs_refill')] +#[ORM\Index(columns: ['vendor_barcode'], name: 'part_lots_idx_barcode')] +#[ValidPartLot] +#[UniqueEntity(['user_barcode'], message: 'validator.part_lot.vendor_barcode_must_be_unique')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@parts.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['part_lot:read', 'part_lot:read:standalone', 'api:basic:read', 'pricedetail:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['part_lot:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["description", "comment"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(BooleanFilter::class, properties: ['instock_unknown', 'needs_refill'])] +#[ApiFilter(RangeFilter::class, properties: ['amount'])] +#[ApiFilter(OrderFilter::class, properties: ['description', 'comment', 'addedDate', 'lastModified'])] class PartLot extends AbstractDBElement implements TimeStampableInterface, NamedElementInterface { use TimestampTrait; /** * @var string A short description about this lot, shown in table - * @ORM\Column(type="text") */ + #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $description = ''; /** * @var string a comment stored with this lot - * @ORM\Column(type="text") */ + #[Groups(['full', 'import', 'part_lot:read', 'part_lot:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $comment = ''; /** - * @var ?DateTime Set a time until when the lot must be used. + * @var \DateTimeImmutable|null Set a time until when the lot must be used. * Set to null, if the lot can be used indefinitely. - * @ORM\Column(type="datetime", name="expiration_date", nullable=true) */ - protected ?DateTime $expiration_date = null; + #[Groups(['extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] + #[ORM\Column(name: 'expiration_date', type: Types::DATETIME_IMMUTABLE, nullable: true)] + #[Year2038BugWorkaround] + protected ?\DateTimeImmutable $expiration_date = null; /** - * @var Storelocation|null The storelocation of this lot - * @ORM\ManyToOne(targetEntity="Storelocation") - * @ORM\JoinColumn(name="id_store_location", referencedColumnName="id", nullable=true) - * @Selectable() + * @var StorageLocation|null The storelocation of this lot */ - protected ?Storelocation $storage_location = null; + #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] + #[ORM\ManyToOne(targetEntity: StorageLocation::class, fetch: 'EAGER')] + #[ORM\JoinColumn(name: 'id_store_location')] + #[Selectable] + protected ?StorageLocation $storage_location = null; /** * @var bool If this is set to true, the instock amount is marked as not known - * @ORM\Column(type="boolean") */ + #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $instock_unknown = false; /** - * @var float For continuous sizes (length, volume, etc.) the instock is saved here. - * @ORM\Column(type="float") - * @Assert\PositiveOrZero() + * @var float The amount of parts in this lot. For integer-quantities this value is rounded to the next integer. */ + #[Assert\PositiveOrZero] + #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] + #[ORM\Column(type: Types::FLOAT)] protected float $amount = 0.0; /** * @var bool determines if this lot was manually marked for refilling - * @ORM\Column(type="boolean") */ + #[Groups(['extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $needs_refill = false; /** - * @var Part The part that is stored in this lot - * @ORM\ManyToOne(targetEntity="Part", inversedBy="partLots") - * @ORM\JoinColumn(name="id_part", referencedColumnName="id", nullable=false, onDelete="CASCADE") - * @Assert\NotNull() + * @var Part|null The part that is stored in this lot */ - protected Part $part; + #[Assert\NotNull] + #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'partLots')] + #[ORM\JoinColumn(name: 'id_part', nullable: false, onDelete: 'CASCADE')] + #[Groups(['part_lot:read:standalone', 'part_lot:write'])] + #[ApiProperty(writableLink: false)] + protected ?Part $part = null; + + /** + * @var User|null The owner of this part lot + */ + #[ORM\ManyToOne(targetEntity: User::class)] + #[ORM\JoinColumn(name: 'id_owner', onDelete: 'SET NULL')] + #[Groups(['part_lot:read', 'part_lot:write'])] + #[ApiProperty(writableLink: false)] + protected ?User $owner = null; + + /** + * @var string|null The content of the barcode of this part lot (e.g. a barcode on the package put by the vendor) + */ + #[ORM\Column(name: "vendor_barcode", type: Types::STRING, nullable: true)] + #[Groups(['part_lot:read', 'part_lot:write'])] + #[Length(max: 255)] + protected ?string $user_barcode = null; public function __clone() { @@ -113,20 +181,19 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named /** * Check if the current part lot is expired. - * This is the case, if the expiration date is greater the the current date. + * This is the case, if the expiration date is greater the current date. * * @return bool|null True, if the part lot is expired. Returns null, if no expiration date was set. * - * @throws Exception If an error with the DateTime occurs */ public function isExpired(): ?bool { - if (null === $this->expiration_date) { + if (!$this->expiration_date instanceof \DateTimeInterface) { return null; } //Check if the expiration date is bigger then current time - return $this->expiration_date < new DateTime('now'); + return $this->expiration_date < new \DateTimeImmutable('now'); } /** @@ -139,8 +206,6 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named /** * Sets the description of the part lot. - * - * @return PartLot */ public function setDescription(string $description): self { @@ -159,8 +224,6 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named /** * Sets the comment for this part lot. - * - * @return PartLot */ public function setComment(string $comment): self { @@ -172,19 +235,17 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named /** * Gets the expiration date for the part lot. Returns null, if no expiration date was set. */ - public function getExpirationDate(): ?DateTime + public function getExpirationDate(): ?\DateTimeImmutable { return $this->expiration_date; } /** - * Sets the expiration date for the part lot. Set to null, if the part lot does not expires. + * Sets the expiration date for the part lot. Set to null, if the part lot does not expire. * - * @param DateTime|null $expiration_date * - * @return PartLot */ - public function setExpirationDate(?DateTime $expiration_date): self + public function setExpirationDate(?\DateTimeImmutable $expiration_date): self { $this->expiration_date = $expiration_date; @@ -194,19 +255,17 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named /** * Gets the storage location, where this part lot is stored. * - * @return Storelocation|null The store location where this part is stored + * @return StorageLocation|null The store location where this part is stored */ - public function getStorageLocation(): ?Storelocation + public function getStorageLocation(): ?StorageLocation { return $this->storage_location; } /** * Sets the storage location, where this part lot is stored. - * - * @return PartLot */ - public function setStorageLocation(?Storelocation $storage_location): self + public function setStorageLocation(?StorageLocation $storage_location): self { $this->storage_location = $storage_location; @@ -216,15 +275,13 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named /** * Return the part that is stored in this part lot. */ - public function getPart(): Part + public function getPart(): ?Part { return $this->part; } /** * Sets the part that is stored in this part lot. - * - * @return PartLot */ public function setPart(Part $part): self { @@ -243,8 +300,6 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named /** * Set the unknown instock status of this part lot. - * - * @return PartLot */ public function setInstockUnknown(bool $instock_unknown): self { @@ -286,9 +341,6 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named return $this->needs_refill; } - /** - * @return PartLot - */ public function setNeedsRefill(bool $needs_refill): self { $this->needs_refill = $needs_refill; @@ -296,8 +348,69 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named return $this; } + /** + * Returns the owner of this part lot. + */ + public function getOwner(): ?User + { + return $this->owner; + } + + /** + * Sets the owner of this part lot. + */ + public function setOwner(?User $owner): PartLot + { + $this->owner = $owner; + return $this; + } + public function getName(): string { return $this->description; } + + /** + * The content of the barcode of this part lot (e.g. a barcode on the package put by the vendor), or + * null if no barcode is set. + * @return string|null + */ + public function getUserBarcode(): ?string + { + return $this->user_barcode; + } + + /** + * Set the content of the barcode of this part lot (e.g. a barcode on the package put by the vendor). + * @param string|null $user_barcode + * @return $this + */ + public function setUserBarcode(?string $user_barcode): PartLot + { + $this->user_barcode = $user_barcode; + return $this; + } + + + + #[Assert\Callback] + public function validate(ExecutionContextInterface $context, $payload): void + { + //Ensure that the owner is not the anonymous user + if ($this->getOwner() && $this->getOwner()->isAnonymousUser()) { + $context->buildViolation('validator.part_lot.owner_must_not_be_anonymous') + ->atPath('owner') + ->addViolation(); + } + + //When the storage location sets the owner must match, the part lot owner must match the storage location owner + if ($this->getStorageLocation() && $this->getStorageLocation()->isPartOwnerMustMatch() + && $this->getStorageLocation()->getOwner() && $this->getOwner() && ($this->getOwner() !== $this->getStorageLocation()->getOwner() + && $this->owner->getID() !== $this->getStorageLocation()->getOwner()->getID())) { + $context->buildViolation('validator.part_lot.owner_must_match_storage_location_owner') + ->setParameter('%owner_name%', $this->getStorageLocation()->getOwner()->getFullName(true)) + ->atPath('owner') + ->addViolation(); + } + } } diff --git a/src/Entity/Parts/PartTraits/AdvancedPropertyTrait.php b/src/Entity/Parts/PartTraits/AdvancedPropertyTrait.php index a798c305..230ba7b7 100644 --- a/src/Entity/Parts/PartTraits/AdvancedPropertyTrait.php +++ b/src/Entity/Parts/PartTraits/AdvancedPropertyTrait.php @@ -22,9 +22,13 @@ declare(strict_types=1); namespace App\Entity\Parts\PartTraits; +use App\Entity\Parts\InfoProviderReference; +use Doctrine\DBAL\Types\Types; use App\Entity\Parts\Part; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Constraints\Length; /** * Advanced properties of a part, not related to a more specific group. @@ -33,31 +37,42 @@ trait AdvancedPropertyTrait { /** * @var bool Determines if this part entry needs review (for example, because it is work in progress) - * @ORM\Column(type="boolean") */ + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $needs_review = false; /** - * @var string a comma separated list of tags, associated with the part - * @ORM\Column(type="text") + * @var string A comma separated list of tags, associated with the part */ + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $tags = ''; /** - * @var float|null how much a single part unit weighs in grams - * @ORM\Column(type="float", nullable=true) - * @Assert\PositiveOrZero() + * @var float|null How much a single part unit weighs in grams */ + #[Assert\PositiveOrZero] + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::FLOAT, nullable: true)] protected ?float $mass = null; /** - * @var string The internal part number of the part - * @ORM\Column(type="string", length=100, nullable=true, unique=true) - * @Assert\Length(max="100") - * + * @var string|null The internal part number of the part */ + #[Assert\Length(max: 100)] + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::STRING, length: 100, unique: true, nullable: true)] + #[Length(max: 100)] protected ?string $ipn = null; + /** + * @var InfoProviderReference The reference to the info provider, that provided the information about this part + */ + #[ORM\Embedded(class: InfoProviderReference::class, columnPrefix: 'provider_reference_')] + #[Groups(['full', 'part:read'])] + protected InfoProviderReference $providerReference; + /** * Checks if this part is marked, for that it needs further review. */ @@ -138,7 +153,6 @@ trait AdvancedPropertyTrait /** * Sets the internal part number of the part * @param string $ipn The new IPN of the part - * @return Part */ public function setIpn(?string $ipn): Part { @@ -146,5 +160,27 @@ trait AdvancedPropertyTrait return $this; } + /** + * Returns the reference to the info provider, that provided the information about this part. + * @return InfoProviderReference + */ + public function getProviderReference(): InfoProviderReference + { + return $this->providerReference; + } + + /** + * Sets the reference to the info provider, that provided the information about this part. + * @param InfoProviderReference $providerReference + * @return Part + */ + public function setProviderReference(InfoProviderReference $providerReference): Part + { + $this->providerReference = $providerReference; + return $this; + } + + + } diff --git a/src/Entity/Parts/PartTraits/AssociationTrait.php b/src/Entity/Parts/PartTraits/AssociationTrait.php new file mode 100644 index 00000000..bb80fc5a --- /dev/null +++ b/src/Entity/Parts/PartTraits/AssociationTrait.php @@ -0,0 +1,110 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\Parts\PartTraits; + +use App\Entity\Parts\PartAssociation; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints\Valid; +use Doctrine\ORM\Mapping as ORM; + +trait AssociationTrait +{ + /** + * @var Collection All associations where this part is the owner + */ + #[Valid] + #[ORM\OneToMany(mappedBy: 'owner', targetEntity: PartAssociation::class, + cascade: ['persist', 'remove'], orphanRemoval: true)] + #[Groups(['part:read', 'part:write', 'full'])] + protected Collection $associated_parts_as_owner; + + /** + * @var Collection All associations where this part is the owned/other part + */ + #[Valid] + #[ORM\OneToMany(mappedBy: 'other', targetEntity: PartAssociation::class, + cascade: ['persist', 'remove'], orphanRemoval: true)] + #[Groups(['part:read'])] + protected Collection $associated_parts_as_other; + + /** + * Returns all associations where this part is the owner. + * @return Collection + */ + public function getAssociatedPartsAsOwner(): Collection + { + return $this->associated_parts_as_owner; + } + + /** + * Add a new association where this part is the owner. + * @param PartAssociation $association + * @return $this + */ + public function addAssociatedPartsAsOwner(PartAssociation $association): self + { + //Ensure that the association is really owned by this part + $association->setOwner($this); + + $this->associated_parts_as_owner->add($association); + return $this; + } + + /** + * Remove an association where this part is the owner. + * @param PartAssociation $association + * @return $this + */ + public function removeAssociatedPartsAsOwner(PartAssociation $association): self + { + $this->associated_parts_as_owner->removeElement($association); + return $this; + } + + /** + * Returns all associations where this part is the owned/other part. + * If you want to modify the association, do it on the owning part + * @return Collection + */ + public function getAssociatedPartsAsOther(): Collection + { + return $this->associated_parts_as_other; + } + + /** + * Returns all associations where this part is the owned or other part. + * @return Collection + */ + public function getAssociatedPartsAll(): Collection + { + return new ArrayCollection( + array_merge( + $this->associated_parts_as_owner->toArray(), + $this->associated_parts_as_other->toArray() + ) + ); + } +} \ No newline at end of file diff --git a/src/Entity/Parts/PartTraits/BasicPropertyTrait.php b/src/Entity/Parts/PartTraits/BasicPropertyTrait.php index c7c7fe50..7e483ed2 100644 --- a/src/Entity/Parts/PartTraits/BasicPropertyTrait.php +++ b/src/Entity/Parts/PartTraits/BasicPropertyTrait.php @@ -22,54 +22,61 @@ declare(strict_types=1); namespace App\Entity\Parts\PartTraits; +use Doctrine\DBAL\Types\Types; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Validator\Constraints\Selectable; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; trait BasicPropertyTrait { /** * @var string A text describing what this part does - * @ORM\Column(type="text") */ + #[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $description = ''; /** * @var string A comment/note related to this part - * @ORM\Column(type="text") */ + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $comment = ''; /** - * @var bool Kept for compatibility (it is not used now, and I dont think it was used in old versions) - * @ORM\Column(type="boolean") + * @var bool Kept for compatibility (it is not used now, and I don't think it was used in old versions) */ + #[ORM\Column(type: Types::BOOLEAN)] protected bool $visible = true; /** * @var bool true, if the part is marked as favorite - * @ORM\Column(type="boolean") */ + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $favorite = false; /** - * @var Category The category this part belongs too (e.g. Resistors). Use tags, for more complex grouping. + * @var Category|null The category this part belongs too (e.g. Resistors). Use tags, for more complex grouping. * Every part must have a category. - * @ORM\ManyToOne(targetEntity="Category") - * @ORM\JoinColumn(name="id_category", referencedColumnName="id", nullable=false) - * @Selectable() - * @Assert\NotNull(message="validator.select_valid_category") */ + #[Assert\NotNull(message: 'validator.select_valid_category')] + #[Selectable] + #[Groups(['simple', 'extended', 'full', 'import', "part:read", "part:write"])] + #[ORM\ManyToOne(targetEntity: Category::class)] + #[ORM\JoinColumn(name: 'id_category', nullable: false)] protected ?Category $category = null; /** * @var Footprint|null The footprint of this part (e.g. DIP8) - * @ORM\ManyToOne(targetEntity="Footprint") - * @ORM\JoinColumn(name="id_footprint", referencedColumnName="id") - * @Selectable() */ + #[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\ManyToOne(targetEntity: Footprint::class)] + #[ORM\JoinColumn(name: 'id_footprint')] + #[Selectable] protected ?Footprint $footprint = null; /** @@ -130,7 +137,7 @@ trait BasicPropertyTrait /** * Gets the Footprint of this part (e.g. DIP8). * - * @return Footprint|null The footprint of this part. Null if this part should no have a footprint. + * @return Footprint|null The footprint of this part. Null if this part should not have a footprint. */ public function getFootprint(): ?Footprint { diff --git a/src/Entity/Parts/PartTraits/EDATrait.php b/src/Entity/Parts/PartTraits/EDATrait.php new file mode 100644 index 00000000..313552e7 --- /dev/null +++ b/src/Entity/Parts/PartTraits/EDATrait.php @@ -0,0 +1,53 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\Parts\PartTraits; + +use App\Entity\EDA\EDAPartInfo; +use Doctrine\ORM\Mapping\Embedded; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints\Valid; + +trait EDATrait +{ + #[Valid] + #[Embedded(class: EDAPartInfo::class)] + #[Groups(['full', 'part:read', 'part:write', 'import'])] + protected EDAPartInfo $eda_info; + + public function getEdaInfo(): EDAPartInfo + { + return $this->eda_info; + } + + public function setEdaInfo(?EDAPartInfo $eda_info): self + { + if ($eda_info !== null) { + //Do a clone, to ensure that the property is updated in the database + $eda_info = clone $eda_info; + } + + $this->eda_info = $eda_info; + return $this; + } +} \ No newline at end of file diff --git a/src/Entity/Parts/PartTraits/InstockTrait.php b/src/Entity/Parts/PartTraits/InstockTrait.php index b79719ec..08b070f3 100644 --- a/src/Entity/Parts/PartTraits/InstockTrait.php +++ b/src/Entity/Parts/PartTraits/InstockTrait.php @@ -22,10 +22,14 @@ declare(strict_types=1); namespace App\Entity\Parts\PartTraits; +use Doctrine\Common\Collections\Criteria; +use Doctrine\DBAL\Types\Types; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\PartLot; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Attribute\SerializedName; use Symfony\Component\Validator\Constraints as Assert; /** @@ -34,32 +38,34 @@ use Symfony\Component\Validator\Constraints as Assert; trait InstockTrait { /** - * @var Collection|PartLot[] A list of part lots where this part is stored - * @ORM\OneToMany(targetEntity="PartLot", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true) - * @Assert\Valid() - * @ORM\OrderBy({"amount" = "DESC"}) + * @var Collection A list of part lots where this part is stored */ - protected $partLots; + #[Assert\Valid] + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\OneToMany(mappedBy: 'part', targetEntity: PartLot::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['amount' => Criteria::DESC])] + protected Collection $partLots; /** * @var float The minimum amount of the part that has to be instock, otherwise more is ordered. * Given in the partUnit. - * @ORM\Column(type="float") - * @Assert\PositiveOrZero() */ + #[Assert\PositiveOrZero] + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::FLOAT)] protected float $minamount = 0; /** * @var ?MeasurementUnit the unit in which the part's amount is measured - * @ORM\ManyToOne(targetEntity="MeasurementUnit") - * @ORM\JoinColumn(name="id_part_unit", referencedColumnName="id", nullable=true) */ + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\ManyToOne(targetEntity: MeasurementUnit::class)] + #[ORM\JoinColumn(name: 'id_part_unit')] protected ?MeasurementUnit $partUnit = null; /** * Get all part lots where this part is stored. - * - * @return PartLot[]|Collection + * @phpstan-return Collection */ public function getPartLots(): Collection { @@ -118,7 +124,7 @@ trait InstockTrait /** * Get the count of parts which must be in stock at least. - * If a integer-based part unit is selected, the value will be rounded to integers. + * If an integer-based part unit is selected, the value will be rounded to integers. * * @return float count of parts which must be in stock at least */ @@ -147,19 +153,45 @@ trait InstockTrait return false; } + /** + * Returns true, if the total instock amount of this part is less than the minimum amount. + */ + public function isNotEnoughInstock(): bool + { + return $this->getAmountSum() < $this->getMinAmount(); + } + + /** + * Returns true, if at least one of the part lots has an unknown amount. + * It is possible that other part lots have a known amount, then getAmountSum() will return sum of all known amounts. + * @return bool True if at least one part lot has an unknown amount. + */ + public function isAmountUnknown(): bool + { + foreach ($this->getPartLots() as $lot) { + if ($lot->isInstockUnknown()) { + return true; + } + } + + return false; + } + /** * Returns the summed amount of this part (over all part lots) - * Part Lots that have unknown value or are expired, are not used for this value. + * Part Lots that have unknown value or are expired, are not used for this value (counted as 0). * * @return float The amount of parts given in partUnit */ + #[Groups(['simple', 'extended', 'full', 'part:read'])] + #[SerializedName('total_instock')] public function getAmountSum(): float { //TODO: Find a method to do this natively in SQL, the current method could be a bit slow $sum = 0; foreach ($this->getPartLots() as $lot) { - //Dont use the instock value, if it is unkown - if ($lot->isInstockUnknown() || $lot->isExpired() ?? false) { + //Don't use the in stock value, if it is unknown + if ($lot->isInstockUnknown() || ($lot->isExpired() ?? false)) { continue; } @@ -175,7 +207,6 @@ trait InstockTrait /** * Returns the summed amount of all part lots that are expired. If no part lots are expired 0 is returned. - * @return float */ public function getExpiredAmountSum(): float { diff --git a/src/Entity/Parts/PartTraits/ManufacturerTrait.php b/src/Entity/Parts/PartTraits/ManufacturerTrait.php index fc27ac14..5d7f8749 100644 --- a/src/Entity/Parts/PartTraits/ManufacturerTrait.php +++ b/src/Entity/Parts/PartTraits/ManufacturerTrait.php @@ -22,11 +22,15 @@ declare(strict_types=1); namespace App\Entity\Parts\PartTraits; +use App\Entity\Parts\ManufacturingStatus; +use Doctrine\DBAL\Types\Types; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\Part; use App\Validator\Constraints\Selectable; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Constraints\Length; /** * In this trait all manufacturer related properties of a part are collected (like MPN, manufacturer URL). @@ -35,31 +39,35 @@ trait ManufacturerTrait { /** * @var Manufacturer|null The manufacturer of this part - * @ORM\ManyToOne(targetEntity="Manufacturer") - * @ORM\JoinColumn(name="id_manufacturer", referencedColumnName="id") - * @Selectable() */ + #[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\ManyToOne(targetEntity: Manufacturer::class)] + #[ORM\JoinColumn(name: 'id_manufacturer')] + #[Selectable] protected ?Manufacturer $manufacturer = null; /** - * @var string the url to the part on the manufacturer's homepage - * @ORM\Column(type="string") - * @Assert\Url() + * @var string The url to the part on the manufacturer's homepage */ + #[Assert\Url] + #[Groups(['full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $manufacturer_product_url = ''; /** * @var string The product number used by the manufacturer. If this is set to "", the name field is used. - * @ORM\Column(type="string") */ + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::STRING)] + #[Length(max: 255)] protected string $manufacturer_product_number = ''; /** - * @var string The production status of this part. Can be one of the specified ones. - * @ORM\Column(type="string", length=255, nullable=true) - * @Assert\Choice({"announced", "active", "nrfnd", "eol", "discontinued", ""}) + * @var ManufacturingStatus|null The production status of this part. Can be one of the specified ones. */ - protected ?string $manufacturing_status = ''; + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::STRING, length: 255, nullable: true, enumType: ManufacturingStatus::class)] + protected ?ManufacturingStatus $manufacturing_status = ManufacturingStatus::NOT_SET; /** * Get the link to the website of the article on the manufacturers website @@ -107,9 +115,9 @@ trait ManufacturerTrait * * "eol": Part will become discontinued soon * * "discontinued": Part is obsolete/discontinued by the manufacturer. * - * @return string + * @return ManufacturingStatus|null */ - public function getManufacturingStatus(): ?string + public function getManufacturingStatus(): ?ManufacturingStatus { return $this->manufacturing_status; } @@ -118,9 +126,9 @@ trait ManufacturerTrait * Sets the manufacturing status for this part * See getManufacturingStatus() for valid values. * - * @return Part + * @return $this */ - public function setManufacturingStatus(string $manufacturing_status): self + public function setManufacturingStatus(ManufacturingStatus $manufacturing_status): self { $this->manufacturing_status = $manufacturing_status; diff --git a/src/Entity/Parts/PartTraits/OrderTrait.php b/src/Entity/Parts/PartTraits/OrderTrait.php index 60de1cba..2c142016 100644 --- a/src/Entity/Parts/PartTraits/OrderTrait.php +++ b/src/Entity/Parts/PartTraits/OrderTrait.php @@ -22,8 +22,10 @@ declare(strict_types=1); namespace App\Entity\Parts\PartTraits; +use Doctrine\Common\Collections\Criteria; +use Doctrine\DBAL\Types\Types; use App\Entity\PriceInformations\Orderdetail; -use Doctrine\Common\Collections\ArrayCollection; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; use function count; use Doctrine\Common\Collections\Collection; @@ -35,36 +37,37 @@ use Doctrine\ORM\Mapping as ORM; trait OrderTrait { /** - * @var Orderdetail[]|Collection the details about how and where you can order this part - * @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true) - * @Assert\Valid() - * @ORM\OrderBy({"supplierpartnr" = "ASC"}) + * @var Collection The details about how and where you can order this part */ - protected $orderdetails; + #[Assert\Valid] + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\OneToMany(mappedBy: 'part', targetEntity: Orderdetail::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['supplierpartnr' => Criteria::ASC])] + protected Collection $orderdetails; /** * @var int - * @ORM\Column(type="integer") */ + #[ORM\Column(type: Types::INTEGER)] protected int $order_quantity = 0; /** * @var bool - * @ORM\Column(type="boolean") */ + #[ORM\Column(type: Types::BOOLEAN)] protected bool $manual_order = false; /** - * @var Orderdetail - * @ORM\OneToOne(targetEntity="App\Entity\PriceInformations\Orderdetail") - * @ORM\JoinColumn(name="order_orderdetails_id", referencedColumnName="id") + * @var Orderdetail|null */ + #[ORM\OneToOne(targetEntity: Orderdetail::class)] + #[ORM\JoinColumn(name: 'order_orderdetails_id')] protected ?Orderdetail $order_orderdetail = null; /** * Get the selected order orderdetails of this part. * - * @return Orderdetail the selected order orderdetails + * @return Orderdetail|null the selected order orderdetails */ public function getOrderOrderdetails(): ?Orderdetail { @@ -96,7 +99,7 @@ trait OrderTrait * * @param bool $hide_obsolete If true, obsolete orderdetails will NOT be returned * - * @return Collection|Orderdetail[] * all orderdetails as a one-dimensional array of Orderdetails objects + * @return Collection * all orderdetails as a one-dimensional array of Orderdetails objects * (empty array if there are no ones) * * the array is sorted by the suppliers names / minimum order quantity */ @@ -104,9 +107,7 @@ trait OrderTrait { //If needed hide the obsolete entries if ($hide_obsolete) { - return $this->orderdetails->filter(function (Orderdetail $orderdetail) { - return ! $orderdetail->getObsolete(); - }); + return $this->orderdetails->filter(fn(Orderdetail $orderdetail) => ! $orderdetail->getObsolete()); } return $this->orderdetails; diff --git a/src/Entity/Parts/PartTraits/ProjectTrait.php b/src/Entity/Parts/PartTraits/ProjectTrait.php index 58697427..45719377 100644 --- a/src/Entity/Parts/PartTraits/ProjectTrait.php +++ b/src/Entity/Parts/PartTraits/ProjectTrait.php @@ -1,30 +1,34 @@ $project_bom_entries - * @ORM\OneToMany(targetEntity="App\Entity\ProjectSystem\ProjectBOMEntry", mappedBy="part", cascade={"remove"}, orphanRemoval=true) + * @var Collection $project_bom_entries */ - protected $project_bom_entries = []; + #[ORM\OneToMany(mappedBy: 'part', targetEntity: ProjectBOMEntry::class, cascade: ['remove'], orphanRemoval: true)] + protected Collection $project_bom_entries; /** * @var Project|null If a project is set here, then this part is special and represents the builds of a project. - * @ORM\OneToOne(targetEntity="App\Entity\ProjectSystem\Project", inversedBy="build_part") - * @ORM\JoinColumn(nullable=true) */ + #[ORM\OneToOne(inversedBy: 'build_part', targetEntity: Project::class)] + #[ORM\JoinColumn] protected ?Project $built_project = null; /** - * Returns all ProjectBOMEntries that use this part. - * @return Collection|ProjectBOMEntry[] + * Returns all ProjectBOMEntries that use this part. + * + * @phpstan-return Collection */ public function getProjectBomEntries(): Collection { @@ -35,14 +39,14 @@ trait ProjectTrait * Checks whether this part represents the builds of a project * @return bool True if it represents the builds, false if not */ + #[Groups(['part:read'])] public function isProjectBuildPart(): bool { return $this->built_project !== null; } /** - * Returns the project that this part represents the builds of, or null if it doesnt - * @return Project|null + * Returns the project that this part represents the builds of, or null if it doesn't */ public function getBuiltProject(): ?Project { @@ -78,4 +82,4 @@ trait ProjectTrait return $projects; } -} \ No newline at end of file +} diff --git a/src/Entity/Parts/StorageLocation.php b/src/Entity/Parts/StorageLocation.php new file mode 100644 index 00000000..6c455ae5 --- /dev/null +++ b/src/Entity/Parts/StorageLocation.php @@ -0,0 +1,303 @@ +. + */ + +declare(strict_types=1); + +namespace App\Entity\Parts; + +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Repository\Parts\StorelocationRepository; +use Doctrine\DBAL\Types\Types; +use Doctrine\Common\Collections\ArrayCollection; +use App\Entity\Attachments\StorageLocationAttachment; +use App\Entity\Base\AbstractPartsContainingDBElement; +use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Parameters\StorageLocationParameter; +use App\Entity\UserSystem\User; +use Doctrine\Common\Collections\Collection; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * This entity represents a storage location, where parts can be stored. + * @extends AbstractPartsContainingDBElement + */ +#[ORM\Entity(repositoryClass: StorelocationRepository::class)] +#[ORM\Table('`storelocations`')] +#[ORM\Index(columns: ['name'], name: 'location_idx_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'location_idx_parent_name')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@storelocations.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['location:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['location:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/storage_locations/{id}/children.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the children elements of a storage location.'), + security: 'is_granted("@storelocations.read")' + ) + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: Manufacturer::class) + ], + normalizationContext: ['groups' => ['location:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] +class StorageLocation extends AbstractPartsContainingDBElement +{ + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; + + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['location:read', 'location:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; + + #[Groups(['location:read', 'location:write'])] + protected string $comment = ''; + + /** + * @var MeasurementUnit|null The measurement unit, which parts can be stored in here + */ + #[ORM\ManyToOne(targetEntity: MeasurementUnit::class)] + #[ORM\JoinColumn(name: 'storage_type_id')] + #[Groups(['location:read', 'location:write'])] + protected ?MeasurementUnit $storage_type = null; + + /** @var Collection + */ + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: StorageLocationParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[Groups(['location:read', 'location:write'])] + protected Collection $parameters; + + /** + * @var bool When this attribute is set, it is not possible to add additional parts or increase the instock of existing parts. + */ + #[Groups(['full', 'import', 'location:read', 'location:write'])] + #[ORM\Column(type: Types::BOOLEAN)] + protected bool $is_full = false; + + /** + * @var bool When this property is set, only one part (but many instock) is allowed to be stored in this store location. + */ + #[Groups(['full', 'import', 'location:read', 'location:write'])] + #[ORM\Column(type: Types::BOOLEAN)] + protected bool $only_single_part = false; + + /** + * @var bool When this property is set, it is only possible to increase the instock of parts, that are already stored here. + */ + #[Groups(['full', 'import', 'location:read', 'location:write'])] + #[ORM\Column(type: Types::BOOLEAN)] + protected bool $limit_to_existing_parts = false; + + /** + * @var User|null The owner of this storage location + */ + #[Assert\Expression('this.getOwner() == null or this.getOwner().isAnonymousUser() === false', message: 'validator.part_lot.owner_must_not_be_anonymous')] + #[ORM\ManyToOne(targetEntity: User::class)] + #[ORM\JoinColumn(name: 'id_owner', onDelete: 'SET NULL')] + #[Groups(['location:read', 'location:write'])] + protected ?User $owner = null; + + /** + * @var bool If this is set to true, only parts lots, which are owned by the same user as the store location are allowed to be stored here. + */ + #[ORM\Column(type: Types::BOOLEAN, options: ['default' => false])] + #[Groups(['location:read', 'location:write'])] + protected bool $part_owner_must_match = false; + + /** + * @var Collection + */ + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: StorageLocationAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[Groups(['location:read', 'location:write'])] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: StorageLocationAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['location:read', 'location:write'])] + protected ?Attachment $master_picture_attachment = null; + + #[Groups(['location:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['location:read'])] + protected ?\DateTimeImmutable $lastModified = null; + + + /******************************************************************************** + * + * Getters + * + *********************************************************************************/ + + /** + * Get the "is full" attribute. + * + * When this attribute is set, it is not possible to add additional parts or increase the instock of existing parts. + * + * @return bool * true if the store location is full + * * false if the store location isn't full + */ + public function isFull(): bool + { + return $this->is_full; + } + + /** + * When this property is set, only one part (but many instock) is allowed to be stored in this store location. + */ + public function isOnlySinglePart(): bool + { + return $this->only_single_part; + } + + public function setOnlySinglePart(bool $only_single_part): self + { + $this->only_single_part = $only_single_part; + + return $this; + } + + /** + * When this property is set, it is only possible to increase the instock of parts, that are already stored here. + */ + public function isLimitToExistingParts(): bool + { + return $this->limit_to_existing_parts; + } + + public function setLimitToExistingParts(bool $limit_to_existing_parts): self + { + $this->limit_to_existing_parts = $limit_to_existing_parts; + + return $this; + } + + public function getStorageType(): ?MeasurementUnit + { + return $this->storage_type; + } + + public function setStorageType(?MeasurementUnit $storage_type): self + { + $this->storage_type = $storage_type; + + return $this; + } + + /** + * Returns the owner of this storage location + */ + public function getOwner(): ?User + { + return $this->owner; + } + + /** + * Sets the owner of this storage location + */ + public function setOwner(?User $owner): StorageLocation + { + $this->owner = $owner; + return $this; + } + + /** + * If this is set to true, only parts lots, which are owned by the same user as the store location are allowed to be stored here. + */ + public function isPartOwnerMustMatch(): bool + { + return $this->part_owner_must_match; + } + + /** + * If this is set to true, only parts lots, which are owned by the same user as the store location are allowed to be stored here. + */ + public function setPartOwnerMustMatch(bool $part_owner_must_match): StorageLocation + { + $this->part_owner_must_match = $part_owner_must_match; + return $this; + } + + + + + /******************************************************************************** + * + * Setters + * + *********************************************************************************/ + /** + * Change the "is full" attribute of this store location. + * + * "is_full" = true means that there is no more space in this storelocation. + * This attribute is only for information, it has no effect. + * + * @param bool $new_is_full * true means that the storelocation is full + * * false means that the storelocation isn't full + */ + public function setIsFull(bool $new_is_full): self + { + $this->is_full = $new_is_full; + + return $this; + } + public function __construct() + { + parent::__construct(); + $this->children = new ArrayCollection(); + $this->parameters = new ArrayCollection(); + $this->attachments = new ArrayCollection(); + } +} diff --git a/src/Entity/Parts/Storelocation.php b/src/Entity/Parts/Storelocation.php deleted file mode 100644 index 53e71060..00000000 --- a/src/Entity/Parts/Storelocation.php +++ /dev/null @@ -1,187 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace App\Entity\Parts; - -use App\Entity\Attachments\StorelocationAttachment; -use App\Entity\Base\AbstractPartsContainingDBElement; -use App\Entity\Parameters\StorelocationParameter; -use Doctrine\Common\Collections\Collection; -use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Validator\Constraints as Assert; - -/** - * Class Store location. - * - * @ORM\Entity(repositoryClass="App\Repository\Parts\StorelocationRepository") - * @ORM\Table("`storelocations`", indexes={ - * @ORM\Index(name="location_idx_name", columns={"name"}), - * @ORM\Index(name="location_idx_parent_name", columns={"parent_id", "name"}), - * }) - */ -class Storelocation extends AbstractPartsContainingDBElement -{ - /** - * @ORM\OneToMany(targetEntity="Storelocation", mappedBy="parent") - * @ORM\OrderBy({"name" = "ASC"}) - * @var Collection - */ - protected $children; - - /** - * @ORM\ManyToOne(targetEntity="Storelocation", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") - */ - protected $parent; - - /** - * @var MeasurementUnit|null The measurement unit, which parts can be stored in here - * @ORM\ManyToOne(targetEntity="MeasurementUnit") - * @ORM\JoinColumn(name="storage_type_id", referencedColumnName="id") - */ - protected ?MeasurementUnit $storage_type = null; - - /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\StorelocationParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() - */ - protected $parameters; - - /** - * @var bool - * @ORM\Column(type="boolean") - */ - protected bool $is_full = false; - - /** - * @var bool - * @ORM\Column(type="boolean") - */ - protected bool $only_single_part = false; - - /** - * @var bool - * @ORM\Column(type="boolean") - */ - protected bool $limit_to_existing_parts = false; - /** - * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\StorelocationAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @Assert\Valid() - */ - protected $attachments; - - /******************************************************************************** - * - * Getters - * - *********************************************************************************/ - - /** - * Get the "is full" attribute. - * - * When this attribute is set, it is not possible to add additional parts or increase the instock of existing parts. - * - * @return bool * true if the store location is full - * * false if the store location isn't full - */ - public function isFull(): bool - { - return $this->is_full; - } - - /** - * When this property is set, only one part (but many instock) is allowed to be stored in this store location. - */ - public function isOnlySinglePart(): bool - { - return $this->only_single_part; - } - - /** - * @return Storelocation - */ - public function setOnlySinglePart(bool $only_single_part): self - { - $this->only_single_part = $only_single_part; - - return $this; - } - - /** - * When this property is set, it is only possible to increase the instock of parts, that are already stored here. - */ - public function isLimitToExistingParts(): bool - { - return $this->limit_to_existing_parts; - } - - /** - * @return Storelocation - */ - public function setLimitToExistingParts(bool $limit_to_existing_parts): self - { - $this->limit_to_existing_parts = $limit_to_existing_parts; - - return $this; - } - - public function getStorageType(): ?MeasurementUnit - { - return $this->storage_type; - } - - /** - * @return Storelocation - */ - public function setStorageType(?MeasurementUnit $storage_type): self - { - $this->storage_type = $storage_type; - - return $this; - } - - /******************************************************************************** - * - * Setters - * - *********************************************************************************/ - - /** - * Change the "is full" attribute of this store location. - * - * "is_full" = true means that there is no more space in this storelocation. - * This attribute is only for information, it has no effect. - * - * @param bool $new_is_full * true means that the storelocation is full - * * false means that the storelocation isn't full - * - * @return Storelocation - */ - public function setIsFull(bool $new_is_full): self - { - $this->is_full = $new_is_full; - - return $this; - } -} diff --git a/src/Entity/Parts/Supplier.php b/src/Entity/Parts/Supplier.php index 8183cf91..2c004e9e 100644 --- a/src/Entity/Parts/Supplier.php +++ b/src/Entity/Parts/Supplier.php @@ -22,8 +22,29 @@ declare(strict_types=1); namespace App\Entity\Parts; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Repository\Parts\SupplierRepository; +use App\Entity\PriceInformations\Orderdetail; +use Doctrine\Common\Collections\ArrayCollection; use App\Entity\Attachments\SupplierAttachment; use App\Entity\Base\AbstractCompany; +use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Parameters\SupplierParameter; use App\Entity\PriceInformations\Currency; use App\Validator\Constraints\BigDecimal\BigDecimalPositiveOrZero; @@ -31,67 +52,103 @@ use App\Validator\Constraints\Selectable; use Brick\Math\BigDecimal; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; /** - * Class Supplier. + * This entity represents a supplier of parts (the company that sells the parts). * - * @ORM\Entity(repositoryClass="App\Repository\Parts\SupplierRepository") - * @ORM\Table("`suppliers`", indexes={ - * @ORM\Index(name="supplier_idx_name", columns={"name"}), - * @ORM\Index(name="supplier_idx_parent_name", columns={"parent_id", "name"}), - * }) + * @extends AbstractCompany */ +#[ORM\Entity(repositoryClass: SupplierRepository::class)] +#[ORM\Table('`suppliers`')] +#[ORM\Index(columns: ['name'], name: 'supplier_idx_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'supplier_idx_parent_name')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@suppliers.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['supplier:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['supplier:write', 'company:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/suppliers/{id}/children.{_format}', + operations: [new GetCollection( + openapi: new Operation(summary: 'Retrieves the children elements of a supplier.'), + security: 'is_granted("@manufacturers.read")' + )], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: Supplier::class) + ], + normalizationContext: ['groups' => ['supplier:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class Supplier extends AbstractCompany { - /** - * @ORM\OneToMany(targetEntity="Supplier", mappedBy="parent") - * @ORM\OrderBy({"name" = "ASC"}) - * @var Collection - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; + + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['supplier:read', 'supplier:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; /** - * @ORM\ManyToOne(targetEntity="Supplier", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") + * @var Collection */ - protected $parent; - - /** - * @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="supplier") - */ - protected $orderdetails; + #[ORM\OneToMany(mappedBy: 'supplier', targetEntity: Orderdetail::class)] + protected Collection $orderdetails; /** * @var Currency|null The currency that should be used by default for order informations with this supplier. * Set to null, to use global base currency. - * @ORM\ManyToOne(targetEntity="App\Entity\PriceInformations\Currency") - * @ORM\JoinColumn(name="default_currency_id", referencedColumnName="id", nullable=true) - * @Selectable() */ + #[ORM\ManyToOne(targetEntity: Currency::class)] + #[ORM\JoinColumn(name: 'default_currency_id')] + #[Selectable] protected ?Currency $default_currency = null; /** - * @var BigDecimal|null the shipping costs that have to be paid, when ordering via this supplier - * @ORM\Column(name="shipping_costs", nullable=true, type="big_decimal", precision=11, scale=5) - * @BigDecimalPositiveOrZero() + * @var BigDecimal|null The shipping costs that have to be paid, when ordering via this supplier */ + #[Groups(['extended', 'full', 'import'])] + #[ORM\Column(name: 'shipping_costs', type: 'big_decimal', precision: 11, scale: 5, nullable: true)] + #[BigDecimalPositiveOrZero] protected ?BigDecimal $shipping_costs = null; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\SupplierAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: SupplierAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + #[Groups(['supplier:read', 'supplier:write'])] + #[ApiProperty(readableLink: false, writableLink: true)] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: SupplierAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['supplier:read', 'supplier:write'])] + #[ApiProperty(readableLink: false, writableLink: true)] + protected ?Attachment $master_picture_attachment = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\SupplierParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() */ - protected $parameters; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: SupplierParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[Groups(['supplier:read', 'supplier:write'])] + #[ApiProperty(readableLink: false, writableLink: true)] + protected Collection $parameters; /** * Gets the currency that should be used by default, when creating a orderdetail with this supplier. @@ -103,8 +160,6 @@ class Supplier extends AbstractCompany /** * Sets the default currency. - * - * @return Supplier */ public function setDefaultCurrency(?Currency $default_currency): self { @@ -127,12 +182,10 @@ class Supplier extends AbstractCompany * Sets the shipping costs for an order with this supplier. * * @param BigDecimal|null $shipping_costs a BigDecimal with the shipping costs - * - * @return Supplier */ public function setShippingCosts(?BigDecimal $shipping_costs): self { - if (null === $shipping_costs) { + if (!$shipping_costs instanceof BigDecimal) { $this->shipping_costs = null; } @@ -143,4 +196,12 @@ class Supplier extends AbstractCompany return $this; } + public function __construct() + { + parent::__construct(); + $this->children = new ArrayCollection(); + $this->orderdetails = new ArrayCollection(); + $this->attachments = new ArrayCollection(); + $this->parameters = new ArrayCollection(); + } } diff --git a/src/Entity/PriceInformations/Currency.php b/src/Entity/PriceInformations/Currency.php index e5d0439d..ce20caf8 100644 --- a/src/Entity/PriceInformations/Currency.php +++ b/src/Entity/PriceInformations/Currency.php @@ -22,6 +22,25 @@ declare(strict_types=1); namespace App\Entity\PriceInformations; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Repository\CurrencyRepository; +use Doctrine\DBAL\Types\Types; use App\Entity\Attachments\CurrencyAttachment; use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Parameters\CurrencyParameter; @@ -32,71 +51,121 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; /** * This entity describes a currency that can be used for price information. * - * @UniqueEntity("iso_code") - * @ORM\Entity() - * @ORM\Table(name="currencies", indexes={ - * @ORM\Index(name="currency_idx_name", columns={"name"}), - * @ORM\Index(name="currency_idx_parent_name", columns={"parent_id", "name"}), - * }) + * @extends AbstractStructuralDBElement */ +#[UniqueEntity('iso_code')] +#[ORM\Entity(repositoryClass: CurrencyRepository::class)] +#[ORM\Table(name: 'currencies')] +#[ORM\Index(columns: ['name'], name: 'currency_idx_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'currency_idx_parent_name')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@currencies.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['currency:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['currency:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/currencies/{id}/children.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the children elements of a currency.'), + security: 'is_granted("@currencies.read")' + ) + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: Currency::class) + ], + normalizationContext: ['groups' => ['currency:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "iso_code"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class Currency extends AbstractStructuralDBElement { - public const PRICE_SCALE = 5; + final public const PRICE_SCALE = 5; /** * @var BigDecimal|null The exchange rate between this currency and the base currency * (how many base units the current currency is worth) - * @ORM\Column(type="big_decimal", precision=11, scale=5, nullable=true) - * @BigDecimalPositive() */ + #[ORM\Column(type: 'big_decimal', precision: 11, scale: 5, nullable: true)] + #[BigDecimalPositive] + #[Groups(['currency:read', 'currency:write', 'simple', 'extended', 'full', 'import'])] + #[ApiProperty(readableLink: false, writableLink: false)] protected ?BigDecimal $exchange_rate = null; + #[Groups(['currency:read', 'currency:write'])] + protected string $comment = ""; + /** * @var string the 3-letter ISO code of the currency - * @ORM\Column(type="string") - * @Assert\Currency() */ + #[Assert\Currency] + #[Assert\NotBlank] + #[Groups(['simple', 'extended', 'full', 'import', 'currency:read', 'currency:write'])] + #[ORM\Column(type: Types::STRING)] protected string $iso_code = ""; - /** - * @ORM\OneToMany(targetEntity="Currency", mappedBy="parent", cascade={"persist"}) - * @ORM\OrderBy({"name" = "ASC"}) - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class, cascade: ['persist'])] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; - /** - * @ORM\ManyToOne(targetEntity="Currency", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") - */ - protected $parent; + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['currency:read', 'currency:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\CurrencyAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: CurrencyAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + #[Groups(['currency:read', 'currency:write'])] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: CurrencyAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['currency:read', 'currency:write'])] + protected ?Attachment $master_picture_attachment = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\CurrencyParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() */ - protected $parameters; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: CurrencyParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[Groups(['currency:read', 'currency:write'])] + protected Collection $parameters; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Pricedetail", mappedBy="currency") */ - protected $pricedetails; + #[ORM\OneToMany(mappedBy: 'currency', targetEntity: Pricedetail::class)] + protected Collection $pricedetails; + + #[Groups(['currency:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['currency:read'])] + protected ?\DateTimeImmutable $lastModified = null; + public function __construct() { + $this->children = new ArrayCollection(); + $this->attachments = new ArrayCollection(); + $this->parameters = new ArrayCollection(); $this->pricedetails = new ArrayCollection(); parent::__construct(); } @@ -111,17 +180,12 @@ class Currency extends AbstractStructuralDBElement * * @return string */ - public function getIsoCode(): ?string + public function getIsoCode(): string { return $this->iso_code; } - /** - * @param string|null $iso_code - * - * @return Currency - */ - public function setIsoCode(?string $iso_code): self + public function setIsoCode(string $iso_code): self { $this->iso_code = $iso_code; @@ -131,11 +195,12 @@ class Currency extends AbstractStructuralDBElement /** * Returns the inverse exchange rate (how many of the current currency the base unit is worth). */ + #[Groups(['currency:read'])] public function getInverseExchangeRate(): ?BigDecimal { $tmp = $this->getExchangeRate(); - if (null === $tmp || $tmp->isZero()) { + if (!$tmp instanceof BigDecimal || $tmp->isZero()) { return null; } @@ -156,17 +221,18 @@ class Currency extends AbstractStructuralDBElement * * @param BigDecimal|null $exchange_rate The new exchange rate of the currency. * Set to null, if the exchange rate is unknown. - * - * @return Currency */ public function setExchangeRate(?BigDecimal $exchange_rate): self { - if (null === $exchange_rate) { + //If the exchange rate is null, set it to null and return. + if ($exchange_rate === null) { $this->exchange_rate = null; + return $this; } - $tmp = $exchange_rate->toScale(self::PRICE_SCALE, RoundingMode::HALF_UP); + //Only change the object, if the value changes, so that doctrine does not detect it as changed. - if ((string) $tmp !== (string) $this->exchange_rate) { + //Or if the current exchange rate is currently null, as we can not compare it then + if ($this->exchange_rate === null || $exchange_rate->compareTo($this->exchange_rate) !== 0) { $this->exchange_rate = $exchange_rate; } diff --git a/src/Entity/PriceInformations/Orderdetail.php b/src/Entity/PriceInformations/Orderdetail.php index e2f0187f..3709b37d 100644 --- a/src/Entity/PriceInformations/Orderdetail.php +++ b/src/Entity/PriceInformations/Orderdetail.php @@ -23,73 +23,128 @@ declare(strict_types=1); namespace App\Entity\PriceInformations; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\TimestampTrait; use App\Entity\Contracts\NamedElementInterface; use App\Entity\Contracts\TimeStampableInterface; use App\Entity\Parts\Part; use App\Entity\Parts\Supplier; -use DateTime; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Constraints\Length; /** * Class Orderdetail. - * - * @ORM\Table("`orderdetails`", indexes={ - * @ORM\Index(name="orderdetails_supplier_part_nr", columns={"supplierpartnr"}), - * }) - * @ORM\Entity() - * @ORM\HasLifecycleCallbacks() - * @UniqueEntity({"supplierpartnr", "supplier", "part"}) */ +#[UniqueEntity(['supplierpartnr', 'supplier', 'part'])] +#[ORM\Entity] +#[ORM\HasLifecycleCallbacks] +#[ORM\Table('`orderdetails`')] +#[ORM\Index(columns: ['supplierpartnr'], name: 'orderdetails_supplier_part_nr')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@parts.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['orderdetail:read', 'orderdetail:read:standalone', 'api:basic:read', 'pricedetail:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['orderdetail:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/parts/{id}/orderdetails.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the orderdetails of a part.'), + security: 'is_granted("@parts.read")' + ) + ], + uriVariables: [ + 'id' => new Link(toProperty: 'part', fromClass: Part::class) + ], + normalizationContext: ['groups' => ['orderdetail:read', 'pricedetail:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["supplierpartnr", "supplier_product_url"])] +#[ApiFilter(BooleanFilter::class, properties: ["obsolete"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['supplierpartnr', 'id', 'addedDate', 'lastModified'])] class Orderdetail extends AbstractDBElement implements TimeStampableInterface, NamedElementInterface { use TimestampTrait; /** - * @ORM\OneToMany(targetEntity="Pricedetail", mappedBy="orderdetail", cascade={"persist", "remove"}, orphanRemoval=true) - * @Assert\Valid() - * @ORM\OrderBy({"min_discount_quantity" = "ASC"}) + * @var Collection */ - protected $pricedetails; + #[Assert\Valid] + #[Groups(['extended', 'full', 'import', 'orderdetail:read', 'orderdetail:write'])] + #[ORM\OneToMany(mappedBy: 'orderdetail', targetEntity: Pricedetail::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['min_discount_quantity' => Criteria::ASC])] + protected Collection $pricedetails; /** - * @var string - * @ORM\Column(type="string") + * @var string The order number of the part at the supplier */ + #[Groups(['extended', 'full', 'import', 'orderdetail:read', 'orderdetail:write'])] + #[ORM\Column(type: Types::STRING)] + #[Length(max: 255)] protected string $supplierpartnr = ''; /** - * @var bool - * @ORM\Column(type="boolean") + * @var bool True if this part is obsolete/not available anymore at the supplier */ + #[Groups(['extended', 'full', 'import', 'orderdetail:read', 'orderdetail:write'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $obsolete = false; /** - * @var string - * @ORM\Column(type="string") - * @Assert\Url() + * @var string The URL to the product on the supplier's website */ + #[Assert\Url] + #[Groups(['full', 'import', 'orderdetail:read', 'orderdetail:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $supplier_product_url = ''; /** - * @var Part - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Part", inversedBy="orderdetails") - * @ORM\JoinColumn(name="part_id", referencedColumnName="id", nullable=false, onDelete="CASCADE") - * @Assert\NotNull() + * @var Part|null The part with which this orderdetail is associated */ + #[Assert\NotNull] + #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'orderdetails')] + #[Groups(['orderdetail:read:standalone', 'orderdetail:write'])] + #[ORM\JoinColumn(name: 'part_id', nullable: false, onDelete: 'CASCADE')] protected ?Part $part = null; /** - * @var Supplier - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Supplier", inversedBy="orderdetails") - * @ORM\JoinColumn(name="id_supplier", referencedColumnName="id") - * @Assert\NotNull(message="validator.orderdetail.supplier_must_not_be_null") + * @var Supplier|null The supplier of this orderdetail */ + #[Assert\NotNull(message: 'validator.orderdetail.supplier_must_not_be_null')] + #[Groups(['extended', 'full', 'import', 'orderdetail:read', 'orderdetail:write'])] + #[ORM\ManyToOne(targetEntity: Supplier::class, inversedBy: 'orderdetails')] + #[ORM\JoinColumn(name: 'id_supplier')] protected ?Supplier $supplier = null; public function __construct() @@ -113,15 +168,14 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N /** * Helper for updating the timestamp. It is automatically called by doctrine before persisting. - * - * @ORM\PrePersist - * @ORM\PreUpdate */ + #[ORM\PrePersist] + #[ORM\PreUpdate] public function updateTimestamps(): void { - $this->lastModified = new DateTime('now'); - if (null === $this->addedDate) { - $this->addedDate = new DateTime('now'); + $this->lastModified = new DateTimeImmutable('now'); + if (!$this->addedDate instanceof \DateTimeInterface) { + $this->addedDate = new DateTimeImmutable('now'); } if ($this->part instanceof Part) { @@ -179,6 +233,11 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N return $this->obsolete; } + public function isObsolete(): bool + { + return $this->getObsolete(); + } + /** * Get the link to the website of the article on the supplier's website. * @@ -193,7 +252,7 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N return $this->supplier_product_url; } - if (null === $this->getSupplier()) { + if (!$this->getSupplier() instanceof Supplier) { return ''; } @@ -203,8 +262,7 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N /** * Get all pricedetails. * - * @return Pricedetail[]|Collection all pricedetails as a one-dimensional array of Pricedetails objects, - * sorted by minimum discount quantity + * @return Collection */ public function getPricedetails(): Collection { @@ -215,8 +273,6 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N * Adds a price detail to this orderdetail. * * @param Pricedetail $pricedetail The pricedetail to add - * - * @return Orderdetail */ public function addPricedetail(Pricedetail $pricedetail): self { @@ -228,8 +284,6 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N /** * Removes a price detail from this orderdetail. - * - * @return Orderdetail */ public function removePricedetail(Pricedetail $pricedetail): self { @@ -272,11 +326,8 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N * Setters * *********************************************************************************/ - /** * Sets a new part with which this orderdetail is associated. - * - * @return Orderdetail */ public function setPart(Part $part): self { @@ -287,8 +338,6 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N /** * Sets the new supplier associated with this orderdetail. - * - * @return Orderdetail */ public function setSupplier(Supplier $new_supplier): self { @@ -301,9 +350,6 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N * Set the supplier part-nr. * * @param string $new_supplierpartnr the new supplier-part-nr - * - * @return Orderdetail - * @return Orderdetail */ public function setSupplierpartnr(string $new_supplierpartnr): self { @@ -316,9 +362,6 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N * Set if the part is obsolete at the supplier of that orderdetails. * * @param bool $new_obsolete true means that this part is obsolete - * - * @return Orderdetail - * @return Orderdetail */ public function setObsolete(bool $new_obsolete): self { @@ -332,13 +375,11 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N * Set this to "", if the function getSupplierProductURL should return the automatic generated URL. * * @param string $new_url The new URL for the supplier URL - * - * @return Orderdetail */ public function setSupplierProductUrl(string $new_url): self { //Only change the internal URL if it is not the auto generated one - if ($new_url === $this->supplier->getAutoProductUrl($this->getSupplierPartNr())) { + if ($this->supplier && $new_url === $this->supplier->getAutoProductUrl($this->getSupplierPartNr())) { return $this; } diff --git a/src/Entity/PriceInformations/Pricedetail.php b/src/Entity/PriceInformations/Pricedetail.php index 0229baf5..86a7bcd5 100644 --- a/src/Entity/PriceInformations/Pricedetail.php +++ b/src/Entity/PriceInformations/Pricedetail.php @@ -22,6 +22,15 @@ declare(strict_types=1); namespace App\Entity\PriceInformations; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\TimestampTrait; use App\Entity\Contracts\TimeStampableInterface; @@ -29,70 +38,87 @@ use App\Validator\Constraints\BigDecimal\BigDecimalPositive; use App\Validator\Constraints\Selectable; use Brick\Math\BigDecimal; use Brick\Math\RoundingMode; -use DateTime; +use DateTimeImmutable; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Annotation\SerializedName; use Symfony\Component\Validator\Constraints as Assert; /** * Class Pricedetail. - * - * @ORM\Entity() - * @ORM\Table("`pricedetails`", indexes={ - * @ORM\Index(name="pricedetails_idx_min_discount", columns={"min_discount_quantity"}), - * @ORM\Index(name="pricedetails_idx_min_discount_price_qty", columns={"min_discount_quantity", "price_related_quantity"}), - * }) - * @ORM\HasLifecycleCallbacks() - * @UniqueEntity(fields={"min_discount_quantity", "orderdetail"}) */ +#[UniqueEntity(fields: ['min_discount_quantity', 'orderdetail'])] +#[ORM\Entity] +#[ORM\HasLifecycleCallbacks] +#[ORM\Table('`pricedetails`')] +#[ORM\Index(columns: ['min_discount_quantity'], name: 'pricedetails_idx_min_discount')] +#[ORM\Index(columns: ['min_discount_quantity', 'price_related_quantity'], name: 'pricedetails_idx_min_discount_price_qty')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@parts.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['pricedetail:read', 'pricedetail:read:standalone', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['pricedetail:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiFilter(PropertyFilter::class)] class Pricedetail extends AbstractDBElement implements TimeStampableInterface { use TimestampTrait; - public const PRICE_PRECISION = 5; + final public const PRICE_PRECISION = 5; /** * @var BigDecimal The price related to the detail. (Given in the selected currency) - * @ORM\Column(type="big_decimal", precision=11, scale=5) - * @BigDecimalPositive() */ + #[Groups(['extended', 'full', 'import', 'pricedetail:read', 'pricedetail:write'])] + #[ORM\Column(type: 'big_decimal', precision: 11, scale: 5)] + #[BigDecimalPositive] protected BigDecimal $price; /** * @var ?Currency The currency used for the current price information. - * If this is null, the global base unit is assumed. - * @ORM\ManyToOne(targetEntity="Currency", inversedBy="pricedetails") - * @ORM\JoinColumn(name="id_currency", referencedColumnName="id", nullable=true) - * @Selectable() + * If this is null, the global base unit is assumed */ + #[Groups(['extended', 'full', 'import', 'pricedetail:read', 'pricedetail:write'])] + #[ORM\ManyToOne(targetEntity: Currency::class, inversedBy: 'pricedetails')] + #[ORM\JoinColumn(name: 'id_currency')] + #[Selectable] protected ?Currency $currency = null; /** - * @var float - * @ORM\Column(type="float") - * @Assert\Positive() + * @var float The amount/quantity for which the price is for (in part unit) */ + #[Assert\Positive] + #[Groups(['extended', 'full', 'import', 'pricedetail:read', 'pricedetail:write'])] + #[ORM\Column(type: Types::FLOAT)] protected float $price_related_quantity = 1.0; /** - * @var float - * @ORM\Column(type="float") - * @Assert\Positive() + * @var float The minimum amount/quantity, which is needed to get this discount (in part unit) */ + #[Assert\Positive] + #[Groups(['extended', 'full', 'import', 'pricedetail:read', 'pricedetail:write'])] + #[ORM\Column(type: Types::FLOAT)] protected float $min_discount_quantity = 1.0; /** * @var bool - * @ORM\Column(type="boolean") */ + #[ORM\Column(type: Types::BOOLEAN)] protected bool $manual_input = true; /** * @var Orderdetail|null - * @ORM\ManyToOne(targetEntity="Orderdetail", inversedBy="pricedetails") - * @ORM\JoinColumn(name="orderdetails_id", referencedColumnName="id", nullable=false, onDelete="CASCADE") - * @Assert\NotNull() */ + #[Assert\NotNull] + #[ORM\ManyToOne(targetEntity: Orderdetail::class, inversedBy: 'pricedetails')] + #[ORM\JoinColumn(name: 'orderdetails_id', nullable: false, onDelete: 'CASCADE')] + #[Groups(['pricedetail:read:standalone', 'pricedetail:write'])] protected ?Orderdetail $orderdetail = null; public function __construct() @@ -110,15 +136,14 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface /** * Helper for updating the timestamp. It is automatically called by doctrine before persisting. - * - * @ORM\PrePersist - * @ORM\PreUpdate */ + #[ORM\PrePersist] + #[ORM\PreUpdate] public function updateTimestamps(): void { - $this->lastModified = new DateTime('now'); - if (null === $this->addedDate) { - $this->addedDate = new DateTime('now'); + $this->lastModified = new DateTimeImmutable('now'); + if (!$this->addedDate instanceof \DateTimeInterface) { + $this->addedDate = new DateTimeImmutable('now'); } if ($this->orderdetail instanceof Orderdetail) { @@ -164,7 +189,9 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface * * @return BigDecimal the price as a bcmath string */ - public function getPricePerUnit($multiplier = 1.0): BigDecimal + #[Groups(['pricedetail:read'])] + #[SerializedName('price_per_unit')] + public function getPricePerUnit(float|string|BigDecimal $multiplier = 1.0): BigDecimal { $tmp = BigDecimal::of($multiplier); $tmp = $tmp->multipliedBy($this->price); @@ -225,6 +252,18 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface return $this->currency; } + /** + * Returns the ISO code of the currency associated with this price information, or null if no currency is selected. + * Then the global base currency should be assumed. + * @return string|null + */ + #[Groups(['pricedetail:read'])] + #[SerializedName('currency_iso_code')] + public function getCurrencyISOCode(): ?string + { + return $this->currency?->getIsoCode(); + } + /******************************************************************************** * * Setters @@ -246,8 +285,6 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface /** * Sets the currency associated with the price information. * Set to null, to use the global base currency. - * - * @return Pricedetail */ public function setCurrency(?Currency $currency): self { @@ -261,9 +298,9 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface * * @param BigDecimal $new_price the new price as a float number * - * * This is the price for "price_related_quantity" parts!! - * * Example: if "price_related_quantity" is '10', - * you have to set here the price for 10 parts! + * This is the price for "price_related_quantity" parts!! + * Example: if "price_related_quantity" is 10, + * you have to set here the price for 10 parts! * * @return $this */ diff --git a/src/Entity/ProjectSystem/Project.php b/src/Entity/ProjectSystem/Project.php index 4c71b507..a103d694 100644 --- a/src/Entity/ProjectSystem/Project.php +++ b/src/Entity/ProjectSystem/Project.php @@ -22,6 +22,24 @@ declare(strict_types=1); namespace App\Entity\ProjectSystem; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Repository\Parts\DeviceRepository; +use App\Validator\Constraints\UniqueObjectCollection; +use Doctrine\DBAL\Types\Types; use App\Entity\Attachments\ProjectAttachment; use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Parameters\ProjectParameter; @@ -30,77 +48,120 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use InvalidArgumentException; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; /** - * Class AttachmentType. + * This class represents a project in the database. * - * @ORM\Entity(repositoryClass="App\Repository\Parts\DeviceRepository") - * @ORM\Table(name="projects") + * @extends AbstractStructuralDBElement */ +#[ORM\Entity(repositoryClass: DeviceRepository::class)] +#[ORM\Table(name: 'projects')] +#[ApiResource( + operations: [ + new Get(security: 'is_granted("read", object)'), + new GetCollection(security: 'is_granted("@projects.read")'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Patch(security: 'is_granted("edit", object)'), + new Delete(security: 'is_granted("delete", object)'), + ], + normalizationContext: ['groups' => ['project:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['project:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/projects/{id}/children.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the children elements of a project.'), + security: 'is_granted("@projects.read")' + ) + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'children', fromClass: Project::class) + ], + normalizationContext: ['groups' => ['project:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] class Project extends AbstractStructuralDBElement { - /** - * @ORM\OneToMany(targetEntity="Project", mappedBy="parent") - * @ORM\OrderBy({"name" = "ASC"}) - * @var Collection - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; + + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + #[Groups(['project:read', 'project:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] + protected ?AbstractStructuralDBElement $parent = null; + + #[Groups(['project:read', 'project:write'])] + protected string $comment = ''; /** - * @ORM\ManyToOne(targetEntity="Project", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") + * @var Collection */ - protected $parent; + #[Assert\Valid] + #[Groups(['extended', 'full', 'import'])] + #[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectBOMEntry::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part'])] + #[UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name'])] + protected Collection $bom_entries; - /** - * @ORM\OneToMany(targetEntity="ProjectBOMEntry", mappedBy="project", cascade={"persist", "remove"}, orphanRemoval=true) - * @Assert\Valid() - */ - protected $bom_entries; - - /** - * @ORM\Column(type="integer") - */ + #[ORM\Column(type: Types::INTEGER)] protected int $order_quantity = 0; /** - * @var string The current status of the project - * @ORM\Column(type="string", length=64, nullable=true) - * @Assert\Choice({"draft","planning","in_production","finished","archived"}) + * @var string|null The current status of the project */ + #[Assert\Choice(['draft', 'planning', 'in_production', 'finished', 'archived'])] + #[Groups(['extended', 'full', 'project:read', 'project:write', 'import'])] + #[ORM\Column(type: Types::STRING, length: 64, nullable: true)] protected ?string $status = null; /** * @var Part|null The (optional) part that represents the builds of this project in the stock - * @ORM\OneToOne(targetEntity="App\Entity\Parts\Part", mappedBy="built_project", cascade={"persist"}, orphanRemoval=true) */ + #[ORM\OneToOne(mappedBy: 'built_project', targetEntity: Part::class, cascade: ['persist'], orphanRemoval: true)] + #[Groups(['project:read', 'project:write'])] protected ?Part $build_part = null; - /** - * @ORM\Column(type="boolean") - */ + #[ORM\Column(type: Types::BOOLEAN)] protected bool $order_only_missing_parts = false; - /** - * @ORM\Column(type="text", nullable=false, columnDefinition="DEFAULT ''") - */ + #[Groups(['simple', 'extended', 'full', 'project:read', 'project:write'])] + #[ORM\Column(type: Types::TEXT)] protected string $description = ''; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\ProjectAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) */ - protected $attachments; + #[ORM\OneToMany(mappedBy: 'element', targetEntity: ProjectAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + #[Groups(['project:read', 'project:write'])] + protected Collection $attachments; + + #[ORM\ManyToOne(targetEntity: ProjectAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['project:read', 'project:write'])] + protected ?Attachment $master_picture_attachment = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\ProjectParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) */ - protected $parameters; + #[ORM\OneToMany(mappedBy: 'element', targetEntity: ProjectParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + #[Groups(['project:read', 'project:write'])] + protected Collection $parameters; + + #[Groups(['project:read'])] + protected ?\DateTimeImmutable $addedDate = null; + #[Groups(['project:read'])] + protected ?\DateTimeImmutable $lastModified = null; + /******************************************************************************** * @@ -110,8 +171,11 @@ class Project extends AbstractStructuralDBElement public function __construct() { + $this->attachments = new ArrayCollection(); + $this->parameters = new ArrayCollection(); parent::__construct(); $this->bom_entries = new ArrayCollection(); + $this->children = new ArrayCollection(); } public function __clone() @@ -123,7 +187,7 @@ class Project extends AbstractStructuralDBElement //Set master attachment is needed foreach ($bom_entries as $bom_entry) { $clone = clone $bom_entry; - $this->bom_entries->add($clone); + $this->addBomEntry($clone); } } @@ -178,8 +242,6 @@ class Project extends AbstractStructuralDBElement * Set the "order_only_missing_parts" attribute. * * @param bool $new_order_only_missing_parts the new "order_only_missing_parts" attribute - * - * @return Project */ public function setOrderOnlyMissingParts(bool $new_order_only_missing_parts): self { @@ -188,16 +250,12 @@ class Project extends AbstractStructuralDBElement return $this; } - /** - * @return Collection|ProjectBOMEntry[] - */ public function getBomEntries(): Collection { return $this->bom_entries; } /** - * @param ProjectBOMEntry $entry * @return $this */ public function addBomEntry(ProjectBOMEntry $entry): self @@ -208,7 +266,6 @@ class Project extends AbstractStructuralDBElement } /** - * @param ProjectBOMEntry $entry * @return $this */ public function removeBomEntry(ProjectBOMEntry $entry): self @@ -217,18 +274,11 @@ class Project extends AbstractStructuralDBElement return $this; } - /** - * @return string - */ public function getDescription(): string { return $this->description; } - /** - * @param string $description - * @return Project - */ public function setDescription(string $description): Project { $this->description = $description; @@ -253,16 +303,14 @@ class Project extends AbstractStructuralDBElement /** * Checks if this project has an associated part representing the builds of this project in the stock. - * @return bool */ public function hasBuildPart(): bool { - return $this->build_part !== null; + return $this->build_part instanceof Part; } /** * Gets the part representing the builds of this project in the stock, if it is existing - * @return Part|null */ public function getBuildPart(): ?Part { @@ -271,25 +319,21 @@ class Project extends AbstractStructuralDBElement /** * Sets the part representing the builds of this project in the stock. - * @param Part|null $build_part */ public function setBuildPart(?Part $build_part): void { $this->build_part = $build_part; - if ($build_part) { + if ($build_part instanceof Part) { $build_part->setBuiltProject($this); } } - /** - * @Assert\Callback - */ - public function validate(ExecutionContextInterface $context, $payload) + #[Assert\Callback] + public function validate(ExecutionContextInterface $context, $payload): void { //If this project has subprojects, and these have builds part, they must be included in the BOM foreach ($this->getChildren() as $child) { - /** @var $child Project */ - if ($child->getBuildPart() === null) { + if (!$child->getBuildPart() instanceof Part) { continue; } //We have to search all bom entries for the build part diff --git a/src/Entity/ProjectSystem/ProjectBOMEntry.php b/src/Entity/ProjectSystem/ProjectBOMEntry.php index e7ef436f..2a7862ec 100644 --- a/src/Entity/ProjectSystem/ProjectBOMEntry.php +++ b/src/Entity/ProjectSystem/ProjectBOMEntry.php @@ -22,6 +22,22 @@ declare(strict_types=1); namespace App\Entity\ProjectSystem; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Doctrine\Orm\Filter\RangeFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Link; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Contracts\TimeStampableInterface; +use App\Validator\UniqueValidatableInterface; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\TimestampTrait; use App\Entity\Parts\Part; @@ -30,119 +46,127 @@ use App\Validator\Constraints\BigDecimal\BigDecimalPositive; use App\Validator\Constraints\Selectable; use Brick\Math\BigDecimal; use Doctrine\ORM\Mapping as ORM; -use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; /** * The ProjectBOMEntry class represents an entry in a project's BOM. - * - * @ORM\Table("project_bom_entries") - * @ORM\HasLifecycleCallbacks() - * @ORM\Entity() - * @UniqueEntity(fields={"part", "project"}, message="project.bom_entry.part_already_in_bom") - * @UniqueEntity(fields={"name", "project"}, message="project.bom_entry.name_already_in_bom", ignoreNull=true) */ -class ProjectBOMEntry extends AbstractDBElement +#[ORM\HasLifecycleCallbacks] +#[ORM\Entity] +#[ORM\Table('project_bom_entries')] +#[ApiResource( + operations: [ + new Get(uriTemplate: '/project_bom_entries/{id}.{_format}', security: 'is_granted("read", object)',), + new GetCollection(uriTemplate: '/project_bom_entries.{_format}', security: 'is_granted("@projects.read")',), + new Post(uriTemplate: '/project_bom_entries.{_format}', securityPostDenormalize: 'is_granted("create", object)',), + new Patch(uriTemplate: '/project_bom_entries/{id}.{_format}', security: 'is_granted("edit", object)',), + new Delete(uriTemplate: '/project_bom_entries/{id}.{_format}', security: 'is_granted("delete", object)',), + ], + normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + denormalizationContext: ['groups' => ['bom_entry:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'], +)] +#[ApiResource( + uriTemplate: '/projects/{id}/bom.{_format}', + operations: [ + new GetCollection( + openapi: new Operation(summary: 'Retrieves the BOM entries of the given project.'), + security: 'is_granted("@projects.read")' + ) + ], + uriVariables: [ + 'id' => new Link(fromProperty: 'bom_entries', fromClass: Project::class) + ], + normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "comment", 'mountnames'])] +#[ApiFilter(RangeFilter::class, properties: ['quantity'])] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified', 'quantity'])] +class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInterface, TimeStampableInterface { use TimestampTrait; - /** - * @var float - * @ORM\Column(type="float", name="quantity") - * @Assert\Positive() - */ - protected float $quantity; + #[Assert\Positive] + #[ORM\Column(name: 'quantity', type: Types::FLOAT)] + #[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])] + protected float $quantity = 1.0; /** * @var string A comma separated list of the names, where this parts should be placed - * @ORM\Column(type="text", name="mountnames") */ - protected string $mountnames; + #[ORM\Column(name: 'mountnames', type: Types::TEXT)] + #[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])] + protected string $mountnames = ''; /** - * @var string An optional name describing this BOM entry (useful for non-part entries) - * @ORM\Column(type="string", nullable=true) - * @Assert\Expression( - * "this.getPart() !== null or this.getName() !== null", - * message="validator.project.bom_entry.name_or_part_needed" - * ) + * @var string|null An optional name describing this BOM entry (useful for non-part entries) */ + #[Assert\Expression('this.getPart() !== null or this.getName() !== null', message: 'validator.project.bom_entry.name_or_part_needed')] + #[ORM\Column(type: Types::STRING, nullable: true)] + #[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])] protected ?string $name = null; /** * @var string An optional comment for this BOM entry - * @ORM\Column(type="text") */ - protected string $comment; + #[ORM\Column(type: Types::TEXT)] + #[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'extended', 'full'])] + protected string $comment = ''; /** - * @var Project - * @ORM\ManyToOne(targetEntity="Project", inversedBy="bom_entries") - * @ORM\JoinColumn(name="id_device", referencedColumnName="id") + * @var Project|null */ + #[ORM\ManyToOne(targetEntity: Project::class, inversedBy: 'bom_entries')] + #[ORM\JoinColumn(name: 'id_device')] + #[Groups(['bom_entry:read', 'bom_entry:write', ])] protected ?Project $project = null; /** * @var Part|null The part associated with this - * @ORM\ManyToOne(targetEntity="App\Entity\Parts\Part", inversedBy="project_bom_entries") - * @ORM\JoinColumn(name="id_part", referencedColumnName="id", nullable=true) */ + #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'project_bom_entries')] + #[ORM\JoinColumn(name: 'id_part')] + #[Groups(['bom_entry:read', 'bom_entry:write', 'full'])] protected ?Part $part = null; /** - * @var BigDecimal The price of this non-part BOM entry - * @ORM\Column(type="big_decimal", precision=11, scale=5, nullable=true) - * @Assert\AtLeastOneOf({ - * @BigDecimalPositive(), - * @Assert\IsNull() - * }) + * @var BigDecimal|null The price of this non-part BOM entry */ - protected ?BigDecimal $price; + #[Assert\AtLeastOneOf([new BigDecimalPositive(), new Assert\IsNull()])] + #[ORM\Column(type: 'big_decimal', precision: 11, scale: 5, nullable: true)] + #[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'extended', 'full'])] + protected ?BigDecimal $price = null; /** * @var ?Currency The currency for the price of this non-part BOM entry - * @ORM\ManyToOne(targetEntity="App\Entity\PriceInformations\Currency") - * @ORM\JoinColumn(nullable=true) - * @Selectable() */ + #[ORM\ManyToOne(targetEntity: Currency::class)] + #[ORM\JoinColumn] + #[Selectable] protected ?Currency $price_currency = null; public function __construct() { - $this->price = BigDecimal::zero()->toScale(5); } - /** - * @return float - */ public function getQuantity(): float { return $this->quantity; } - /** - * @param float $quantity - * @return ProjectBOMEntry - */ public function setQuantity(float $quantity): ProjectBOMEntry { $this->quantity = $quantity; return $this; } - /** - * @return string - */ public function getMountnames(): string { return $this->mountnames; } - /** - * @param string $mountnames - * @return ProjectBOMEntry - */ public function setMountnames(string $mountnames): ProjectBOMEntry { $this->mountnames = $mountnames; @@ -159,7 +183,6 @@ class ProjectBOMEntry extends AbstractDBElement /** * @param string $name - * @return ProjectBOMEntry */ public function setName(?string $name): ProjectBOMEntry { @@ -167,36 +190,22 @@ class ProjectBOMEntry extends AbstractDBElement return $this; } - /** - * @return string - */ public function getComment(): string { return $this->comment; } - /** - * @param string $comment - * @return ProjectBOMEntry - */ public function setComment(string $comment): ProjectBOMEntry { $this->comment = $comment; return $this; } - /** - * @return Project|null - */ public function getProject(): ?Project { return $this->project; } - /** - * @param Project|null $project - * @return ProjectBOMEntry - */ public function setProject(?Project $project): ProjectBOMEntry { $this->project = $project; @@ -205,18 +214,11 @@ class ProjectBOMEntry extends AbstractDBElement - /** - * @return Part|null - */ public function getPart(): ?Part { return $this->part; } - /** - * @param Part|null $part - * @return ProjectBOMEntry - */ public function setPart(?Part $part): ProjectBOMEntry { $this->part = $part; @@ -226,7 +228,6 @@ class ProjectBOMEntry extends AbstractDBElement /** * Returns the price of this BOM entry, if existing. * Prices are only valid on non-Part BOM entries. - * @return BigDecimal|null */ public function getPrice(): ?BigDecimal { @@ -236,24 +237,17 @@ class ProjectBOMEntry extends AbstractDBElement /** * Sets the price of this BOM entry. * Prices are only valid on non-Part BOM entries. - * @param BigDecimal|null $price */ public function setPrice(?BigDecimal $price): void { $this->price = $price; } - /** - * @return Currency|null - */ public function getPriceCurrency(): ?Currency { return $this->price_currency; } - /** - * @param Currency|null $price_currency - */ public function setPriceCurrency(?Currency $price_currency): void { $this->price_currency = $price_currency; @@ -265,22 +259,18 @@ class ProjectBOMEntry extends AbstractDBElement */ public function isPartBomEntry(): bool { - return $this->part !== null; + return $this->part instanceof Part; } - /** - * @Assert\Callback - */ + #[Assert\Callback] public function validate(ExecutionContextInterface $context, $payload): void { //Round quantity to whole numbers, if the part is not a decimal part - if ($this->part) { - if (!$this->part->getPartUnit() || $this->part->getPartUnit()->isInteger()) { - $this->quantity = round($this->quantity); - } + if ($this->part instanceof Part && (!$this->part->getPartUnit() || $this->part->getPartUnit()->isInteger())) { + $this->quantity = round($this->quantity); } //Non-Part BOM entries are rounded - if ($this->part === null) { + if (!$this->part instanceof Part) { $this->quantity = round($this->quantity); } @@ -297,21 +287,21 @@ class ProjectBOMEntry extends AbstractDBElement } //Check that the number of mountnames is the same as the (rounded) quantity - if (!empty($this->mountnames) && count($uniq_mountnames) !== (int) round ($this->quantity)) { + if ($this->mountnames !== '' && count($uniq_mountnames) !== (int) round ($this->quantity)) { $context->buildViolation('project.bom_entry.mountnames_quantity_mismatch') ->atPath('mountnames') ->addViolation(); } //Prices are only allowed on non-part BOM entries - if ($this->part !== null && $this->price !== null) { + if ($this->part instanceof Part && $this->price instanceof BigDecimal) { $context->buildViolation('project.bom_entry.price_not_allowed_on_parts') ->atPath('price') ->addViolation(); } //Check that the part is not the build representation part of this device or one of its parents - if ($this->part && $this->part->getBuiltProject() !== null) { + if ($this->part && $this->part->getBuiltProject() instanceof Project) { //Get the associated project $associated_project = $this->part->getBuiltProject(); //Check that it is not the same as the current project neither one of its parents @@ -328,4 +318,11 @@ class ProjectBOMEntry extends AbstractDBElement } + public function getComparableFields(): array + { + return [ + 'name' => $this->getName(), + 'part' => $this->getPart()?->getID(), + ]; + } } diff --git a/src/Entity/UserSystem/ApiToken.php b/src/Entity/UserSystem/ApiToken.php new file mode 100644 index 00000000..f5cbf541 --- /dev/null +++ b/src/Entity/UserSystem/ApiToken.php @@ -0,0 +1,199 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\UserSystem; + +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\Entity\Base\TimestampTrait; +use App\Entity\Contracts\TimeStampableInterface; +use App\Repository\UserSystem\ApiTokenRepository; +use App\State\CurrentApiTokenProvider; +use App\Validator\Constraints\Year2038BugWorkaround; +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; + +#[ORM\Entity(repositoryClass: ApiTokenRepository::class)] +#[ORM\Table(name: 'api_tokens')] +#[ORM\HasLifecycleCallbacks] +#[UniqueEntity(fields: ['name', 'user'])] + +#[ApiResource( + uriTemplate: '/tokens/current.{_format}', + description: 'A token used to authenticate API requests.', + operations: [new Get( + openapi: new Operation(summary: 'Get information about the API token that is currently used.'), + )], + normalizationContext: ['groups' => ['token:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], + provider: CurrentApiTokenProvider::class, +)] +#[ApiFilter(PropertyFilter::class)] +class ApiToken implements TimeStampableInterface +{ + + use TimestampTrait; + + #[ORM\Id] + #[ORM\Column(type: Types::INTEGER)] + #[ORM\GeneratedValue] + protected int $id; + + #[ORM\Column(type: Types::STRING)] + #[Length(max: 255)] + #[NotBlank] + #[Groups('token:read')] + protected string $name = ''; + + #[ORM\ManyToOne(inversedBy: 'api_tokens')] + #[Groups('token:read')] + private ?User $user = null; + + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + #[Groups('token:read')] + #[Year2038BugWorkaround] + private ?\DateTimeImmutable $valid_until; + + #[ORM\Column(length: 68, unique: true)] + private string $token; + + #[ORM\Column(type: Types::SMALLINT, enumType: ApiTokenLevel::class)] + #[Groups('token:read')] + private ApiTokenLevel $level = ApiTokenLevel::READ_ONLY; + + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + #[Groups('token:read')] + private ?\DateTimeImmutable $last_time_used = null; + + public function __construct(ApiTokenType $tokenType = ApiTokenType::PERSONAL_ACCESS_TOKEN) + { + // Generate a rondom token on creation. The tokenType is 3 characters long (plus underscore), so the token is 68 characters long. + $this->token = $tokenType->getTokenPrefix() . bin2hex(random_bytes(32)); + + //By default, tokens are valid for 1 year. + $this->valid_until = new \DateTimeImmutable('+1 year'); + } + + public function getTokenType(): ApiTokenType + { + return ApiTokenType::getTypeFromToken($this->token); + } + + public function getUser(): ?User + { + return $this->user; + } + + public function setUser(?User $user): ApiToken + { + $this->user = $user; + return $this; + } + + public function getValidUntil(): ?\DateTimeImmutable + { + return $this->valid_until; + } + + /** + * Checks if the token is still valid. + * @return bool + */ + public function isValid(): bool + { + return $this->valid_until === null || $this->valid_until > new \DateTimeImmutable(); + } + + public function setValidUntil(?\DateTimeImmutable $valid_until): ApiToken + { + $this->valid_until = $valid_until; + return $this; + } + + public function getToken(): string + { + return $this->token; + } + + public function getId(): int + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function setName(string $name): ApiToken + { + $this->name = $name; + return $this; + } + + /** + * Gets the last time the token was used to authenticate or null if it was never used. + */ + public function getLastTimeUsed(): ?\DateTimeImmutable + { + return $this->last_time_used; + } + + /** + * Sets the last time the token was used to authenticate. + * @return ApiToken + */ + public function setLastTimeUsed(?\DateTimeImmutable $last_time_used): ApiToken + { + $this->last_time_used = $last_time_used; + return $this; + } + + public function getLevel(): ApiTokenLevel + { + return $this->level; + } + + public function setLevel(ApiTokenLevel $level): ApiToken + { + $this->level = $level; + return $this; + } + + /** + * Returns the last 4 characters of the token secret, which can be used to identify the token. + * @return string + */ + public function getLastTokenChars(): string + { + return substr($this->token, -4); + } + + +} \ No newline at end of file diff --git a/src/Entity/UserSystem/ApiTokenLevel.php b/src/Entity/UserSystem/ApiTokenLevel.php new file mode 100644 index 00000000..3f997300 --- /dev/null +++ b/src/Entity/UserSystem/ApiTokenLevel.php @@ -0,0 +1,73 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\UserSystem; + +enum ApiTokenLevel: int +{ + private const ROLE_READ_ONLY = 'ROLE_API_READ_ONLY'; + private const ROLE_EDIT = 'ROLE_API_EDIT'; + private const ROLE_ADMIN = 'ROLE_API_ADMIN'; + private const ROLE_FULL = 'ROLE_API_FULL'; + + /** + * The token can only read (non-sensitive) data. + */ + case READ_ONLY = 1; + /** + * The token can read and edit (non-sensitive) data. + */ + case EDIT = 2; + /** + * The token can do some administrative tasks (like viewing all log entries), but can not change passwords and create new tokens. + */ + case ADMIN = 3; + /** + * The token can do everything the user can do. + */ + case FULL = 4; + + /** + * Returns the additional roles that the authenticated user should have when using this token. + * @return string[] + */ + public function getAdditionalRoles(): array + { + //The higher roles should always include the lower ones + return match ($this) { + self::READ_ONLY => [self::ROLE_READ_ONLY], + self::EDIT => [self::ROLE_READ_ONLY, self::ROLE_EDIT], + self::ADMIN => [self::ROLE_READ_ONLY, self::ROLE_EDIT, self::ROLE_ADMIN], + self::FULL => [self::ROLE_READ_ONLY, self::ROLE_EDIT, self::ROLE_ADMIN, self::ROLE_FULL], + }; + } + + /** + * Returns the translation key for the name of this token level. + * @return string + */ + public function getTranslationKey(): string + { + return 'api_token.level.' . strtolower($this->name); + } +} \ No newline at end of file diff --git a/src/Entity/UserSystem/ApiTokenType.php b/src/Entity/UserSystem/ApiTokenType.php new file mode 100644 index 00000000..f8beb378 --- /dev/null +++ b/src/Entity/UserSystem/ApiTokenType.php @@ -0,0 +1,56 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Entity\UserSystem; + +/** + * The type of ApiToken. + * The enum value is the prefix of the token. It must be 3 characters long. + */ +enum ApiTokenType: string +{ + case PERSONAL_ACCESS_TOKEN = 'tcp'; + + /** + * Get the prefix of the token including the underscore + * @return string + */ + public function getTokenPrefix(): string + { + return $this->value . '_'; + } + + /** + * Get the type from the token prefix + * @param string $api_token + * @return ApiTokenType + */ + public static function getTypeFromToken(string $api_token): ApiTokenType + { + $parts = explode('_', $api_token); + if (count($parts) !== 2) { + throw new \InvalidArgumentException('Invalid token format'); + } + return self::from($parts[0]); + } +} diff --git a/src/Entity/UserSystem/Group.php b/src/Entity/UserSystem/Group.php index 0b29036f..6da9d35f 100644 --- a/src/Entity/UserSystem/Group.php +++ b/src/Entity/UserSystem/Group.php @@ -22,6 +22,10 @@ declare(strict_types=1); namespace App\Entity\UserSystem; +use Doctrine\Common\Collections\Criteria; +use App\Entity\Attachments\Attachment; +use App\Validator\Constraints\NoLockout; +use Doctrine\DBAL\Types\Types; use App\Entity\Attachments\GroupAttachment; use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Parameters\GroupParameter; @@ -30,70 +34,75 @@ use App\Validator\Constraints\ValidPermission; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; /** - * This entity represents an user group. + * This entity represents a user group. * - * @ORM\Entity() - * @ORM\Table("`groups`", indexes={ - * @ORM\Index(name="group_idx_name", columns={"name"}), - * @ORM\Index(name="group_idx_parent_name", columns={"parent_id", "name"}), - * }) + * @extends AbstractStructuralDBElement */ +#[ORM\Entity] +#[ORM\Table('`groups`')] +#[ORM\Index(columns: ['name'], name: 'group_idx_name')] +#[ORM\Index(columns: ['parent_id', 'name'], name: 'group_idx_parent_name')] +#[NoLockout] class Group extends AbstractStructuralDBElement implements HasPermissionsInterface { - /** - * @ORM\OneToMany(targetEntity="Group", mappedBy="parent") - * @ORM\OrderBy({"name" = "ASC"}) - * @var Collection - */ - protected $children; + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $children; + + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')] + #[ORM\JoinColumn(name: 'parent_id')] + protected ?AbstractStructuralDBElement $parent = null; /** - * @ORM\ManyToOne(targetEntity="Group", inversedBy="children") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") + * @var Collection */ - protected $parent; + #[ORM\OneToMany(mappedBy: 'group', targetEntity: User::class)] + protected Collection $users; /** - * @ORM\OneToMany(targetEntity="User", mappedBy="group") - * @var Collection + * @var bool If true all users associated with this group must have enabled some kind of two-factor authentication */ - protected $users; + #[Groups(['extended', 'full', 'import'])] + #[ORM\Column(name: 'enforce_2fa', type: Types::BOOLEAN)] + protected bool $enforce2FA = false; - /** - * @var bool If true all users associated with this group must have enabled some kind of 2 factor authentication - * @ORM\Column(type="boolean", name="enforce_2fa") - */ - protected $enforce2FA = false; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\GroupAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) - * @Assert\Valid() */ - protected $attachments; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: GroupAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + protected Collection $attachments; - /** - * @var PermissionData|null - * @ValidPermission() - * @ORM\Embedded(class="PermissionData", columnPrefix="permissions_") - */ + #[ORM\ManyToOne(targetEntity: GroupAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + protected ?Attachment $master_picture_attachment = null; + + #[Groups(['full'])] + #[ORM\Embedded(class: PermissionData::class, columnPrefix: 'permissions_')] + #[ValidPermission] protected ?PermissionData $permissions = null; - /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Parameters\GroupParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"group" = "ASC" ,"name" = "ASC"}) - * @Assert\Valid() + /** + * @var Collection */ - protected $parameters; + #[Assert\Valid] + #[ORM\OneToMany(mappedBy: 'element', targetEntity: GroupParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])] + protected Collection $parameters; public function __construct() { + $this->attachments = new ArrayCollection(); + $this->parameters = new ArrayCollection(); parent::__construct(); $this->permissions = new PermissionData(); $this->users = new ArrayCollection(); + $this->children = new ArrayCollection(); } /** @@ -120,7 +129,7 @@ class Group extends AbstractStructuralDBElement implements HasPermissionsInterfa public function getPermissions(): PermissionData { - if ($this->permissions === null) { + if (!$this->permissions instanceof PermissionData) { $this->permissions = new PermissionData(); } diff --git a/src/Entity/UserSystem/PermissionData.php b/src/Entity/UserSystem/PermissionData.php index 0ca9cff3..9ebdc9c9 100644 --- a/src/Entity/UserSystem/PermissionData.php +++ b/src/Entity/UserSystem/PermissionData.php @@ -1,4 +1,7 @@ . */ - namespace App\Entity\UserSystem; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; /** * This class is used to store the permissions of a user. * This has to be an embeddable or otherwise doctrine could not track the changes of the underlying data array (which is serialized to JSON in the database) - * - * @ORM\Embeddable() + * @see \App\Tests\Entity\UserSystem\PermissionDataTest */ +#[ORM\Embeddable] final class PermissionData implements \JsonSerializable { /** @@ -40,29 +43,24 @@ final class PermissionData implements \JsonSerializable /** * The current schema version of the permission data */ - public const CURRENT_SCHEMA_VERSION = 2; - - /** - * @var array This array contains the permission values for each permission - * This array contains the permission values for each permission, in the form of: - * permission => [ - * operation => value, - * ] - * @ORM\Column(type="json", name="data", options={"default": "[]"}) - */ - protected ?array $data = [ - //$ prefixed entries are used for metadata - '$ver' => self::CURRENT_SCHEMA_VERSION, //The schema version of the permission data - ]; + public const CURRENT_SCHEMA_VERSION = 3; /** * Creates a new Permission Data Instance using the given data. - * By default, a empty array is used, meaning + * By default, an empty array is used, meaning */ - public function __construct(array $data = []) + public function __construct( + /** + * @var array This array contains the permission values for each permission + * This array contains the permission values for each permission, in the form of: + * permission => [ + * operation => value, + * ] + */ + #[ORM\Column(name: 'data', type: Types::JSON)] + protected array $data = [] + ) { - $this->data = $data; - //If the passed data did not contain a schema version, we set it to the current version if (!isset($this->data['$ver'])) { $this->data['$ver'] = self::CURRENT_SCHEMA_VERSION; @@ -71,8 +69,6 @@ final class PermissionData implements \JsonSerializable /** * Checks if any of the operations of the given permission is defined (meaning it is either ALLOW or DENY) - * @param string $permission - * @return bool */ public function isAnyOperationOfPermissionSet(string $permission): bool { @@ -81,7 +77,6 @@ final class PermissionData implements \JsonSerializable /** * Returns an associative array containing all defined (non-INHERIT) operations of the given permission. - * @param string $permission * @return array An array in the form ["operation" => value], returns an empty array if no operations are defined */ public function getAllDefinedOperationsOfPermission(string $permission): array @@ -96,8 +91,6 @@ final class PermissionData implements \JsonSerializable /** * Sets all operations of the given permission via the given array. * The data is an array in the form [$operation => $value], all existing values will be overwritten/deleted. - * @param string $permission - * @param array $data * @return $this */ public function setAllOperationsOfPermission(string $permission, array $data): self @@ -109,7 +102,6 @@ final class PermissionData implements \JsonSerializable /** * Removes a whole permission from the data including all operations (effectivly setting them to INHERIT) - * @param string $permission * @return $this */ public function removePermission(string $permission): self @@ -121,14 +113,12 @@ final class PermissionData implements \JsonSerializable /** * Check if a permission value is set for the given permission and operation (meaning there value is not inherit). - * @param string $permission - * @param string $operation * @return bool True if the permission value is set, false otherwise */ public function isPermissionSet(string $permission, string $operation): bool { //We cannot access metadata via normal permission data - if (strpos($permission, '$') !== false) { + if (str_contains($permission, '$')) { return false; } @@ -137,8 +127,6 @@ final class PermissionData implements \JsonSerializable /** * Returns the permission value for the given permission and operation. - * @param string $permission - * @param string $operation * @return bool|null True means allow, false means disallow, null means inherit */ public function getPermissionValue(string $permission, string $operation): ?bool @@ -153,15 +141,12 @@ final class PermissionData implements \JsonSerializable /** * Sets the permission value for the given permission and operation. - * @param string $permission - * @param string $operation - * @param bool|null $value * @return $this */ public function setPermissionValue(string $permission, string $operation, ?bool $value): self { - if ($value === null) { - //If the value is null, unset the permission value (meaning implicit inherit) + //If the value is null, unset the permission value, if it was set befoere (meaning implicit inherit) + if ($value === null && isset($this->data[$permission][$operation])) { unset($this->data[$permission][$operation]); } else { //Otherwise, set the pemission value @@ -186,8 +171,6 @@ final class PermissionData implements \JsonSerializable /** * Creates a new Permission Data Instance using the given JSON encoded data - * @param string $json - * @return static * @throws \JsonException */ public static function fromJSON(string $json): self @@ -203,9 +186,8 @@ final class PermissionData implements \JsonSerializable /** * Returns an JSON encodable representation of this object. - * @return array|mixed */ - public function jsonSerialize() + public function jsonSerialize(): array { $ret = []; @@ -216,9 +198,7 @@ final class PermissionData implements \JsonSerializable continue; } - $ret[$permission] = array_filter($operations, function ($value) { - return $value !== null; - }); + $ret[$permission] = array_filter($operations, static fn($value) => $value !== null); //If the permission has no operations, unset it if (empty($ret[$permission])) { @@ -240,7 +220,6 @@ final class PermissionData implements \JsonSerializable /** * Sets the schema version of this permission data - * @param int $new_version * @return $this */ public function setSchemaVersion(int $new_version): self @@ -253,4 +232,4 @@ final class PermissionData implements \JsonSerializable return $this; } -} \ No newline at end of file +} diff --git a/src/Entity/UserSystem/U2FKey.php b/src/Entity/UserSystem/U2FKey.php index 9c1c68a3..d1d864bc 100644 --- a/src/Entity/UserSystem/U2FKey.php +++ b/src/Entity/UserSystem/U2FKey.php @@ -22,20 +22,18 @@ declare(strict_types=1); namespace App\Entity\UserSystem; +use App\Entity\Contracts\TimeStampableInterface; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\TimestampTrait; use Doctrine\ORM\Mapping as ORM; use Jbtronics\TFAWebauthn\Model\LegacyU2FKeyInterface; +use Symfony\Component\Validator\Constraints\Length; -/** - * @ORM\Entity - * @ORM\Table(name="u2f_keys", - * uniqueConstraints={ - * @ORM\UniqueConstraint(name="user_unique",columns={"user_id", - * "key_handle"}) - * }) - * @ORM\HasLifecycleCallbacks() - */ -class U2FKey implements LegacyU2FKeyInterface +#[ORM\Entity] +#[ORM\HasLifecycleCallbacks] +#[ORM\Table(name: 'u2f_keys')] +#[ORM\UniqueConstraint(name: 'user_unique', columns: ['user_id', 'key_handle'])] +class U2FKey implements LegacyU2FKeyInterface, TimeStampableInterface { use TimestampTrait; @@ -43,50 +41,43 @@ class U2FKey implements LegacyU2FKeyInterface * We have to restrict the length here, as InnoDB only supports key index with max. 767 Bytes. * Max length of keyhandles should be 128. (According to U2F_MAX_KH_SIZE in FIDO example C code). * - * @ORM\Column(type="string", length=128) * * @var string **/ - public string $keyHandle; + #[ORM\Column(type: Types::STRING, length: 128)] + #[Length(max: 128)] + public string $keyHandle = ''; /** - * @ORM\Column(type="string") - * * @var string **/ - public string $publicKey; + #[ORM\Column(type: Types::STRING)] + public string $publicKey = ''; /** - * @ORM\Column(type="text") - * * @var string **/ - public string $certificate; + #[ORM\Column(type: Types::TEXT)] + public string $certificate = ''; /** - * @ORM\Column(type="string") - * - * @var int + * @var string **/ - public int $counter; + #[ORM\Column(type: Types::STRING)] + public string $counter = '0'; - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - */ + #[ORM\Id] + #[ORM\Column(type: Types::INTEGER)] + #[ORM\GeneratedValue] protected int $id; /** - * @ORM\Column(type="string") - * * @var string **/ - protected string $name; + #[ORM\Column(type: Types::STRING)] + protected string $name = ''; - /** - * @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User", inversedBy="u2fKeys") - **/ + #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'u2fKeys')] protected ?User $user = null; public function getKeyHandle(): string @@ -126,7 +117,7 @@ class U2FKey implements LegacyU2FKeyInterface return $this; } - public function getCounter(): int + public function getCounter(): string { return $this->counter; } @@ -151,9 +142,9 @@ class U2FKey implements LegacyU2FKeyInterface } /** - * Gets the user, this U2F key belongs to. + * Gets the user, this U2F key belongs to. */ - public function getUser(): User + public function getUser(): User|null { return $this->user; } diff --git a/src/Entity/UserSystem/User.php b/src/Entity/UserSystem/User.php index 56034dfd..b39bea4f 100644 --- a/src/Entity/UserSystem/User.php +++ b/src/Entity/UserSystem/User.php @@ -22,6 +22,23 @@ declare(strict_types=1); namespace App\Entity\UserSystem; +use Doctrine\Common\Collections\Criteria; +use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\DateFilter; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\OpenApi\Model\Operation; +use ApiPlatform\Serializer\Filter\PropertyFilter; +use App\ApiPlatform\Filter\LikeFilter; +use App\Entity\Attachments\Attachment; +use App\Repository\UserRepository; +use App\EntityListeners\TreeCacheInvalidationListener; +use App\Validator\Constraints\NoLockout; +use Doctrine\DBAL\Types\Types; use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\UserAttachment; use App\Entity\Base\AbstractNamedDBElement; @@ -31,7 +48,10 @@ use App\Validator\Constraints\Selectable; use App\Validator\Constraints\ValidPermission; use App\Validator\Constraints\ValidTheme; use Jbtronics\TFAWebauthn\Model\LegacyU2FKeyInterface; +use Nbgrp\OneloginSamlBundle\Security\User\SamlUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints\Length; use Webauthn\PublicKeyCredentialUserEntity; use function count; use DateTime; @@ -51,203 +71,275 @@ use Jbtronics\TFAWebauthn\Model\TwoFactorInterface as WebauthnTwoFactorInterface /** * This entity represents a user, which can log in and have permissions. - * Also this entity is able to save some informations about the user, like the names, email-address and other info. + * Also, this entity is able to save some information about the user, like the names, email-address and other info. + * @see \App\Tests\Entity\UserSystem\UserTest * - * @ORM\Entity(repositoryClass="App\Repository\UserRepository") - * @ORM\Table("`users`", indexes={ - * @ORM\Index(name="user_idx_username", columns={"name"}) - * }) - * @ORM\EntityListeners({"App\EntityListeners\TreeCacheInvalidationListener"}) - * @UniqueEntity("name", message="validator.user.username_already_used") + * @extends AttachmentContainingDBElement */ -class User extends AttachmentContainingDBElement implements UserInterface, HasPermissionsInterface, TwoFactorInterface, BackupCodeInterface, TrustedDeviceInterface, WebauthnTwoFactorInterface, PreferredProviderInterface, PasswordAuthenticatedUserInterface +#[UniqueEntity('name', message: 'validator.user.username_already_used')] +#[ORM\Entity(repositoryClass: UserRepository::class)] +#[ORM\EntityListeners([TreeCacheInvalidationListener::class])] +#[ORM\Table('`users`')] +#[ORM\Index(columns: ['name'], name: 'user_idx_username')] +#[ORM\AttributeOverrides([ + new ORM\AttributeOverride(name: 'name', column: new ORM\Column(type: Types::STRING, length: 180, unique: true)) +])] +#[ApiResource( + shortName: 'User', + operations: [ + new Get( + openapi: new Operation(summary: 'Get information about the current user.'), + security: 'is_granted("read", object)' + ), + new GetCollection( + openapi: new Operation(summary: 'Get all users defined in the system.'), + security: 'is_granted("@users.read")' + ), + ], + normalizationContext: ['groups' => ['user:read'], 'openapi_definition_name' => 'Read'], +)] +#[ApiFilter(PropertyFilter::class)] +#[ApiFilter(LikeFilter::class, properties: ["name", "aboutMe"])] +#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] +#[NoLockout(groups: ['permissions:edit'])] +class User extends AttachmentContainingDBElement implements UserInterface, HasPermissionsInterface, TwoFactorInterface, + BackupCodeInterface, TrustedDeviceInterface, WebauthnTwoFactorInterface, PreferredProviderInterface, PasswordAuthenticatedUserInterface, SamlUserInterface { //use MasterAttachmentTrait; /** * The User id of the anonymous user. */ - public const ID_ANONYMOUS = 1; + final public const ID_ANONYMOUS = 1; + + #[Groups(['user:read'])] + protected ?int $id = null; + + #[Groups(['user:read'])] + protected ?\DateTimeImmutable $lastModified = null; + + #[Groups(['user:read'])] + protected ?\DateTimeImmutable $addedDate = null; /** * @var bool Determines if the user is disabled (user can not log in) - * @ORM\Column(type="boolean") */ + #[Groups(['extended', 'full', 'import', 'user:read'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $disabled = false; /** * @var string|null The theme - * @ORM\Column(type="string", name="config_theme", nullable=true) - * @ValidTheme() */ + #[Groups(['full', 'import', 'user:read'])] + #[ORM\Column(name: 'config_theme', type: Types::STRING, nullable: true)] + #[ValidTheme] protected ?string $theme = null; /** * @var string|null the hash of a token the user must provide when he wants to reset his password - * @ORM\Column(type="string", nullable=true) */ + #[ORM\Column(type: Types::STRING, nullable: true)] protected ?string $pw_reset_token = null; - /** - * @ORM\Column(type="text", name="config_instock_comment_a") - */ + #[ORM\Column(name: 'config_instock_comment_a', type: Types::TEXT)] + #[Groups(['extended', 'full', 'import'])] protected string $instock_comment_a = ''; - /** - * @ORM\Column(type="text", name="config_instock_comment_w") - */ + #[ORM\Column(name: 'config_instock_comment_w', type: Types::TEXT)] + #[Groups(['extended', 'full', 'import'])] protected string $instock_comment_w = ''; - /** @var int The version of the trusted device cookie. Used to invalidate all trusted device cookies at once. - * @ORM\Column(type="integer") + /** + * @var string A self-description of the user as markdown text */ + #[Groups(['full', 'import', 'user:read'])] + #[ORM\Column(type: Types::TEXT)] + protected string $aboutMe = ''; + + /** @var int The version of the trusted device cookie. Used to invalidate all trusted device cookies at once. + */ + #[ORM\Column(type: Types::INTEGER)] protected int $trustedDeviceCookieVersion = 0; /** * @var string[]|null A list of backup codes that can be used, if the user has no access to its Google Authenticator device - * @ORM\Column(type="json") */ + #[ORM\Column(type: Types::JSON)] protected ?array $backupCodes = []; - /** - * @ORM\Id() - * @ORM\GeneratedValue() - * @ORM\Column(type="integer") - */ - protected ?int $id = null; - /** * @var Group|null the group this user belongs to - * DO NOT PUT A fetch eager here! Otherwise you can not unset the group of a user! This seems to be some kind of bug in doctrine. Maybe this is fixed in future versions. - * @ORM\ManyToOne(targetEntity="Group", inversedBy="users") - * @ORM\JoinColumn(name="group_id", referencedColumnName="id") - * @Selectable() + * DO NOT PUT A fetch eager here! Otherwise, you can not unset the group of a user! This seems to be some kind of bug in doctrine. Maybe this is fixed in future versions. */ + #[Groups(['extended', 'full', 'import', 'user:read'])] + #[ORM\ManyToOne(targetEntity: Group::class, inversedBy: 'users')] + #[ORM\JoinColumn(name: 'group_id')] + #[Selectable] + #[ApiProperty(readableLink: true, writableLink: false)] protected ?Group $group = null; /** - * @var string|null The secret used for google authenticator - * @ORM\Column(name="google_authenticator_secret", type="string", nullable=true) + * @var string|null The secret used for Google authenticator */ + #[ORM\Column(name: 'google_authenticator_secret', type: Types::STRING, nullable: true)] protected ?string $googleAuthenticatorSecret = null; /** * @var string|null The timezone the user prefers - * @ORM\Column(type="string", name="config_timezone", nullable=true) - * @Assert\Timezone() */ + #[Assert\Timezone] + #[Groups(['full', 'import', 'user:read'])] + #[ORM\Column(name: 'config_timezone', type: Types::STRING, nullable: true)] protected ?string $timezone = ''; /** * @var string|null The language/locale the user prefers - * @ORM\Column(type="string", name="config_language", nullable=true) - * @Assert\Language() */ + #[Assert\Language] + #[Groups(['full', 'import', 'user:read'])] + #[ORM\Column(name: 'config_language', type: Types::STRING, nullable: true)] protected ?string $language = ''; /** * @var string|null The email address of the user - * @ORM\Column(type="string", length=255, nullable=true) - * @Assert\Email() */ + #[Assert\Email] + #[Groups(['simple', 'extended', 'full', 'import', 'user:read'])] + #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] + #[Length(max: 255)] protected ?string $email = ''; /** - * @var string|null The department the user is working - * @ORM\Column(type="string", length=255, nullable=true) + * @var bool True if the user wants to show his email address on his (public) profile */ + #[ORM\Column(type: Types::BOOLEAN, options: ['default' => false])] + #[Groups(['full', 'import', 'user:read'])] + protected bool $show_email_on_profile = false; + + /** + * @var string|null The department the user is working + */ + #[Groups(['simple', 'extended', 'full', 'import', 'user:read'])] + #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] + #[Length(max: 255)] protected ?string $department = ''; /** * @var string|null The last name of the User - * @ORM\Column(type="string", length=255, nullable=true) */ + #[Groups(['simple', 'extended', 'full', 'import', 'user:read'])] + #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] + #[Length(max: 255)] protected ?string $last_name = ''; /** * @var string|null The first name of the User - * @ORM\Column(type="string", length=255, nullable=true) */ + #[Groups(['simple', 'extended', 'full', 'import', 'user:read'])] + #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] + #[Length(max: 255)] protected ?string $first_name = ''; /** * @var bool True if the user needs to change password after log in - * @ORM\Column(type="boolean") */ + #[Groups(['extended', 'full', 'import', 'user:read'])] + #[ORM\Column(type: Types::BOOLEAN)] protected bool $need_pw_change = true; /** * @var string|null The hashed password - * @ORM\Column(type="string", nullable=true) */ + #[ORM\Column(type: Types::STRING, nullable: true)] protected ?string $password = null; - /** - * @ORM\Column(type="string", length=180, unique=true) - * @Assert\NotBlank - * @Assert\Regex("/^[\w\.\+\-\$]+$/", message="user.invalid_username") - */ + #[Assert\NotBlank] + #[Assert\Regex('/^[\w\.\+\-\$]+[\w\.\+\-\$\@]*$/', message: 'user.invalid_username')] + #[Groups(['user:read'])] protected string $name = ''; /** - * @var array - * @ORM\Column(type="json") + * @var array|null */ + #[ORM\Column(type: Types::JSON)] protected ?array $settings = []; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\Attachments\UserAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true) - * @ORM\OrderBy({"name" = "ASC"}) */ - protected $attachments; + #[ORM\OneToMany(mappedBy: 'element', targetEntity: UserAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OrderBy(['name' => Criteria::ASC])] + #[Groups(['user:read', 'user:write'])] + protected Collection $attachments; - /** @var DateTime|null The time when the backup codes were generated - * @ORM\Column(type="datetime", nullable=true) + #[ORM\ManyToOne(targetEntity: UserAttachment::class)] + #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] + #[Groups(['user:read', 'user:write'])] + protected ?Attachment $master_picture_attachment = null; + + /** @var \DateTimeImmutable|null The time when the backup codes were generated */ - protected ?DateTime $backupCodesGenerationDate = null; + #[Groups(['full'])] + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + protected ?\DateTimeImmutable $backupCodesGenerationDate = null; /** @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\UserSystem\U2FKey", mappedBy="user", cascade={"REMOVE"}, orphanRemoval=true) */ - protected $u2fKeys; + #[ORM\OneToMany(mappedBy: 'user', targetEntity: U2FKey::class, cascade: ['REMOVE'], fetch: 'EXTRA_LAZY', orphanRemoval: true)] + protected Collection $u2fKeys; /** * @var Collection - * @ORM\OneToMany(targetEntity="App\Entity\UserSystem\WebauthnKey", mappedBy="user", cascade={"REMOVE"}, orphanRemoval=true) */ - protected $webauthn_keys; + #[ORM\OneToMany(mappedBy: 'user', targetEntity: WebauthnKey::class, cascade: ['REMOVE'], fetch: 'EXTRA_LAZY', orphanRemoval: true)] + protected Collection $webauthn_keys; + + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'user', targetEntity: ApiToken::class, cascade: ['REMOVE'], fetch: 'EXTRA_LAZY', orphanRemoval: true)] + private Collection $api_tokens; /** * @var Currency|null The currency the user wants to see prices in. * Dont use fetch=EAGER here, this will cause problems with setting the currency setting. * TODO: This is most likely a bug in doctrine/symfony related to the UniqueEntity constraint (it makes a db call). * TODO: Find a way to use fetch EAGER (this improves performance a bit) - * @ORM\ManyToOne(targetEntity="App\Entity\PriceInformations\Currency") - * @ORM\JoinColumn(name="currency_id", referencedColumnName="id") - * @Selectable() */ - protected $currency; + #[Groups(['extended', 'full', 'import'])] + #[ORM\ManyToOne(targetEntity: Currency::class)] + #[ORM\JoinColumn(name: 'currency_id')] + #[Selectable] + protected ?Currency $currency = null; - /** - * @var PermissionData - * @ValidPermission() - * @ORM\Embedded(class="PermissionData", columnPrefix="permissions_") - */ + #[Groups(['simple', 'extended', 'full', 'import'])] + #[ORM\Embedded(class: 'PermissionData', columnPrefix: 'permissions_')] + #[ValidPermission] protected ?PermissionData $permissions = null; /** - * @var DateTime the time until the password reset token is valid - * @ORM\Column(type="datetime", nullable=true) + * @var \DateTimeImmutable|null the time until the password reset token is valid */ - protected $pw_reset_expires; + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + protected ?\DateTimeImmutable $pw_reset_expires = null; + + /** + * @var bool True if the user was created by a SAML provider (and therefore cannot change its password) + */ + #[Groups(['extended', 'full'])] + #[ORM\Column(type: Types::BOOLEAN)] + protected bool $saml_user = false; public function __construct() { + $this->attachments = new ArrayCollection(); parent::__construct(); $this->permissions = new PermissionData(); $this->u2fKeys = new ArrayCollection(); $this->webauthn_keys = new ArrayCollection(); + $this->api_tokens = new ArrayCollection(); } /** @@ -256,7 +348,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe * * @return string */ - public function __toString() + public function __toString(): string { $tmp = $this->isDisabled() ? ' [DISABLED]' : ''; @@ -298,6 +390,10 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe // guarantee every user at least has ROLE_USER $roles[] = 'ROLE_USER'; + if ($this->saml_user) { + $roles[] = 'ROLE_SAML_USER'; + } + return array_unique($roles); } @@ -319,8 +415,6 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe /** * Sets the password hash for this user. - * - * @return User */ public function setPassword(string $password): self { @@ -358,8 +452,6 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe /** * Sets the currency the users prefers to see prices in. - * - * @return User */ public function setCurrency(?Currency $currency): self { @@ -369,7 +461,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe } /** - * Checks if this user is disabled (user cannot login any more). + * Checks if this user is disabled (user cannot log in any more). * * @return bool true, if the user is disabled */ @@ -382,8 +474,6 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe * Sets the status if a user is disabled. * * @param bool $disabled true if the user should be disabled - * - * @return User */ public function setDisabled(bool $disabled): self { @@ -394,7 +484,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe public function getPermissions(): PermissionData { - if ($this->permissions === null) { + if (!$this->permissions instanceof PermissionData) { $this->permissions = new PermissionData(); } @@ -411,8 +501,6 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe /** * Set the status, if the user needs a password change. - * - * @return User */ public function setNeedPwChange(bool $need_pw_change): self { @@ -431,8 +519,6 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe /** * Sets the encrypted password reset token. - * - * @return User */ public function setPwResetToken(?string $pw_reset_token): self { @@ -442,19 +528,17 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe } /** - * Gets the datetime when the password reset token expires. + * Gets the datetime when the password reset token expires. */ - public function getPwResetExpires(): DateTime + public function getPwResetExpires(): \DateTimeImmutable|null { return $this->pw_reset_expires; } /** * Sets the datetime when the password reset token expires. - * - * @return User */ - public function setPwResetExpires(DateTime $pw_reset_expires): self + public function setPwResetExpires(\DateTimeImmutable $pw_reset_expires): self { $this->pw_reset_expires = $pw_reset_expires; @@ -473,11 +557,12 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe * * @return string a string with the full name of this user */ + #[Groups(['user:read'])] public function getFullName(bool $including_username = false): string { $tmp = $this->getFirstName(); - //Dont add a space, if the name has only one part (it would look strange) - if (!empty($this->getFirstName()) && !empty($this->getLastName())) { + //Don't add a space, if the name has only one part (it would look strange) + if ($this->getFirstName() !== null && $this->getFirstName() !== '' && ($this->getLastName() !== null && $this->getLastName() !== '')) { $tmp .= ' '; } $tmp .= $this->getLastName(); @@ -564,8 +649,6 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe * Change the department of the user. * * @param string|null $department The new department - * - * @return User */ public function setDepartment(?string $department): self { @@ -599,9 +682,47 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe } /** - * Gets the language the user prefers (as 2 letter ISO code). + * Gets whether the email address of the user is shown on the public profile page. + */ + public function isShowEmailOnProfile(): bool + { + return $this->show_email_on_profile; + } + + /** + * Sets whether the email address of the user is shown on the public profile page. + */ + public function setShowEmailOnProfile(bool $show_email_on_profile): User + { + $this->show_email_on_profile = $show_email_on_profile; + return $this; + } + + + + /** + * Returns the about me text of the user. + */ + public function getAboutMe(): string + { + return $this->aboutMe; + } + + /** + * Change the about me text of the user. + */ + public function setAboutMe(string $aboutMe): User + { + $this->aboutMe = $aboutMe; + return $this; + } + + + + /** + * Gets the language the user prefers (as 2-letter ISO code). * - * @return string|null The 2 letter ISO code of the preferred language (e.g. 'en' or 'de'). + * @return string|null The 2-letter ISO code of the preferred language (e.g. 'en' or 'de'). * If null is returned, the user has not specified a language and the server wide language should be used. */ public function getLanguage(): ?string @@ -612,10 +733,8 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe /** * Change the language the user prefers. * - * @param string|null $language The new language as 2 letter ISO code (e.g. 'en' or 'de'). - * Set to null, to use the system wide language. - * - * @return User + * @param string|null $language The new language as 2-letter ISO code (e.g. 'en' or 'de'). + * Set to null, to use the system-wide language. */ public function setLanguage(?string $language): self { @@ -660,8 +779,8 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe /** * Change the theme the user wants to see. * - * @param string|null $theme The name of the theme (See See self::AVAILABLE_THEMES for valid values). Set to null - * if the system wide theme should be used. + * @param string|null $theme The name of the theme (See self::AVAILABLE_THEMES for valid values). Set to null + * if the system-wide theme should be used. * * @return $this */ @@ -705,7 +824,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe } /** - * Return the user name that should be shown in Google Authenticator. + * Return the username that should be shown in Google Authenticator. */ public function getGoogleAuthenticatorUsername(): string { @@ -774,17 +893,11 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe * @param string[] $codes An array containing the backup codes * * @return $this - * - * @throws Exception If an error with the datetime occurs */ public function setBackupCodes(array $codes): self { $this->backupCodes = $codes; - if (empty($codes)) { - $this->backupCodesGenerationDate = null; - } else { - $this->backupCodesGenerationDate = new DateTime(); - } + $this->backupCodesGenerationDate = $codes === [] ? null : new \DateTimeImmutable(); return $this; } @@ -792,7 +905,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe /** * Return the date when the backup codes were generated. */ - public function getBackupCodesGenerationDate(): ?DateTime + public function getBackupCodesGenerationDate(): ?\DateTimeImmutable { return $this->backupCodesGenerationDate; } @@ -809,7 +922,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe /** * Invalidate all trusted device tokens at once, by incrementing the token version. - * You have to flush the changes to database afterwards. + * You have to flush the changes to database afterward. */ public function invalidateTrustedDeviceTokens(): void { @@ -846,7 +959,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe { return new PublicKeyCredentialUserEntity( $this->getUsername(), - (string) $this->getId(), + (string) $this->getID(), $this->getFullName(), ); } @@ -860,4 +973,81 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe { $this->webauthn_keys->add($webauthnKey); } + + /** + * Returns true, if the user was created by the SAML authentication. + */ + public function isSamlUser(): bool + { + return $this->saml_user; + } + + /** + * Sets the saml_user flag. + */ + public function setSamlUser(bool $saml_user): User + { + $this->saml_user = $saml_user; + return $this; + } + + public function setSamlAttributes(array $attributes): void + { + //When mail attribute exists, set it + if (isset($attributes['email'])) { + $this->setEmail($attributes['email'][0]); + } + //When first name attribute exists, set it + if (isset($attributes['firstName'])) { + $this->setFirstName($attributes['firstName'][0]); + } + //When last name attribute exists, set it + if (isset($attributes['lastName'])) { + $this->setLastName($attributes['lastName'][0]); + } + if (isset($attributes['department'])) { + $this->setDepartment($attributes['department'][0]); + } + + //Use X500 attributes as userinfo + if (isset($attributes['urn:oid:2.5.4.42'])) { + $this->setFirstName($attributes['urn:oid:2.5.4.42'][0]); + } + if (isset($attributes['urn:oid:2.5.4.4'])) { + $this->setLastName($attributes['urn:oid:2.5.4.4'][0]); + } + if (isset($attributes['urn:oid:1.2.840.113549.1.9.1'])) { + $this->setEmail($attributes['urn:oid:1.2.840.113549.1.9.1'][0]); + } + } + + /** + * Return all API tokens of the user. + * @return Collection + */ + public function getApiTokens(): Collection + { + return $this->api_tokens; + } + + /** + * Add an API token to the user. + * @param ApiToken $apiToken + * @return void + */ + public function addApiToken(ApiToken $apiToken): void + { + $apiToken->setUser($this); + $this->api_tokens->add($apiToken); + } + + /** + * Remove an API token from the user. + * @param ApiToken $apiToken + * @return void + */ + public function removeApiToken(ApiToken $apiToken): void + { + $this->api_tokens->removeElement($apiToken); + } } diff --git a/src/Entity/UserSystem/WebauthnKey.php b/src/Entity/UserSystem/WebauthnKey.php index 5d5c654d..b2716e07 100644 --- a/src/Entity/UserSystem/WebauthnKey.php +++ b/src/Entity/UserSystem/WebauthnKey.php @@ -1,4 +1,7 @@ . */ - namespace App\Entity\UserSystem; +use App\Entity\Contracts\TimeStampableInterface; +use Doctrine\DBAL\Types\Types; use App\Entity\Base\TimestampTrait; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; use Webauthn\PublicKeyCredentialSource as BasePublicKeyCredentialSource; -/** - * @ORM\Table(name="webauthn_keys") - * @ORM\Entity() - * @ORM\HasLifecycleCallbacks() - */ -class WebauthnKey extends BasePublicKeyCredentialSource +#[ORM\Entity] +#[ORM\HasLifecycleCallbacks] +#[ORM\Table(name: 'webauthn_keys')] +class WebauthnKey extends BasePublicKeyCredentialSource implements TimeStampableInterface { use TimestampTrait; - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - */ + #[ORM\Id] + #[ORM\Column(type: Types::INTEGER)] + #[ORM\GeneratedValue] protected int $id; - /** - * @ORM\Column(type="string") - */ - protected string $name; + #[ORM\Column(type: Types::STRING)] + #[NotBlank] + #[Length(max: 255)] + protected string $name = ''; - /** - * @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User", inversedBy="webauthn_keys") - **/ + #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'webauthn_keys')] protected ?User $user = null; - /** - * @return string - */ + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + protected ?\DateTimeImmutable $last_time_used = null; + public function getName(): string { return $this->name; } - /** - * @param string $name - * @return WebauthnKey - */ public function setName(string $name): WebauthnKey { $this->name = $name; return $this; } - /** - * @return User|null - */ public function getUser(): ?User { return $this->user; } - /** - * @param User|null $user - * @return WebauthnKey - */ public function setUser(?User $user): WebauthnKey { $this->user = $user; return $this; } - /** - * @return int - */ public function getId(): int { return $this->id; } + /** + * Retrieve the last time when the key was used. + */ + public function getLastTimeUsed(): ?\DateTimeImmutable + { + return $this->last_time_used; + } - - + /** + * Update the last time when the key was used. + * @return void + */ + public function updateLastTimeUsed(): void + { + $this->last_time_used = new \DateTimeImmutable('now'); + } public static function fromRegistration(BasePublicKeyCredentialSource $registration): self { @@ -113,4 +112,4 @@ class WebauthnKey extends BasePublicKeyCredentialSource $registration->getOtherUI() ); } -} \ No newline at end of file +} diff --git a/src/EntityListeners/AttachmentDeleteListener.php b/src/EntityListeners/AttachmentDeleteListener.php index edf68e25..1f39b2d0 100644 --- a/src/EntityListeners/AttachmentDeleteListener.php +++ b/src/EntityListeners/AttachmentDeleteListener.php @@ -22,12 +22,12 @@ declare(strict_types=1); namespace App\EntityListeners; +use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\Attachment; use App\Services\Attachments\AttachmentManager; use App\Services\Attachments\AttachmentPathResolver; use App\Services\Attachments\AttachmentReverseSearch; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\ORM\Event\PostRemoveEventArgs; use Doctrine\ORM\Event\PreRemoveEventArgs; use Doctrine\ORM\Event\PreUpdateEventArgs; @@ -38,30 +38,22 @@ use SplFileInfo; /** * This listener watches for changes on attachments and deletes the files associated with an attachment, that are not - * used any more. This can happens after an attachment is delteted or the path is changed. + * used anymore. This can happen after an attachment is deleted or the path is changed. */ class AttachmentDeleteListener { - protected AttachmentReverseSearch $attachmentReverseSearch; - protected AttachmentManager $attachmentHelper; - protected AttachmentPathResolver $pathResolver; - - public function __construct(AttachmentReverseSearch $attachmentReverseSearch, AttachmentManager $attachmentHelper, AttachmentPathResolver $pathResolver) + public function __construct(protected AttachmentReverseSearch $attachmentReverseSearch, protected AttachmentManager $attachmentHelper, protected AttachmentPathResolver $pathResolver) { - $this->attachmentReverseSearch = $attachmentReverseSearch; - $this->attachmentHelper = $attachmentHelper; - $this->pathResolver = $pathResolver; } /** * Removes the file associated with the attachment, if the file associated with the attachment changes. - * - * @PreUpdate */ + #[PreUpdate] public function preUpdateHandler(Attachment $attachment, PreUpdateEventArgs $event): void { - if ($event->hasChangedField('path')) { - $old_path = $event->getOldValue('path'); + if ($event->hasChangedField('internal_path')) { + $old_path = $event->getOldValue('internal_path'); //Dont delete file if the attachment uses a builtin ressource: if (Attachment::checkIfBuiltin($old_path)) { @@ -82,15 +74,14 @@ class AttachmentDeleteListener /** * Ensure that attachments are not used in preview, so that they can be deleted (without integrity violation). - * - * @ORM\PreRemove() */ + #[ORM\PreRemove] public function preRemoveHandler(Attachment $attachment, PreRemoveEventArgs $event): void { //Ensure that the attachment that will be deleted, is not used as preview picture anymore... $attachment_holder = $attachment->getElement(); - if (null === $attachment_holder) { + if (!$attachment_holder instanceof AttachmentContainingDBElement) { return; } @@ -103,16 +94,15 @@ class AttachmentDeleteListener if (!$em instanceof EntityManagerInterface) { throw new \RuntimeException('Invalid EntityManagerInterface!'); } - $classMetadata = $em->getClassMetadata(get_class($attachment_holder)); + $classMetadata = $em->getClassMetadata($attachment_holder::class); $em->getUnitOfWork()->computeChangeSet($classMetadata, $attachment_holder); } } /** * Removes the file associated with the attachment, after the attachment was deleted. - * - * @PostRemove */ + #[PostRemove] public function postRemoveHandler(Attachment $attachment, PostRemoveEventArgs $event): void { //Dont delete file if the attachment uses a builtin ressource: @@ -122,7 +112,7 @@ class AttachmentDeleteListener $file = $this->attachmentHelper->attachmentToFile($attachment); //Only delete if the attachment has a valid file. - if (null !== $file) { + if ($file instanceof \SplFileInfo) { /* The original file has already been removed, so we have to decrease the threshold to zero, as any remaining attachment depends on this attachment, and we must not delete this file! */ $this->attachmentReverseSearch->deleteIfNotUsed($file, 0); diff --git a/src/EntityListeners/TreeCacheInvalidationListener.php b/src/EntityListeners/TreeCacheInvalidationListener.php index 017c8018..eae7ce35 100644 --- a/src/EntityListeners/TreeCacheInvalidationListener.php +++ b/src/EntityListeners/TreeCacheInvalidationListener.php @@ -24,56 +24,52 @@ namespace App\EntityListeners; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractStructuralDBElement; -use App\Entity\LabelSystem\LabelProfile; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; -use App\Services\UserSystem\UserCacheKeyGenerator; -use Doctrine\ORM\Event\LifecycleEventArgs; +use App\Services\Cache\ElementCacheTagGenerator; +use App\Services\Cache\UserCacheKeyGenerator; +use Doctrine\ORM\Event\PostPersistEventArgs; +use Doctrine\ORM\Event\PostRemoveEventArgs; +use Doctrine\ORM\Event\PostUpdateEventArgs; use Doctrine\ORM\Mapping as ORM; -use function get_class; use Symfony\Contracts\Cache\TagAwareCacheInterface; class TreeCacheInvalidationListener { - protected TagAwareCacheInterface $cache; - protected UserCacheKeyGenerator $keyGenerator; - - public function __construct(TagAwareCacheInterface $treeCache, UserCacheKeyGenerator $keyGenerator) + public function __construct( + protected TagAwareCacheInterface $cache, + protected UserCacheKeyGenerator $keyGenerator, + protected ElementCacheTagGenerator $tagGenerator + ) { - $this->cache = $treeCache; - $this->keyGenerator = $keyGenerator; } - /** - * @ORM\PostUpdate() - * @ORM\PostPersist() - * @ORM\PostRemove() - */ - public function invalidate(AbstractDBElement $element, LifecycleEventArgs $event): void + #[ORM\PostUpdate] + #[ORM\PostPersist] + #[ORM\PostRemove] + public function invalidate(AbstractDBElement $element, PostUpdateEventArgs|PostPersistEventArgs|PostRemoveEventArgs $event): void { - //If an element was changed, then invalidate all cached trees with this element class - if ($element instanceof AbstractStructuralDBElement || $element instanceof LabelProfile) { - $secure_class_name = str_replace('\\', '_', get_class($element)); - $this->cache->invalidateTags([$secure_class_name]); + //For all changes, we invalidate the cache for all elements of this class + $tags = [$this->tagGenerator->getElementTypeCacheTag($element)]; - //Trigger a sidebar reload for all users (see SidebarTreeUpdater service) - if(!$element instanceof LabelProfile) { - $this->cache->invalidateTags(['sidebar_tree_update']); - } + + //For changes on structural elements, we also invalidate the sidebar tree + if ($element instanceof AbstractStructuralDBElement) { + $tags[] = 'sidebar_tree_update'; } - //If a user change, then invalidate all cached trees for him + //For user changes, we invalidate the cache for this user if ($element instanceof User) { - $secure_class_name = str_replace('\\', '_', get_class($element)); - $tag = $this->keyGenerator->generateKey($element); - $this->cache->invalidateTags([$tag, $secure_class_name]); + $tags[] = $this->keyGenerator->generateKey($element); } /* If any group change, then invalidate all cached trees. Users Permissions can be inherited from groups, so a change in any group can cause big permisssion changes for users. So to be sure, invalidate all trees */ if ($element instanceof Group) { - $tag = 'groups'; - $this->cache->invalidateTags([$tag]); + $tags[] = 'groups'; } + + //Invalidate the cache for the given tags + $this->cache->invalidateTags($tags); } } diff --git a/src/EventListener/AddEditCommentRequestListener.php b/src/EventListener/AddEditCommentRequestListener.php new file mode 100644 index 00000000..33c72b3f --- /dev/null +++ b/src/EventListener/AddEditCommentRequestListener.php @@ -0,0 +1,62 @@ +. + */ + +declare(strict_types=1); + + +namespace App\EventListener; + +use App\Services\LogSystem\EventCommentHelper; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; +use Symfony\Component\HttpKernel\Event\RequestEvent; + +#[AsEventListener] +class AddEditCommentRequestListener +{ + public function __construct(private readonly EventCommentHelper $helper) + { + + } + + public function __invoke(RequestEvent $event) + { + if (!$event->isMainRequest()) { + return; + } + $request = $event->getRequest(); + + //Do not add comment if the request is a GET request + if ($request->isMethod('GET')) { + return; + } + + //Check if the user tries to access a /api/ endpoint, if not skip + if (!str_contains($request->getPathInfo(), '/api/')) { + return; + } + + //Extract the comment from the query parameter + $comment = $request->query->getString('_comment', ''); + + if ($comment !== '') { + $this->helper->setMessage($comment); + } + } +} \ No newline at end of file diff --git a/src/EventListener/AllowSlowNaturalSortListener.php b/src/EventListener/AllowSlowNaturalSortListener.php new file mode 100644 index 00000000..02ec6144 --- /dev/null +++ b/src/EventListener/AllowSlowNaturalSortListener.php @@ -0,0 +1,49 @@ +. + */ + +declare(strict_types=1); + + +namespace App\EventListener; + +use App\Doctrine\Functions\Natsort; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; +use Symfony\Component\HttpKernel\Event\RequestEvent; + +/** + * This is a workaround to the fact that we can not inject parameters into doctrine custom functions. + * Therefore we use this event listener to call the static function on the custom function, to inject the value, before + * any NATSORT function is called. + */ +#[AsEventListener] +class AllowSlowNaturalSortListener +{ + public function __construct( + #[Autowire(param: 'partdb.db.emulate_natural_sort')] + private readonly bool $allowNaturalSort) + { + } + + public function __invoke(RequestEvent $event) + { + Natsort::allowSlowNaturalSort($this->allowNaturalSort); + } +} \ No newline at end of file diff --git a/src/EventListener/ConsoleEnsureWebserverUserListener.php b/src/EventListener/ConsoleEnsureWebserverUserListener.php new file mode 100644 index 00000000..7c119304 --- /dev/null +++ b/src/EventListener/ConsoleEnsureWebserverUserListener.php @@ -0,0 +1,159 @@ +. + */ + +declare(strict_types=1); + + +namespace App\EventListener; + +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; + +/** + * This event listener is called before any console command is executed and should ensure that the webserver + * user is used for all operations (and show a warning if not). This ensures that all files are created with the + * correct permissions. + * If the console is in non-interactive mode, a warning is shown, but the command is still executed. + */ +#[AsEventListener(ConsoleEvents::COMMAND)] +class ConsoleEnsureWebserverUserListener +{ + public function __construct( + #[Autowire('%kernel.project_dir%')] + private readonly string $project_root) + { + } + + public function __invoke(ConsoleCommandEvent $event): void + { + $input = $event->getInput(); + $io = new SymfonyStyle($event->getInput(), $event->getOutput()); + + //Check if we are (not) running as the webserver user + $webserver_user = $this->getWebserverUser(); + $running_user = $this->getRunningUser(); + + //Check if we are trying to run as root + if ($this->isRunningAsRoot()) { + //If the COMPOSER_ALLOW_SUPERUSER environment variable is set, we allow running as root + if ($_SERVER['COMPOSER_ALLOW_SUPERUSER'] ?? false) { + return; + } + + $io->warning('You are running this command as root. This is not recommended, as it can cause permission problems. Please run this command as the webserver user "'. ($webserver_user ?? '??') . '" instead.'); + $io->info('You might have already caused permission problems by running this command as wrong user. If you encounter issues with Part-DB, delete the var/cache directory completely and let it be recreated by Part-DB.'); + if ($input->isInteractive() && !$io->confirm('Do you want to continue?', false)) { + $event->disableCommand(); + } + + return; + } + + if ($webserver_user !== null && $running_user !== null && $webserver_user !== $running_user) { + $io->warning('You are running this command as the user "' . $running_user . '". This is not recommended, as it can cause permission problems. Please run this command as the webserver user "' . $webserver_user . '" instead.'); + $io->info('You might have already caused permission problems by running this command as wrong user. If you encounter issues with Part-DB, delete the var/cache directory completely and let it be recreated by Part-DB.'); + if ($input->isInteractive() && !$io->confirm('Do you want to continue?', false)) { + $event->disableCommand(); + } + + return; + } + } + + /** @noinspection PhpUndefinedFunctionInspection */ + private function isRunningAsRoot(): bool + { + //If we are on windows, we can't run as root + if (PHP_OS_FAMILY === 'Windows') { + return false; + } + + //Try to use the posix extension if available (Linux) + if (function_exists('posix_geteuid')) { + //Check if the current user is root + return posix_geteuid() === 0; + } + + //Otherwise we can't determine the username + return false; + } + + /** + * Determines the username of the user who started the current script if possible. + * Returns null if the username could not be determined. + * @return string|null + * @noinspection PhpUndefinedFunctionInspection + */ + private function getRunningUser(): ?string + { + //Try to use the posix extension if available (Linux) + if (function_exists('posix_geteuid') && function_exists('posix_getpwuid')) { + $id = posix_geteuid(); + + $user = posix_getpwuid($id); + //Try to get the username from the posix extension or return the id + return $user['name'] ?? ("ID: " . $id); + } + + //Otherwise we can't determine the username + return $_SERVER['USERNAME'] ?? $_SERVER['USER'] ?? null; + } + + private function getWebserverUser(): ?string + { + //Determine the webserver user, by checking who owns the uploads/ directory + $path_to_check = $this->project_root . '/uploads/'; + + //Determine the owner of this directory + if (!is_dir($path_to_check)) { + return null; + } + + //If we are on windows we need some special logic + if (PHP_OS_FAMILY === 'Windows') { + //If we have the COM extension available, we can use it to determine the owner + if (extension_loaded('com_dotnet')) { + /** @noinspection PhpUndefinedClassInspection */ + $su = new \COM("ADsSecurityUtility"); // Call interface + //@phpstan-ignore-next-line + $securityInfo = $su->GetSecurityDescriptor($path_to_check, 1, 1); // Call method + return $securityInfo->owner; // Get file owner + } + + //Otherwise we can't determine the owner + return null; + } + + //When we are on a POSIX system, we can use the fileowner function + $owner = fileowner($path_to_check); + + if (function_exists('posix_getpwuid')) { + $user = posix_getpwuid($owner); + //Try to get the username from the posix extension or return the id + return $user['name'] ?? ("ID: " . $owner); + } + + return null; + } + +} \ No newline at end of file diff --git a/src/EventListener/DisallowSearchEngineIndexingRequestListener.php b/src/EventListener/DisallowSearchEngineIndexingRequestListener.php new file mode 100644 index 00000000..b969b6b2 --- /dev/null +++ b/src/EventListener/DisallowSearchEngineIndexingRequestListener.php @@ -0,0 +1,54 @@ +. + */ + +declare(strict_types=1); + + +namespace App\EventListener; + +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; +use Symfony\Component\HttpKernel\Event\ResponseEvent; + +#[AsEventListener] +class DisallowSearchEngineIndexingRequestListener +{ + private const HEADER_NAME = 'X-Robots-Tag'; + + private readonly bool $enabled; + + public function __construct(#[Autowire(param: 'partdb.demo_mode')] bool $demo_mode) + { + // Disable this listener in demo mode + $this->enabled = !$demo_mode; + } + + public function __invoke(ResponseEvent $event): void + { + //Skip if disabled + if (!$this->enabled) { + return; + } + + if (!$event->getResponse()->headers->has(self::HEADER_NAME)) { + $event->getResponse()->headers->set(self::HEADER_NAME, 'noindex'); + } + } +} \ No newline at end of file diff --git a/src/EventSubscriber/LogSystem/EventLoggerSubscriber.php b/src/EventListener/LogSystem/EventLoggerListener.php similarity index 81% rename from src/EventSubscriber/LogSystem/EventLoggerSubscriber.php rename to src/EventListener/LogSystem/EventLoggerListener.php index 1f38c66c..6fe3d8dc 100644 --- a/src/EventSubscriber/LogSystem/EventLoggerSubscriber.php +++ b/src/EventListener/LogSystem/EventLoggerListener.php @@ -20,7 +20,7 @@ declare(strict_types=1); -namespace App\EventSubscriber\LogSystem; +namespace App\EventListener\LogSystem; use App\Entity\Attachments\Attachment; use App\Entity\Base\AbstractDBElement; @@ -29,6 +29,7 @@ use App\Entity\LogSystem\CollectionElementDeleted; use App\Entity\LogSystem\ElementCreatedLogEntry; use App\Entity\LogSystem\ElementDeletedLogEntry; use App\Entity\LogSystem\ElementEditedLogEntry; +use App\Entity\OAuthToken; use App\Entity\Parameters\AbstractParameter; use App\Entity\Parts\PartLot; use App\Entity\PriceInformations\Orderdetail; @@ -37,7 +38,7 @@ use App\Entity\UserSystem\User; use App\Services\LogSystem\EventCommentHelper; use App\Services\LogSystem\EventLogger; use App\Services\LogSystem\EventUndoHelper; -use Doctrine\Common\EventSubscriber; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\OnFlushEventArgs; use Doctrine\ORM\Event\PostFlushEventArgs; @@ -47,12 +48,15 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Serializer\SerializerInterface; /** - * This event subscriber write to event log when entities are changed, removed, created. + * This event subscriber writes to the event log when entities are changed, removed, created. */ -class EventLoggerSubscriber implements EventSubscriber +#[AsDoctrineListener(event: Events::onFlush)] +#[AsDoctrineListener(event: Events::postPersist)] +#[AsDoctrineListener(event: Events::postFlush)] +class EventLoggerListener { /** - * @var array The given fields will not be saved, because they contain sensitive informations + * @var array The given fields will not be saved, because they contain sensitive information */ protected const FIELD_BLACKLIST = [ User::class => ['password', 'need_pw_change', 'googleAuthenticatorSecret', 'backupCodes', 'trustedDeviceCookieVersion', 'pw_reset_token', 'backupCodesGenerationDate'], @@ -70,34 +74,19 @@ class EventLoggerSubscriber implements EventSubscriber ]; protected const MAX_STRING_LENGTH = 2000; + protected bool $save_new_data; - protected EventLogger $logger; - protected SerializerInterface $serializer; - protected EventCommentHelper $eventCommentHelper; - protected EventUndoHelper $eventUndoHelper; - protected bool $save_changed_fields; - protected bool $save_changed_data; - protected bool $save_removed_data; - protected PropertyAccessorInterface $propertyAccessor; - - public function __construct(EventLogger $logger, SerializerInterface $serializer, EventCommentHelper $commentHelper, - bool $save_changed_fields, bool $save_changed_data, bool $save_removed_data, PropertyAccessorInterface $propertyAccessor, - 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->logger = $logger; - $this->serializer = $serializer; - $this->eventCommentHelper = $commentHelper; - $this->propertyAccessor = $propertyAccessor; - $this->eventUndoHelper = $eventUndoHelper; - - $this->save_changed_fields = $save_changed_fields; - $this->save_changed_data = $save_changed_data; - $this->save_removed_data = $save_removed_data; + //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 { - $em = $eventArgs->getEntityManager(); + $em = $eventArgs->getObjectManager(); $uow = $em->getUnitOfWork(); /* @@ -128,7 +117,7 @@ class EventLoggerSubscriber implements EventSubscriber public function postPersist(LifecycleEventArgs $args): void { - //Create an log entry, we have to do this post persist, cause we have to know the ID + //Create a log entry, we have to do this post persist, because we have to know the ID /** @var AbstractDBElement $entity */ $entity = $args->getObject(); @@ -150,17 +139,18 @@ class EventLoggerSubscriber implements EventSubscriber $log->setTargetElementID($undoEvent->getDeletedElementID()); } } + $this->logger->log($log); } } public function postFlush(PostFlushEventArgs $args): void { - $em = $args->getEntityManager(); + $em = $args->getObjectManager(); $uow = $em->getUnitOfWork(); - // If the we have added any ElementCreatedLogEntries added in postPersist, we flush them here. + // If we have added any ElementCreatedLogEntries added in postPersist, we flush them here. $uow->computeChangeSets(); - if ($uow->hasPendingInsertions() || !empty($uow->getScheduledEntityUpdates())) { + if ($uow->hasPendingInsertions() || $uow->getScheduledEntityUpdates() !== []) { $em->flush(); } @@ -177,7 +167,7 @@ class EventLoggerSubscriber implements EventSubscriber public function hasFieldRestrictions(AbstractDBElement $element): bool { foreach (array_keys(static::FIELD_BLACKLIST) as $class) { - if (is_a($element, $class)) { + if ($element instanceof $class) { return true; } } @@ -191,24 +181,15 @@ class EventLoggerSubscriber implements EventSubscriber public function shouldFieldBeSaved(AbstractDBElement $element, string $field_name): bool { foreach (static::FIELD_BLACKLIST as $class => $blacklist) { - if (is_a($element, $class) && in_array($field_name, $blacklist, true)) { + if ($element instanceof $class && in_array($field_name, $blacklist, true)) { return false; } } - //By default allow every field. + //By default, allow every field. return true; } - public function getSubscribedEvents(): array - { - return[ - Events::onFlush, - Events::postPersist, - Events::postFlush, - ]; - } - protected function logElementDeleted(AbstractDBElement $entity, EntityManagerInterface $em): void { $log = new ElementDeletedLogEntry($entity); @@ -227,11 +208,11 @@ class EventLoggerSubscriber implements EventSubscriber //Check if we have to log CollectionElementDeleted entries if ($this->save_changed_data) { - $metadata = $em->getClassMetadata(get_class($entity)); + $metadata = $em->getClassMetadata($entity::class); $mappings = $metadata->getAssociationMappings(); //Check if class is whitelisted for CollectionElementDeleted entry foreach (static::TRIGGER_ASSOCIATION_LOG_WHITELIST as $class => $whitelist) { - if (is_a($entity, $class)) { + if ($entity instanceof $class) { //Check names foreach ($mappings as $field => $mapping) { if (in_array($field, $whitelist, true)) { @@ -257,7 +238,7 @@ class EventLoggerSubscriber implements EventSubscriber $changeSet = $uow->getEntityChangeSet($entity); //Skip log entry, if only the lastModified field has changed... - if (isset($changeSet['lastModified']) && count($changeSet)) { + if (count($changeSet) === 1 && isset($changeSet['lastModified'])) { return; } @@ -301,8 +282,25 @@ class EventLoggerSubscriber implements EventSubscriber }, ARRAY_FILTER_USE_BOTH); } + /** + * Restrict the length of every string in the given array to MAX_STRING_LENGTH, to save memory in the case of very + * long strings (e.g. images in notes) + */ + protected function fieldLengthRestrict(array $fields): array + { + return array_map( + static function ($value) { + if (is_string($value)) { + return mb_strimwidth($value, 0, self::MAX_STRING_LENGTH, '...'); + } + + return $value; + }, $fields); + } + protected function saveChangeSet(AbstractDBElement $entity, AbstractLogEntry $logEntry, EntityManagerInterface $em, bool $element_deleted = false): void { + $new_data = null; $uow = $em->getUnitOfWork(); if (!$logEntry instanceof ElementEditedLogEntry && !$logEntry instanceof ElementDeletedLogEntry) { @@ -314,20 +312,24 @@ class EventLoggerSubscriber implements EventSubscriber } else { //Otherwise we have to get it from entity changeset $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->save_new_data) { + $new_data = array_combine(array_keys($changeSet), array_column($changeSet, 1)); + } } $old_data = $this->filterFieldRestrictions($entity, $old_data); //Restrict length of string fields, to save memory... - $old_data = array_map( - static function ($value) { - if (is_string($value)) { - return mb_strimwidth($value, 0, self::MAX_STRING_LENGTH, '...'); - } - - return $value; - }, $old_data); + $old_data = $this->fieldLengthRestrict($old_data); $logEntry->setOldData($old_data); + + if ($new_data !== [] && $new_data !== null) { + $new_data = $this->filterFieldRestrictions($entity, $new_data); + $new_data = $this->fieldLengthRestrict($new_data); + + $logEntry->setNewData($new_data); + } } /** @@ -337,6 +339,11 @@ class EventLoggerSubscriber implements EventSubscriber */ protected function validEntity(object $entity): bool { + //Dont log OAuthTokens + if ($entity instanceof OAuthToken) { + return false; + } + //Dont log logentries itself! return $entity instanceof AbstractDBElement && !$entity instanceof AbstractLogEntry; } diff --git a/src/EventSubscriber/LogSystem/LogDBMigrationSubscriber.php b/src/EventListener/LogSystem/LogDBMigrationListener.php similarity index 81% rename from src/EventSubscriber/LogSystem/LogDBMigrationSubscriber.php rename to src/EventListener/LogSystem/LogDBMigrationListener.php index 610c0b5e..c8b60e18 100644 --- a/src/EventSubscriber/LogSystem/LogDBMigrationSubscriber.php +++ b/src/EventListener/LogSystem/LogDBMigrationListener.php @@ -20,11 +20,11 @@ declare(strict_types=1); -namespace App\EventSubscriber\LogSystem; +namespace App\EventListener\LogSystem; use App\Entity\LogSystem\DatabaseUpdatedLogEntry; use App\Services\LogSystem\EventLogger; -use Doctrine\Common\EventSubscriber; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\Migrations\DependencyFactory; use Doctrine\Migrations\Event\MigrationsEventArgs; use Doctrine\Migrations\Events; @@ -32,18 +32,15 @@ use Doctrine\Migrations\Events; /** * This subscriber logs databaseMigrations to Event log. */ -class LogDBMigrationSubscriber implements EventSubscriber +#[AsDoctrineListener(event: Events::onMigrationsMigrated)] +#[AsDoctrineListener(event: Events::onMigrationsMigrating)] +class LogDBMigrationListener { protected ?string $old_version = null; protected ?string $new_version = null; - protected EventLogger $eventLogger; - protected DependencyFactory $dependencyFactory; - - public function __construct(EventLogger $eventLogger, DependencyFactory $dependencyFactory) + public function __construct(protected EventLogger $eventLogger, protected DependencyFactory $dependencyFactory) { - $this->eventLogger = $eventLogger; - $this->dependencyFactory = $dependencyFactory; } public function onMigrationsMigrated(MigrationsEventArgs $args): void @@ -60,13 +57,13 @@ class LogDBMigrationSubscriber implements EventSubscriber $this->new_version = (string) $aliasResolver->resolveVersionAlias('current'); //After everything is done, write the results to DB log - $this->old_version = empty($this->old_version) ? 'legacy/empty' : $this->old_version; - $this->new_version = empty($this->new_version) ? 'unknown' : $this->new_version; + $this->old_version = $this->old_version === null || $this->old_version === '' ? 'legacy/empty' : $this->old_version; + $this->new_version = $this->new_version === '' ? 'unknown' : $this->new_version; try { $log = new DatabaseUpdatedLogEntry($this->old_version, $this->new_version); $this->eventLogger->logAndFlush($log); - } catch (\Throwable $exception) { + } catch (\Throwable) { //Ignore any exception occuring here... } } diff --git a/src/EventSubscriber/LogSystem/LogAccessDeniedSubscriber.php b/src/EventSubscriber/LogSystem/LogAccessDeniedSubscriber.php index a2b9f1ff..abe2f9ba 100644 --- a/src/EventSubscriber/LogSystem/LogAccessDeniedSubscriber.php +++ b/src/EventSubscriber/LogSystem/LogAccessDeniedSubscriber.php @@ -49,15 +49,12 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Security\Core\Exception\AccessDeniedException; /** - * Write to event log when a user tries to access an forbidden page and recevies an 403 Access Denied message. + * Write to event log when a user tries to access a forbidden page and receives an 403 Access Denied message. */ class LogAccessDeniedSubscriber implements EventSubscriberInterface { - private EventLogger $logger; - - public function __construct(EventLogger $logger) + public function __construct(private readonly EventLogger $logger) { - $this->logger = $logger; } public function onKernelException(ExceptionEvent $event): void diff --git a/src/EventSubscriber/LogSystem/LogoutLoggerListener.php b/src/EventSubscriber/LogSystem/LogLogoutEventSubscriber.php similarity index 70% rename from src/EventSubscriber/LogSystem/LogoutLoggerListener.php rename to src/EventSubscriber/LogSystem/LogLogoutEventSubscriber.php index 3b197b38..7e6d2da7 100644 --- a/src/EventSubscriber/LogSystem/LogoutLoggerListener.php +++ b/src/EventSubscriber/LogSystem/LogLogoutEventSubscriber.php @@ -22,6 +22,8 @@ declare(strict_types=1); namespace App\EventSubscriber\LogSystem; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use App\Entity\LogSystem\UserLogoutLogEntry; use App\Entity\UserSystem\User; use App\Services\LogSystem\EventLogger; @@ -30,27 +32,22 @@ use Symfony\Component\Security\Http\Event\LogoutEvent; /** * This handler logs to event log, if a user logs out. */ -class LogoutLoggerListener +final class LogLogoutEventSubscriber implements EventSubscriberInterface { - protected EventLogger $logger; - protected bool $gpdr_compliance; - - public function __construct(EventLogger $logger, bool $gpdr_compliance) + public function __construct(private readonly EventLogger $logger, private readonly bool $gdpr_compliance) { - $this->logger = $logger; - $this->gpdr_compliance = $gpdr_compliance; } - public function __invoke(LogoutEvent $event) + public function logLogout(LogoutEvent $event): void { $request = $event->getRequest(); $token = $event->getToken(); - if (null === $token) { + if (!$token instanceof TokenInterface) { return; } - $log = new UserLogoutLogEntry($request->getClientIp(), $this->gpdr_compliance); + $log = new UserLogoutLogEntry($request->getClientIp(), $this->gdpr_compliance); $user = $token->getUser(); if ($user instanceof User) { $log->setTargetElement($user); @@ -58,4 +55,12 @@ class LogoutLoggerListener $this->logger->logAndFlush($log); } + /** + * @return array + */ + public static function getSubscribedEvents(): array + { + return [LogoutEvent::class => 'logLogout']; + } + } diff --git a/src/EventSubscriber/LogSystem/SecurityEventLoggerSubscriber.php b/src/EventSubscriber/LogSystem/SecurityEventLoggerSubscriber.php index 78402633..7880a41d 100644 --- a/src/EventSubscriber/LogSystem/SecurityEventLoggerSubscriber.php +++ b/src/EventSubscriber/LogSystem/SecurityEventLoggerSubscriber.php @@ -41,6 +41,7 @@ declare(strict_types=1); namespace App\EventSubscriber\LogSystem; +use Symfony\Component\HttpFoundation\Request; use App\Entity\LogSystem\SecurityEventLogEntry; use App\Events\SecurityEvent; use App\Events\SecurityEvents; @@ -49,19 +50,12 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RequestStack; /** - * This subscriber writes entries to log if an security related event happens (e.g. the user changes its password). + * This subscriber writes entries to log if a security related event happens (e.g. the user changes its password). */ final class SecurityEventLoggerSubscriber implements EventSubscriberInterface { - private RequestStack $requestStack; - private bool $gpdr_compliant; - private EventLogger $eventLogger; - - public function __construct(RequestStack $requestStack, EventLogger $eventLogger, bool $gpdr_compliance) + public function __construct(private readonly RequestStack $requestStack, private readonly EventLogger $eventLogger, private readonly bool $gdpr_compliance) { - $this->requestStack = $requestStack; - $this->gpdr_compliant = $gpdr_compliance; - $this->eventLogger = $eventLogger; } public static function getSubscribedEvents(): array @@ -76,6 +70,7 @@ final class SecurityEventLoggerSubscriber implements EventSubscriberInterface SecurityEvents::GOOGLE_DISABLED => 'google_disabled', SecurityEvents::GOOGLE_ENABLED => 'google_enabled', SecurityEvents::TFA_ADMIN_RESET => 'tfa_admin_reset', + SecurityEvents::USER_IMPERSONATED => 'user_impersonated', ]; } @@ -109,6 +104,11 @@ final class SecurityEventLoggerSubscriber implements EventSubscriberInterface $this->addLog(SecurityEvents::U2F_REMOVED, $event); } + public function user_impersonated(SecurityEvent $event): void + { + $this->addLog(SecurityEvents::USER_IMPERSONATED, $event); + } + public function u2f_added(SecurityEvent $event): void { $this->addLog(SecurityEvents::U2F_ADDED, $event); @@ -126,14 +126,14 @@ final class SecurityEventLoggerSubscriber implements EventSubscriberInterface private function addLog(string $type, SecurityEvent $event): void { - $anonymize = $this->gpdr_compliant; + $anonymize = $this->gdpr_compliance; $request = $this->requestStack->getCurrentRequest(); - if (null !== $request) { + if ($request instanceof Request) { $ip = $request->getClientIp() ?? 'unknown'; } else { $ip = 'Console'; - //Dont try to apply IP filter rules to non numeric string + //Don't try to apply IP filter rules to non-numeric string $anonymize = false; } diff --git a/src/EventSubscriber/RedirectToHttpsSubscriber.php b/src/EventSubscriber/RedirectToHttpsSubscriber.php new file mode 100644 index 00000000..7089109e --- /dev/null +++ b/src/EventSubscriber/RedirectToHttpsSubscriber.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + + +namespace App\EventSubscriber; + +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\RequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Security\Http\HttpUtils; + +/** + * The purpose of this event listener is (if enabled) to redirect all requests to https. + */ +final class RedirectToHttpsSubscriber implements EventSubscriberInterface +{ + + public function __construct( + #[Autowire('%env(bool:REDIRECT_TO_HTTPS)%')] + private readonly bool $enabled, + private readonly HttpUtils $httpUtils) + { + } + + public static function getSubscribedEvents(): array + { + return [ + KernelEvents::REQUEST => 'onKernelRequest', + ]; + } + + public function onKernelRequest(RequestEvent $event): void + { + //If the feature is disabled, or we are not the main request, we do nothing + if (!$this->enabled || !$event->isMainRequest()) { + return; + } + + + $request = $event->getRequest(); + + //If the request is already https, we do nothing + if ($request->isSecure()) { + return; + } + + + //Change the request to https + $new_url = str_replace('http://', 'https://' ,$request->getUri()); + $event->setResponse($this->httpUtils->createRedirectResponse($event->getRequest(), $new_url)); + } +} \ No newline at end of file diff --git a/src/EventSubscriber/SetMailFromSubscriber.php b/src/EventSubscriber/SetMailFromSubscriber.php index 6c9f563c..3675c487 100644 --- a/src/EventSubscriber/SetMailFromSubscriber.php +++ b/src/EventSubscriber/SetMailFromSubscriber.php @@ -32,13 +32,8 @@ use Symfony\Component\Mime\Email; */ final class SetMailFromSubscriber implements EventSubscriberInterface { - private string $email; - private string $name; - - public function __construct(string $email, string $name) + public function __construct(private readonly string $email, private readonly string $name) { - $this->email = $email; - $this->name = $name; } public function onMessage(MessageEvent $event): void diff --git a/src/EventSubscriber/SwitchUserEventSubscriber.php b/src/EventSubscriber/SwitchUserEventSubscriber.php new file mode 100644 index 00000000..b68f6b4f --- /dev/null +++ b/src/EventSubscriber/SwitchUserEventSubscriber.php @@ -0,0 +1,64 @@ +. + */ + +declare(strict_types=1); + + +namespace App\EventSubscriber; + +use App\Entity\UserSystem\User; +use App\Events\SecurityEvent; +use App\Events\SecurityEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; +use Symfony\Component\Security\Http\Event\SwitchUserEvent; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +class SwitchUserEventSubscriber implements EventSubscriberInterface +{ + + public function __construct(private readonly EventDispatcherInterface $eventDispatcher) + { + } + + public static function getSubscribedEvents(): array + { + return [ + 'security.switch_user' => 'onSwitchUser', + ]; + } + + public function onSwitchUser(SwitchUserEvent $event): void + { + $target_user = $event->getTargetUser(); + // We can only handle User objects + if (!$target_user instanceof User) { + return; + } + + //We are only interested in impersonation (not unimpersonation) + if (!$event->getToken() instanceof SwitchUserToken) { + return; + } + + $security_event = new SecurityEvent($target_user); + $this->eventDispatcher->dispatch($security_event, SecurityEvents::USER_IMPERSONATED); + } +} \ No newline at end of file diff --git a/src/EventSubscriber/SymfonyDebugToolbarSubscriber.php b/src/EventSubscriber/SymfonyDebugToolbarSubscriber.php index f6f4fc25..6f17e399 100644 --- a/src/EventSubscriber/SymfonyDebugToolbarSubscriber.php +++ b/src/EventSubscriber/SymfonyDebugToolbarSubscriber.php @@ -26,15 +26,12 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; /** - * This subscriber sets an Header in Debug mode that signals the Symfony Profiler to also update on Ajax requests. + * This subscriber sets a Header in Debug mode that signals the Symfony Profiler to also update on Ajax requests. */ final class SymfonyDebugToolbarSubscriber implements EventSubscriberInterface { - private bool $kernel_debug; - - public function __construct(bool $kernel_debug) + public function __construct(private readonly bool $kernel_debug_enabled) { - $this->kernel_debug = $kernel_debug; } /** @@ -62,7 +59,7 @@ final class SymfonyDebugToolbarSubscriber implements EventSubscriberInterface public function onKernelResponse(ResponseEvent $event): void { - if (!$this->kernel_debug) { + if (!$this->kernel_debug_enabled) { return; } diff --git a/src/EventSubscriber/UserSystem/LoginSuccessSubscriber.php b/src/EventSubscriber/UserSystem/LoginSuccessSubscriber.php index 00b99fca..d67a8e14 100644 --- a/src/EventSubscriber/UserSystem/LoginSuccessSubscriber.php +++ b/src/EventSubscriber/UserSystem/LoginSuccessSubscriber.php @@ -26,43 +26,36 @@ use App\Entity\LogSystem\UserLoginLogEntry; use App\Entity\UserSystem\User; use App\Services\LogSystem\EventLogger; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Contracts\Translation\TranslatorInterface; /** - * This event listener shows an login successful flash to the user after login and write the login to event log. + * This event listener shows a login successful flash to the user after login and write the login to event log. */ final class LoginSuccessSubscriber implements EventSubscriberInterface { - private TranslatorInterface $translator; - private FlashBagInterface $flashBag; - private EventLogger $eventLogger; - private bool $gpdr_compliance; - - public function __construct(TranslatorInterface $translator, SessionInterface $session, EventLogger $eventLogger, bool $gpdr_compliance) + public function __construct(private readonly TranslatorInterface $translator, private readonly RequestStack $requestStack, private readonly EventLogger $eventLogger, private readonly bool $gdpr_compliance) { - /** @var Session $session */ - $this->translator = $translator; - $this->flashBag = $session->getFlashBag(); - $this->eventLogger = $eventLogger; - $this->gpdr_compliance = $gpdr_compliance; } public function onLogin(InteractiveLoginEvent $event): void { $ip = $event->getRequest()->getClientIp(); - $log = new UserLoginLogEntry($ip, $this->gpdr_compliance); + $log = new UserLoginLogEntry($ip, $this->gdpr_compliance); $user = $event->getAuthenticationToken()->getUser(); - if ($user instanceof User) { + if ($user instanceof User && $user->getID()) { $log->setTargetElement($user); + $this->eventLogger->logAndFlush($log); } - $this->eventLogger->logAndFlush($log); - $this->flashBag->add('notice', $this->translator->trans('flash.login_successful')); + /** @var Session $session */ + $session = $this->requestStack->getSession(); + $flashBag = $session->getFlashBag(); + + $flashBag->add('notice', $this->translator->trans('flash.login_successful')); } /** diff --git a/src/EventSubscriber/UserSystem/LogoutDisabledUserSubscriber.php b/src/EventSubscriber/UserSystem/LogoutDisabledUserSubscriber.php index 2ab3f8f7..33c5e919 100644 --- a/src/EventSubscriber/UserSystem/LogoutDisabledUserSubscriber.php +++ b/src/EventSubscriber/UserSystem/LogoutDisabledUserSubscriber.php @@ -22,35 +22,29 @@ declare(strict_types=1); namespace App\EventSubscriber\UserSystem; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\UserSystem\User; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; /** - * This subscriber is used to log out a disabled user, as soon as he to do an request. - * It is not possible for him to login again, afterwards. + * This subscriber is used to log out a disabled user, as soon as he to do a request. + * It is not possible for him to login again, afterward. */ final class LogoutDisabledUserSubscriber implements EventSubscriberInterface { - private Security $security; - private UrlGeneratorInterface $urlGenerator; - - public function __construct(Security $security, UrlGeneratorInterface $urlGenerator) + public function __construct(private readonly Security $security, private readonly UrlGeneratorInterface $urlGenerator) { - $this->security = $security; - - $this->urlGenerator = $urlGenerator; } public function onRequest(RequestEvent $event): void { $user = $this->security->getUser(); if ($user instanceof User && $user->isDisabled()) { - //Redirect to login + //Redirect to log in $response = new RedirectResponse($this->urlGenerator->generate('logout')); $event->setResponse($response); } diff --git a/src/EventSubscriber/UserSystem/PasswordChangeNeededSubscriber.php b/src/EventSubscriber/UserSystem/PasswordChangeNeededSubscriber.php index 98020f03..2eb32436 100644 --- a/src/EventSubscriber/UserSystem/PasswordChangeNeededSubscriber.php +++ b/src/EventSubscriber/UserSystem/PasswordChangeNeededSubscriber.php @@ -22,14 +22,14 @@ declare(strict_types=1); namespace App\EventSubscriber\UserSystem; +use Symfony\Bundle\SecurityBundle\Security; +use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; /** @@ -55,16 +55,9 @@ final class PasswordChangeNeededSubscriber implements EventSubscriberInterface * @var string The route the user will redirected to, if he needs to change this password */ public const REDIRECT_TARGET = 'user_settings'; - private Security $security; - private FlashBagInterface $flashBag; - private HttpUtils $httpUtils; - public function __construct(Security $security, SessionInterface $session, HttpUtils $httpUtils) + public function __construct(private readonly Security $security, private readonly HttpUtils $httpUtils) { - /** @var Session $session */ - $this->security = $security; - $this->flashBag = $session->getFlashBag(); - $this->httpUtils = $httpUtils; } /** @@ -84,8 +77,13 @@ final class PasswordChangeNeededSubscriber implements EventSubscriberInterface return; } + //If the user is impersonated, we don't need to redirect him + if ($this->security->isGranted('IS_IMPERSONATOR')) { + return; + } + //Abort if we dont need to redirect the user. - if (!$user->isNeedPwChange() && !static::TFARedirectNeeded($user)) { + if (!$user->isNeedPwChange() && !self::TFARedirectNeeded($user, $request)) { return; } @@ -99,17 +97,21 @@ final class PasswordChangeNeededSubscriber implements EventSubscriberInterface /* Dont redirect tree endpoints, as this would cause trouble and creates multiple flash warnigs for one page reload */ - if (false !== strpos($request->getUri(), '/tree/')) { + if (str_contains($request->getUri(), '/tree/')) { return; } + /** @var Session $session */ + $session = $request->getSession(); + $flashBag = $session->getFlashBag(); + //Show appropriate message to user about the reason he was redirected if ($user->isNeedPwChange()) { - $this->flashBag->add('warning', 'user.pw_change_needed.flash'); + $flashBag->add('warning', 'user.pw_change_needed.flash'); } - if (static::TFARedirectNeeded($user)) { - $this->flashBag->add('warning', 'user.2fa_needed.flash'); + if (self::TFARedirectNeeded($user, $request)) { + $flashBag->add('warning', 'user.2fa_needed.flash'); } $event->setResponse($this->httpUtils->createRedirectResponse($request, static::REDIRECT_TARGET)); @@ -119,16 +121,35 @@ final class PasswordChangeNeededSubscriber implements EventSubscriberInterface * Check if a redirect because of a missing 2FA method is needed. * That is the case if the group of the user enforces 2FA, but the user has neither Google Authenticator nor an * U2F key setup. + * The result is cached for some minutes in the session to prevent unnecessary database queries. * * @param User $user the user for which should be checked if it needs to be redirected * * @return bool true if the user needs to be redirected */ - public static function TFARedirectNeeded(User $user): bool + public static function TFARedirectNeeded(User $user, Request $request): bool { + //Check if when we have checked the user the last time + $session = $request->getSession(); + $last_check = $session->get('tfa_redirect_check', 0); + + //If we have checked the user already in the last 10 minutes, we don't need to check it again + if ($last_check > time() - 600) { + return false; + } + + //Otherwise we check the user again + $tfa_enabled = $user->isWebAuthnAuthenticatorEnabled() || $user->isGoogleAuthenticatorEnabled(); - return null !== $user->getGroup() && $user->getGroup()->isEnforce2FA() && !$tfa_enabled; + $result = $user->getGroup() instanceof Group && $user->getGroup()->isEnforce2FA() && !$tfa_enabled; + + //If no redirect is needed, we set the last check time to now + if (!$result) { + $session->set('tfa_redirect_check', time()); + } + + return $result; } public static function getSubscribedEvents(): array diff --git a/src/EventSubscriber/UserSystem/SetUserTimezoneSubscriber.php b/src/EventSubscriber/UserSystem/SetUserTimezoneSubscriber.php index c39d4b37..10ecaddf 100644 --- a/src/EventSubscriber/UserSystem/SetUserTimezoneSubscriber.php +++ b/src/EventSubscriber/UserSystem/SetUserTimezoneSubscriber.php @@ -23,23 +23,18 @@ declare(strict_types=1); namespace App\EventSubscriber\UserSystem; use App\Entity\UserSystem\User; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\Security\Core\Security; /** * The purpose of this event listener is to set the timezone to the one preferred by the user. */ final class SetUserTimezoneSubscriber implements EventSubscriberInterface { - private string $default_timezone; - private Security $security; - - public function __construct(string $timezone, Security $security) + public function __construct(private readonly string $default_timezone, private readonly Security $security) { - $this->default_timezone = $timezone; - $this->security = $security; } public function setTimeZone(ControllerEvent $event): void @@ -48,12 +43,12 @@ final class SetUserTimezoneSubscriber implements EventSubscriberInterface //Check if the user has set a timezone $user = $this->security->getUser(); - if ($user instanceof User && !empty($user->getTimezone())) { + if ($user instanceof User && ($user->getTimezone() !== null && $user->getTimezone() !== '')) { $timezone = $user->getTimezone(); } //Fill with default value if needed - if (null === $timezone && !empty($this->default_timezone)) { + if (null === $timezone && $this->default_timezone !== '') { $timezone = $this->default_timezone; } diff --git a/src/EventSubscriber/UserSystem/UpgradePermissionsSchemaSubscriber.php b/src/EventSubscriber/UserSystem/UpgradePermissionsSchemaSubscriber.php index 7d891df0..613bc6ec 100644 --- a/src/EventSubscriber/UserSystem/UpgradePermissionsSchemaSubscriber.php +++ b/src/EventSubscriber/UserSystem/UpgradePermissionsSchemaSubscriber.php @@ -1,4 +1,7 @@ . */ - namespace App\EventSubscriber\UserSystem; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Security\Core\User\UserInterface; use App\Entity\UserSystem\User; use App\Services\LogSystem\EventCommentHelper; use App\Services\UserSystem\PermissionSchemaUpdater; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; +use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\Security\Core\Security; /** - * The purpose of this event subscriber is to check if the permission schema of the current user is up to date and upgrade it automatically if needed. + * The purpose of this event subscriber is to check if the permission schema of the current user is up-to-date and upgrade it automatically if needed. */ class UpgradePermissionsSchemaSubscriber implements EventSubscriberInterface { - private Security $security; - private PermissionSchemaUpdater $permissionSchemaUpdater; - private EntityManagerInterface $entityManager; - private FlashBagInterface $flashBag; - private EventCommentHelper $eventCommentHelper; - - public function __construct(Security $security, PermissionSchemaUpdater $permissionSchemaUpdater, EntityManagerInterface $entityManager, FlashBagInterface $flashBag, EventCommentHelper $eventCommentHelper) + public function __construct(private readonly Security $security, private readonly PermissionSchemaUpdater $permissionSchemaUpdater, private readonly EntityManagerInterface $entityManager, private readonly EventCommentHelper $eventCommentHelper) { - $this->security = $security; - $this->permissionSchemaUpdater = $permissionSchemaUpdater; - $this->entityManager = $entityManager; - $this->flashBag = $flashBag; - $this->eventCommentHelper = $eventCommentHelper; } public function onRequest(RequestEvent $event): void @@ -57,16 +49,25 @@ class UpgradePermissionsSchemaSubscriber implements EventSubscriberInterface } $user = $this->security->getUser(); - if (null === $user) { + if (!$user instanceof UserInterface) { //Retrieve anonymous user $user = $this->entityManager->getRepository(User::class)->getAnonymousUser(); } + /** @var Session $session */ + $session = $event->getRequest()->getSession(); + $flashBag = $session->getFlashBag(); + + //Check if the user is an instance of User, otherwise we can't upgrade the schema + if (!$user instanceof User) { + return; + } + if ($this->permissionSchemaUpdater->isSchemaUpdateNeeded($user)) { $this->eventCommentHelper->setMessage('Automatic permission schema update'); $this->permissionSchemaUpdater->userUpgradeSchemaRecursively($user); $this->entityManager->flush(); - $this->flashBag->add('notice', 'user.permissions_schema_updated'); + $flashBag->add('notice', 'user.permissions_schema_updated'); } } @@ -74,4 +75,4 @@ class UpgradePermissionsSchemaSubscriber implements EventSubscriberInterface { return [KernelEvents::REQUEST => 'onRequest']; } -} \ No newline at end of file +} diff --git a/src/Events/SecurityEvent.php b/src/Events/SecurityEvent.php index bf8056d3..883460a7 100644 --- a/src/Events/SecurityEvent.php +++ b/src/Events/SecurityEvent.php @@ -46,15 +46,12 @@ use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered when something security related to a user happens. - * For example when the password is reset or the an two factor authentication method was disabled. + * For example when the password is reset or the two-factor authentication method was disabled. */ class SecurityEvent extends Event { - protected User $targetUser; - - public function __construct(User $targetUser) + public function __construct(protected User $targetUser) { - $this->targetUser = $targetUser; } /** diff --git a/src/Events/SecurityEvents.php b/src/Events/SecurityEvents.php index 6ac1f882..f2e44c6f 100644 --- a/src/Events/SecurityEvents.php +++ b/src/Events/SecurityEvents.php @@ -43,13 +43,15 @@ namespace App\Events; class SecurityEvents { - public const PASSWORD_CHANGED = 'security.password_changed'; - public const PASSWORD_RESET = 'security.password_reset'; - public const BACKUP_KEYS_RESET = 'security.backup_keys_reset'; - public const U2F_ADDED = 'security.u2f_added'; - public const U2F_REMOVED = 'security.u2f_removed'; - public const GOOGLE_ENABLED = 'security.google_enabled'; - public const GOOGLE_DISABLED = 'security.google_disabled'; - public const TRUSTED_DEVICE_RESET = 'security.trusted_device_reset'; - public const TFA_ADMIN_RESET = 'security.2fa_admin_reset'; + final public const PASSWORD_CHANGED = 'security.password_changed'; + final public const PASSWORD_RESET = 'security.password_reset'; + final public const BACKUP_KEYS_RESET = 'security.backup_keys_reset'; + final public const U2F_ADDED = 'security.u2f_added'; + final public const U2F_REMOVED = 'security.u2f_removed'; + final public const GOOGLE_ENABLED = 'security.google_enabled'; + final public const GOOGLE_DISABLED = 'security.google_disabled'; + final public const TRUSTED_DEVICE_RESET = 'security.trusted_device_reset'; + final public const TFA_ADMIN_RESET = 'security.2fa_admin_reset'; + + final public const USER_IMPERSONATED = 'security.user_impersonated'; } diff --git a/src/Exceptions/InvalidRegexException.php b/src/Exceptions/InvalidRegexException.php new file mode 100644 index 00000000..aaa2a897 --- /dev/null +++ b/src/Exceptions/InvalidRegexException.php @@ -0,0 +1,74 @@ +. + */ +namespace App\Exceptions; + +use Doctrine\DBAL\Exception\DriverException; +use ErrorException; + +class InvalidRegexException extends \RuntimeException +{ + public function __construct(private readonly ?string $reason = null) + { + parent::__construct('Invalid regular expression'); + } + + /** + * Returns the reason for the exception (what the regex driver deemed invalid) + */ + public function getReason(): ?string + { + return $this->reason; + } + + /** + * Creates a new exception from a driver exception happening, when MySQL encounters an invalid regex + */ + public static function fromDriverException(DriverException $exception): self + { + //1139 means invalid regex error + if ($exception->getCode() !== 1139) { + throw new \InvalidArgumentException('The given exception is not a driver exception', 0, $exception); + } + + //Reason is the part after the erorr code + $reason = preg_replace('/^.*1139 /', '', $exception->getMessage()); + + return new self($reason); + } + + /** + * Creates a new exception from the errorException thrown by mb_ereg + */ + public static function fromMBRegexError(ErrorException $ex): self + { + //Ensure that the error is really a mb_ereg error + if ($ex->getSeverity() !== E_WARNING || !strpos($ex->getMessage(), 'mb_ereg()')) { + throw new \InvalidArgumentException('The given exception is not a mb_ereg error', 0, $ex); + } + + //Reason is the part after the erorr code + $reason = preg_replace('/^.*mb_ereg\(\): /', '', $ex->getMessage()); + + return new self($reason); + } +} diff --git a/src/Exceptions/TwigModeException.php b/src/Exceptions/TwigModeException.php index adcc86aa..b76d14d3 100644 --- a/src/Exceptions/TwigModeException.php +++ b/src/Exceptions/TwigModeException.php @@ -46,8 +46,23 @@ use Twig\Error\Error; class TwigModeException extends RuntimeException { + private const PROJECT_PATH = __DIR__ . '/../../'; + public function __construct(?Error $previous = null) { parent::__construct($previous->getMessage(), 0, $previous); } + + /** + * Returns the message of this exception, where it is tried to remove any sensitive information (like filepaths). + * @return string + */ + public function getSafeMessage(): string + { + //Resolve project root path + $projectPath = realpath(self::PROJECT_PATH); + + //Remove occurrences of the project path from the message + return str_replace($projectPath, '[Part-DB Root Folder]', $this->getMessage()); + } } diff --git a/src/Form/AdminPages/AttachmentTypeAdminForm.php b/src/Form/AdminPages/AttachmentTypeAdminForm.php index 75174279..d777d4d4 100644 --- a/src/Form/AdminPages/AttachmentTypeAdminForm.php +++ b/src/Form/AdminPages/AttachmentTypeAdminForm.php @@ -22,21 +22,19 @@ declare(strict_types=1); namespace App\Form\AdminPages; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Base\AbstractNamedDBElement; use App\Services\Attachments\FileTypeFilterTools; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Security; class AttachmentTypeAdminForm extends BaseEntityAdminForm { - protected FileTypeFilterTools $filterTools; - - public function __construct(Security $security, FileTypeFilterTools $filterTools) + public function __construct(Security $security, protected FileTypeFilterTools $filterTools, EventCommentNeededHelper $eventCommentNeededHelper) { - $this->filterTools = $filterTools; - parent::__construct($security); + parent::__construct($security, $eventCommentNeededHelper); } protected function additionalFormElements(FormBuilderInterface $builder, array $options, AbstractNamedDBElement $entity): void @@ -57,12 +55,8 @@ class AttachmentTypeAdminForm extends BaseEntityAdminForm //Normalize data before writing it to database $builder->get('filetype_filter')->addViewTransformer(new CallbackTransformer( - static function ($value) { - return $value; - }, - function ($value) { - return $this->filterTools->normalizeFilterString($value); - } + static fn($value) => $value, + fn($value) => $this->filterTools->normalizeFilterString($value) )); } } diff --git a/src/Form/AdminPages/BaseEntityAdminForm.php b/src/Form/AdminPages/BaseEntityAdminForm.php index a28e0211..d1a0ffd0 100644 --- a/src/Form/AdminPages/BaseEntityAdminForm.php +++ b/src/Form/AdminPages/BaseEntityAdminForm.php @@ -22,17 +22,19 @@ declare(strict_types=1); namespace App\Form\AdminPages; +use App\Entity\PriceInformations\Currency; +use App\Entity\ProjectSystem\Project; +use App\Entity\UserSystem\Group; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\LabelSystem\LabelProfile; -use App\Entity\Parameters\AbstractParameter; use App\Form\AttachmentFormType; use App\Form\ParameterType; use App\Form\Type\MasterPictureAttachmentType; use App\Form\Type\RichTextEditorType; use App\Form\Type\StructuralEntityType; -use FOS\CKEditorBundle\Form\Type\CKEditorType; -use function get_class; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; @@ -41,15 +43,11 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; class BaseEntityAdminForm extends AbstractType { - protected Security $security; - - public function __construct(Security $security) + public function __construct(protected Security $security, protected EventCommentNeededHelper $eventCommentNeededHelper) { - $this->security = $security; } public function configureOptions(OptionsResolver $resolver): void @@ -81,7 +79,7 @@ class BaseEntityAdminForm extends AbstractType 'parent', StructuralEntityType::class, [ - 'class' => get_class($entity), + 'class' => $entity::class, 'required' => false, 'label' => 'parent.label', 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity), @@ -115,6 +113,19 @@ class BaseEntityAdminForm extends AbstractType ); } + if ($entity instanceof AbstractStructuralDBElement && !($entity instanceof Group || $entity instanceof Project || $entity instanceof Currency)) { + $builder->add('alternative_names', TextType::class, [ + 'required' => false, + 'label' => 'entity.edit.alternative_names.label', + 'help' => 'entity.edit.alternative_names.help', + 'empty_data' => null, + 'attr' => [ + 'class' => 'tagsinput', + 'data-controller' => 'elements--tagsinput', + ] + ]); + } + $this->additionalFormElements($builder, $options, $entity); //Attachment section @@ -141,7 +152,7 @@ class BaseEntityAdminForm extends AbstractType $builder->add('log_comment', TextType::class, [ 'label' => 'edit.log_comment', 'mapped' => false, - 'required' => false, + 'required' => $this->eventCommentNeededHelper->isCommentNeeded($is_new ? 'datastructure_create': 'datastructure_edit'), 'empty_data' => null, ]); diff --git a/src/Form/AdminPages/CategoryAdminForm.php b/src/Form/AdminPages/CategoryAdminForm.php index 10a56646..44c1dede 100644 --- a/src/Form/AdminPages/CategoryAdminForm.php +++ b/src/Form/AdminPages/CategoryAdminForm.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace App\Form\AdminPages; use App\Entity\Base\AbstractNamedDBElement; +use App\Form\Part\EDA\EDACategoryInfoType; use App\Form\Type\RichTextEditorType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -104,5 +105,11 @@ class CategoryAdminForm extends BaseEntityAdminForm ], 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity), ]); + + //EDA info + $builder->add('eda_info', EDACategoryInfoType::class, [ + 'label' => false, + 'required' => false, + ]); } } diff --git a/src/Form/AdminPages/CurrencyAdminForm.php b/src/Form/AdminPages/CurrencyAdminForm.php index 19123465..0fab055d 100644 --- a/src/Form/AdminPages/CurrencyAdminForm.php +++ b/src/Form/AdminPages/CurrencyAdminForm.php @@ -22,21 +22,19 @@ declare(strict_types=1); namespace App\Form\AdminPages; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Base\AbstractNamedDBElement; use App\Form\Type\BigDecimalMoneyType; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\Extension\Core\Type\CurrencyType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Security; class CurrencyAdminForm extends BaseEntityAdminForm { - private string $default_currency; - - public function __construct(Security $security, string $default_currency) + public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, private readonly string $base_currency) { - parent::__construct($security); - $this->default_currency = $default_currency; + parent::__construct($security, $eventCommentNeededHelper); } protected function additionalFormElements(FormBuilderInterface $builder, array $options, AbstractNamedDBElement $entity): void @@ -44,7 +42,7 @@ class CurrencyAdminForm extends BaseEntityAdminForm $is_new = null === $entity->getID(); $builder->add('iso_code', CurrencyType::class, [ - 'required' => false, + 'required' => true, 'label' => 'currency.edit.iso_code', 'preferred_choices' => ['EUR', 'USD', 'GBP', 'JPY', 'CNY'], 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity), @@ -53,7 +51,7 @@ class CurrencyAdminForm extends BaseEntityAdminForm $builder->add('exchange_rate', BigDecimalMoneyType::class, [ 'required' => false, 'label' => 'currency.edit.exchange_rate', - 'currency' => $this->default_currency, + 'currency' => $this->base_currency, 'scale' => 6, 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity), ]); diff --git a/src/Form/AdminPages/FootprintAdminForm.php b/src/Form/AdminPages/FootprintAdminForm.php index a01e91bc..f96564d0 100644 --- a/src/Form/AdminPages/FootprintAdminForm.php +++ b/src/Form/AdminPages/FootprintAdminForm.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace App\Form\AdminPages; use App\Entity\Base\AbstractNamedDBElement; +use App\Form\Part\EDA\EDAFootprintInfoType; use App\Form\Type\MasterPictureAttachmentType; use Symfony\Component\Form\FormBuilderInterface; @@ -37,5 +38,11 @@ class FootprintAdminForm extends BaseEntityAdminForm 'filter' => '3d_model', 'entity' => $entity, ]); + + //EDA info + $builder->add('eda_info', EDAFootprintInfoType::class, [ + 'label' => false, + 'required' => false, + ]); } } diff --git a/src/Form/AdminPages/ImportType.php b/src/Form/AdminPages/ImportType.php index 03e8800b..3e87812c 100644 --- a/src/Form/AdminPages/ImportType.php +++ b/src/Form/AdminPages/ImportType.php @@ -22,7 +22,10 @@ declare(strict_types=1); namespace App\Form\AdminPages; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Parts\Category; +use App\Entity\Parts\Part; use App\Form\Type\StructuralEntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; @@ -31,15 +34,11 @@ use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Security; class ImportType extends AbstractType { - protected Security $security; - - public function __construct(Security $security) + public function __construct(protected Security $security) { - $this->security = $security; } public function buildForm(FormBuilderInterface $builder, array $options): void @@ -48,13 +47,14 @@ class ImportType extends AbstractType //Disable import if user is not allowed to create elements. $entity = new $data['entity_class'](); - $perm_name = 'create'; + $perm_name = 'import'; $disabled = !$this->security->isGranted($perm_name, $entity); $builder ->add('format', ChoiceType::class, [ 'choices' => [ + 'parts.import.format.auto' => 'auto', 'JSON' => 'json', 'XML' => 'xml', 'CSV' => 'csv', @@ -63,7 +63,7 @@ class ImportType extends AbstractType 'label' => 'export.format', 'disabled' => $disabled, ]) - ->add('csv_separator', TextType::class, [ + ->add('csv_delimiter', TextType::class, [ 'data' => ';', 'label' => 'import.csv_separator', 'disabled' => $disabled, @@ -78,6 +78,51 @@ class ImportType extends AbstractType ]); } + if ($entity instanceof Part) { + $builder->add('part_category', StructuralEntityType::class, [ + 'class' => Category::class, + 'required' => false, + 'label' => 'parts.import.part_category.label', + 'help' => 'parts.import.part_category.help', + 'disabled' => $disabled, + 'disable_not_selectable' => true, + 'allow_add' => true + ]); + $builder->add('part_needs_review', CheckboxType::class, [ + 'data' => false, + 'required' => false, + 'label' => 'parts.import.part_needs_review.label', + 'help' => 'parts.import.part_needs_review.help', + 'disabled' => $disabled, + ]); + } + + if ($entity instanceof AbstractStructuralDBElement) { + $builder->add('preserve_children', CheckboxType::class, [ + 'data' => true, + 'required' => false, + 'label' => 'import.preserve_children', + 'disabled' => $disabled, + ]); + } + + if ($entity instanceof Part) { + $builder->add('create_unknown_datastructures', CheckboxType::class, [ + 'data' => true, + 'required' => false, + 'label' => 'import.create_unknown_datastructures', + 'help' => 'import.create_unknown_datastructures.help', + 'disabled' => $disabled, + ]); + + $builder->add('path_delimiter', TextType::class, [ + 'data' => '->', + 'label' => 'import.path_delimiter', + 'help' => 'import.path_delimiter.help', + 'disabled' => $disabled, + ]); + } + $builder->add('file', FileType::class, [ 'label' => 'import.file', 'attr' => [ @@ -86,21 +131,15 @@ class ImportType extends AbstractType 'data-show-upload' => 'false', ], 'disabled' => $disabled, - ]) + ]); - ->add('preserve_children', CheckboxType::class, [ - 'data' => true, - 'required' => false, - 'label' => 'import.preserve_children', - 'disabled' => $disabled, - ]) - ->add('abort_on_validation_error', CheckboxType::class, [ - 'data' => true, - 'required' => false, - 'label' => 'import.abort_on_validation', - 'help' => 'import.abort_on_validation.help', - 'disabled' => $disabled, - ]) + $builder->add('abort_on_validation_error', CheckboxType::class, [ + 'data' => true, + 'required' => false, + 'label' => 'import.abort_on_validation', + 'help' => 'import.abort_on_validation.help', + 'disabled' => $disabled, + ]) //Buttons ->add('import', SubmitType::class, [ diff --git a/src/Form/AdminPages/MassCreationForm.php b/src/Form/AdminPages/MassCreationForm.php index 27ee8287..4948cdd5 100644 --- a/src/Form/AdminPages/MassCreationForm.php +++ b/src/Form/AdminPages/MassCreationForm.php @@ -22,21 +22,18 @@ declare(strict_types=1); namespace App\Form\AdminPages; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Base\AbstractStructuralDBElement; use App\Form\Type\StructuralEntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Security; class MassCreationForm extends AbstractType { - protected Security $security; - - public function __construct(Security $security) + public function __construct(protected Security $security) { - $this->security = $security; } public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/AdminPages/ProjectAdminForm.php b/src/Form/AdminPages/ProjectAdminForm.php index 3547d094..2d4683c9 100644 --- a/src/Form/AdminPages/ProjectAdminForm.php +++ b/src/Form/AdminPages/ProjectAdminForm.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\AdminPages; use App\Entity\Base\AbstractNamedDBElement; use App\Form\ProjectSystem\ProjectBOMEntryCollectionType; -use App\Form\ProjectSystem\ProjectBOMEntryType; use App\Form\Type\RichTextEditorType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\FormBuilderInterface; class ProjectAdminForm extends BaseEntityAdminForm @@ -61,4 +61,4 @@ class ProjectAdminForm extends BaseEntityAdminForm ], ]); } -} \ No newline at end of file +} diff --git a/src/Form/AdminPages/StorelocationAdminForm.php b/src/Form/AdminPages/StorelocationAdminForm.php index 8a85e4ec..c5c76b16 100644 --- a/src/Form/AdminPages/StorelocationAdminForm.php +++ b/src/Form/AdminPages/StorelocationAdminForm.php @@ -25,6 +25,7 @@ namespace App\Form\AdminPages; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Parts\MeasurementUnit; use App\Form\Type\StructuralEntityType; +use App\Form\Type\UserSelectType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\FormBuilderInterface; @@ -63,5 +64,16 @@ class StorelocationAdminForm extends BaseEntityAdminForm 'disable_not_selectable' => true, 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity), ]); + + $builder->add('owner', UserSelectType::class, [ + 'required' => false, + 'label' => 'storelocation.owner.label', + 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity), + ]); + $builder->add('part_owner_must_match', CheckboxType::class, [ + 'required' => false, + 'label' => 'storelocation.part_owner_must_match.label', + 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity), + ]); } } diff --git a/src/Form/AdminPages/SupplierForm.php b/src/Form/AdminPages/SupplierForm.php index 95cecfd3..34b3b27a 100644 --- a/src/Form/AdminPages/SupplierForm.php +++ b/src/Form/AdminPages/SupplierForm.php @@ -22,21 +22,19 @@ declare(strict_types=1); namespace App\Form\AdminPages; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\PriceInformations\Currency; use App\Form\Type\BigDecimalMoneyType; use App\Form\Type\StructuralEntityType; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Security; class SupplierForm extends CompanyForm { - protected string $default_currency; - - public function __construct(Security $security, string $default_currency) + public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, protected string $base_currency) { - parent::__construct($security); - $this->default_currency = $default_currency; + parent::__construct($security, $eventCommentNeededHelper); } protected function additionalFormElements(FormBuilderInterface $builder, array $options, AbstractNamedDBElement $entity): void @@ -55,7 +53,7 @@ class SupplierForm extends CompanyForm $builder->add('shipping_costs', BigDecimalMoneyType::class, [ 'required' => false, - 'currency' => $this->default_currency, + 'currency' => $this->base_currency, 'scale' => 3, 'label' => 'supplier.shipping_costs.label', 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity), diff --git a/src/Form/AttachmentFormType.php b/src/Form/AttachmentFormType.php index ad1c53a9..957d692b 100644 --- a/src/Form/AttachmentFormType.php +++ b/src/Form/AttachmentFormType.php @@ -22,12 +22,12 @@ declare(strict_types=1); namespace App\Form; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentType; use App\Form\Type\StructuralEntityType; use App\Services\Attachments\AttachmentManager; use App\Services\Attachments\AttachmentSubmitHandler; -use App\Validator\Constraints\AllowedFileExtension; use App\Validator\Constraints\UrlOrBuiltin; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; @@ -37,40 +37,37 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\Constraints\Url; use Symfony\Contracts\Translation\TranslatorInterface; class AttachmentFormType extends AbstractType { - protected AttachmentManager $attachment_helper; - protected UrlGeneratorInterface $urlGenerator; - protected bool $allow_attachments_download; - protected Security $security; - protected AttachmentSubmitHandler $submitHandler; - protected TranslatorInterface $translator; - - public function __construct(AttachmentManager $attachmentHelper, - UrlGeneratorInterface $urlGenerator, Security $security, - bool $allow_attachments_downloads, AttachmentSubmitHandler $submitHandler, TranslatorInterface $translator) - { - $this->attachment_helper = $attachmentHelper; - $this->urlGenerator = $urlGenerator; - $this->allow_attachments_download = $allow_attachments_downloads; - $this->security = $security; - $this->submitHandler = $submitHandler; - $this->translator = $translator; + public function __construct( + protected AttachmentManager $attachment_helper, + protected UrlGeneratorInterface $urlGenerator, + protected Security $security, + protected AttachmentSubmitHandler $submitHandler, + protected TranslatorInterface $translator, + protected bool $allow_attachments_download, + protected bool $download_by_default, + protected string $max_file_size + ) { } public function buildForm(FormBuilderInterface $builder, array $options): void { - $builder->add('name', TextType::class, [ - 'label' => 'attachment.edit.name', - ]) + $builder + ->add('name', TextType::class, [ + 'label' => 'attachment.edit.name', + 'required' => false, + 'empty_data' => '', + ]) ->add('attachment_type', StructuralEntityType::class, [ 'label' => 'attachment.edit.attachment_type', 'class' => AttachmentType::class, @@ -96,7 +93,8 @@ class AttachmentFormType extends AbstractType 'required' => false, 'attr' => [ 'data-controller' => 'elements--attachment-autocomplete', - 'data-autocomplete' => $this->urlGenerator->generate('typeahead_builtInRessources', ['query' => '__QUERY__']), + 'data-autocomplete' => $this->urlGenerator->generate('typeahead_builtInRessources', + ['query' => '__QUERY__']), //Disable browser autocomplete 'autocomplete' => 'off', ], @@ -130,6 +128,7 @@ class AttachmentFormType extends AbstractType ], ]); + $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event): void { $form = $event->getForm(); $attachment = $form->getData(); @@ -137,13 +136,33 @@ class AttachmentFormType extends AbstractType $file_form = $form->get('file'); $file = $file_form->getData(); - if ($attachment instanceof Attachment && $file instanceof UploadedFile && $attachment->getAttachmentType( - ) && !$this->submitHandler->isValidFileExtension($attachment->getAttachmentType(), $file)) { - $event->getForm()->get('file')->addError( - new FormError($this->translator->trans('validator.file_ext_not_allowed')) - ); + if (!$attachment instanceof Attachment) { + return; } - }); + + if (!$file instanceof UploadedFile) { + //When no file was uploaded, but a URL was entered, try to determine the attachment name from the URL + if ((trim($attachment->getName()) === '') && ($attachment->getURL() !== null && $attachment->getURL() !== '')) { + $name = basename(parse_url($attachment->getURL(), PHP_URL_PATH)); + $attachment->setName($name); + } + + return; + } + + //Ensure that the file extension is allowed for the selected attachment type + if ($attachment->getAttachmentType() + && !$this->submitHandler->isValidFileExtension($attachment->getAttachmentType(), $file)) { + $event->getForm()->get('file')->addError( + new FormError($this->translator->trans('validator.file_ext_not_allowed')) + ); + } + + //If the name is empty, use the original file name as attachment name + if ($attachment->getName() === '') { + $attachment->setName($file->getClientOriginalName()); + } + }, 100000); //Check the secure file checkbox, if file is in securefile location $builder->get('secureFile')->addEventListener( @@ -155,17 +174,46 @@ 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->download_by_default && $this->allow_attachments_download) { + $builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event): void { + $form = $event->getForm(); + $attachment = $form->getData(); + + if (!$attachment instanceof Attachment && $attachment !== null) { + return; + } + + //If the attachment was not created yet, set the downloadURL checkbox to true + if ($attachment === null || $attachment->getId() === null) { + $checkbox = $form->get('downloadURL'); + //Ensure that the checkbox is not disabled + if ($checkbox->isDisabled()) { + return; + } + //Set the checkbox + $checkbox->setData(true); + } + }); + } } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'data_class' => Attachment::class, - 'max_file_size' => '16M', + 'max_file_size' => $this->max_file_size, 'allow_builtins' => true, ]); } + public function finishView(FormView $view, FormInterface $form, array $options): void + { + $view->vars['max_upload_size'] = $this->submitHandler->getMaximumAllowedUploadSize(); + } + public function getBlockPrefix(): string { return 'attachment'; diff --git a/src/Form/CollectionTypeExtension.php b/src/Form/CollectionTypeExtension.php index 98807f01..4fa93852 100644 --- a/src/Form/CollectionTypeExtension.php +++ b/src/Form/CollectionTypeExtension.php @@ -44,14 +44,18 @@ namespace App\Form; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use ReflectionClass; -use ReflectionException; use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Event\PreSubmitEvent; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormConfigInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; @@ -59,15 +63,12 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; * Perform a reindexing on CollectionType elements, by assigning the database id as index. * This prevents issues when the collection that is edited uses a OrderBy annotation and therefore the direction of the * elements can change during requests. - * Must me enabled by setting reindex_enable to true in Type options. + * Must be enabled by setting reindex_enable to true in Type options. */ class CollectionTypeExtension extends AbstractTypeExtension { - protected PropertyAccessorInterface $propertyAccess; - - public function __construct(PropertyAccessorInterface $propertyAccess) + public function __construct(protected PropertyAccessorInterface $propertyAccess) { - $this->propertyAccess = $propertyAccess; } public static function getExtendedTypes(): iterable @@ -87,11 +88,23 @@ class CollectionTypeExtension extends AbstractTypeExtension 'reindex_path' => 'id', ]); + //Set a unique prototype name, so that we can use nested collections + $resolver->setDefaults([ + 'prototype_name' => fn(Options $options): string => '__name_'.uniqid("", false) . '__', + ]); + $resolver->setAllowedTypes('reindex_enable', 'bool'); $resolver->setAllowedTypes('reindex_prefix', 'string'); $resolver->setAllowedTypes('reindex_path', 'string'); } + public function finishView(FormView $view, FormInterface $form, array $options): void + { + parent::finishView($view, $form, $options); + //Add prototype name to view, so that we can pass it to the stimulus controller + $view->vars['prototype_name'] = $options['prototype_name']; + } + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options): void { @@ -118,22 +131,52 @@ class CollectionTypeExtension extends AbstractTypeExtension } } }, 100); //We need to have a higher priority then the PRE_SET_DATA listener on CollectionType + + // This event listener fixes the error mapping for newly created elements of collection types + // Without this method, the errors for newly created elements are shown on the parent element, as forms + // can not map it to the correct element. + $builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event) { + $data = $event->getData(); + $form = $event->getForm(); + $config = $form->getConfig(); + + if (!is_array($data) && !$data instanceof Collection) { + return; + } + + if ($data instanceof Collection) { + $data = $data->toArray(); + } + + //The validator uses the number of the element as index, so we have to map the errors to the correct index + $error_mapping = []; + $n = 0; + foreach (array_keys($data) as $key) { + $error_mapping['['.$n.']'] = $key; + $n++; + } + $this->setOption($config, 'error_mapping', $error_mapping); + }); } /** * Set the option of the form. - * This a bit hacky cause we access private properties.... - * + * This a bit hacky because we access private properties.... + * @param FormConfigInterface $builder The form on which the option should be set + * @param string $option The option which should be changed + * @param mixed $value The new value */ - public function setOption(FormBuilder $builder, string $option, $value): void + public function setOption(FormConfigInterface $builder, string $option, mixed $value): void { - //We have to use FormConfigBuilder::class here, because options is private and not available in sub classes + if (!$builder instanceof FormConfigBuilder) { + throw new \RuntimeException('This method only works with FormConfigBuilder instances.'); + } + + //We have to use FormConfigBuilder::class here, because options is private and not available in subclasses $reflection = new ReflectionClass(FormConfigBuilder::class); $property = $reflection->getProperty('options'); - $property->setAccessible(true); $tmp = $property->getValue($builder); $tmp[$option] = $value; $property->setValue($builder, $tmp); - $property->setAccessible(false); } } diff --git a/src/Form/Filters/AttachmentFilterType.php b/src/Form/Filters/AttachmentFilterType.php index 57967be7..ff80bd38 100644 --- a/src/Form/Filters/AttachmentFilterType.php +++ b/src/Form/Filters/AttachmentFilterType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters; use App\DataTables\Filters\AttachmentFilter; @@ -30,19 +32,16 @@ use App\Entity\Attachments\FootprintAttachment; use App\Entity\Attachments\GroupAttachment; use App\Entity\Attachments\LabelAttachment; use App\Entity\Attachments\PartAttachment; -use App\Entity\Attachments\StorelocationAttachment; +use App\Entity\Attachments\StorageLocationAttachment; use App\Entity\Attachments\SupplierAttachment; use App\Entity\Attachments\UserAttachment; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; -use App\Entity\Parts\Supplier; -use App\Form\AdminPages\FootprintAdminForm; use App\Form\Filters\Constraints\BooleanConstraintType; use App\Form\Filters\Constraints\DateTimeConstraintType; use App\Form\Filters\Constraints\InstanceOfConstraintType; use App\Form\Filters\Constraints\NumberConstraintType; use App\Form\Filters\Constraints\StructuralEntityConstraintType; -use App\Form\Filters\Constraints\UserEntityConstraintType; use App\Form\Filters\Constraints\TextConstraintType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ResetType; @@ -61,7 +60,7 @@ class AttachmentFilterType extends AbstractType ]); } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->add('dbId', NumberConstraintType::class, [ 'label' => 'part.filter.dbId', @@ -86,7 +85,7 @@ class AttachmentFilterType extends AbstractType 'label_profile.label' => LabelAttachment::class, 'manufacturer.label' => Manufacturer::class, 'measurement_unit.label' => MeasurementUnit::class, - 'storelocation.label' => StorelocationAttachment::class, + 'storelocation.label' => StorageLocationAttachment::class, 'supplier.label' => SupplierAttachment::class, 'user.label' => UserAttachment::class, ] @@ -101,6 +100,15 @@ class AttachmentFilterType extends AbstractType 'label' => 'attachment.edit.show_in_table' ]); + $builder->add('originalFileName', TextConstraintType::class, [ + 'label' => 'attachment.file_name' + ]); + + $builder->add('externalLink', TextConstraintType::class, [ + 'label' => 'attachment.table.external_link' + ]); + + $builder->add('lastModified', DateTimeConstraintType::class, [ 'label' => 'lastModified' ]); @@ -117,4 +125,4 @@ class AttachmentFilterType extends AbstractType 'label' => 'filter.discard', ]); } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/BooleanConstraintType.php b/src/Form/Filters/Constraints/BooleanConstraintType.php index e04e88d3..6669b5a7 100644 --- a/src/Form/Filters/Constraints/BooleanConstraintType.php +++ b/src/Form/Filters/Constraints/BooleanConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\BooleanConstraint; use App\Form\Type\TriStateCheckboxType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; class BooleanConstraintType extends AbstractType @@ -43,4 +47,10 @@ class BooleanConstraintType extends AbstractType 'required' => false, ]); } -} \ No newline at end of file + + public function finishView(FormView $view, FormInterface $form, array $options): void + { + //Remove the label from the compound form, as the checkbox already has a label + $view->vars['label'] = false; + } +} diff --git a/src/Form/Filters/Constraints/ChoiceConstraintType.php b/src/Form/Filters/Constraints/ChoiceConstraintType.php index 16014c7f..70d37b08 100644 --- a/src/Form/Filters/Constraints/ChoiceConstraintType.php +++ b/src/Form/Filters/Constraints/ChoiceConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\ChoiceConstraint; @@ -63,4 +65,4 @@ class ChoiceConstraintType extends AbstractType ]); } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/DateTimeConstraintType.php b/src/Form/Filters/Constraints/DateTimeConstraintType.php index f476f0ed..ffd3aafd 100644 --- a/src/Form/Filters/Constraints/DateTimeConstraintType.php +++ b/src/Form/Filters/Constraints/DateTimeConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\DateTimeConstraint; -use App\DataTables\Filters\Constraints\NumberConstraint; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\DateTimeType; -use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; @@ -86,10 +86,10 @@ class DateTimeConstraintType extends AbstractType ]); } - public function buildView(FormView $view, FormInterface $form, array $options) + public function buildView(FormView $view, FormInterface $form, array $options): void { parent::buildView($view, $form, $options); $view->vars['text_suffix'] = $options['text_suffix']; } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/EnumConstraintType.php b/src/Form/Filters/Constraints/EnumConstraintType.php new file mode 100644 index 00000000..59259169 --- /dev/null +++ b/src/Form/Filters/Constraints/EnumConstraintType.php @@ -0,0 +1,73 @@ +. + */ +namespace App\Form\Filters\Constraints; + +use App\DataTables\Filters\Constraints\ChoiceConstraint; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\EnumType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class EnumConstraintType extends AbstractType +{ + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setRequired('enum_class'); + $resolver->setAllowedTypes('enum_class', 'string'); + + $resolver->setRequired('choice_label'); + $resolver->setAllowedTypes('choice_label', ['string', 'callable']); + + $resolver->setDefaults([ + 'compound' => true, + 'data_class' => ChoiceConstraint::class, + ]); + + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $choices = [ + '' => '', + 'filter.choice_constraint.operator.ANY' => 'ANY', + 'filter.choice_constraint.operator.NONE' => 'NONE', + ]; + + $builder->add('operator', ChoiceType::class, [ + 'choices' => $choices, + 'required' => false, + ]); + + $builder->add('value', EnumType::class, [ + 'class' => $options['enum_class'], + 'choice_label' => $options['choice_label'], + 'required' => false, + 'multiple' => true, + 'attr' => [ + 'data-controller' => 'elements--select-multiple', + ] + ]); + } + +} diff --git a/src/Form/Filters/Constraints/InstanceOfConstraintType.php b/src/Form/Filters/Constraints/InstanceOfConstraintType.php index 4a53e0f5..02de15e5 100644 --- a/src/Form/Filters/Constraints/InstanceOfConstraintType.php +++ b/src/Form/Filters/Constraints/InstanceOfConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\InstanceOfConstraint; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; class InstanceOfConstraintType extends AbstractType { - protected EntityManagerInterface $em; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $entityManager; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefault('data_class', InstanceOfConstraint::class); } - public function getParent() + public function getParent(): string { return ChoiceConstraintType::class; } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/NumberConstraintType.php b/src/Form/Filters/Constraints/NumberConstraintType.php index cbfd6897..ad792fa0 100644 --- a/src/Form/Filters/Constraints/NumberConstraintType.php +++ b/src/Form/Filters/Constraints/NumberConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\NumberConstraint; @@ -47,7 +49,7 @@ class NumberConstraintType extends AbstractType $resolver->setDefaults([ 'compound' => true, 'data_class' => NumberConstraint::class, - 'text_suffix' => '', // An suffix which is attached as text-append to the input group. This can for example be used for units + 'text_suffix' => '', // A suffix which is attached as text-append to the input group. This can for example be used for units 'min' => null, 'max' => null, @@ -91,10 +93,8 @@ class NumberConstraintType extends AbstractType ]); } - public function buildView(FormView $view, FormInterface $form, array $options) + public function buildView(FormView $view, FormInterface $form, array $options): void { - parent::buildView($view, $form, $options); - $view->vars['text_suffix'] = $options['text_suffix']; } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/ParameterConstraintType.php b/src/Form/Filters/Constraints/ParameterConstraintType.php index f9f2b311..3c3b396d 100644 --- a/src/Form/Filters/Constraints/ParameterConstraintType.php +++ b/src/Form/Filters/Constraints/ParameterConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\Part\ParameterConstraint; -use Svg\Tag\Text; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\SearchType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -49,10 +50,12 @@ class ParameterConstraintType extends AbstractType $builder->add('unit', SearchType::class, [ 'required' => false, + 'empty_data' => '', ]); $builder->add('symbol', SearchType::class, [ - 'required' => false + 'required' => false, + 'empty_data' => '', ]); $builder->add('value_text', TextConstraintType::class, [ @@ -69,7 +72,6 @@ class ParameterConstraintType extends AbstractType * Ensure that the data is never null, but use an empty ParameterConstraint instead */ $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { - $form = $event->getForm(); $data = $event->getData(); if ($data === null) { @@ -77,4 +79,4 @@ class ParameterConstraintType extends AbstractType } }); } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/ParameterValueConstraintType.php b/src/Form/Filters/Constraints/ParameterValueConstraintType.php index a99fd2a4..01fbca5c 100644 --- a/src/Form/Filters/Constraints/ParameterValueConstraintType.php +++ b/src/Form/Filters/Constraints/ParameterValueConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; class ParameterValueConstraintType extends NumberConstraintType @@ -48,4 +50,4 @@ class ParameterValueConstraintType extends NumberConstraintType { return NumberConstraintType::class; } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/StructuralEntityConstraintType.php b/src/Form/Filters/Constraints/StructuralEntityConstraintType.php index c9d6f25a..5191881b 100644 --- a/src/Form/Filters/Constraints/StructuralEntityConstraintType.php +++ b/src/Form/Filters/Constraints/StructuralEntityConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\EntityConstraint; @@ -36,7 +38,7 @@ class StructuralEntityConstraintType extends AbstractType $resolver->setDefaults([ 'compound' => true, 'data_class' => EntityConstraint::class, - 'text_suffix' => '', // An suffix which is attached as text-append to the input group. This can for example be used for units + 'text_suffix' => '', // A suffix which is attached as text-append to the input group. This can for example be used for units ]); $resolver->setRequired('entity_class'); @@ -65,9 +67,9 @@ class StructuralEntityConstraintType extends AbstractType ]); } - public function buildView(FormView $view, FormInterface $form, array $options) + public function buildView(FormView $view, FormInterface $form, array $options): void { parent::buildView($view, $form, $options); $view->vars['text_suffix'] = $options['text_suffix']; } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/TagsConstraintType.php b/src/Form/Filters/Constraints/TagsConstraintType.php index e6134a65..0a7661dd 100644 --- a/src/Form/Filters/Constraints/TagsConstraintType.php +++ b/src/Form/Filters/Constraints/TagsConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\Part\TagsConstraint; @@ -30,11 +32,8 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class TagsConstraintType extends AbstractType { - protected UrlGeneratorInterface $urlGenerator; - - public function __construct(UrlGeneratorInterface $urlGenerator) + public function __construct(protected UrlGeneratorInterface $urlGenerator) { - $this->urlGenerator = $urlGenerator; } public function configureOptions(OptionsResolver $resolver): void @@ -71,4 +70,4 @@ class TagsConstraintType extends AbstractType 'required' => false, ]); } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/TextConstraintType.php b/src/Form/Filters/Constraints/TextConstraintType.php index b9415977..c1007cf9 100644 --- a/src/Form/Filters/Constraints/TextConstraintType.php +++ b/src/Form/Filters/Constraints/TextConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\TextConstraint; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\Extension\Core\Type\SearchType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; @@ -70,10 +71,10 @@ class TextConstraintType extends AbstractType ]); } - public function buildView(FormView $view, FormInterface $form, array $options) + public function buildView(FormView $view, FormInterface $form, array $options): void { parent::buildView($view, $form, $options); $view->vars['text_suffix'] = $options['text_suffix']; } -} \ No newline at end of file +} diff --git a/src/Form/Filters/Constraints/UserEntityConstraintType.php b/src/Form/Filters/Constraints/UserEntityConstraintType.php index 9b28e2ce..8c82e0d8 100644 --- a/src/Form/Filters/Constraints/UserEntityConstraintType.php +++ b/src/Form/Filters/Constraints/UserEntityConstraintType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters\Constraints; use App\DataTables\Filters\Constraints\EntityConstraint; -use App\Entity\UserSystem\User; -use App\Form\Type\StructuralEntityType; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use App\Form\Type\UserSelectType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; -use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; class UserEntityConstraintType extends AbstractType @@ -39,7 +38,7 @@ class UserEntityConstraintType extends AbstractType $resolver->setDefaults([ 'compound' => true, 'data_class' => EntityConstraint::class, - 'text_suffix' => '', // An suffix which is attached as text-append to the input group. This can for example be used for units + 'text_suffix' => '', //A suffix which is attached as text-append to the input group. This can for example be used for units ]); } @@ -51,8 +50,7 @@ class UserEntityConstraintType extends AbstractType 'filter.entity_constraint.operator.NEQ' => '!=', ]; - $builder->add('value', EntityType::class, [ - 'class' => User::class, + $builder->add('value', UserSelectType::class, [ 'required' => false, ]); @@ -64,9 +62,9 @@ class UserEntityConstraintType extends AbstractType ]); } - public function buildView(FormView $view, FormInterface $form, array $options) + public function buildView(FormView $view, FormInterface $form, array $options): void { parent::buildView($view, $form, $options); $view->vars['text_suffix'] = $options['text_suffix']; } -} \ No newline at end of file +} diff --git a/src/Form/Filters/LogFilterType.php b/src/Form/Filters/LogFilterType.php index f7b460b6..42b367b7 100644 --- a/src/Form/Filters/LogFilterType.php +++ b/src/Form/Filters/LogFilterType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters; use App\DataTables\Filters\LogFilter; -use App\Entity\Attachments\Attachment; -use App\Entity\Attachments\AttachmentType; +use App\Entity\LogSystem\LogLevel; +use App\Entity\LogSystem\LogTargetType; use App\Entity\LogSystem\PartStockChangedLogEntry; -use App\Entity\ProjectSystem\Project; -use App\Entity\ProjectSystem\ProjectBOMEntry; -use App\Entity\LabelSystem\LabelProfile; -use App\Entity\LogSystem\AbstractLogEntry; use App\Entity\LogSystem\CollectionElementDeleted; use App\Entity\LogSystem\DatabaseUpdatedLogEntry; use App\Entity\LogSystem\ElementCreatedLogEntry; @@ -38,25 +36,10 @@ use App\Entity\LogSystem\SecurityEventLogEntry; use App\Entity\LogSystem\UserLoginLogEntry; use App\Entity\LogSystem\UserLogoutLogEntry; use App\Entity\LogSystem\UserNotAllowedLogEntry; -use App\Entity\Parameters\AbstractParameter; -use App\Entity\Parts\Category; -use App\Entity\Parts\Footprint; -use App\Entity\Parts\Manufacturer; -use App\Entity\Parts\MeasurementUnit; -use App\Entity\Parts\Part; -use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; -use App\Entity\Parts\Supplier; -use App\Entity\PriceInformations\Currency; -use App\Entity\PriceInformations\Orderdetail; -use App\Entity\PriceInformations\Pricedetail; -use App\Entity\UserSystem\Group; -use App\Entity\UserSystem\User; -use App\Form\Filters\Constraints\ChoiceConstraintType; use App\Form\Filters\Constraints\DateTimeConstraintType; +use App\Form\Filters\Constraints\EnumConstraintType; use App\Form\Filters\Constraints\InstanceOfConstraintType; use App\Form\Filters\Constraints\NumberConstraintType; -use App\Form\Filters\Constraints\StructuralEntityConstraintType; use App\Form\Filters\Constraints\UserEntityConstraintType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ResetType; @@ -66,17 +49,6 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class LogFilterType extends AbstractType { - protected const LEVEL_CHOICES = [ - 'log.level.debug' => AbstractLogEntry::LEVEL_DEBUG, - 'log.level.info' => AbstractLogEntry::LEVEL_INFO, - 'log.level.notice' => AbstractLogEntry::LEVEL_NOTICE, - 'log.level.warning' => AbstractLogEntry::LEVEL_WARNING, - 'log.level.error' => AbstractLogEntry::LEVEL_ERROR, - 'log.level.critical' => AbstractLogEntry::LEVEL_CRITICAL, - 'log.level.alert' => AbstractLogEntry::LEVEL_ALERT, - 'log.level.emergency' => AbstractLogEntry::LEVEL_EMERGENCY, - ]; - protected const TARGET_TYPE_CHOICES = [ 'log.type.collection_element_deleted' => CollectionElementDeleted::class, 'log.type.database_updated' => DatabaseUpdatedLogEntry::class, @@ -102,7 +74,7 @@ class LogFilterType extends AbstractType ]); } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->add('dbId', NumberConstraintType::class, [ 'label' => 'part.filter.dbId', @@ -116,9 +88,10 @@ class LogFilterType extends AbstractType - $builder->add('level', ChoiceConstraintType::class, [ + $builder->add('level', EnumConstraintType::class, [ 'label' => 'log.level', - 'choices' => self::LEVEL_CHOICES, + 'enum_class' => LogLevel::class, + 'choice_label' => fn(LogLevel $level): string => 'log.level.' . $level->toPSR3LevelString(), ]); $builder->add('eventType', InstanceOfConstraintType::class, [ @@ -130,29 +103,32 @@ class LogFilterType extends AbstractType 'label' => 'log.user', ]); - $builder->add('targetType', ChoiceConstraintType::class, [ + $builder->add('targetType', EnumConstraintType::class, [ 'label' => 'log.target_type', - 'choices' => [ - 'user.label' => AbstractLogEntry::targetTypeClassToID(User::class), - 'attachment.label' => AbstractLogEntry::targetTypeClassToID(Attachment::class), - 'attachment_type.label' => AbstractLogEntry::targetTypeClassToID(AttachmentType::class), - 'category.label' => AbstractLogEntry::targetTypeClassToID(Category::class), - 'project.label' => AbstractLogEntry::targetTypeClassToID(Project::class), - 'project_bom_entry.label' => AbstractLogEntry::targetTypeClassToID(ProjectBOMEntry::class), - 'footprint.label' => AbstractLogEntry::targetTypeClassToID(Footprint::class), - 'group.label' => AbstractLogEntry::targetTypeClassToID(Group::class), - 'manufacturer.label' => AbstractLogEntry::targetTypeClassToID(Manufacturer::class), - 'part.label' => AbstractLogEntry::targetTypeClassToID(Part::class), - 'storelocation.label' => AbstractLogEntry::targetTypeClassToID(Storelocation::class), - 'supplier.label' => AbstractLogEntry::targetTypeClassToID(Supplier::class), - 'part_lot.label' => AbstractLogEntry::targetTypeClassToID(PartLot::class), - 'currency.label' => AbstractLogEntry::targetTypeClassToID(Currency::class), - 'orderdetail.label' => AbstractLogEntry::targetTypeClassToID(Orderdetail::class), - 'pricedetail.label' => AbstractLogEntry::targetTypeClassToID(Pricedetail::class), - 'measurement_unit.label' => AbstractLogEntry::targetTypeClassToID(MeasurementUnit::class), - 'parameter.label' => AbstractLogEntry::targetTypeClassToID(AbstractParameter::class), - 'label_profile.label' => AbstractLogEntry::targetTypeClassToID(LabelProfile::class), - ] + 'enum_class' => LogTargetType::class, + 'choice_label' => fn(LogTargetType $type): string => match ($type) { + LogTargetType::NONE => 'log.target_type.none', + LogTargetType::USER => 'user.label', + LogTargetType::ATTACHMENT => 'attachment.label', + LogTargetType::ATTACHMENT_TYPE => 'attachment_type.label', + LogTargetType::CATEGORY => 'category.label', + LogTargetType::PROJECT => 'project.label', + LogTargetType::BOM_ENTRY => 'project_bom_entry.label', + LogTargetType::FOOTPRINT => 'footprint.label', + LogTargetType::GROUP => 'group.label', + LogTargetType::MANUFACTURER => 'manufacturer.label', + LogTargetType::PART => 'part.label', + LogTargetType::STORELOCATION => 'storelocation.label', + LogTargetType::SUPPLIER => 'supplier.label', + LogTargetType::PART_LOT => 'part_lot.label', + LogTargetType::CURRENCY => 'currency.label', + LogTargetType::ORDERDETAIL => 'orderdetail.label', + LogTargetType::PRICEDETAIL => 'pricedetail.label', + LogTargetType::MEASUREMENT_UNIT => 'measurement_unit.label', + LogTargetType::PARAMETER => 'parameter.label', + LogTargetType::LABEL_PROFILE => 'label_profile.label', + LogTargetType::PART_ASSOCIATION => 'part_association.label', + }, ]); $builder->add('targetId', NumberConstraintType::class, [ @@ -169,4 +145,4 @@ class LogFilterType extends AbstractType 'label' => 'filter.discard', ]); } -} \ No newline at end of file +} diff --git a/src/Form/Filters/PartFilterType.php b/src/Form/Filters/PartFilterType.php index 347948a2..dfe449d1 100644 --- a/src/Form/Filters/PartFilterType.php +++ b/src/Form/Filters/PartFilterType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Filters; use App\DataTables\Filters\Constraints\Part\ParameterConstraint; @@ -27,7 +29,9 @@ use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; +use App\Entity\Parts\Supplier; +use App\Entity\ProjectSystem\Project; use App\Form\Filters\Constraints\BooleanConstraintType; use App\Form\Filters\Constraints\ChoiceConstraintType; use App\Form\Filters\Constraints\DateTimeConstraintType; @@ -36,19 +40,22 @@ use App\Form\Filters\Constraints\ParameterConstraintType; use App\Form\Filters\Constraints\StructuralEntityConstraintType; use App\Form\Filters\Constraints\TagsConstraintType; use App\Form\Filters\Constraints\TextConstraintType; -use Svg\Tag\Text; +use App\Form\Filters\Constraints\UserEntityConstraintType; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; -use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\ResetType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\SubmitButton; use Symfony\Component\OptionsResolver\OptionsResolver; class PartFilterType extends AbstractType { + public function __construct(private readonly Security $security) + { + } + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ @@ -58,7 +65,7 @@ class PartFilterType extends AbstractType ]); } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { /* * Common tab @@ -169,13 +176,13 @@ class PartFilterType extends AbstractType $builder->add('supplier', StructuralEntityConstraintType::class, [ 'label' => 'supplier.label', - 'entity_class' => Manufacturer::class + 'entity_class' => Supplier::class ]); $builder->add('orderdetailsCount', NumberConstraintType::class, [ 'label' => 'part.filter.orderdetails_count', - 'step' => 1, - 'min' => 0, + 'step' => 1, + 'min' => 0, ]); $builder->add('obsolete', BooleanConstraintType::class, [ @@ -187,7 +194,7 @@ class PartFilterType extends AbstractType */ $builder->add('storelocation', StructuralEntityConstraintType::class, [ 'label' => 'storelocation.label', - 'entity_class' => Storelocation::class + 'entity_class' => StorageLocation::class ]); $builder->add('minAmount', NumberConstraintType::class, [ @@ -206,6 +213,10 @@ class PartFilterType extends AbstractType 'min' => 0, ]); + $builder->add('lessThanDesired', BooleanConstraintType::class, [ + 'label' => 'part.filter.lessThanDesired' + ]); + $builder->add('lotNeedsRefill', BooleanConstraintType::class, [ 'label' => 'part.filter.lotNeedsRefill' ]); @@ -223,6 +234,10 @@ class PartFilterType extends AbstractType 'label' => 'part.filter.lotDescription', ]); + $builder->add('lotOwner', UserEntityConstraintType::class, [ + 'label' => 'part.filter.lotOwner', + ]); + /** * Attachments count */ @@ -259,6 +274,31 @@ class PartFilterType extends AbstractType 'min' => 0, ]); + /************************************************************************** + * Project tab + **************************************************************************/ + if ($this->security->isGranted('read', Project::class)) { + $builder + ->add('project', StructuralEntityConstraintType::class, [ + 'label' => 'project.label', + 'entity_class' => Project::class + ]) + ->add('bomQuantity', NumberConstraintType::class, [ + 'label' => 'project.bom.quantity', + 'min' => 0, + 'step' => "any", + ]) + ->add('bomName', TextConstraintType::class, [ + 'label' => 'project.bom.name', + ]) + ->add('bomComment', TextConstraintType::class, [ + 'label' => 'project.bom.comment', + ]) + ; + + } + + $builder->add('submit', SubmitType::class, [ 'label' => 'filter.submit', ]); @@ -268,4 +308,4 @@ class PartFilterType extends AbstractType ]); } -} \ No newline at end of file +} diff --git a/src/Form/InfoProviderSystem/PartSearchType.php b/src/Form/InfoProviderSystem/PartSearchType.php new file mode 100644 index 00000000..9d582ca4 --- /dev/null +++ b/src/Form/InfoProviderSystem/PartSearchType.php @@ -0,0 +1,47 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\InfoProviderSystem; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\SearchType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\FormBuilderInterface; + +class PartSearchType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder->add('keyword', SearchType::class, [ + 'label' => 'info_providers.search.keyword', + ]); + $builder->add('providers', ProviderSelectType::class, [ + 'label' => 'info_providers.search.providers', + 'help' => 'info_providers.search.providers.help', + ]); + + $builder->add('submit', SubmitType::class, [ + 'label' => 'info_providers.search.submit' + ]); + } +} \ No newline at end of file diff --git a/src/Form/InfoProviderSystem/ProviderSelectType.php b/src/Form/InfoProviderSystem/ProviderSelectType.php new file mode 100644 index 00000000..a9373390 --- /dev/null +++ b/src/Form/InfoProviderSystem/ProviderSelectType.php @@ -0,0 +1,56 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\InfoProviderSystem; + +use App\Services\InfoProviderSystem\ProviderRegistry; +use App\Services\InfoProviderSystem\Providers\InfoProviderInterface; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\ChoiceList\ChoiceList; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class ProviderSelectType extends AbstractType +{ + public function __construct(private readonly ProviderRegistry $providerRegistry) + { + + } + + public function getParent(): string + { + return ChoiceType::class; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'choices' => $this->providerRegistry->getActiveProviders(), + 'choice_label' => ChoiceList::label($this, static fn (?InfoProviderInterface $choice) => $choice?->getProviderInfo()['name']), + 'choice_value' => ChoiceList::value($this, static fn(?InfoProviderInterface $choice) => $choice?->getProviderKey()), + + 'multiple' => true, + ]); + } + +} \ No newline at end of file diff --git a/src/Form/LabelOptionsType.php b/src/Form/LabelOptionsType.php index 5af69ce6..ad458374 100644 --- a/src/Form/LabelOptionsType.php +++ b/src/Form/LabelOptionsType.php @@ -41,23 +41,23 @@ declare(strict_types=1); namespace App\Form; +use App\Entity\LabelSystem\BarcodeType; +use App\Entity\LabelSystem\LabelProcessMode; +use App\Entity\LabelSystem\LabelSupportedElement; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\LabelSystem\LabelOptions; use App\Form\Type\RichTextEditorType; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\EnumType; use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; class LabelOptionsType extends AbstractType { - private Security $security; - - public function __construct(Security $security) + public function __construct(private readonly Security $security) { - $this->security = $security; } public function buildForm(FormBuilderInterface $builder, array $options): void @@ -81,31 +81,33 @@ class LabelOptionsType extends AbstractType ], ]); - $builder->add('supported_element', ChoiceType::class, [ + $builder->add('supported_element', EnumType::class, [ 'label' => 'label_options.supported_elements.label', - 'choices' => [ - 'part.label' => 'part', - 'part_lot.label' => 'part_lot', - 'storelocation.label' => 'storelocation', - ], + 'class' => LabelSupportedElement::class, + 'choice_label' => fn(LabelSupportedElement $choice) => match($choice) { + LabelSupportedElement::PART => 'part.label', + LabelSupportedElement::PART_LOT => 'part_lot.label', + LabelSupportedElement::STORELOCATION => 'storelocation.label', + }, ]); - $builder->add('barcode_type', ChoiceType::class, [ + $builder->add('barcode_type', EnumType::class, [ 'label' => 'label_options.barcode_type.label', 'empty_data' => 'none', - 'choices' => [ - 'label_options.barcode_type.none' => 'none', - 'label_options.barcode_type.qr' => 'qr', - 'label_options.barcode_type.code128' => 'code128', - 'label_options.barcode_type.code39' => 'code39', - 'label_options.barcode_type.code93' => 'code93', - 'label_options.barcode_type.datamatrix' => 'datamatrix', - ], - 'group_by' => static function ($choice, $key, $value) { - if (in_array($choice, ['qr', 'datamatrix'], true)) { + 'class' => BarcodeType::class, + 'choice_label' => fn(BarcodeType $choice) => match($choice) { + BarcodeType::NONE => 'label_options.barcode_type.none', + BarcodeType::QR => 'label_options.barcode_type.qr', + BarcodeType::CODE128 => 'label_options.barcode_type.code128', + BarcodeType::CODE39 => 'label_options.barcode_type.code39', + BarcodeType::CODE93 => 'label_options.barcode_type.code93', + BarcodeType::DATAMATRIX => 'label_options.barcode_type.datamatrix', + }, + 'group_by' => static function (BarcodeType $choice, $key, $value): ?string { + if ($choice->is2D()) { return 'label_options.barcode_type.2D'; } - if (in_array($choice, ['code39', 'code93', 'code128'], true)) { + if ($choice->is1D()) { return 'label_options.barcode_type.1D'; } @@ -132,12 +134,13 @@ class LabelOptionsType extends AbstractType 'required' => false, ]); - $builder->add('lines_mode', ChoiceType::class, [ + $builder->add('process_mode', EnumType::class, [ 'label' => 'label_options.lines_mode.label', - 'choices' => [ - 'label_options.lines_mode.html' => 'html', - 'label.options.lines_mode.twig' => 'twig', - ], + 'class' => LabelProcessMode::class, + 'choice_label' => fn(LabelProcessMode $choice) => match($choice) { + LabelProcessMode::PLACEHOLDER => 'label_options.lines_mode.html', + LabelProcessMode::TWIG => 'label.options.lines_mode.twig', + }, 'help' => 'label_options.lines_mode.help', 'help_html' => true, 'expanded' => true, diff --git a/src/Form/LabelSystem/LabelDialogType.php b/src/Form/LabelSystem/LabelDialogType.php index 9bc3dfdf..f2710b19 100644 --- a/src/Form/LabelSystem/LabelDialogType.php +++ b/src/Form/LabelSystem/LabelDialogType.php @@ -41,6 +41,7 @@ declare(strict_types=1); namespace App\Form\LabelSystem; +use Symfony\Bundle\SecurityBundle\Security; use App\Form\LabelOptionsType; use App\Validator\Constraints\Misc\ValidRange; use Symfony\Component\Form\AbstractType; @@ -48,15 +49,11 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; class LabelDialogType extends AbstractType { - protected Security $security; - - public function __construct(Security $security) + public function __construct(protected Security $security) { - $this->security = $security; } public function buildForm(FormBuilderInterface $builder, array $options): void @@ -74,6 +71,22 @@ class LabelDialogType extends AbstractType 'label' => false, 'disabled' => !$this->security->isGranted('@labels.edit_options') || $options['disable_options'], ]); + + $builder->add('save_profile_name', TextType::class, [ + 'required' => false, + 'attr' =>[ + 'placeholder' => 'label_generator.save_profile_name', + ] + ]); + + $builder->add('save_profile', SubmitType::class, [ + 'label' => 'label_generator.save_profile', + 'disabled' => !$this->security->isGranted('@labels.create_profiles'), + 'attr' => [ + 'class' => 'btn btn-outline-success' + ] + ]); + $builder->add('update', SubmitType::class, [ 'label' => 'label_generator.update', ]); diff --git a/src/Form/LabelSystem/ScanDialogType.php b/src/Form/LabelSystem/ScanDialogType.php index 163ee9c2..13ff8e6f 100644 --- a/src/Form/LabelSystem/ScanDialogType.php +++ b/src/Form/LabelSystem/ScanDialogType.php @@ -41,7 +41,10 @@ declare(strict_types=1); namespace App\Form\LabelSystem; +use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\EnumType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -53,12 +56,34 @@ class ScanDialogType extends AbstractType { $builder->add('input', TextType::class, [ 'label' => 'scan_dialog.input', + //Do not trim the input, otherwise this damages Format06 barcodes which end with non-printable characters + 'trim' => false, 'attr' => [ 'autofocus' => true, 'id' => 'scan_dialog_input', ], ]); + $builder->add('mode', EnumType::class, [ + 'label' => 'scan_dialog.mode', + 'expanded' => true, + 'class' => BarcodeSourceType::class, + 'required' => false, + 'placeholder' => 'scan_dialog.mode.auto', + 'choice_label' => fn (?BarcodeSourceType $enum) => match($enum) { + null => 'scan_dialog.mode.auto', + BarcodeSourceType::INTERNAL => 'scan_dialog.mode.internal', + BarcodeSourceType::IPN => 'scan_dialog.mode.ipn', + BarcodeSourceType::USER_DEFINED => 'scan_dialog.mode.user', + BarcodeSourceType::EIGP114 => 'scan_dialog.mode.eigp' + }, + ]); + + $builder->add('info_mode', CheckboxType::class, [ + 'label' => 'scan_dialog.info_mode', + 'required' => false, + ]); + $builder->add('submit', SubmitType::class, [ 'label' => 'scan_dialog.submit', ]); diff --git a/src/Form/ParameterType.php b/src/Form/ParameterType.php index 09293b97..4c2174ae 100644 --- a/src/Form/ParameterType.php +++ b/src/Form/ParameterType.php @@ -50,9 +50,10 @@ use App\Entity\Parameters\FootprintParameter; use App\Entity\Parameters\GroupParameter; use App\Entity\Parameters\ManufacturerParameter; use App\Entity\Parameters\PartParameter; -use App\Entity\Parameters\StorelocationParameter; +use App\Entity\Parameters\StorageLocationParameter; use App\Entity\Parameters\SupplierParameter; use App\Entity\Parts\MeasurementUnit; +use App\Form\Type\ExponentialNumberType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -93,7 +94,7 @@ class ParameterType extends AbstractType ], ]); - $builder->add('value_max', NumberType::class, [ + $builder->add('value_max', ExponentialNumberType::class, [ 'label' => false, 'required' => false, 'html5' => true, @@ -101,10 +102,10 @@ class ParameterType extends AbstractType 'step' => 'any', 'placeholder' => 'parameters.max.placeholder', 'class' => 'form-control-sm', - 'style' => 'max-width: 12ch;', + 'style' => 'max-width: 25ch;', ], ]); - $builder->add('value_min', NumberType::class, [ + $builder->add('value_min', ExponentialNumberType::class, [ 'label' => false, 'required' => false, 'html5' => true, @@ -112,10 +113,10 @@ class ParameterType extends AbstractType 'step' => 'any', 'placeholder' => 'parameters.min.placeholder', 'class' => 'form-control-sm', - 'style' => 'max-width: 12ch;', + 'style' => 'max-width: 25ch;', ], ]); - $builder->add('value_typical', NumberType::class, [ + $builder->add('value_typical', ExponentialNumberType::class, [ 'label' => false, 'required' => false, 'html5' => true, @@ -123,7 +124,7 @@ class ParameterType extends AbstractType 'step' => 'any', 'placeholder' => 'parameters.typical.placeholder', 'class' => 'form-control-sm', - 'style' => 'max-width: 12ch;', + 'style' => 'max-width: 25ch;', ], ]); $builder->add('unit', TextType::class, [ @@ -148,7 +149,7 @@ class ParameterType extends AbstractType ]); } - public function finishView(FormView $view, FormInterface $form, array $options) + public function finishView(FormView $view, FormInterface $form, array $options): void { //By default use part parameters for autocomplete $view->vars['type'] = 'part'; @@ -163,7 +164,7 @@ class ParameterType extends AbstractType GroupParameter::class => 'group', ManufacturerParameter::class => 'manufacturer', MeasurementUnit::class => 'measurement_unit', - StorelocationParameter::class => 'storelocation', + StorageLocationParameter::class => 'storelocation', SupplierParameter::class => 'supplier', ]; diff --git a/src/Form/Part/EDA/EDACategoryInfoType.php b/src/Form/Part/EDA/EDACategoryInfoType.php new file mode 100644 index 00000000..f45bd697 --- /dev/null +++ b/src/Form/Part/EDA/EDACategoryInfoType.php @@ -0,0 +1,87 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\Part\EDA; + +use App\Entity\EDA\EDACategoryInfo; +use App\Form\Type\TriStateCheckboxType; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +use function Symfony\Component\Translation\t; + +class EDACategoryInfoType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('reference_prefix', TextType::class, [ + 'label' => 'eda_info.reference_prefix', + 'attr' => [ + 'placeholder' => t('eda_info.reference_prefix.placeholder'), + ] + ] + ) + ->add('visibility', TriStateCheckboxType::class, [ + 'help' => 'eda_info.visibility.help', + 'label' => 'eda_info.visibility', + ]) + ->add('exclude_from_bom', TriStateCheckboxType::class, [ + 'label' => 'eda_info.exclude_from_bom', + 'label_attr' => [ + 'class' => 'checkbox-inline' + ] + ]) + ->add('exclude_from_board', TriStateCheckboxType::class, [ + 'label' => 'eda_info.exclude_from_board', + 'label_attr' => [ + 'class' => 'checkbox-inline' + ] + ]) + ->add('exclude_from_sim', TriStateCheckboxType::class, [ + 'label' => 'eda_info.exclude_from_sim', + 'label_attr' => [ + 'class' => 'checkbox-inline' + ] + ]) + ->add('kicad_symbol', KicadFieldAutocompleteType::class, [ + 'label' => 'eda_info.kicad_symbol', + 'type' => KicadFieldAutocompleteType::TYPE_SYMBOL, + 'attr' => [ + 'placeholder' => t('eda_info.kicad_symbol.placeholder'), + ] + ]) + ; + + + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => EDACategoryInfo::class, + ]); + } +} \ No newline at end of file diff --git a/src/Form/Part/EDA/EDAFootprintInfoType.php b/src/Form/Part/EDA/EDAFootprintInfoType.php new file mode 100644 index 00000000..bdfa346c --- /dev/null +++ b/src/Form/Part/EDA/EDAFootprintInfoType.php @@ -0,0 +1,55 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\Part\EDA; + +use App\Entity\EDA\EDAFootprintInfo; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +use function Symfony\Component\Translation\t; + +class EDAFootprintInfoType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('kicad_footprint', KicadFieldAutocompleteType::class, [ + 'type' => KicadFieldAutocompleteType::TYPE_FOOTPRINT, + 'label' => 'eda_info.kicad_footprint', + 'attr' => [ + 'placeholder' => t('eda_info.kicad_footprint.placeholder'), + ] + ]); + + + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => EDAFootprintInfo::class, + ]); + } +} \ No newline at end of file diff --git a/src/Form/Part/EDA/EDAPartInfoType.php b/src/Form/Part/EDA/EDAPartInfoType.php new file mode 100644 index 00000000..e8cac681 --- /dev/null +++ b/src/Form/Part/EDA/EDAPartInfoType.php @@ -0,0 +1,97 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\Part\EDA; + +use App\Entity\EDA\EDAPartInfo; +use App\Form\Type\TriStateCheckboxType; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +use function Symfony\Component\Translation\t; + +class EDAPartInfoType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('reference_prefix', TextType::class, [ + 'label' => 'eda_info.reference_prefix', + 'attr' => [ + 'placeholder' => t('eda_info.reference_prefix.placeholder'), + ] + ] + ) + ->add('value', TextType::class, [ + 'label' => 'eda_info.value', + 'attr' => [ + 'placeholder' => t('eda_info.value.placeholder'), + ] + ]) + ->add('visibility', TriStateCheckboxType::class, [ + 'help' => 'eda_info.visibility.help', + 'label' => 'eda_info.visibility', + ]) + ->add('exclude_from_bom', TriStateCheckboxType::class, [ + 'label' => 'eda_info.exclude_from_bom', + 'label_attr' => [ + 'class' => 'checkbox-inline' + ] + ]) + ->add('exclude_from_board', TriStateCheckboxType::class, [ + 'label' => 'eda_info.exclude_from_board', + 'label_attr' => [ + 'class' => 'checkbox-inline' + ] + ]) + ->add('exclude_from_sim', TriStateCheckboxType::class, [ + 'label' => 'eda_info.exclude_from_sim', + 'label_attr' => [ + 'class' => 'checkbox-inline' + ] + ]) + ->add('kicad_symbol', KicadFieldAutocompleteType::class, [ + 'label' => 'eda_info.kicad_symbol', + 'type' => KicadFieldAutocompleteType::TYPE_SYMBOL, + 'attr' => [ + 'placeholder' => t('eda_info.kicad_symbol.placeholder'), + ] + ]) + ->add('kicad_footprint', KicadFieldAutocompleteType::class, [ + 'label' => 'eda_info.kicad_footprint', + 'type' => KicadFieldAutocompleteType::TYPE_FOOTPRINT, + 'attr' => [ + 'placeholder' => t('eda_info.kicad_footprint.placeholder'), + ] + ]); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => EDAPartInfo::class, + ]); + } +} \ No newline at end of file diff --git a/src/Form/Part/EDA/KicadFieldAutocompleteType.php b/src/Form/Part/EDA/KicadFieldAutocompleteType.php new file mode 100644 index 00000000..50de81d0 --- /dev/null +++ b/src/Form/Part/EDA/KicadFieldAutocompleteType.php @@ -0,0 +1,61 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\Part\EDA; + +use App\Form\Type\StaticFileAutocompleteType; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * This is a specialized version of the StaticFileAutocompleteType, which loads the different types of Kicad lists. + */ +class KicadFieldAutocompleteType extends AbstractType +{ + public const TYPE_FOOTPRINT = 'footprint'; + public const TYPE_SYMBOL = 'symbol'; + + //Do not use a leading slash here! otherwise it will not work under prefixed reverse proxies + public const FOOTPRINT_PATH = 'kicad/footprints.txt'; + public const SYMBOL_PATH = 'kicad/symbols.txt'; + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setRequired('type'); + $resolver->setAllowedValues('type', [self::TYPE_SYMBOL, self::TYPE_FOOTPRINT]); + + $resolver->setDefaults([ + 'file' => fn(Options $options) => match ($options['type']) { + self::TYPE_FOOTPRINT => self::FOOTPRINT_PATH, + self::TYPE_SYMBOL => self::SYMBOL_PATH, + default => throw new \InvalidArgumentException('Invalid type'), + } + ]); + } + + public function getParent(): string + { + return StaticFileAutocompleteType::class; + } +} \ No newline at end of file diff --git a/src/Form/Part/OrderdetailType.php b/src/Form/Part/OrderdetailType.php index 8489bc73..53240821 100644 --- a/src/Form/Part/OrderdetailType.php +++ b/src/Form/Part/OrderdetailType.php @@ -22,12 +22,12 @@ declare(strict_types=1); namespace App\Form\Part; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Orderdetail; use App\Entity\PriceInformations\Pricedetail; use App\Form\Type\StructuralEntityType; -use App\Form\WorkaroundCollectionType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; @@ -37,15 +37,11 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; class OrderdetailType extends AbstractType { - protected Security $security; - - public function __construct(Security $security) + public function __construct(protected Security $security) { - $this->security = $security; } public function buildForm(FormBuilderInterface $builder, array $options): void @@ -83,7 +79,7 @@ class OrderdetailType extends AbstractType $orderdetail = $event->getData(); $dummy_pricedetail = new Pricedetail(); - if (null !== $orderdetail && null !== $orderdetail->getSupplier()) { + if ($orderdetail instanceof Orderdetail && $orderdetail->getSupplier() instanceof Supplier) { $dummy_pricedetail->setCurrency($orderdetail->getSupplier()->getDefaultCurrency()); } diff --git a/src/Form/Part/PartAssociationType.php b/src/Form/Part/PartAssociationType.php new file mode 100644 index 00000000..bf9ec4f9 --- /dev/null +++ b/src/Form/Part/PartAssociationType.php @@ -0,0 +1,73 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\Part; + +use App\Entity\Parts\AssociationType; +use App\Entity\Parts\PartAssociation; +use App\Form\Type\PartSelectType; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\EnumType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class PartAssociationType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('other', PartSelectType::class, [ + 'label' => 'part_association.edit.other_part', + ]) + ->add('type', EnumType::class, [ + 'class' => AssociationType::class, + 'label' => 'part_association.edit.type', + 'choice_label' => fn(AssociationType $type) => $type->getTranslationKey(), + 'help' => 'part_association.edit.type.help', + 'attr' => [ + 'data-pages--association-edit-type-select-target' => 'select' + ] + ]) + ->add('other_type', TextType::class, [ + 'required' => false, + 'label' => 'part_association.edit.other_type', + 'row_attr' => [ + 'data-pages--association-edit-type-select-target' => 'display' + ] + ]) + ->add('comment', TextType::class, [ + 'required' => false, + 'label' => 'part_association.edit.comment' + ]) + ; + + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => PartAssociation::class, + ]); + } +} \ No newline at end of file diff --git a/src/Form/Part/PartBaseType.php b/src/Form/Part/PartBaseType.php index 3951a2ac..b1d2ebea 100644 --- a/src/Form/Part/PartBaseType.php +++ b/src/Form/Part/PartBaseType.php @@ -27,20 +27,24 @@ use App\Entity\Parameters\PartParameter; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\ManufacturingStatus; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; use App\Entity\PriceInformations\Orderdetail; use App\Form\AttachmentFormType; use App\Form\ParameterType; +use App\Form\Part\EDA\EDAPartInfoType; use App\Form\Type\MasterPictureAttachmentType; use App\Form\Type\RichTextEditorType; use App\Form\Type\SIUnitType; use App\Form\Type\StructuralEntityType; -use App\Form\WorkaroundCollectionType; +use App\Services\InfoProviderSystem\DTOs\PartDetailDTO; +use App\Services\LogSystem\EventCommentNeededHelper; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; +use Symfony\Component\Form\Extension\Core\Type\EnumType; use Symfony\Component\Form\Extension\Core\Type\ResetType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -48,32 +52,21 @@ use Symfony\Component\Form\Extension\Core\Type\UrlType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; class PartBaseType extends AbstractType { - protected Security $security; - protected UrlGeneratorInterface $urlGenerator; - - public function __construct(Security $security, UrlGeneratorInterface $urlGenerator) + public function __construct(protected Security $security, protected UrlGeneratorInterface $urlGenerator, protected EventCommentNeededHelper $event_comment_needed_helper) { - $this->security = $security; - $this->urlGenerator = $urlGenerator; } public function buildForm(FormBuilderInterface $builder, array $options): void { /** @var Part $part */ $part = $builder->getData(); + $new_part = null === $part->getID(); - $status_choices = [ - 'm_status.unknown' => '', - 'm_status.announced' => 'announced', - 'm_status.active' => 'active', - 'm_status.nrfnd' => 'nrfnd', - 'm_status.eol' => 'eol', - 'm_status.discontinued' => 'discontinued', - ]; + /** @var PartDetailDTO|null $dto */ + $dto = $options['info_provider_dto']; //Common section $builder @@ -105,13 +98,17 @@ class PartBaseType extends AbstractType ->add('category', StructuralEntityType::class, [ 'class' => Category::class, 'allow_add' => $this->security->isGranted('@categories.create'), + 'dto_value' => $dto?->category, 'label' => 'part.edit.category', 'disable_not_selectable' => true, + //Do not require category for new parts, so that the user must select the category by hand and cannot forget it (the requirement is handled by the constraint in the entity) + 'required' => !$new_part, ]) ->add('footprint', StructuralEntityType::class, [ 'class' => Footprint::class, 'required' => false, 'label' => 'part.edit.footprint', + 'dto_value' => $dto?->footprint, 'allow_add' => $this->security->isGranted('@footprints.create'), 'disable_not_selectable' => true, ]) @@ -132,6 +129,7 @@ class PartBaseType extends AbstractType 'required' => false, 'label' => 'part.edit.manufacturer.label', 'allow_add' => $this->security->isGranted('@manufacturers.create'), + 'dto_value' => $dto?->manufacturer, 'disable_not_selectable' => true, ]) ->add('manufacturer_product_url', UrlType::class, [ @@ -144,9 +142,10 @@ class PartBaseType extends AbstractType 'empty_data' => '', 'label' => 'part.edit.mpn', ]) - ->add('manufacturing_status', ChoiceType::class, [ + ->add('manufacturing_status', EnumType::class, [ 'label' => 'part.edit.manufacturing_status', - 'choices' => $status_choices, + 'class' => ManufacturingStatus::class, + 'choice_label' => fn (ManufacturingStatus $status) => $status->toTranslationKey(), 'required' => false, ]); @@ -247,10 +246,26 @@ class PartBaseType extends AbstractType ], ]); + //Part associations + $builder->add('associated_parts_as_owner', CollectionType::class, [ + 'entry_type' => PartAssociationType::class, + 'allow_add' => true, + 'allow_delete' => true, + 'reindex_enable' => true, + 'label' => false, + 'by_reference' => false, + ]); + + //EDA info + $builder->add('eda_info', EDAPartInfoType::class, [ + 'label' => false, + 'required' => false, + ]); + $builder->add('log_comment', TextType::class, [ 'label' => 'edit.log_comment', 'mapped' => false, - 'required' => false, + 'required' => $this->event_comment_needed_helper->isCommentNeeded($new_part ? 'part_create' : 'part_edit'), 'empty_data' => null, ]); @@ -277,10 +292,15 @@ class PartBaseType extends AbstractType ->add('reset', ResetType::class, ['label' => 'part.edit.reset']); } + + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'data_class' => Part::class, + 'info_provider_dto' => null, ]); + + $resolver->setAllowedTypes('info_provider_dto', [PartDetailDTO::class, 'null']); } } diff --git a/src/Form/Part/PartLotType.php b/src/Form/Part/PartLotType.php index 2e23c94a..7d545340 100644 --- a/src/Form/Part/PartLotType.php +++ b/src/Form/Part/PartLotType.php @@ -22,26 +22,24 @@ declare(strict_types=1); namespace App\Form\Part; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Form\Type\SIUnitType; use App\Form\Type\StructuralEntityType; +use App\Form\Type\UserSelectType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; class PartLotType extends AbstractType { - protected Security $security; - - public function __construct(Security $security) + public function __construct(protected Security $security) { - $this->security = $security; } public function buildForm(FormBuilderInterface $builder, array $options): void @@ -56,7 +54,7 @@ class PartLotType extends AbstractType ]); $builder->add('storage_location', StructuralEntityType::class, [ - 'class' => Storelocation::class, + 'class' => StorageLocation::class, 'label' => 'part_lot.edit.location', 'required' => false, 'disable_not_selectable' => true, @@ -82,7 +80,7 @@ class PartLotType extends AbstractType 'required' => false, ]); - $builder->add('expirationDate', DateType::class, [ + $builder->add('expiration_date', DateType::class, [ 'label' => 'part_lot.edit.expiration_date', 'attr' => [], 'widget' => 'single_text', @@ -98,6 +96,20 @@ class PartLotType extends AbstractType 'required' => false, 'empty_data' => '', ]); + + $builder->add('owner', UserSelectType::class, [ + 'label' => 'part_lot.owner', + 'required' => false, + 'help' => 'part_lot.owner.help', + ]); + + $builder->add('user_barcode', TextType::class, [ + 'label' => 'part_lot.edit.user_barcode', + 'help' => 'part_lot.edit.vendor_barcode.help', + 'required' => false, + //Do not remove whitespace chars on the beginning and end of the string + 'trim' => false, + ]); } public function configureOptions(OptionsResolver $resolver): void diff --git a/src/Form/Part/PricedetailType.php b/src/Form/Part/PricedetailType.php index c8df4c71..cabb112d 100644 --- a/src/Form/Part/PricedetailType.php +++ b/src/Form/Part/PricedetailType.php @@ -27,12 +27,18 @@ use App\Entity\PriceInformations\Pricedetail; use App\Form\Type\BigDecimalNumberType; use App\Form\Type\CurrencyEntityType; use App\Form\Type\SIUnitType; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class PricedetailType extends AbstractType { + + public function __construct(private readonly Security $security) + { + } + public function buildForm(FormBuilderInterface $builder, array $options): void { //No labels needed, we define translation in templates @@ -63,6 +69,7 @@ class PricedetailType extends AbstractType 'required' => false, 'label' => false, 'short' => true, + 'allow_add' => $this->security->isGranted('@currencies.create'), ]); } diff --git a/src/Form/PasswordTypeExtension.php b/src/Form/PasswordTypeExtension.php new file mode 100644 index 00000000..64711c53 --- /dev/null +++ b/src/Form/PasswordTypeExtension.php @@ -0,0 +1,56 @@ +. + */ +namespace App\Form; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Purpose of this class is to add the setting 'password_estimator' to the PasswordType. + */ +class PasswordTypeExtension extends AbstractTypeExtension +{ + + public static function getExtendedTypes(): iterable + { + return [PasswordType::class]; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'password_estimator' => false, + ]); + + $resolver->setAllowedTypes('password_estimator', 'bool'); + } + + public function finishView(FormView $view, FormInterface $form, array $options): void + { + $view->vars['password_estimator'] = $options['password_estimator']; + } + +} diff --git a/src/Form/Permissions/PermissionGroupType.php b/src/Form/Permissions/PermissionGroupType.php index c395337f..f3f7ffec 100644 --- a/src/Form/Permissions/PermissionGroupType.php +++ b/src/Form/Permissions/PermissionGroupType.php @@ -30,12 +30,10 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class PermissionGroupType extends AbstractType { - protected PermissionManager $resolver; protected array $perm_structure; - public function __construct(PermissionManager $resolver) + public function __construct(protected PermissionManager $resolver) { - $this->resolver = $resolver; $this->perm_structure = $resolver->getPermissionStructure(); } @@ -68,9 +66,7 @@ class PermissionGroupType extends AbstractType { parent::configureOptions($resolver); - $resolver->setDefault('group_name', static function (Options $options) { - return trim($options['name']); - }); + $resolver->setDefault('group_name', static fn(Options $options): string => trim((string) $options['name'])); $resolver->setDefault('inherit', false); diff --git a/src/Form/Permissions/PermissionType.php b/src/Form/Permissions/PermissionType.php index 804fed0a..ab5ee86b 100644 --- a/src/Form/Permissions/PermissionType.php +++ b/src/Form/Permissions/PermissionType.php @@ -33,12 +33,10 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class PermissionType extends AbstractType { - protected PermissionManager $resolver; protected array $perm_structure; - public function __construct(PermissionManager $resolver) + public function __construct(protected PermissionManager $resolver) { - $this->resolver = $resolver; $this->perm_structure = $resolver->getPermissionStructure(); } @@ -46,9 +44,7 @@ class PermissionType extends AbstractType { parent::configureOptions($resolver); - $resolver->setDefault('perm_name', static function (Options $options) { - return $options['name']; - }); + $resolver->setDefault('perm_name', static fn(Options $options) => $options['name']); $resolver->setDefault('label', function (Options $options) { if (!empty($this->perm_structure['perms'][$options['perm_name']]['label'])) { @@ -58,9 +54,7 @@ class PermissionType extends AbstractType return $options['name']; }); - $resolver->setDefault('multi_checkbox', static function (Options $options) { - return !$options['disabled']; - }); + $resolver->setDefault('multi_checkbox', static fn(Options $options) => !$options['disabled']); $resolver->setDefaults([ 'inherit' => false, diff --git a/src/Form/Permissions/PermissionsMapper.php b/src/Form/Permissions/PermissionsMapper.php index c08d2954..d4b937bc 100644 --- a/src/Form/Permissions/PermissionsMapper.php +++ b/src/Form/Permissions/PermissionsMapper.php @@ -34,25 +34,20 @@ use Traversable; */ final class PermissionsMapper implements DataMapperInterface { - private PermissionManager $resolver; - private bool $inherit; - - public function __construct(PermissionManager $resolver, bool $inherit = false) + public function __construct(private readonly PermissionManager $resolver, private readonly bool $inherit = false) { - $this->inherit = $inherit; - $this->resolver = $resolver; } /** - * Maps the view data of a compound form to its children. + * Maps the view data of a compound form to its children. * - * The method is responsible for calling {@link FormInterface::setData()} - * on the children of compound forms, defining their underlying model data. + * The method is responsible for calling {@link FormInterface::setData()} + * on the children of compound forms, defining their underlying model data. * * @param mixed $viewData View data of the compound form being initialized - * @param FormInterface[]|Traversable $forms A list of {@link FormInterface} instances + * @param Traversable $forms A list of {@link FormInterface} instances */ - public function mapDataToForms($viewData, $forms): void + public function mapDataToForms(mixed $viewData, \Traversable $forms): void { foreach ($forms as $form) { if ($this->inherit) { @@ -73,33 +68,33 @@ final class PermissionsMapper implements DataMapperInterface } /** - * Maps the model data of a list of children forms into the view data of their parent. + * Maps the model data of a list of children forms into the view data of their parent. * - * This is the internal cascade call of FormInterface::submit for compound forms, since they - * cannot be bound to any input nor the request as scalar, but their children may: + * This is the internal cascade call of FormInterface::submit for compound forms, since they + * cannot be bound to any input nor the request as scalar, but their children may: * - * $compoundForm->submit($arrayOfChildrenViewData) - * // inside: - * $childForm->submit($childViewData); - * // for each entry, do the same and/or reverse transform - * $this->dataMapper->mapFormsToData($compoundForm, $compoundInitialViewData) - * // then reverse transform + * $compoundForm->submit($arrayOfChildrenViewData) + * // inside: + * $childForm->submit($childViewData); + * // for each entry, do the same and/or reverse transform + * $this->dataMapper->mapFormsToData($compoundForm, $compoundInitialViewData) + * // then reverse transform * - * When a simple form is submitted the following is happening: + * When a simple form is submitted the following is happening: * - * $simpleForm->submit($submittedViewData) - * // inside: - * $this->viewData = $submittedViewData - * // then reverse transform + * $simpleForm->submit($submittedViewData) + * // inside: + * $this->viewData = $submittedViewData + * // then reverse transform * - * The model data can be an array or an object, so this second argument is always passed - * by reference. + * The model data can be an array or an object, so this second argument is always passed + * by reference. * - * @param FormInterface[]|Traversable $forms A list of {@link FormInterface} instances + * @param Traversable $forms A list of {@link FormInterface} instances * @param mixed $viewData The compound form's view data that get mapped * its children model data */ - public function mapFormsToData($forms, &$viewData): void + public function mapFormsToData(\Traversable $forms, mixed &$viewData): void { if ($this->inherit) { throw new RuntimeException('The permission type is readonly when it is showing read only data!'); diff --git a/src/Form/Permissions/PermissionsType.php b/src/Form/Permissions/PermissionsType.php index e5688729..86fdbc2c 100644 --- a/src/Form/Permissions/PermissionsType.php +++ b/src/Form/Permissions/PermissionsType.php @@ -33,12 +33,10 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class PermissionsType extends AbstractType { - protected PermissionManager $resolver; protected array $perm_structure; - public function __construct(PermissionManager $resolver) + public function __construct(protected PermissionManager $resolver) { - $this->resolver = $resolver; $this->perm_structure = $resolver->getPermissionStructure(); } @@ -47,6 +45,7 @@ class PermissionsType extends AbstractType $resolver->setDefaults([ 'show_legend' => true, 'show_presets' => false, + 'show_dependency_notice' => static fn(Options $options) => !$options['disabled'], 'constraints' => static function (Options $options) { if (!$options['disabled']) { return [new NoLockout()]; @@ -62,6 +61,7 @@ class PermissionsType extends AbstractType { $view->vars['show_legend'] = $options['show_legend']; $view->vars['show_presets'] = $options['show_presets']; + $view->vars['show_dependency_notice'] = $options['show_dependency_notice']; } public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ProjectSystem/ProjectAddPartsType.php b/src/Form/ProjectSystem/ProjectAddPartsType.php new file mode 100644 index 00000000..61f72c41 --- /dev/null +++ b/src/Form/ProjectSystem/ProjectAddPartsType.php @@ -0,0 +1,88 @@ +. + */ +namespace App\Form\ProjectSystem; + +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use App\Form\Type\StructuralEntityType; +use App\Validator\Constraints\UniqueObjectCollection; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Constraints\NotNull; + +class ProjectAddPartsType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder->add('project', StructuralEntityType::class, [ + 'class' => Project::class, + 'required' => true, + 'disabled' => $options['project'] instanceof Project, //If a project is given, disable the field + 'data' => $options['project'], + 'constraints' => [ + new NotNull() + ] + ]); + $builder->add('bom_entries', ProjectBOMEntryCollectionType::class, [ + 'entry_options' => [ + 'constraints' => [ + new UniqueEntity(fields: ['part', 'project'], message: 'project.bom_entry.part_already_in_bom', + entityClass: ProjectBOMEntry::class), + new UniqueEntity(fields: ['name', 'project'], message: 'project.bom_entry.name_already_in_bom', + entityClass: ProjectBOMEntry::class, ignoreNull: true), + ] + ], + 'constraints' => [ + new UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part']), + new UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name']), + ] + ]); + $builder->add('submit', SubmitType::class, ['label' => 'save']); + + //After submit set the project for all bom entries, so that it can be validated properly + $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) { + $form = $event->getForm(); + /** @var Project $project */ + $project = $form->get('project')->getData(); + $bom_entries = $form->get('bom_entries')->getData(); + + foreach ($bom_entries as $bom_entry) { + $bom_entry->setProject($project); + } + }); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'project' => null, + ]); + + $resolver->setAllowedTypes('project', ['null', Project::class]); + } +} diff --git a/src/Form/ProjectSystem/ProjectBOMEntryCollectionType.php b/src/Form/ProjectSystem/ProjectBOMEntryCollectionType.php index 71c745c7..53ec5f70 100644 --- a/src/Form/ProjectSystem/ProjectBOMEntryCollectionType.php +++ b/src/Form/ProjectSystem/ProjectBOMEntryCollectionType.php @@ -1,5 +1,7 @@ setDefaults([ 'entry_type' => ProjectBOMEntryType::class, @@ -27,9 +29,4 @@ class ProjectBOMEntryCollectionType extends AbstractType 'label' => false, ]); } - - public function getBlockPrefix() - { - return 'project_bom_entry_collection'; - } -} \ No newline at end of file +} diff --git a/src/Form/ProjectSystem/ProjectBOMEntryType.php b/src/Form/ProjectSystem/ProjectBOMEntryType.php index 49292235..cac362fb 100644 --- a/src/Form/ProjectSystem/ProjectBOMEntryType.php +++ b/src/Form/ProjectSystem/ProjectBOMEntryType.php @@ -1,19 +1,17 @@ ProjectBOMEntry::class, ]); } - - - public function getBlockPrefix() - { - return 'project_bom_entry'; - } -} \ No newline at end of file +} diff --git a/src/Form/ProjectSystem/ProjectBuildType.php b/src/Form/ProjectSystem/ProjectBuildType.php index 3758bb21..2b7b52e2 100644 --- a/src/Form/ProjectSystem/ProjectBuildType.php +++ b/src/Form/ProjectSystem/ProjectBuildType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\ProjectSystem; +use Symfony\Bundle\SecurityBundle\Security; +use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; use App\Form\Type\PartLotSelectType; use App\Form\Type\SIUnitType; @@ -34,18 +38,14 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; class ProjectBuildType extends AbstractType implements DataMapperInterface { - private Security $security; - - public function __construct(Security $security) + public function __construct(private readonly Security $security) { - $this->security = $security; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'compound' => true, @@ -53,7 +53,7 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface ]); } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->setDataMapper($this); @@ -62,6 +62,15 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface 'disabled' => !$this->security->isGranted('@parts_stock.withdraw'), ]); + $builder->add('dontCheckQuantity', CheckboxType::class, [ + 'label' => 'project.build.dont_check_quantity', + 'help' => 'project.build.dont_check_quantity.help', + 'required' => false, + 'attr' => [ + 'data-controller' => 'pages--dont-check-quantity-checkbox' + ] + ]); + $builder->add('comment', TextType::class, [ 'label' => 'part.info.withdraw_modal.comment', 'help' => 'part.info.withdraw_modal.comment.hint', @@ -79,10 +88,10 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface $form->add('addBuildsToBuildsPart', CheckboxType::class, [ 'label' => 'project.build.add_builds_to_builds_part', 'required' => false, - 'disabled' => $build_request->getProject()->getBuildPart() === null, + 'disabled' => !$build_request->getProject()->getBuildPart() instanceof Part, ]); - if ($build_request->getProject()->getBuildPart()) { + if ($build_request->getProject()->getBuildPart() instanceof Part) { $form->add('buildsPartLot', PartLotSelectType::class, [ 'label' => 'project.build.builds_part_lot', 'required' => false, @@ -106,7 +115,7 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface }); } - public function mapDataToForms($data, \Traversable $forms) + public function mapDataToForms($data, \Traversable $forms): void { if (!$data instanceof ProjectBuildRequest) { throw new \RuntimeException('Data must be an instance of ' . ProjectBuildRequest::class); @@ -124,6 +133,7 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface } $forms['comment']->setData($data->getComment()); + $forms['dontCheckQuantity']->setData($data->isDontCheckQuantity()); $forms['addBuildsToBuildsPart']->setData($data->getAddBuildsToBuildsPart()); if (isset($forms['buildsPartLot'])) { $forms['buildsPartLot']->setData($data->getBuildsPartLot()); @@ -131,7 +141,7 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface } - public function mapFormsToData(\Traversable $forms, &$data) + public function mapFormsToData(\Traversable $forms, &$data): void { if (!$data instanceof ProjectBuildRequest) { throw new \RuntimeException('Data must be an instance of ' . ProjectBuildRequest::class); @@ -145,17 +155,19 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface $matches = []; if (preg_match('/^lot_(\d+)$/', $key, $matches)) { $lot_id = (int) $matches[1]; - $data->setLotWithdrawAmount($lot_id, $form->getData()); + $data->setLotWithdrawAmount($lot_id, (float) $form->getData()); } } $data->setComment($forms['comment']->getData()); + $data->setDontCheckQuantity($forms['dontCheckQuantity']->getData()); + if (isset($forms['buildsPartLot'])) { $lot = $forms['buildsPartLot']->getData(); if (!$lot) { //When the user selected "Create new lot", create a new lot $lot = new PartLot(); $description = 'Build ' . date('Y-m-d H:i:s'); - if (!empty($data->getComment())) { + if ($data->getComment() !== '') { $description .= ' (' . $data->getComment() . ')'; } $lot->setDescription($description); @@ -168,4 +180,4 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface //This has to be set after the builds part lot, so that it can disable the option $data->setAddBuildsToBuildsPart($forms['addBuildsToBuildsPart']->getData()); } -} \ No newline at end of file +} diff --git a/src/Form/TFAGoogleSettingsType.php b/src/Form/TFAGoogleSettingsType.php index e00ba494..7917f705 100644 --- a/src/Form/TFAGoogleSettingsType.php +++ b/src/Form/TFAGoogleSettingsType.php @@ -53,6 +53,7 @@ class TFAGoogleSettingsType extends AbstractType 'google_confirmation', TextType::class, [ + 'label' => 'tfa.check.code.confirmation', 'mapped' => false, 'attr' => [ 'maxlength' => '6', @@ -60,7 +61,7 @@ class TFAGoogleSettingsType extends AbstractType 'pattern' => '\d*', 'autocomplete' => 'off', ], - 'constraints' => [new ValidGoogleAuthCode()], + 'constraints' => [new ValidGoogleAuthCode(groups: ["google_authenticator"])], ] ); @@ -92,6 +93,7 @@ class TFAGoogleSettingsType extends AbstractType { $resolver->setDefaults([ 'data_class' => User::class, + 'validation_groups' => ['google_authenticator'], ]); } } diff --git a/src/Form/Type/BigDecimalMoneyType.php b/src/Form/Type/BigDecimalMoneyType.php index 2fb8d7ee..189416ff 100644 --- a/src/Form/Type/BigDecimalMoneyType.php +++ b/src/Form/Type/BigDecimalMoneyType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Type; use Brick\Math\BigDecimal; @@ -28,7 +30,7 @@ use Symfony\Component\Form\FormBuilderInterface; class BigDecimalMoneyType extends AbstractType implements DataTransformerInterface { - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->addModelTransformer($this); } diff --git a/src/Form/Type/BigDecimalNumberType.php b/src/Form/Type/BigDecimalNumberType.php index 8ee0911e..c225f0a4 100644 --- a/src/Form/Type/BigDecimalNumberType.php +++ b/src/Form/Type/BigDecimalNumberType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Type; use Brick\Math\BigDecimal; @@ -28,7 +30,7 @@ use Symfony\Component\Form\FormBuilderInterface; class BigDecimalNumberType extends AbstractType implements DataTransformerInterface { - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->addModelTransformer($this); } diff --git a/src/Form/Type/CurrencyEntityType.php b/src/Form/Type/CurrencyEntityType.php index 856acc9e..07f0a9f8 100644 --- a/src/Form/Type/CurrencyEntityType.php +++ b/src/Form/Type/CurrencyEntityType.php @@ -22,14 +22,10 @@ declare(strict_types=1); namespace App\Form\Type; -use App\Entity\Attachments\AttachmentType; -use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\PriceInformations\Currency; use App\Form\Type\Helper\StructuralEntityChoiceHelper; -use App\Services\Attachments\AttachmentURLGenerator; use App\Services\Trees\NodesListBuilder; use Doctrine\ORM\EntityManagerInterface; -use RuntimeException; use Symfony\Component\Intl\Currencies; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -40,12 +36,9 @@ use Symfony\Contracts\Translation\TranslatorInterface; */ class CurrencyEntityType extends StructuralEntityType { - protected ?string $base_currency; - - public function __construct(EntityManagerInterface $em, NodesListBuilder $builder, TranslatorInterface $translator, StructuralEntityChoiceHelper $choiceHelper, ?string $base_currency) + public function __construct(EntityManagerInterface $em, NodesListBuilder $builder, TranslatorInterface $translator, StructuralEntityChoiceHelper $choiceHelper, protected ?string $base_currency) { parent::__construct($em, $builder, $translator, $choiceHelper); - $this->base_currency = $base_currency; } public function configureOptions(OptionsResolver $resolver): void @@ -60,14 +53,10 @@ class CurrencyEntityType extends StructuralEntityType // This options allows you to override the currency shown for the null value $resolver->setDefault('base_currency', null); - $resolver->setDefault('choice_attr', function (Options $options) { - return function ($choice) use ($options) { - return $this->choice_helper->generateChoiceAttrCurrency($choice, $options); - }; - }); + $resolver->setDefault('choice_attr', fn(Options $options) => fn($choice) => $this->choice_helper->generateChoiceAttrCurrency($choice, $options)); $resolver->setDefault('empty_message', function (Options $options) { - //By default we use the global base currency: + //By default, we use the global base currency: $iso_code = $this->base_currency; if ($options['base_currency']) { //Allow to override it @@ -79,7 +68,7 @@ class CurrencyEntityType extends StructuralEntityType $resolver->setDefault('used_to_select_parent', false); - //If short is set to true, then the name of the entity will only shown in the dropdown list not in the selected value. + //If short is set to true, then the name of the entity will only show in the dropdown list not in the selected value. $resolver->setDefault('short', false); } } diff --git a/src/Form/Type/ExponentialNumberType.php b/src/Form/Type/ExponentialNumberType.php new file mode 100644 index 00000000..f566afbb --- /dev/null +++ b/src/Form/Type/ExponentialNumberType.php @@ -0,0 +1,61 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\Type; + +use App\Form\Type\Helper\ExponentialNumberTransformer; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\NumberType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Similar to the NumberType, but formats small values in scienfitic notation instead of rounding it to 0, like NumberType + */ +class ExponentialNumberType extends AbstractType +{ + public function getParent(): string + { + return NumberType::class; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + //We want to allow the full precision of the number, so disable rounding + 'scale' => null, + ]); + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder->resetViewTransformers(); + + $builder->addViewTransformer(new ExponentialNumberTransformer( + $options['scale'], + $options['grouping'], + $options['rounding_mode'], + $options['html5'] ? 'en' : null + )); + } +} \ No newline at end of file diff --git a/src/Form/Type/Helper/ExponentialNumberTransformer.php b/src/Form/Type/Helper/ExponentialNumberTransformer.php new file mode 100644 index 00000000..ee2f4a4c --- /dev/null +++ b/src/Form/Type/Helper/ExponentialNumberTransformer.php @@ -0,0 +1,113 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\Type\Helper; + +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer; + +/** + * This transformer formats small values in scienfitic notation instead of rounding it to 0, like the default + * NumberFormatter. + */ +class ExponentialNumberTransformer extends NumberToLocalizedStringTransformer +{ + public function __construct( + private ?int $scale = null, + ?bool $grouping = false, + ?int $roundingMode = \NumberFormatter::ROUND_HALFUP, + protected ?string $locale = null + ) { + //Set scale to null, to disable rounding of values + parent::__construct($scale, $grouping, $roundingMode, $locale); + } + + /** + * Transforms a number type into localized number. + * + * @param int|float|null $value Number value + * + * @throws TransformationFailedException if the given value is not numeric + * or if the value cannot be transformed + */ + public function transform(mixed $value): string + { + if (null === $value) { + return ''; + } + + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + //If the value is too small, the number formatter would return 0, therfore use exponential notation for small numbers + if (abs($value) < 1e-3) { + $formatter = $this->getScientificNumberFormatter(); + } else { + $formatter = $this->getNumberFormatter(); + } + + + + $value = $formatter->format($value); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + // Convert non-breaking and narrow non-breaking spaces to normal ones + $value = str_replace(["\xc2\xa0", "\xe2\x80\xaf"], ' ', $value); + + return $value; + } + + protected function getScientificNumberFormatter(): \NumberFormatter + { + $formatter = new \NumberFormatter($this->locale ?? \Locale::getDefault(), \NumberFormatter::SCIENTIFIC); + + if (null !== $this->scale) { + $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $this->scale); + $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode); + } + + $formatter->setAttribute(\NumberFormatter::GROUPING_USED, (int) $this->grouping); + + return $formatter; + } + + protected function getNumberFormatter(): \NumberFormatter + { + $formatter = parent::getNumberFormatter(); + + //Unset the fraction digits, as we don't want to round the number + $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, 0); + if (null !== $this->scale) { + $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $this->scale); + } else { + $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, 100); + } + + + return $formatter; + } +} \ No newline at end of file diff --git a/src/Form/Type/Helper/StructuralEntityChoiceHelper.php b/src/Form/Type/Helper/StructuralEntityChoiceHelper.php index db25e6da..1210d188 100644 --- a/src/Form/Type/Helper/StructuralEntityChoiceHelper.php +++ b/src/Form/Type/Helper/StructuralEntityChoiceHelper.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Type\Helper; +use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentType; +use App\Entity\Base\AbstractDBElement; +use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Contracts\HasMasterAttachmentInterface; use App\Entity\PriceInformations\Currency; use App\Services\Attachments\AttachmentURLGenerator; -use RuntimeException; use Symfony\Component\Intl\Currencies; use Symfony\Component\OptionsResolver\Options; use Symfony\Contracts\Translation\TranslatorInterface; @@ -32,124 +37,116 @@ use Symfony\Contracts\Translation\TranslatorInterface; class StructuralEntityChoiceHelper { - private AttachmentURLGenerator $attachmentURLGenerator; - private TranslatorInterface $translator; - - public function __construct(AttachmentURLGenerator $attachmentURLGenerator, TranslatorInterface $translator) + public function __construct(private readonly AttachmentURLGenerator $attachmentURLGenerator, private readonly TranslatorInterface $translator) { - $this->attachmentURLGenerator = $attachmentURLGenerator; - $this->translator = $translator; } /** * Generates the choice attributes for the given AbstractStructuralDBElement. - * @param AbstractStructuralDBElement $choice - * @param Options|array $options - * @return array|string[] + * @return array */ - public function generateChoiceAttr(AbstractStructuralDBElement $choice, $options): array + public function generateChoiceAttr(AbstractNamedDBElement $choice, Options|array $options): array { - $tmp = []; - - //Disable attribute if the choice is marked as not selectable - if (($options['disable_not_selectable'] ?? false) && $choice->isNotSelectable()) { - $tmp += ['disabled' => 'disabled']; - } - - if ($choice instanceof AttachmentType) { - $tmp += ['data-filetype_filter' => $choice->getFiletypeFilter()]; - } - - $level = $choice->getLevel(); - /** @var AbstractStructuralDBElement|null $parent */ - $parent = $options['subentities_of'] ?? null; - if (null !== $parent) { - $level -= $parent->getLevel() - 1; - } - - $tmp += [ - 'data-level' => $level, - 'data-parent' => $choice->getParent() ? $choice->getParent()->getFullPath() : null, - 'data-path' => $choice->getFullPath('->'), - 'data-image' => $choice->getMasterPictureAttachment() ? $this->attachmentURLGenerator->getThumbnailURL($choice->getMasterPictureAttachment(), 'thumbnail_xs') : null, + $tmp = [ + 'data-level' => 0, + 'data-path' => $choice->getName(), ]; - if ($choice instanceof AttachmentType && !empty($choice->getFiletypeFilter())) { + if ($choice instanceof AbstractStructuralDBElement) { + //Disable attribute if the choice is marked as not selectable + if (($options['disable_not_selectable'] ?? false) && $choice->isNotSelectable()) { + $tmp += ['disabled' => 'disabled']; + } + + if ($choice instanceof AttachmentType) { + $tmp += ['data-filetype_filter' => $choice->getFiletypeFilter()]; + } + + $level = $choice->getLevel(); + /** @var AbstractStructuralDBElement|null $parent */ + $parent = $options['subentities_of'] ?? null; + if ($parent instanceof AbstractStructuralDBElement) { + $level -= $parent->getLevel() - 1; + } + + $tmp['data-level'] = $level; + $tmp['data-parent'] = $choice->getParent() instanceof AbstractStructuralDBElement ? $choice->getParent()->getFullPath() : null; + $tmp['data-path'] = $choice->getFullPath('->'); + } + + if ($choice instanceof HasMasterAttachmentInterface) { + $tmp['data-image'] = ($choice->getMasterPictureAttachment() instanceof Attachment + && $choice->getMasterPictureAttachment()->isPicture()) ? + $this->attachmentURLGenerator->getThumbnailURL($choice->getMasterPictureAttachment(), + 'thumbnail_xs') + : null + ; + } + + if ($choice instanceof AttachmentType && $choice->getFiletypeFilter() !== '') { $tmp += ['data-filetype_filter' => $choice->getFiletypeFilter()]; } + //Show entities that are not added to DB yet separately from other entities + $tmp['data-not_in_db_yet'] = $choice->getID() === null; + return $tmp; } /** * Generates the choice attributes for the given AbstractStructuralDBElement. - * @param Currency $choice - * @param Options|array $options * @return array|string[] */ - public function generateChoiceAttrCurrency(Currency $choice, $options): array + public function generateChoiceAttrCurrency(Currency $choice, Options|array $options): array { $tmp = $this->generateChoiceAttr($choice, $options); + $symbol = $choice->getIsoCode() === '' ? null : Currencies::getSymbol($choice->getIsoCode()); + $tmp['data-short'] = $options['short'] ? $symbol : $choice->getName(); - if(!empty($choice->getIsoCode())) { - $symbol = Currencies::getSymbol($choice->getIsoCode()); - } else { - $symbol = null; - } + //Show entities that are not added to DB yet separately from other entities + $tmp['data-not_in_db_yet'] = $choice->getID() === null; - if ($options['short']) { - $tmp['data-short'] = $symbol; - } else { - $tmp['data-short'] = $choice->getName(); - } - - $tmp += [ + return $tmp + [ 'data-symbol' => $symbol, ]; - - return $tmp; } /** * Returns the choice label for the given AbstractStructuralDBElement. - * @param AbstractStructuralDBElement $choice - * @return string */ - public function generateChoiceLabel(AbstractStructuralDBElement $choice): string + public function generateChoiceLabel(AbstractNamedDBElement $choice): string { return $choice->getName(); } /** * Returns the choice value for the given AbstractStructuralDBElement. - * @param AbstractStructuralDBElement|null $element - * @return string|int|null */ - public function generateChoiceValue(?AbstractStructuralDBElement $element) + public function generateChoiceValue(?AbstractNamedDBElement $element): string|int|null { - if ($element === null) { + if (!$element instanceof AbstractNamedDBElement) { return null; } /** * Do not change the structure below, even when inspection says it can be replaced with a null coalescing operator. - * It is important that the value returned here for a existing element is an int, and for a new element a string. - * I dont really understand why, but it seems to be important for the choice_loader to work correctly. + * It is important that the value returned here for an existing element is an int, and for a new element a string. + * I don't really understand why, but it seems to be important for the choice_loader to work correctly. * So please do not change this! */ if ($element->getID() === null) { - //Must be the same as the separator in the choice_loader, otherwise this will not work! - return $element->getFullPath('->'); + if ($element instanceof AbstractStructuralDBElement) { + //Must be the same as the separator in the choice_loader, otherwise this will not work! + return '$%$' . $element->getFullPath('->'); + } + // '$%$' is the indicator prefix for a new entity + return '$%$' . $element->getName(); } return $element->getID(); } - /** - * @param AbstractStructuralDBElement $element - * @return string|null - */ - public function generateGroupBy(AbstractStructuralDBElement $element): ?string + public function generateGroupBy(AbstractDBElement $element): ?string { //Show entities that are not added to DB yet separately from other entities if ($element->getID() === null) { @@ -158,4 +155,4 @@ class StructuralEntityChoiceHelper return null; } -} \ No newline at end of file +} diff --git a/src/Form/Type/Helper/StructuralEntityChoiceLoader.php b/src/Form/Type/Helper/StructuralEntityChoiceLoader.php index 6bca91b4..e2e4e841 100644 --- a/src/Form/Type/Helper/StructuralEntityChoiceLoader.php +++ b/src/Form/Type/Helper/StructuralEntityChoiceLoader.php @@ -1,4 +1,7 @@ options = $options; - $this->builder = $builder; - $this->entityManager = $entityManager; + private ?AbstractNamedDBElement $starting_element = null; + + private ?FormInterface $form = null; + + public function __construct( + private readonly Options $options, + private readonly NodesListBuilder $builder, + private readonly EntityManagerInterface $entityManager, + private readonly TranslatorInterface $translator + ) { } protected function loadChoices(): iterable { - $tmp = []; + //If the starting_element is set and not persisted yet, add it to the list + $tmp = $this->starting_element !== null && $this->starting_element->getID() === null ? [$this->starting_element] : []; + if ($this->additional_element) { $tmp = $this->createNewEntitiesFromValue($this->additional_element); $this->additional_element = null; @@ -56,26 +68,51 @@ class StructuralEntityChoiceLoader extends AbstractChoiceLoader public function createNewEntitiesFromValue(string $value): array { - if (!$this->options['allow_add']) { - throw new \RuntimeException('Cannot create new entity, because allow_add is not enabled!'); - } - if (trim($value) === '') { throw new \InvalidArgumentException('Cannot create new entity, because the name is empty!'); } + //Check if the value is matching the starting value element, we use the choice_value option to get the name of the starting element + if ($this->starting_element !== null + && $this->starting_element->getID() === null //Element must not be persisted yet + && $this->options['choice_value']($this->starting_element) === $value) { + //Then reuse the starting element + $this->entityManager->persist($this->starting_element); + return [$this->starting_element]; + } + + + if (!$this->options['allow_add']) { + //If we have a form, add an error to it, to improve the user experience + if ($this->form !== null) { + $this->form->addError( + new FormError($this->translator->trans('entity.select.creating_new_entities_not_allowed') + ) + ); + } else { + throw new \RuntimeException('Cannot create new entity, because allow_add is not enabled!'); + } + } + + + /** @var class-string $class */ $class = $this->options['class']; - /** @var StructuralDBElementRepository $repo */ + + /** @var StructuralDBElementRepository $repo */ $repo = $this->entityManager->getRepository($class); + $entities = $repo->getNewEntityFromPath($value, '->'); $results = []; - foreach($entities as $entity) { + foreach ($entities as $entity) { //If the entity is newly created (ID null), add it as result and persist it. if ($entity->getID() === null) { - $this->entityManager->persist($entity); + //Only persist the entities if it is allowed + if ($this->options['allow_add']) { + $this->entityManager->persist($entity); + } $results[] = $entity; } } @@ -93,4 +130,50 @@ class StructuralEntityChoiceLoader extends AbstractChoiceLoader return $this->additional_element; } -} \ No newline at end of file + /** + * Gets the initial value used to populate the field. + * @return AbstractNamedDBElement|null + */ + public function getStartingElement(): ?AbstractNamedDBElement + { + return $this->starting_element; + } + + /** + * Sets the form that this loader is bound to. + * @param FormInterface|null $form + * @return void + */ + public function setForm(?FormInterface $form): void + { + $this->form = $form; + } + + /** + * Sets the initial value used to populate the field. This will always be an allowed value. + * @param AbstractNamedDBElement|null $starting_element + * @return StructuralEntityChoiceLoader + */ + public function setStartingElement(?AbstractNamedDBElement $starting_element): StructuralEntityChoiceLoader + { + $this->starting_element = $starting_element; + return $this; + } + + protected function doLoadChoicesForValues(array $values, ?callable $value): array + { + // Normalize the data (remove whitespaces around the arrow sign) and leading/trailing whitespaces + // This is required so that the value that is generated for an new entity based on its name structure is + // the same as the value that is generated for the same entity after it is persisted. + // Otherwise, errors occurs that the element could not be found. + foreach ($values as &$data) { + $data = trim((string) $data); + $data = preg_replace('/\s*->\s*/', '->', $data); + } + unset ($data); + + return $this->loadChoiceList($value)->getChoicesForValues($values); + } + + +} diff --git a/src/Form/Type/MasterPictureAttachmentType.php b/src/Form/Type/MasterPictureAttachmentType.php index 7aba9cc6..b5edbd55 100644 --- a/src/Form/Type/MasterPictureAttachmentType.php +++ b/src/Form/Type/MasterPictureAttachmentType.php @@ -42,32 +42,28 @@ class MasterPictureAttachmentType extends AbstractType $resolver->setDefaults([ 'filter' => 'picture', 'choice_translation_domain' => false, - 'choice_attr' => static function (Options $options) { - return static function ($choice, $key, $value) use ($options) { - /** @var Attachment $choice */ - $tmp = ['data-subtext' => $choice->getFilename() ?? 'URL']; + 'choice_attr' => static fn(Options $options) => static function ($choice, $key, $value) use ($options) { + /** @var Attachment $choice */ + $tmp = ['data-subtext' => $choice->getFilename() ?? 'URL']; - if ('picture' === $options['filter'] && !$choice->isPicture()) { - $tmp += ['disabled' => 'disabled']; - } elseif ('3d_model' === $options['filter'] && !$choice->is3DModel()) { - $tmp += ['disabled' => 'disabled']; - } + if ('picture' === $options['filter'] && !$choice->isPicture()) { + $tmp += ['disabled' => 'disabled']; + } elseif ('3d_model' === $options['filter'] && !$choice->is3DModel()) { + $tmp += ['disabled' => 'disabled']; + } - return $tmp; - }; + return $tmp; }, 'choice_label' => 'name', - 'choice_loader' => static function (Options $options) { - return new CallbackChoiceLoader( - static function () use ($options) { - $entity = $options['entity']; - if (!$entity instanceof AttachmentContainingDBElement) { - throw new RuntimeException('$entity must have Attachments! (be of type AttachmentContainingDBElement)'); - } + 'choice_loader' => static fn(Options $options) => new CallbackChoiceLoader( + static function () use ($options) { + $entity = $options['entity']; + if (!$entity instanceof AttachmentContainingDBElement) { + throw new RuntimeException('$entity must have Attachments! (be of type AttachmentContainingDBElement)'); + } - return $entity->getAttachments()->toArray(); - }); - }, + return $entity->getAttachments()->toArray(); + }), ]); $resolver->setAllowedValues('filter', ['', 'picture', '3d_model']); diff --git a/src/Form/Type/PartLotSelectType.php b/src/Form/Type/PartLotSelectType.php index 61e119b0..c68535a7 100644 --- a/src/Form/Type/PartLotSelectType.php +++ b/src/Form/Type/PartLotSelectType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Type; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; use Doctrine\ORM\EntityRepository; @@ -31,29 +34,23 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class PartLotSelectType extends AbstractType { - public function getParent() + public function getParent(): string { return EntityType::class; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setRequired('part'); $resolver->setAllowedTypes('part', Part::class); $resolver->setDefaults([ 'class' => PartLot::class, - 'choice_label' => ChoiceList::label($this, function (PartLot $part_lot) { - return ($part_lot->getStorageLocation() ? $part_lot->getStorageLocation()->getFullPath() : '') - . ' (' . $part_lot->getName() . '): ' . $part_lot->getAmount(); - }), - 'query_builder' => function (Options $options) { - return function (EntityRepository $er) use ($options) { - return $er->createQueryBuilder('l') - ->where('l.part = :part') - ->setParameter('part', $options['part']); - }; - } + 'choice_label' => ChoiceList::label($this, static fn(PartLot $part_lot): string => ($part_lot->getStorageLocation() instanceof StorageLocation ? $part_lot->getStorageLocation()->getFullPath() : '') + . ' (' . $part_lot->getName() . '): ' . $part_lot->getAmount()), + 'query_builder' => fn(Options $options) => static fn(EntityRepository $er) => $er->createQueryBuilder('l') + ->where('l.part = :part') + ->setParameter('part', $options['part']) ]); } -} \ No newline at end of file +} diff --git a/src/Form/Type/PartSelectType.php b/src/Form/Type/PartSelectType.php index 5ca543f7..34b8fc7c 100644 --- a/src/Form/Type/PartSelectType.php +++ b/src/Form/Type/PartSelectType.php @@ -1,7 +1,12 @@ urlGenerator = $urlGenerator; - $this->em = $em; - $this->previewGenerator = $previewGenerator; - $this->attachmentURLGenerator = $attachmentURLGenerator; } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { - //At initialization we have to fill the form element with our selected data, so the user can see it + //At initialization, we have to fill the form element with our selected data, so the user can see it $builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) { $form = $event->getForm(); $config = $form->getConfig()->getOptions(); @@ -73,7 +68,7 @@ class PartSelectType extends AbstractType implements DataMapperInterface $builder->setDataMapper($this); } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'class' => Part::class, @@ -96,10 +91,10 @@ class PartSelectType extends AbstractType implements DataMapperInterface $resolver->setDefaults([ //Prefill the selected choice with the needed data, so the user can see it without an additional Ajax request 'choice_attr' => ChoiceList::attr($this, function (?Part $part) { - if($part) { + if($part instanceof Part) { //Determine the picture to show: $preview_attachment = $this->previewGenerator->getTablePreviewAttachment($part); - if ($preview_attachment !== null) { + if ($preview_attachment instanceof Attachment) { $preview_url = $this->attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm'); } else { @@ -107,31 +102,26 @@ class PartSelectType extends AbstractType implements DataMapperInterface } } - return $part ? [ + return $part instanceof Part ? [ 'data-description' => mb_strimwidth($part->getDescription(), 0, 127, '...'), - 'data-category' => $part->getCategory() ? $part->getCategory()->getName() : '', - 'data-footprint' => $part->getFootprint() ? $part->getFootprint()->getName() : '', + 'data-category' => $part->getCategory() instanceof Category ? $part->getCategory()->getName() : '', + 'data-footprint' => $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getName() : '', 'data-image' => $preview_url, ] : []; }) ]); } - public function getBlockPrefix() - { - return 'part_select'; - } - - public function mapDataToForms($data, $forms) + public function mapDataToForms($data, \Traversable $forms): void { $form = current(iterator_to_array($forms, false)); $form->setData($data); } - public function mapFormsToData($forms, &$data) + public function mapFormsToData(\Traversable $forms, &$data): void { $form = current(iterator_to_array($forms, false)); $data = $form->getData(); } -} \ No newline at end of file +} diff --git a/src/Form/Type/RichTextEditorType.php b/src/Form/Type/RichTextEditorType.php index a572fa2e..50683b0c 100644 --- a/src/Form/Type/RichTextEditorType.php +++ b/src/Form/Type/RichTextEditorType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Type; use Symfony\Component\Form\AbstractType; @@ -28,7 +30,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class RichTextEditorType extends AbstractType { - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { parent::configureOptions($resolver); // TODO: Change the autogenerated stub @@ -39,12 +41,7 @@ class RichTextEditorType extends AbstractType } - public function getBlockPrefix(): string - { - return 'rich_text_editor'; - } - - public function finishView(FormView $view, FormInterface $form, array $options) + public function finishView(FormView $view, FormInterface $form, array $options): void { $view->vars['attr'] = array_merge($view->vars['attr'], $this->optionsToAttrArray($options)); @@ -53,22 +50,17 @@ class RichTextEditorType extends AbstractType protected function optionsToAttrArray(array $options): array { - $tmp = []; - - //Set novalidate attribute, or we will get problems that form can not be submitted as textarea is not focusable - $tmp['novalidate'] = 'novalidate'; - - $tmp['data-mode'] = $options['mode']; - - //Add our data-controller element to the textarea - $tmp['data-controller'] = 'elements--ckeditor'; - - - return $tmp; + return [ + //Set novalidate attribute, or we will get problems that form can not be submitted as textarea is not focusable + 'novalidate' => 'novalidate', + 'data-mode' => $options['mode'], + //Add our data-controller element to the textarea + 'data-controller' => 'elements--ckeditor', + ]; } public function getParent(): string { return TextareaType::class; } -} \ No newline at end of file +} diff --git a/src/Form/Type/SIUnitType.php b/src/Form/Type/SIUnitType.php index bfec23e2..52bcaa3d 100644 --- a/src/Form/Type/SIUnitType.php +++ b/src/Form/Type/SIUnitType.php @@ -38,11 +38,8 @@ use Traversable; final class SIUnitType extends AbstractType implements DataMapperInterface { - protected SIFormatter $si_formatter; - - public function __construct(SIFormatter $SIFormatter) + public function __construct(protected SIFormatter $si_formatter) { - $this->si_formatter = $SIFormatter; } public function configureOptions(OptionsResolver $resolver): void @@ -91,7 +88,7 @@ final class SIUnitType extends AbstractType implements DataMapperInterface $resolver->setDefaults([ 'min' => 0, 'max' => '', - 'step' => static function (Options $options) { + 'step' => static function (Options $options): int|string { if (true === $options['is_integer']) { return 1; } @@ -138,7 +135,7 @@ final class SIUnitType extends AbstractType implements DataMapperInterface //Check if we need to make this thing small if (isset($options['attr']['class'])) { - $view->vars['sm'] = false !== strpos($options['attr']['class'], 'form-control-sm'); + $view->vars['sm'] = str_contains((string) $options['attr']['class'], 'form-control-sm'); } $view->vars['unit'] = $options['unit']; @@ -146,17 +143,17 @@ final class SIUnitType extends AbstractType implements DataMapperInterface } /** - * Maps the view data of a compound form to its children. + * Maps the view data of a compound form to its children. * - * The method is responsible for calling {@link FormInterface::setData()} - * on the children of compound forms, defining their underlying model data. + * The method is responsible for calling {@link FormInterface::setData()} + * on the children of compound forms, defining their underlying model data. * * @param mixed $viewData View data of the compound form being initialized - * @param FormInterface[]|Traversable $forms A list of {@link FormInterface} instances + * @param Traversable $forms A list of {@link FormInterface} instances * * @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported */ - public function mapDataToForms($viewData, $forms): void + public function mapDataToForms(mixed $viewData, \Traversable $forms): void { $forms = iterator_to_array($forms); @@ -179,35 +176,35 @@ final class SIUnitType extends AbstractType implements DataMapperInterface } /** - * Maps the model data of a list of children forms into the view data of their parent. + * Maps the model data of a list of children forms into the view data of their parent. * - * This is the internal cascade call of FormInterface::submit for compound forms, since they - * cannot be bound to any input nor the request as scalar, but their children may: + * This is the internal cascade call of FormInterface::submit for compound forms, since they + * cannot be bound to any input nor the request as scalar, but their children may: * - * $compoundForm->submit($arrayOfChildrenViewData) - * // inside: - * $childForm->submit($childViewData); - * // for each entry, do the same and/or reverse transform - * $this->dataMapper->mapFormsToData($compoundForm, $compoundInitialViewData) - * // then reverse transform + * $compoundForm->submit($arrayOfChildrenViewData) + * // inside: + * $childForm->submit($childViewData); + * // for each entry, do the same and/or reverse transform + * $this->dataMapper->mapFormsToData($compoundForm, $compoundInitialViewData) + * // then reverse transform * - * When a simple form is submitted the following is happening: + * When a simple form is submitted the following is happening: * - * $simpleForm->submit($submittedViewData) - * // inside: - * $this->viewData = $submittedViewData - * // then reverse transform + * $simpleForm->submit($submittedViewData) + * // inside: + * $this->viewData = $submittedViewData + * // then reverse transform * - * The model data can be an array or an object, so this second argument is always passed - * by reference. + * The model data can be an array or an object, so this second argument is always passed + * by reference. * - * @param FormInterface[]|Traversable $forms A list of {@link FormInterface} instances + * @param Traversable $forms A list of {@link FormInterface} instances * @param mixed $viewData The compound form's view data that get mapped * its children model data * * @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported */ - public function mapFormsToData($forms, &$viewData): void + public function mapFormsToData(\Traversable $forms, mixed &$viewData): void { //Convert both fields to a single float value. diff --git a/src/Form/Type/StaticFileAutocompleteType.php b/src/Form/Type/StaticFileAutocompleteType.php new file mode 100644 index 00000000..4d483e2a --- /dev/null +++ b/src/Form/Type/StaticFileAutocompleteType.php @@ -0,0 +1,63 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form\Type; + +use Symfony\Component\Asset\Packages; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Implements a text type with autocomplete functionality based on a static file, containing a list of autocomplete + * suggestions. + * Other values are allowed, but the user can select from the list of suggestions. + * The file must be located in the public directory! + */ +class StaticFileAutocompleteType extends AbstractType +{ + public function __construct( + private readonly Packages $assets + ) { + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setRequired('file'); + $resolver->setAllowedTypes('file', 'string'); + } + + public function getParent(): string + { + return TextType::class; + } + + public function finishView(FormView $view, FormInterface $form, array $options): void + { + //Add the data-controller and data-url attributes to the form field + $view->vars['attr']['data-controller'] = 'elements--static-file-autocomplete'; + $view->vars['attr']['data-url'] = $this->assets->getUrl($options['file']); + } +} \ No newline at end of file diff --git a/src/Form/Type/StructuralEntityType.php b/src/Form/Type/StructuralEntityType.php index 9c65a7ef..1018eeeb 100644 --- a/src/Form/Type/StructuralEntityType.php +++ b/src/Form/Type/StructuralEntityType.php @@ -22,28 +22,19 @@ declare(strict_types=1); namespace App\Form\Type; -use App\Entity\Attachments\AttachmentType; -use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Base\AbstractNamedDBElement; use App\Form\Type\Helper\StructuralEntityChoiceHelper; use App\Form\Type\Helper\StructuralEntityChoiceLoader; -use App\Services\Attachments\AttachmentURLGenerator; use App\Services\Trees\NodesListBuilder; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; -use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; -use Symfony\Component\Form\Event\PostSubmitEvent; use Symfony\Component\Form\Event\PreSubmitEvent; -use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvents; -use Symfony\Component\Form\FormInterface; -use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Validator\Constraints\AtLeastOneOf; -use Symfony\Component\Validator\Constraints\IsNull; use Symfony\Component\Validator\Constraints\Valid; use Symfony\Contracts\Translation\TranslatorInterface; @@ -53,31 +44,21 @@ use Symfony\Contracts\Translation\TranslatorInterface; */ class StructuralEntityType extends AbstractType { - protected EntityManagerInterface $em; - protected TranslatorInterface $translator; - protected StructuralEntityChoiceHelper $choice_helper; - - /** - * @var NodesListBuilder - */ - protected NodesListBuilder $builder; - - public function __construct(EntityManagerInterface $em, NodesListBuilder $builder, TranslatorInterface $translator, StructuralEntityChoiceHelper $choice_helper) + public function __construct(protected EntityManagerInterface $em, protected NodesListBuilder $builder, protected TranslatorInterface $translator, protected StructuralEntityChoiceHelper $choice_helper) { - $this->em = $em; - $this->builder = $builder; - $this->translator = $translator; - $this->choice_helper = $choice_helper; } public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event) { - //When the data contains non-digit characters, we assume that the user entered a new element. + //When the data starts with "$%$", we assume that the user entered a new element. //In that case we add the new element to our choice_loader $data = $event->getData(); - if (null === $data || !is_string($data) || $data === "" || ctype_digit($data)) { + if (is_string($data) && str_starts_with($data, '$%$')) { + //Extract the real name from the data + $data = substr($data, 3); + } else { return; } @@ -86,15 +67,12 @@ class StructuralEntityType extends AbstractType $choice_loader = $options['choice_loader']; if ($choice_loader instanceof StructuralEntityChoiceLoader) { $choice_loader->setAdditionalElement($data); + $choice_loader->setForm($form); } }); $builder->addModelTransformer(new CallbackTransformer( - function ($value) use ($options) { - return $this->modelTransform($value, $options); - }, function ($value) use ($options) { - return $this->modelReverseTransform($value, $options); - })); + fn($value) => $this->modelTransform($value, $options), fn($value) => $this->modelReverseTransform($value, $options))); } public function configureOptions(OptionsResolver $resolver): void @@ -105,29 +83,15 @@ class StructuralEntityType extends AbstractType 'show_fullpath_in_subtext' => true, //When this is enabled, the full path will be shown in subtext 'subentities_of' => null, //Only show entities with the given parent class 'disable_not_selectable' => false, //Disable entries with not selectable property - 'choice_value' => function (?AbstractStructuralDBElement $element) { - return $this->choice_helper->generateChoiceValue($element); - }, //Use the element id as option value and for comparing items - 'choice_loader' => function (Options $options) { - return new StructuralEntityChoiceLoader($options, $this->builder, $this->em); - }, - 'choice_label' => function (Options $options) { - return function ($choice, $key, $value) { - return $this->choice_helper->generateChoiceLabel($choice); - }; - }, - 'choice_attr' => function (Options $options) { - return function ($choice, $key, $value) use ($options) { - return $this->choice_helper->generateChoiceAttr($choice, $options); - }; - }, - 'group_by' => function (AbstractStructuralDBElement $element) { - return $this->choice_helper->generateGroupBy($element); - }, + 'choice_value' => fn(?AbstractNamedDBElement $element) => $this->choice_helper->generateChoiceValue($element), //Use the element id as option value and for comparing items + 'choice_loader' => fn(Options $options) => new StructuralEntityChoiceLoader($options, $this->builder, $this->em, $this->translator), + 'choice_label' => fn(Options $options) => fn($choice, $key, $value) => $this->choice_helper->generateChoiceLabel($choice), + 'choice_attr' => fn(Options $options) => fn($choice, $key, $value) => $this->choice_helper->generateChoiceAttr($choice, $options), + 'group_by' => fn(AbstractNamedDBElement $element) => $this->choice_helper->generateGroupBy($element), 'choice_translation_domain' => false, //Don't translate the entity names ]); - //Set the constraints for the case that allow add is enabled (we then have to check that the new element is valid) + //Set the constraints for the case that allow to add is enabled (we then have to check that the new element is valid) $resolver->setNormalizer('constraints', function (Options $options, $value) { if ($options['allow_add']) { $value[] = new Valid(); @@ -140,6 +104,13 @@ class StructuralEntityType extends AbstractType $resolver->setDefault('controller', 'elements--structural-entity-select'); + //Options for DTO values + $resolver->setDefault('dto_value', null); + $resolver->setAllowedTypes('dto_value', ['null', 'string']); + //If no help text is explicitly set, we use the dto value as help text and show it as html + $resolver->setDefault('help', fn(Options $options) => $this->dtoText($options['dto_value'])); + $resolver->setDefault('help_html', fn(Options $options) => $options['dto_value'] !== null); + $resolver->setDefault('attr', function (Options $options) { $tmp = [ 'data-controller' => $options['controller'], @@ -154,6 +125,16 @@ class StructuralEntityType extends AbstractType }); } + private function dtoText(?string $text): ?string + { + if ($text === null) { + return null; + } + + $result = '' . $this->translator->trans('info_providers.form.help_prefix') . ': '; + + return $result . htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ; + } public function getParent(): string { @@ -162,14 +143,19 @@ class StructuralEntityType extends AbstractType public function modelTransform($value, array $options) { + $choice_loader = $options['choice_loader']; + if ($choice_loader instanceof StructuralEntityChoiceLoader) { + $choice_loader->setStartingElement($value); + } + return $value; } public function modelReverseTransform($value, array $options) { /* This step is important in combination with the caching! - The elements deserialized from cache, are not known to Doctrinte ORM any more, so doctrine thinks, - that the entity has changed (and so throws an exception about non-persited entities). + The elements deserialized from cache, are not known to Doctrine ORM anymore, so doctrine thinks, + that the entity has changed (and so throws an exception about non-persisted entities). This function just retrieves a fresh copy of the entity from database, so doctrine detect correctly that no change happened. The performance impact of this should be very small in comparison of the boost, caused by the caching. diff --git a/src/Form/Type/ThemeChoiceType.php b/src/Form/Type/ThemeChoiceType.php index 88843903..7cdc0aa9 100644 --- a/src/Form/Type/ThemeChoiceType.php +++ b/src/Form/Type/ThemeChoiceType.php @@ -1,4 +1,7 @@ . */ - namespace App\Form\Type; -use App\Entity\UserSystem\User; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\OptionsResolver\OptionsResolver; class ThemeChoiceType extends AbstractType { - private array $available_themes; - - public function __construct(array $available_themes) + public function __construct(private readonly array $available_themes) { - $this->available_themes = $available_themes; } - public function getParent() + public function getParent(): string { return ChoiceType::class; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'choices' => $this->available_themes, - 'choice_label' => static function ($entity, $key, $value) { - return $value; - }, + 'choice_label' => static fn($entity, $key, $value) => $value, 'choice_translation_domain' => false, 'placeholder' => 'user_settings.theme.placeholder' ]); } -} \ No newline at end of file +} diff --git a/src/Form/Type/TriStateCheckboxType.php b/src/Form/Type/TriStateCheckboxType.php index 6e8dafc4..4523a839 100644 --- a/src/Form/Type/TriStateCheckboxType.php +++ b/src/Form/Type/TriStateCheckboxType.php @@ -99,9 +99,8 @@ final class TriStateCheckboxType extends AbstractType implements DataTransformer * * @return mixed The value in the transformed representation * - * @throws TransformationFailedException when the transformation fails */ - public function transform($value) + public function transform(mixed $value) { if (true === $value) { return 'true'; @@ -142,22 +141,14 @@ final class TriStateCheckboxType extends AbstractType implements DataTransformer * @param mixed $value The value in the transformed representation * * @return mixed The value in the original representation - * - * @throws TransformationFailedException when the transformation fails */ - public function reverseTransform($value) + public function reverseTransform(mixed $value) { - switch ($value) { - case 'true': - return true; - case 'false': - return false; - case 'indeterminate': - case 'null': - case '': - return null; - default: - throw new InvalidArgumentException('Invalid value encountered!: '.$value); - } + return match ($value) { + 'true' => true, + 'false' => false, + 'indeterminate', 'null', '' => null, + default => throw new InvalidArgumentException('Invalid value encountered!: '.$value), + }; } } diff --git a/src/Form/Type/UserSelectType.php b/src/Form/Type/UserSelectType.php new file mode 100644 index 00000000..8862cdf7 --- /dev/null +++ b/src/Form/Type/UserSelectType.php @@ -0,0 +1,45 @@ +. + */ +namespace App\Form\Type; + +use App\Entity\UserSystem\User; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class UserSelectType extends AbstractType +{ + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'class' => User::class, + 'choice_label' => fn(Options $options) => static fn(User $choice, $key, $value) => $choice->getFullName(true), + ]); + } + + public function getParent(): string + { + return StructuralEntityType::class; + } +} diff --git a/src/Form/UserAdminForm.php b/src/Form/UserAdminForm.php index 774cf8f7..864bcf6b 100644 --- a/src/Form/UserAdminForm.php +++ b/src/Form/UserAdminForm.php @@ -22,18 +22,18 @@ declare(strict_types=1); namespace App\Form; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Base\AbstractNamedDBElement; -use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use App\Form\Permissions\PermissionsType; use App\Form\Type\CurrencyEntityType; use App\Form\Type\MasterPictureAttachmentType; +use App\Form\Type\RichTextEditorType; use App\Form\Type\StructuralEntityType; 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\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\LanguageType; use Symfony\Component\Form\Extension\Core\Type\PasswordType; @@ -44,16 +44,12 @@ use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TimezoneType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; use Symfony\Component\Validator\Constraints\Length; class UserAdminForm extends AbstractType { - protected Security $security; - - public function __construct(Security $security) + public function __construct(protected Security $security) { - $this->security = $security; } public function configureOptions(OptionsResolver $resolver): void @@ -61,11 +57,13 @@ class UserAdminForm extends AbstractType parent::configureOptions($resolver); // TODO: Change the autogenerated stub $resolver->setRequired('attachment_class'); $resolver->setDefault('parameter_class', false); + + $resolver->setDefault('validation_groups', ['Default', 'permissions:edit']); } public function buildForm(FormBuilderInterface $builder, array $options): void { - /** @var AbstractStructuralDBElement $entity */ + /** @var User $entity */ $entity = $options['data']; $is_new = null === $entity->getID(); @@ -116,7 +114,11 @@ class UserAdminForm extends AbstractType 'required' => false, 'disabled' => !$this->security->isGranted('edit_infos', $entity), ]) - + ->add('showEmailOnProfile', CheckboxType::class, [ + 'required' => false, + 'label' => 'user.show_email_on_profile.label', + 'disabled' => !$this->security->isGranted('edit_infos', $entity), + ]) ->add('department', TextType::class, [ 'empty_data' => '', 'label' => 'user.department.label', @@ -126,6 +128,16 @@ class UserAdminForm extends AbstractType 'required' => false, 'disabled' => !$this->security->isGranted('edit_infos', $entity), ]) + ->add('aboutMe', RichTextEditorType::class, [ + 'required' => false, + 'empty_data' => '', + 'label' => 'user.aboutMe.label', + 'attr' => [ + 'rows' => 4, + ], + 'mode' => 'markdown-full', + 'disabled' => !$this->security->isGranted('edit_infos', $entity), + ]) //Config section ->add('language', LanguageType::class, [ @@ -157,6 +169,7 @@ class UserAdminForm extends AbstractType 'type' => PasswordType::class, 'first_options' => [ 'label' => 'user.settings.pw_new.label', + 'password_estimator' => true, ], 'second_options' => [ 'label' => 'user.settings.pw_confirm.label', @@ -164,7 +177,7 @@ class UserAdminForm extends AbstractType 'invalid_message' => 'password_must_match', 'required' => false, 'mapped' => false, - 'disabled' => !$this->security->isGranted('set_password', $entity), + 'disabled' => !$this->security->isGranted('set_password', $entity) || $entity->isSamlUser(), 'constraints' => [new Length([ 'min' => 6, 'max' => 128, @@ -174,7 +187,7 @@ class UserAdminForm extends AbstractType ->add('need_pw_change', CheckboxType::class, [ 'required' => false, 'label' => 'user.edit.needs_pw_change', - 'disabled' => !$this->security->isGranted('set_password', $entity), + 'disabled' => !$this->security->isGranted('set_password', $entity) || $entity->isSamlUser(), ]) ->add('disabled', CheckboxType::class, [ diff --git a/src/Form/UserSettingsType.php b/src/Form/UserSettingsType.php index fd07ea3b..05f63df4 100644 --- a/src/Form/UserSettingsType.php +++ b/src/Form/UserSettingsType.php @@ -22,12 +22,15 @@ declare(strict_types=1); namespace App\Form; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\UserSystem\User; use App\Form\Type\CurrencyEntityType; +use App\Form\Type\RichTextEditorType; use App\Form\Type\ThemeChoiceType; +use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Event\PreSetDataEvent; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +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; @@ -38,18 +41,14 @@ use Symfony\Component\Form\Extension\Core\Type\TimezoneType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; use Symfony\Component\Validator\Constraints\File; class UserSettingsType extends AbstractType { - protected Security $security; - protected bool $demo_mode; - - public function __construct(Security $security, bool $demo_mode) + public function __construct(protected Security $security, + protected bool $demo_mode, + #[Autowire(param: 'partdb.locale_menu')] private readonly array $preferred_languages) { - $this->security = $security; - $this->demo_mode = $demo_mode; } public function buildForm(FormBuilderInterface $builder, array $options): void @@ -57,7 +56,7 @@ class UserSettingsType extends AbstractType $builder ->add('name', TextType::class, [ 'label' => 'user.username.label', - 'disabled' => !$this->security->isGranted('edit_username', $options['data']) || $this->demo_mode, + 'disabled' => !$this->security->isGranted('edit_username', $options['data']) || $this->demo_mode || $options['data']->isSamlUser(), ]) ->add('first_name', TextType::class, [ 'required' => false, @@ -79,6 +78,11 @@ class UserSettingsType extends AbstractType 'label' => 'user.email.label', 'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode, ]) + ->add('showEmailOnProfile', CheckboxType::class, [ + 'required' => false, + 'label' => 'user.show_email_on_profile.label', + 'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode, + ]) ->add('avatar_file', FileType::class, [ 'label' => 'user_settings.change_avatar.label', 'mapped' => false, @@ -89,16 +93,26 @@ class UserSettingsType extends AbstractType ], 'constraints' => [ new File([ - 'maxSize' => '2M', + 'maxSize' => '5M', ]), ], ]) + ->add('aboutMe', RichTextEditorType::class, [ + 'required' => false, + 'empty_data' => '', + 'label' => 'user.aboutMe.label', + 'attr' => [ + 'rows' => 4, + ], + 'mode' => 'markdown-full', + 'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode, + ]) ->add('language', LanguageType::class, [ 'disabled' => $this->demo_mode, 'required' => false, 'placeholder' => 'user_settings.language.placeholder', 'label' => 'user.language_select', - 'preferred_choices' => ['en', 'de'], + 'preferred_choices' => $this->preferred_languages, ]) ->add('timezone', TimezoneType::class, [ 'disabled' => $this->demo_mode, diff --git a/src/Helpers/BBCodeToMarkdownConverter.php b/src/Helpers/BBCodeToMarkdownConverter.php index 9ee4dff1..922e6a7e 100644 --- a/src/Helpers/BBCodeToMarkdownConverter.php +++ b/src/Helpers/BBCodeToMarkdownConverter.php @@ -24,8 +24,10 @@ namespace App\Helpers; use League\HTMLToMarkdown\HtmlConverter; use s9e\TextFormatter\Bundles\Forum as TextFormatter; -use SebastianBergmann\CodeCoverage\Report\Text; +/** + * @see \App\Tests\Helpers\BBCodeToMarkdownConverterTest + */ class BBCodeToMarkdownConverter { protected HtmlConverter $html_to_markdown; diff --git a/src/Helpers/FilenameSanatizer.php b/src/Helpers/FilenameSanatizer.php new file mode 100644 index 00000000..f6744b1a --- /dev/null +++ b/src/Helpers/FilenameSanatizer.php @@ -0,0 +1,58 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Helpers; + +/** + * This class provides functions to sanitize filenames. + */ +class FilenameSanatizer +{ + /** + * Converts a given filename to a version, which is guaranteed to be safe to use on all filesystems. + * This function is adapted from https://stackoverflow.com/a/42058764/21879970 + * @param string $filename + * @return string + */ + public static function sanitizeFilename(string $filename): string + { + //Convert to ASCII + $filename = iconv('UTF-8', 'ASCII//TRANSLIT', $filename); + + $filename = preg_replace( + '~ + [<>:"/\\\|?*]| # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words + [\x00-\x1F]| # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx + [\x7F\xA0\xAD]| # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN + [#\[\]@!$&\'()+,;=]| # URI reserved https://www.rfc-editor.org/rfc/rfc3986#section-2.2 + [{}^\~`] # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt + ~x', + '-', $filename); + + // avoids ".", ".." or ".hiddenFiles" + $filename = ltrim((string) $filename, '.-'); + //Limit filename length to 255 bytes + $ext = pathinfo($filename, PATHINFO_EXTENSION); + return mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext !== '' && $ext !== '0' ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext !== '' && $ext !== '0' ? '.' . $ext : ''); + } +} \ No newline at end of file diff --git a/src/Helpers/IPAnonymizer.php b/src/Helpers/IPAnonymizer.php new file mode 100644 index 00000000..9662852f --- /dev/null +++ b/src/Helpers/IPAnonymizer.php @@ -0,0 +1,49 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Helpers; + +use Symfony\Component\HttpFoundation\IpUtils; + +/** + * Utils to assist with IP anonymization. + * The IPUtils::anonymize has a certain edgecase with local-link addresses, which is handled here. + * See: https://github.com/Part-DB/Part-DB-server/issues/782 + */ +final class IPAnonymizer +{ + public static function anonymize(string $ip): string + { + /** + * If the IP contains a % symbol, then it is a local-link address with scoping according to RFC 4007 + * In that case, we only care about the part before the % symbol, as the following functions, can only work with + * the IP address itself. As the scope can leak information (containing interface name), we do not want to + * include it in our anonymized IP data. + */ + if (str_contains($ip, '%')) { + $ip = substr($ip, 0, strpos($ip, '%')); + } + + return IpUtils::anonymize($ip); + } +} \ No newline at end of file diff --git a/src/Helpers/LabelResponse.php b/src/Helpers/LabelResponse.php index 1dbb947b..2973eb7e 100644 --- a/src/Helpers/LabelResponse.php +++ b/src/Helpers/LabelResponse.php @@ -54,7 +54,7 @@ class LabelResponse extends Response parent::__construct($content, $status, $headers); } - public function setContent($content): self + public function setContent($content): static { parent::setContent($content); @@ -64,7 +64,7 @@ class LabelResponse extends Response return $this; } - public function prepare(Request $request): self + public function prepare(Request $request): static { parent::prepare($request); @@ -84,7 +84,7 @@ class LabelResponse extends Response */ public function setAutoLastModified(): LabelResponse { - $this->setLastModified(new DateTime()); + $this->setLastModified(new \DateTimeImmutable()); return $this; } @@ -110,7 +110,7 @@ class LabelResponse extends Response */ public function setContentDisposition(string $disposition, string $filename, string $filenameFallback = ''): self { - if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) { + if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || str_contains($filename, '%'))) { $encoding = mb_detect_encoding($filename, null, true) ?: '8bit'; for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) { diff --git a/src/Helpers/Projects/ProjectBuildRequest.php b/src/Helpers/Projects/ProjectBuildRequest.php index 093581f4..430d37b5 100644 --- a/src/Helpers/Projects/ProjectBuildRequest.php +++ b/src/Helpers/Projects/ProjectBuildRequest.php @@ -1,4 +1,7 @@ . */ - namespace App\Helpers\Projects; +use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; use App\Validator\Constraints\ProjectSystem\ValidProjectBuildRequest; /** - * @ValidProjectBuildRequest() + * @see \App\Tests\Helpers\Projects\ProjectBuildRequestTest */ +#[ValidProjectBuildRequest] final class ProjectBuildRequest { - private Project $project; - private int $number_of_builds; + private readonly int $number_of_builds; /** * @var array @@ -44,23 +47,23 @@ final class ProjectBuildRequest private bool $add_build_to_builds_part = false; + private bool $dont_check_quantity = false; + /** * @param Project $project The project that should be build * @param int $number_of_builds The number of builds that should be created */ - public function __construct(Project $project, int $number_of_builds) + public function __construct(private readonly Project $project, int $number_of_builds) { if ($number_of_builds < 1) { throw new \InvalidArgumentException('Number of builds must be at least 1!'); } - - $this->project = $project; $this->number_of_builds = $number_of_builds; $this->initializeArray(); //By default, use the first available lot of builds part if there is one. - if($project->getBuildPart() !== null) { + if($project->getBuildPart() instanceof Part) { $this->add_build_to_builds_part = true; foreach( $project->getBuildPart()->getPartLots() as $lot) { if (!$lot->isInstockUnknown()) { @@ -89,8 +92,6 @@ final class ProjectBuildRequest /** * Ensure that the projectBOMEntry belongs to the project, otherwise throw an exception. - * @param ProjectBOMEntry $entry - * @return void */ private function ensureBOMEntryValid(ProjectBOMEntry $entry): void { @@ -101,7 +102,6 @@ final class ProjectBuildRequest /** * Returns the partlot where the builds should be added to, or null if it should not be added to any lot. - * @return PartLot|null */ public function getBuildsPartLot(): ?PartLot { @@ -110,7 +110,6 @@ final class ProjectBuildRequest /** * Return if the builds should be added to the builds part of this project as new stock - * @return bool */ public function getAddBuildsToBuildsPart(): bool { @@ -119,7 +118,6 @@ final class ProjectBuildRequest /** * Set if the builds should be added to the builds part of this project as new stock - * @param bool $new_value * @return $this */ public function setAddBuildsToBuildsPart(bool $new_value): self @@ -136,17 +134,16 @@ final class ProjectBuildRequest /** * Set the partlot where the builds should be added to, or null if it should not be added to any lot. * The part lot must belong to the project build part, or an exception is thrown! - * @param PartLot|null $new_part_lot * @return $this */ public function setBuildsPartLot(?PartLot $new_part_lot): self { //Ensure that this new_part_lot belongs to the project - if (($new_part_lot !== null && $new_part_lot->getPart() !== $this->project->getBuildPart()) || $this->project->getBuildPart() === null) { + if (($new_part_lot instanceof PartLot && $new_part_lot->getPart() !== $this->project->getBuildPart()) || !$this->project->getBuildPart() instanceof Part) { throw new \InvalidArgumentException('The given part lot does not belong to the projects build part!'); } - if ($new_part_lot !== null) { + if ($new_part_lot instanceof PartLot) { $this->setAddBuildsToBuildsPart(true); } @@ -157,7 +154,6 @@ final class ProjectBuildRequest /** * Returns the comment where the user can write additional information about the build. - * @return string */ public function getComment(): string { @@ -166,7 +162,6 @@ final class ProjectBuildRequest /** * Sets the comment where the user can write additional information about the build. - * @param string $comment */ public function setComment(string $comment): void { @@ -175,18 +170,11 @@ final class ProjectBuildRequest /** * Returns the amount of parts that should be withdrawn from the given lot for the corresponding BOM entry. - * @param PartLot|int $lot The part lot (or the ID of the part lot) for which the withdraw amount should be get - * @return float + * @param PartLot|int $lot The part lot (or the ID of the part lot) for which the withdrawal amount should be got */ - public function getLotWithdrawAmount($lot): float + public function getLotWithdrawAmount(PartLot|int $lot): float { - if ($lot instanceof PartLot) { - $lot_id = $lot->getID(); - } elseif (is_int($lot)) { - $lot_id = $lot; - } else { - throw new \InvalidArgumentException('The given lot must be an instance of PartLot or an ID of a PartLot!'); - } + $lot_id = $lot instanceof PartLot ? $lot->getID() : $lot; if (! array_key_exists($lot_id, $this->withdraw_amounts)) { throw new \InvalidArgumentException('The given lot is not in the withdraw amounts array!'); @@ -197,11 +185,10 @@ final class ProjectBuildRequest /** * Sets the amount of parts that should be withdrawn from the given lot for the corresponding BOM entry. - * @param PartLot|int $lot The part lot (or the ID of the part lot) for which the withdraw amount should be get - * @param float $amount + * @param PartLot|int $lot The part lot (or the ID of the part lot) for which the withdrawal amount should be got * @return $this */ - public function setLotWithdrawAmount($lot, float $amount): self + public function setLotWithdrawAmount(PartLot|int $lot, float $amount): self { if ($lot instanceof PartLot) { $lot_id = $lot->getID(); @@ -218,8 +205,6 @@ final class ProjectBuildRequest /** * Returns the sum of all withdraw amounts for the given BOM entry. - * @param ProjectBOMEntry $entry - * @return float */ public function getWithdrawAmountSum(ProjectBOMEntry $entry): float { @@ -239,14 +224,13 @@ final class ProjectBuildRequest /** * Returns the number of available lots to take stock from for the given BOM entry. - * @param ProjectBOMEntry $projectBOMEntry * @return PartLot[]|null Returns null if the entry is a non-part BOM entry */ public function getPartLotsForBOMEntry(ProjectBOMEntry $projectBOMEntry): ?array { $this->ensureBOMEntryValid($projectBOMEntry); - if ($projectBOMEntry->getPart() === null) { + if (!$projectBOMEntry->getPart() instanceof Part) { return null; } @@ -256,8 +240,6 @@ final class ProjectBuildRequest /** * Returns the needed amount of parts for the given BOM entry. - * @param ProjectBOMEntry $entry - * @return float */ public function getNeededAmountForBOMEntry(ProjectBOMEntry $entry): float { @@ -267,7 +249,7 @@ final class ProjectBuildRequest } /** - * Returns the list of all bom entries that have to be build. + * Returns the list of all bom entries that have to be built. * @return ProjectBOMEntry[] */ public function getBomEntries(): array @@ -276,19 +258,16 @@ final class ProjectBuildRequest } /** - * Returns the all part bom entries that have to be build. + * Returns the all part bom entries that have to be built. * @return ProjectBOMEntry[] */ public function getPartBomEntries(): array { - return $this->project->getBomEntries()->filter(function (ProjectBOMEntry $entry) { - return $entry->isPartBomEntry(); - })->toArray(); + return $this->project->getBomEntries()->filter(fn(ProjectBOMEntry $entry) => $entry->isPartBomEntry())->toArray(); } /** * Returns which project should be build - * @return Project */ public function getProject(): Project { @@ -297,10 +276,31 @@ final class ProjectBuildRequest /** * Returns the number of builds that should be created. - * @return int */ public function getNumberOfBuilds(): int { return $this->number_of_builds; } -} \ No newline at end of file + + /** + * If Set to true, the given withdraw amounts are used without any checks for requirements. + * @return bool + */ + public function isDontCheckQuantity(): bool + { + return $this->dont_check_quantity; + } + + /** + * Set to true, the given withdraw amounts are used without any checks for requirements. + * @param bool $dont_check_quantity + * @return $this + */ + public function setDontCheckQuantity(bool $dont_check_quantity): ProjectBuildRequest + { + $this->dont_check_quantity = $dont_check_quantity; + return $this; + } + + +} diff --git a/src/Helpers/Trees/TreeViewNode.php b/src/Helpers/Trees/TreeViewNode.php index 89e4a02e..0c5fcdce 100644 --- a/src/Helpers/Trees/TreeViewNode.php +++ b/src/Helpers/Trees/TreeViewNode.php @@ -30,17 +30,13 @@ use JsonSerializable; */ final class TreeViewNode implements JsonSerializable { - private $text; - private $href; - private $nodes; + private ?TreeViewNodeState $state = null; - private $state = null; + private ?array $tags = null; - private $tags; + private ?int $id = null; - private $id; - - private $icon; + private ?string $icon = null; /** * Creates a new TreeView node with the given parameters. @@ -51,12 +47,8 @@ final class TreeViewNode implements JsonSerializable * @param array|null $nodes An array containing other TreeViewNodes. They will be used as children nodes of the * newly created nodes. Set to null, if it should not have children. */ - public function __construct(string $text, ?string $href = null, ?array $nodes = null) + public function __construct(private string $text, private ?string $href = null, private ?array $nodes = null) { - $this->text = $text; - $this->href = $href; - $this->nodes = $nodes; - //$this->state = new TreeViewNodeState(); } @@ -94,8 +86,6 @@ final class TreeViewNode implements JsonSerializable * Sets the node text. * * @param string $text the new node text - * - * @return TreeViewNode */ public function setText(string $text): self { @@ -116,8 +106,6 @@ final class TreeViewNode implements JsonSerializable * Sets the href link. * * @param string|null $href the new href link - * - * @return TreeViewNode */ public function setHref(?string $href): self { @@ -140,8 +128,6 @@ final class TreeViewNode implements JsonSerializable * Sets the children nodes. * * @param array|null $nodes The new children nodes - * - * @return TreeViewNode */ public function setNodes(?array $nodes): self { @@ -165,7 +151,7 @@ final class TreeViewNode implements JsonSerializable public function setDisabled(?bool $disabled): self { //Lazy loading of state, so it does not need to get serialized and transfered, when it is empty. - if (null === $this->state) { + if (!$this->state instanceof TreeViewNodeState) { $this->state = new TreeViewNodeState(); } @@ -177,7 +163,7 @@ final class TreeViewNode implements JsonSerializable public function setSelected(?bool $selected): self { //Lazy loading of state, so it does not need to get serialized and transfered, when it is empty. - if (null === $this->state) { + if (!$this->state instanceof TreeViewNodeState) { $this->state = new TreeViewNodeState(); } @@ -189,7 +175,7 @@ final class TreeViewNode implements JsonSerializable public function setExpanded(?bool $selected = true): self { //Lazy loading of state, so it does not need to get serialized and transfered, when it is empty. - if (null === $this->state) { + if (!$this->state instanceof TreeViewNodeState) { $this->state = new TreeViewNodeState(); } @@ -215,17 +201,11 @@ final class TreeViewNode implements JsonSerializable return $this; } - /** - * @return string|null - */ public function getIcon(): ?string { return $this->icon; } - /** - * @param string|null $icon - */ public function setIcon(?string $icon): self { $this->icon = $icon; @@ -252,7 +232,7 @@ final class TreeViewNode implements JsonSerializable $ret['nodes'] = $this->nodes; } - if (null !== $this->state) { + if ($this->state instanceof TreeViewNodeState) { $ret['state'] = $this->state; } diff --git a/src/Helpers/Trees/TreeViewNodeIterator.php b/src/Helpers/Trees/TreeViewNodeIterator.php index 073218c0..ab8b4907 100644 --- a/src/Helpers/Trees/TreeViewNodeIterator.php +++ b/src/Helpers/Trees/TreeViewNodeIterator.php @@ -40,7 +40,7 @@ final class TreeViewNodeIterator extends ArrayIterator implements RecursiveItera /** @var TreeViewNode $element */ $element = $this->current(); - return !empty($element->getNodes()); + return $element->getNodes() !== null && $element->getNodes() !== []; } public function getChildren(): TreeViewNodeIterator diff --git a/src/Helpers/Trees/TreeViewNodeState.php b/src/Helpers/Trees/TreeViewNodeState.php index 92b4611c..727135bc 100644 --- a/src/Helpers/Trees/TreeViewNodeState.php +++ b/src/Helpers/Trees/TreeViewNodeState.php @@ -29,17 +29,17 @@ final class TreeViewNodeState implements JsonSerializable /** * @var bool|null */ - private $disabled = null; + private ?bool $disabled = null; /** * @var bool|null */ - private $expanded = null; + private ?bool $expanded = null; /** * @var bool|null */ - private $selected = null; + private ?bool $selected = null; public function getDisabled(): ?bool { diff --git a/src/Helpers/TrinaryLogicHelper.php b/src/Helpers/TrinaryLogicHelper.php new file mode 100644 index 00000000..f4b460de --- /dev/null +++ b/src/Helpers/TrinaryLogicHelper.php @@ -0,0 +1,107 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Helpers; + +/** + * Helper functions for logic operations with trinary logic. + * True and false are represented as classical boolean values, undefined is represented as null. + * @see \App\Tests\Helpers\TrinaryLogicHelperTest + */ +class TrinaryLogicHelper +{ + + /** + * Implements the trinary logic NOT. + * @param bool|null $a + * @return bool|null + */ + public static function not(?bool $a): ?bool + { + if ($a === null) { + return null; + } + return !$a; + } + + + /** + * Returns the trinary logic OR of the given parameters. At least one parameter is required. + * @param bool|null ...$args + * @return bool|null + */ + public static function or(?bool ...$args): ?bool + { + if (count($args) === 0) { + throw new \LogicException('At least one parameter is required.'); + } + + // The trinary or is the maximum of the integer representation of the parameters. + return self::intToBool( + max(array_map(self::boolToInt(...), $args)) + ); + } + + /** + * Returns the trinary logic AND of the given parameters. At least one parameter is required. + * @param bool|null ...$args + * @return bool|null + */ + public static function and(?bool ...$args): ?bool + { + if (count($args) === 0) { + throw new \LogicException('At least one parameter is required.'); + } + + // The trinary and is the minimum of the integer representation of the parameters. + return self::intToBool( + min(array_map(self::boolToInt(...), $args)) + ); + } + + /** + * Convert the trinary bool to an integer, where true is 1, false is -1 and null is 0. + * @param bool|null $a + * @return int + */ + private static function boolToInt(?bool $a): int + { + if ($a === null) { + return 0; + } + return $a ? 1 : -1; + } + + /** + * Convert the integer to a trinary bool, where 1 is true, -1 is false and 0 is null. + * @param int $a + * @return bool|null + */ + private static function intToBool(int $a): ?bool + { + if ($a === 0) { + return null; + } + return $a > 0; + } +} \ No newline at end of file diff --git a/src/Kernel.php b/src/Kernel.php index a406b6c8..97c7a69e 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -1,4 +1,7 @@ . */ - namespace App; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; diff --git a/src/Migration/AbstractMultiPlatformMigration.php b/src/Migration/AbstractMultiPlatformMigration.php index 12d4cf87..bc2b3f19 100644 --- a/src/Migration/AbstractMultiPlatformMigration.php +++ b/src/Migration/AbstractMultiPlatformMigration.php @@ -1,4 +1,7 @@ . */ - namespace App\Migration; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Driver\AbstractMySQLDriver; -use Doctrine\DBAL\Driver\AbstractSQLiteDriver; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; -use Doctrine\DBAL\Platforms\MariaDBPlatform; -use Doctrine\DBAL\Platforms\MySQLPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; use Psr\Log\LoggerInterface; abstract class AbstractMultiPlatformMigration extends AbstractMigration { - public const ADMIN_PW_LENGTH = 10; - - protected bool $permissions_updated = false; + final public const ADMIN_PW_LENGTH = 10; protected string $admin_pw = ''; - protected LoggerInterface $logger; - - public function __construct(Connection $connection, LoggerInterface $logger) + /** @noinspection SenselessProxyMethodInspection + * This method is required to redefine the logger type hint to protected + */ + public function __construct(Connection $connection, protected LoggerInterface $logger) { - $this->logger = $logger; - AbstractMigration::__construct($connection, $logger); + parent::__construct($connection, $logger); } public function up(Schema $schema): void { $db_type = $this->getDatabaseType(); - switch ($db_type) { - case 'mysql': - $this->mySQLUp($schema); - break; - case 'sqlite': - $this->sqLiteUp($schema); - break; - default: - $this->abortIf(true, "Database type '$db_type' is not supported!"); - break; - } + match ($db_type) { + 'mysql' => $this->mySQLUp($schema), + 'sqlite' => $this->sqLiteUp($schema), + 'postgresql' => $this->postgreSQLUp($schema), + default => $this->abortIf(true, "Database type '$db_type' is not supported!"), + }; } public function down(Schema $schema): void { $db_type = $this->getDatabaseType(); - switch ($db_type) { - case 'mysql': - $this->mySQLDown($schema); - break; - case 'sqlite': - $this->sqLiteDown($schema); - break; - default: - $this->abortIf(true, "Database type is not supported!"); - break; - } + match ($db_type) { + 'mysql' => $this->mySQLDown($schema), + 'sqlite' => $this->sqLiteDown($schema), + 'postgresql' => $this->postgreSQLDown($schema), + default => $this->abortIf(true, "Database type is not supported!"), + }; } /** - * Gets the legacy Part-DB version number. Returns 0, if target database is not an legacy Part-DB database. + * Gets the legacy Part-DB version number. Returns 0, if target database is not a legacy Part-DB database. */ public function getOldDBVersion(): int { @@ -98,7 +84,7 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration return 0; } return (int) $version; - } catch (Exception $dBALException) { + } catch (Exception) { //when the table was not found, we can proceed, because we have an empty DB! return 0; } @@ -110,7 +96,7 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration */ public function getInitalAdminPW(): string { - if (empty($this->admin_pw)) { + if ($this->admin_pw === '') { if (!empty($_ENV['INITIAL_ADMIN_PW'])) { $this->admin_pw = $_ENV['INITIAL_ADMIN_PW']; } else { @@ -118,29 +104,58 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration } } - //As we dont have access to container, just use the default PHP pw hash function - return password_hash($this->admin_pw, PASSWORD_DEFAULT); - } - - public function printPermissionUpdateMessage(): void - { - $this->permissions_updated = true; + //As we don't have access to container, just use the default PHP pw hash function + return password_hash((string) $this->admin_pw, PASSWORD_DEFAULT); } public function postUp(Schema $schema): void { parent::postUp($schema); - if($this->permissions_updated) { - $this->logger->warning('[!!!] Permissions were updated! Please check if they fit your expectations!'); - } - if (!empty($this->admin_pw)) { + if ($this->admin_pw !== '') { $this->logger->warning(''); $this->logger->warning('The initial password for the "admin" user is: '.$this->admin_pw.''); $this->logger->warning(''); } } + /** + * Checks if a foreign key on a table exists in the database. + * This method is only supported for MySQL/MariaDB databases yet! + * @return bool Returns true, if the foreign key exists + * @throws Exception + */ + public function doesFKExists(string $table, string $fk_name): bool + { + $db_type = $this->getDatabaseType(); + if ($db_type !== 'mysql') { + throw new \RuntimeException('This method is only supported for MySQL/MariaDB databases!'); + } + + $sql = "SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = '$fk_name' AND TABLE_NAME = '$table' AND CONSTRAINT_TYPE = 'FOREIGN KEY'"; + $result = (int) $this->connection->fetchOne($sql); + + return $result > 0; + } + + /** + * Checks if a column exists in a table. + * @return bool Returns true, if the column exists + * @throws Exception + */ + public function doesColumnExist(string $table, string $column_name): bool + { + $db_type = $this->getDatabaseType(); + if ($db_type !== 'mysql') { + throw new \RuntimeException('This method is only supported for MySQL/MariaDB databases!'); + } + + $sql = "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '$table' AND COLUMN_NAME = '$column_name'"; + $result = (int) $this->connection->fetchOne($sql); + + return $result > 0; + } + /** * Returns the database type of the used database. * @return string|null Returns 'mysql' for MySQL/MariaDB and 'sqlite' for SQLite. Returns null if unknown type @@ -151,10 +166,14 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration return 'mysql'; } - if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { return 'sqlite'; } + if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + return 'postgresql'; + } + return null; } @@ -165,4 +184,8 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration abstract public function sqLiteUp(Schema $schema): void; abstract public function sqLiteDown(Schema $schema): void; + + abstract public function postgreSQLUp(Schema $schema): void; + + abstract public function postgreSQLDown(Schema $schema): void; } diff --git a/src/Migration/WithPermPresetsTrait.php b/src/Migration/WithPermPresetsTrait.php new file mode 100644 index 00000000..44bc4510 --- /dev/null +++ b/src/Migration/WithPermPresetsTrait.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Migration; + +use App\Entity\UserSystem\PermissionData; +use App\Security\Interfaces\HasPermissionsInterface; +use App\Services\UserSystem\PermissionPresetsHelper; +use Symfony\Component\DependencyInjection\ContainerInterface; + +trait WithPermPresetsTrait +{ + private ?ContainerInterface $container = null; + private ?PermissionPresetsHelper $permission_presets_helper = null; + + private function getJSONPermDataFromPreset(string $preset): string + { + if ($this->permission_presets_helper === null) { + throw new \RuntimeException('PermissionPresetsHelper not set! There seems to be some issue with the dependency injection!'); + } + + //Create a virtual user on which we can apply the preset + $user = new class implements HasPermissionsInterface { + + public PermissionData $perm_data; + + public function __construct() + { + $this->perm_data = new PermissionData(); + } + + public function getPermissions(): PermissionData + { + return $this->perm_data; + } + }; + + //Apply the preset to the virtual user + $this->permission_presets_helper->applyPreset($user, $preset); + + //And return the json data + return json_encode($user->getPermissions()); + } + + public function setContainer(?ContainerInterface $container = null): void + { + if ($container !== null) { + $this->container = $container; + $this->permission_presets_helper = $container->get(PermissionPresetsHelper::class); + } + } +} \ No newline at end of file diff --git a/src/Repository/AbstractPartsContainingRepository.php b/src/Repository/AbstractPartsContainingRepository.php index eab2c26c..4fd0bbff 100644 --- a/src/Repository/AbstractPartsContainingRepository.php +++ b/src/Repository/AbstractPartsContainingRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository; use App\Entity\Base\AbstractPartsContainingDBElement; @@ -25,17 +27,24 @@ use App\Entity\Base\PartsContainingRepositoryInterface; use App\Entity\Parts\Part; use InvalidArgumentException; +/** + * @template TEntityClass of AbstractPartsContainingDBElement + * @extends StructuralDBElementRepository + */ abstract class AbstractPartsContainingRepository extends StructuralDBElementRepository implements PartsContainingRepositoryInterface { + /** @var int The maximum number of levels for which we can recurse before throwing an error */ + private const RECURSION_LIMIT = 50; + /** * Returns all parts associated with this element. * * @param object $element the element for which the parts should be determined - * @param array $order_by The order of the parts. Format ['name' => 'ASC'] + * @param string $nameOrderDirection the direction in which the parts should be ordered by name, either ASC or DESC * * @return Part[] */ - abstract public function getParts(object $element, array $order_by = ['name' => 'ASC']): array; + abstract public function getParts(object $element, string $nameOrderDirection = "ASC"): array; /** * Gets the count of the parts associated with this element. @@ -48,21 +57,63 @@ abstract class AbstractPartsContainingRepository extends StructuralDBElementRepo /** * Returns the count of the parts associated with this element and all its children. * Please be aware that this function is pretty slow on large trees! - * @param AbstractPartsContainingDBElement $element * @return int */ public function getPartsCountRecursive(AbstractPartsContainingDBElement $element): int { + return $this->getPartsCountRecursiveWithDepthN($element, self::RECURSION_LIMIT); + } + + public function getPartsRecursive(AbstractPartsContainingDBElement $element): array + { + return $this->getPartsRecursiveWithDepthN($element, self::RECURSION_LIMIT); + } + + /** + * The implementation of the recursive function to get the parts count. + * This function is used to limit the recursion depth (remaining_depth is decreased on each call). + * If the recursion limit is reached (remaining_depth <= 0), a RuntimeException is thrown. + * @internal This function is not intended to be called directly, use getPartsCountRecursive() instead. + * @return int + */ + protected function getPartsCountRecursiveWithDepthN(AbstractPartsContainingDBElement $element, int $remaining_depth): int + { + if ($remaining_depth <= 0) { + throw new \RuntimeException('Recursion limit reached!'); + } + $count = $this->getPartsCount($element); + //If the element is its own parent, we have a loop in the tree, so we stop here. + if ($element->getParent() === $element) { + return 0; + } + foreach ($element->getChildren() as $child) { - $count += $this->getPartsCountRecursive($child); + $count += $this->getPartsCountRecursiveWithDepthN($child, $remaining_depth - 1); } return $count; } - protected function getPartsByField(object $element, array $order_by, string $field_name): array + protected function getPartsRecursiveWithDepthN(AbstractPartsContainingDBElement $element, int $remaining_depth): array + { + if ($remaining_depth <= 0) { + throw new \RuntimeException('Recursion limit reached!'); + } + + //Add direct parts + $parts = $this->getParts($element); + + //Then iterate over all children and add their parts + foreach ($element->getChildren() as $child) { + $parts = array_merge($parts, $this->getPartsRecursiveWithDepthN($child, $remaining_depth - 1)); + } + + return $parts; + } + + protected function getPartsByField(object $element, string $nameOrderDirection, string $field_name): array { if (!$element instanceof AbstractPartsContainingDBElement) { throw new InvalidArgumentException('$element must be an instance of AbstractPartContainingDBElement!'); @@ -70,7 +121,14 @@ abstract class AbstractPartsContainingRepository extends StructuralDBElementRepo $repo = $this->getEntityManager()->getRepository(Part::class); - return $repo->findBy([$field_name => $element], $order_by); + //Build a query builder to get the parts with a custom order by + + $qb = $repo->createQueryBuilder('part') + ->where('part.'.$field_name.' = :element') + ->setParameter('element', $element) + ->orderBy('NATSORT(part.name)', $nameOrderDirection); + + return $qb->getQuery()->getResult(); } protected function getPartsCountByField(object $element, string $field_name): int diff --git a/src/Repository/AttachmentContainingDBElementRepository.php b/src/Repository/AttachmentContainingDBElementRepository.php new file mode 100644 index 00000000..40869662 --- /dev/null +++ b/src/Repository/AttachmentContainingDBElementRepository.php @@ -0,0 +1,83 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Repository; + +use App\Doctrine\Helpers\FieldHelper; +use App\Entity\Attachments\AttachmentContainingDBElement; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * @template TEntityClass of AttachmentContainingDBElement + * @extends NamedDBElementRepository + * @see \App\Tests\Repository\AttachmentContainingDBElementRepositoryTest + */ +class AttachmentContainingDBElementRepository extends NamedDBElementRepository +{ + /** + * @var array This array is used to cache the results of getElementsAndPreviewAttachmentByIDs function. + */ + private array $elementsAndPreviewAttachmentCache = []; + + /** + * Similar to the findByIDInMatchingOrder function, but it also hints to doctrine that the master picture attachment should be fetched eagerly. + * @param array $ids + * @return array + * @phpstan-return array + */ + public function getElementsAndPreviewAttachmentByIDs(array $ids): array + { + //If no IDs are given, return an empty array + if (count($ids) === 0) { + return []; + } + + //Convert the ids to a string + $cache_key = implode(',', $ids); + + //Check if the result is already cached + if (isset($this->elementsAndPreviewAttachmentCache[$cache_key])) { + return $this->elementsAndPreviewAttachmentCache[$cache_key]; + } + + $qb = $this->createQueryBuilder('element') + ->select('element') + ->where('element.id IN (?1)') + //Order the results in the same order as the IDs in the input array (mysql supports this native, for SQLite we emulate it) + ->setParameter(1, $ids); + + //Order the results in the same order as the IDs in the input array + FieldHelper::addOrderByFieldParam($qb, 'element.id', 1); + + $q = $qb->getQuery(); + + $q->setFetchMode($this->getEntityName(), 'master_picture_attachment', ClassMetadata::FETCH_EAGER); + + $result = $q->getResult(); + + //Cache the result + $this->elementsAndPreviewAttachmentCache[$cache_key] = $result; + + return $result; + } +} \ No newline at end of file diff --git a/src/Repository/AttachmentRepository.php b/src/Repository/AttachmentRepository.php index 9ad9191b..4fc0abc9 100644 --- a/src/Repository/AttachmentRepository.php +++ b/src/Repository/AttachmentRepository.php @@ -41,9 +41,14 @@ declare(strict_types=1); namespace App\Repository; +use App\Entity\Attachments\Attachment; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\NoResultException; +/** + * @template TEntityClass of Attachment + * @extends DBElementRepository + */ class AttachmentRepository extends DBElementRepository { /** @@ -53,15 +58,15 @@ class AttachmentRepository extends DBElementRepository { $qb = $this->createQueryBuilder('attachment'); $qb->select('COUNT(attachment)') - ->where('attachment.path LIKE :like'); - $qb->setParameter('like', '\\%SECURE\\%%'); + ->where('attachment.internal_path LIKE :like ESCAPE \'#\''); + $qb->setParameter('like', '#%SECURE#%%'); $query = $qb->getQuery(); return (int) $query->getSingleScalarResult(); } /** - * Gets the count of all external attachments (attachments only containing an URL). + * Gets the count of all external attachments (attachments containing only an external path). * * @throws NoResultException * @throws NonUniqueResultException @@ -70,17 +75,16 @@ class AttachmentRepository extends DBElementRepository { $qb = $this->createQueryBuilder('attachment'); $qb->select('COUNT(attachment)') - ->where('attachment.path LIKE :http') - ->orWhere('attachment.path LIKE :https'); - $qb->setParameter('http', 'http://%'); - $qb->setParameter('https', 'https://%'); + ->where('attachment.external_path IS NOT NULL') + ->andWhere('attachment.internal_path IS NULL'); + $query = $qb->getQuery(); return (int) $query->getSingleScalarResult(); } /** - * Gets the count of all attachments where an user uploaded an file. + * Gets the count of all attachments where a user uploaded a file or a file was downloaded from an external source. * * @throws NoResultException * @throws NonUniqueResultException @@ -89,12 +93,12 @@ class AttachmentRepository extends DBElementRepository { $qb = $this->createQueryBuilder('attachment'); $qb->select('COUNT(attachment)') - ->where('attachment.path LIKE :base') - ->orWhere('attachment.path LIKE :media') - ->orWhere('attachment.path LIKE :secure'); - $qb->setParameter('secure', '\\%SECURE\\%%'); - $qb->setParameter('base', '\\%BASE\\%%'); - $qb->setParameter('media', '\\%MEDIA\\%%'); + ->where('attachment.internal_path LIKE :base ESCAPE \'#\'') + ->orWhere('attachment.internal_path LIKE :media ESCAPE \'#\'') + ->orWhere('attachment.internal_path LIKE :secure ESCAPE \'#\''); + $qb->setParameter('secure', '#%SECURE#%%'); + $qb->setParameter('base', '#%BASE#%%'); + $qb->setParameter('media', '#%MEDIA#%%'); $query = $qb->getQuery(); return (int) $query->getSingleScalarResult(); diff --git a/src/Repository/CurrencyRepository.php b/src/Repository/CurrencyRepository.php new file mode 100644 index 00000000..63473229 --- /dev/null +++ b/src/Repository/CurrencyRepository.php @@ -0,0 +1,58 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Repository; + +use App\Entity\PriceInformations\Currency; +use Symfony\Component\Intl\Currencies; + +/** + * @extends StructuralDBElementRepository + */ +class CurrencyRepository extends StructuralDBElementRepository +{ + /** + * Finds or create a currency with the given ISO code. + * @param string $iso_code + * @return Currency + */ + public function findOrCreateByISOCode(string $iso_code): Currency + { + //Normalize ISO code + $iso_code = strtoupper($iso_code); + + //Try to find currency + $currency = $this->findOneBy(['iso_code' => $iso_code]); + if ($currency !== null) { + return $currency; + } + + //Create currency if it does not exist + $name = Currencies::getName($iso_code); + + $currency = $this->findOrCreateForInfoProvider($name); + $currency->setIsoCode($iso_code); + + return $currency; + } +} \ No newline at end of file diff --git a/src/Repository/DBElementRepository.php b/src/Repository/DBElementRepository.php index 0f7024b6..2437e848 100644 --- a/src/Repository/DBElementRepository.php +++ b/src/Repository/DBElementRepository.php @@ -41,23 +41,32 @@ declare(strict_types=1); namespace App\Repository; +use App\Doctrine\Helpers\FieldHelper; use App\Entity\Base\AbstractDBElement; use Doctrine\ORM\EntityRepository; use ReflectionClass; +/** + * @template TEntityClass of AbstractDBElement + * @extends EntityRepository + * @see \App\Tests\Repository\DBElementRepositoryTest + */ class DBElementRepository extends EntityRepository { + private array $find_elements_by_id_cache = []; + /** * Changes the ID of the given element to a new value. * You should only use it to undelete former existing elements, everything else is most likely a bad idea! * * @param AbstractDBElement $element The element whose ID should be changed - * @param int $new_id The new ID + * @phpstan-param TEntityClass $element + * @param int $new_id The new ID */ public function changeID(AbstractDBElement $element, int $new_id): void { $qb = $this->createQueryBuilder('element'); - $q = $qb->update(get_class($element), 'element') + $q = $qb->update($element::class, 'element') ->set('element.id', $new_id) ->where('element.id = ?1') ->setParameter(1, $element->getID()) @@ -71,8 +80,9 @@ class DBElementRepository extends EntityRepository /** * Find all elements that match a list of IDs. - * + * They are ordered by IDs in an ascending order. * @return AbstractDBElement[] + * @phpstan-return list */ public function getElementsFromIDArray(array $ids): array { @@ -80,14 +90,66 @@ class DBElementRepository extends EntityRepository $q = $qb->select('element') ->where('element.id IN (?1)') ->setParameter(1, $ids) + ->orderBy('element.id', 'ASC') ->getQuery(); return $q->getResult(); } + /** + * Returns the elements with the given IDs in the same order, as they were given in the input array. + * + * @param array $ids + * @return array + */ + public function findByIDInMatchingOrder(array $ids): array + { + //If no IDs are given, return an empty array + if (count($ids) === 0) { + return []; + } + + $cache_key = implode(',', $ids); + + //Check if the result is already cached + if (isset($this->find_elements_by_id_cache[$cache_key])) { + return $this->find_elements_by_id_cache[$cache_key]; + } + + //Otherwise do the query + $qb = $this->createQueryBuilder('element'); + $qb->select('element') + ->where('element.id IN (?1)') + ->setParameter(1, $ids); + + //Order the results in the same order as the IDs in the input array + FieldHelper::addOrderByFieldParam($qb, 'element.id', 1); + + $q = $qb->getQuery(); + + $result = $q->getResult(); + + //Cache the result + $this->find_elements_by_id_cache[$cache_key] = $result; + + return $result; + } + + /** + * The elements in the result array will be sorted, so that their order of their IDs matches the order of the IDs in the input array. + * @param array $result_array + * @phpstan-param list $result_array + * @param int[] $ids + * @return void + */ + protected function sortResultArrayByIDArray(array &$result_array, array $ids): void + { + usort($result_array, static fn(AbstractDBElement $a, AbstractDBElement $b) => array_search($a->getID(), $ids, true) <=> array_search($b->getID(), $ids, true)); + } + protected function setField(AbstractDBElement $element, string $field, int $new_value): void { - $reflection = new ReflectionClass(get_class($element)); + $reflection = new ReflectionClass($element::class); $property = $reflection->getProperty($field); $property->setAccessible(true); $property->setValue($element, $new_value); diff --git a/src/Repository/LabelProfileRepository.php b/src/Repository/LabelProfileRepository.php index 76372f34..ad9e40f1 100644 --- a/src/Repository/LabelProfileRepository.php +++ b/src/Repository/LabelProfileRepository.php @@ -43,21 +43,21 @@ namespace App\Repository; use App\Entity\LabelSystem\LabelOptions; use App\Entity\LabelSystem\LabelProfile; +use App\Entity\LabelSystem\LabelSupportedElement; use App\Helpers\Trees\TreeViewNode; -use InvalidArgumentException; +/** + * @template TEntityClass of LabelProfile + * @extends NamedDBElementRepository + */ class LabelProfileRepository extends NamedDBElementRepository { /** * Find the profiles that are shown in the dropdown for the given type. * You should maybe use the cached version of this in LabelProfileDropdownHelper. */ - public function getDropdownProfiles(string $type): array + public function getDropdownProfiles(LabelSupportedElement $type): array { - if (!in_array($type, LabelOptions::SUPPORTED_ELEMENTS, true)) { - throw new InvalidArgumentException('Invalid supported_element type given.'); - } - return $this->findBy([ 'options.supported_element' => $type, 'show_in_dropdown' => true, @@ -74,7 +74,7 @@ class LabelProfileRepository extends NamedDBElementRepository { $result = []; - foreach (LabelOptions::SUPPORTED_ELEMENTS as $type) { + foreach (LabelSupportedElement::cases() as $type) { $type_children = []; $entities = $this->findForSupportedElement($type); foreach ($entities as $entity) { @@ -84,9 +84,9 @@ class LabelProfileRepository extends NamedDBElementRepository $type_children[] = $node; } - if (!empty($type_children)) { + if ($type_children !== []) { //Use default label e.g. 'part_label'. $$ marks that it will be translated in TreeViewGenerator - $tmp = new TreeViewNode('$$'.$type.'.label', null, $type_children); + $tmp = new TreeViewNode('$$'.$type->value.'.label', null, $type_children); $result[] = $tmp; } @@ -98,42 +98,35 @@ class LabelProfileRepository extends NamedDBElementRepository /** * Find all LabelProfiles that can be used with the given type. * - * @param string $type see LabelOptions::SUPPORTED_ELEMENTS for valid values + * @param LabelSupportedElement $type see LabelOptions::SUPPORTED_ELEMENTS for valid values * @param array $order_by The way the results should be sorted. By default ordered by */ - public function findForSupportedElement(string $type, array $order_by = ['name' => 'ASC']): array + public function findForSupportedElement(LabelSupportedElement $type, array $order_by = ['name' => 'ASC']): array { - if (!in_array($type, LabelOptions::SUPPORTED_ELEMENTS, true)) { - throw new InvalidArgumentException('Invalid supported_element type given.'); - } - return $this->findBy(['options.supported_element' => $type], $order_by); } /** * Returns all LabelProfiles that can be used for parts - * @return array */ public function getPartLabelProfiles(): array { - return $this->getDropdownProfiles('part'); + return $this->getDropdownProfiles(LabelSupportedElement::PART); } /** * Returns all LabelProfiles that can be used for part lots - * @return array */ public function getPartLotsLabelProfiles(): array { - return $this->getDropdownProfiles('part_lot'); + return $this->getDropdownProfiles(LabelSupportedElement::PART_LOT); } /** * Returns all LabelProfiles that can be used for storelocations - * @return array */ public function getStorelocationsLabelProfiles(): array { - return $this->getDropdownProfiles('storelocation'); + return $this->getDropdownProfiles(LabelSupportedElement::STORELOCATION); } } diff --git a/src/Repository/LogEntryRepository.php b/src/Repository/LogEntryRepository.php index 857aac5b..6850d06b 100644 --- a/src/Repository/LogEntryRepository.php +++ b/src/Repository/LogEntryRepository.php @@ -28,10 +28,14 @@ use App\Entity\LogSystem\CollectionElementDeleted; use App\Entity\LogSystem\ElementCreatedLogEntry; use App\Entity\LogSystem\ElementDeletedLogEntry; use App\Entity\LogSystem\ElementEditedLogEntry; +use App\Entity\LogSystem\LogTargetType; use App\Entity\UserSystem\User; -use DateTime; use RuntimeException; +/** + * @template TEntityClass of AbstractLogEntry + * @extends DBElementRepository + */ class LogEntryRepository extends DBElementRepository { public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array @@ -41,7 +45,7 @@ class LogEntryRepository extends DBElementRepository /** @var AbstractDBElement $element */ $element = $criteria['target']; $criteria['target_id'] = $element->getID(); - $criteria['target_type'] = AbstractLogEntry::targetTypeClassToID(get_class($element)); + $criteria['target_type'] = LogTargetType::fromElementClass($element); unset($criteria['target']); } @@ -52,15 +56,16 @@ class LogEntryRepository extends DBElementRepository * Find log entries associated with the given element (the history of the element). * * @param AbstractDBElement $element The element for which the history should be generated - * @param string $order By default, the newest entries are shown first. Change this to ASC to show the oldest entries first. - * @param null $limit - * @param null $offset + * @param string $order By default, the newest entries are shown first. Change this to ASC to show the oldest entries first. + * @param int|null $limit + * @param int|null $offset * * @return AbstractLogEntry[] */ - public function getElementHistory(AbstractDBElement $element, string $order = 'DESC', $limit = null, $offset = null): array + public function getElementHistory(AbstractDBElement $element, string $order = 'DESC', ?int $limit = null, ?int $offset = null): array { - return $this->findBy(['element' => $element], ['timestamp' => $order], $limit, $offset); + //@phpstan-ignore-next-line Target is parsed dynamically in findBy + return $this->findBy(['target' => $element], ['timestamp' => $order], $limit, $offset); } /** @@ -80,10 +85,8 @@ class LogEntryRepository extends DBElementRepository ->orderBy('log.timestamp', 'DESC') ->setMaxResults(1); - $qb->setParameters([ - 'target_type' => AbstractLogEntry::targetTypeClassToID($class), - 'target_id' => $id, - ]); + $qb->setParameter('target_type', LogTargetType::fromElementClass($class)); + $qb->setParameter('target_id', $id); $query = $qb->getQuery(); @@ -100,27 +103,26 @@ class LogEntryRepository extends DBElementRepository * Gets all log entries that are related to time travelling. * * @param AbstractDBElement $element The element for which the time travel data should be retrieved - * @param DateTime $until Back to which timestamp should the data be get (including the timestamp) + * @param \DateTimeInterface $until Back to which timestamp should the data be got (including the timestamp) * * @return AbstractLogEntry[] */ - public function getTimetravelDataForElement(AbstractDBElement $element, DateTime $until): array + public function getTimetravelDataForElement(AbstractDBElement $element, \DateTimeInterface $until): array { $qb = $this->createQueryBuilder('log'); $qb->select('log') - //->where('log INSTANCE OF App\Entity\LogSystem\ElementEditedLogEntry') ->where('log INSTANCE OF '.ElementEditedLogEntry::class) ->orWhere('log INSTANCE OF '.CollectionElementDeleted::class) ->andWhere('log.target_type = :target_type') ->andWhere('log.target_id = :target_id') ->andWhere('log.timestamp >= :until') - ->orderBy('log.timestamp', 'DESC'); + ->orderBy('log.timestamp', 'DESC') + ; + + $qb->setParameter('target_type', LogTargetType::fromElementClass($element)); + $qb->setParameter('target_id', $element->getID()); + $qb->setParameter('until', $until); - $qb->setParameters([ - 'target_type' => AbstractLogEntry::targetTypeClassToID(get_class($element)), - 'target_id' => $element->getID(), - 'until' => $until, - ]); $query = $qb->getQuery(); @@ -132,7 +134,7 @@ class LogEntryRepository extends DBElementRepository * * @return bool True if the element existed at the given timestamp */ - public function getElementExistedAtTimestamp(AbstractDBElement $element, DateTime $timestamp): bool + public function getElementExistedAtTimestamp(AbstractDBElement $element, \DateTimeInterface $timestamp): bool { $qb = $this->createQueryBuilder('log'); $qb->select('count(log)') @@ -140,28 +142,25 @@ class LogEntryRepository extends DBElementRepository ->andWhere('log.target_type = :target_type') ->andWhere('log.target_id = :target_id') ->andWhere('log.timestamp >= :until') - ->orderBy('log.timestamp', 'DESC'); + ; - $qb->setParameters([ - 'target_type' => AbstractLogEntry::targetTypeClassToID(get_class($element)), - 'target_id' => $element->getID(), - 'until' => $timestamp, - ]); + $qb->setParameter('target_type', LogTargetType::fromElementClass($element)); + $qb->setParameter('target_id', $element->getID()); + $qb->setParameter('until', $timestamp); $query = $qb->getQuery(); $count = $query->getSingleScalarResult(); - return !($count > 0); + return $count <= 0; } /** * Gets the last log entries ordered by timestamp. * - * @param string $order - * @param null $limit - * @param null $offset + * @param int|null $limit + * @param int|null $offset */ - public function getLogsOrderedByTimestamp(string $order = 'DESC', $limit = null, $offset = null): array + public function getLogsOrderedByTimestamp(string $order = 'DESC', ?int $limit = null, ?int $offset = null): array { return $this->findBy([], ['timestamp' => $order], $limit, $offset); } @@ -205,23 +204,38 @@ class LogEntryRepository extends DBElementRepository return $this->getLastUser($element, ElementCreatedLogEntry::class); } - protected function getLastUser(AbstractDBElement $element, string $class): ?User + /** + * Returns the last user that has created a log entry with the given class on the given element. + * @param AbstractDBElement $element + * @param string $log_class + * @return User|null + */ + protected function getLastUser(AbstractDBElement $element, string $log_class): ?User { $qb = $this->createQueryBuilder('log'); + /** + * The select and join with user here are important, to get true null user values, if the user was deleted. + * This happens for sqlite database, before the SET NULL constraint was added, and doctrine generates a proxy + * entity which fails to resolve, without this line. + * This was the cause of issue #414 (https://github.com/Part-DB/Part-DB-server/issues/414) + */ $qb->select('log') - //->where('log INSTANCE OF App\Entity\LogSystem\ElementEditedLogEntry') - ->where('log INSTANCE OF '.$class) + ->addSelect('user') + ->where('log INSTANCE OF '.$log_class) + ->leftJoin('log.user', 'user') ->andWhere('log.target_type = :target_type') ->andWhere('log.target_id = :target_id') - ->orderBy('log.timestamp', 'DESC'); + ->orderBy('log.timestamp', 'DESC') + //Use id as fallback, if timestamp is the same (higher id means newer entry) + ->addOrderBy('log.id', 'DESC') + ; - $qb->setParameters([ - 'target_type' => AbstractLogEntry::targetTypeClassToID(get_class($element)), - 'target_id' => $element->getID(), - ]); + $qb->setParameter('target_type', LogTargetType::fromElementClass($element)); + $qb->setParameter('target_id', $element->getID()); $query = $qb->getQuery(); $query->setMaxResults(1); + /** @var AbstractLogEntry[] $results */ $results = $query->execute(); if (isset($results[0])) { diff --git a/src/Repository/NamedDBElementRepository.php b/src/Repository/NamedDBElementRepository.php index 0985b8e1..8c78cb5e 100644 --- a/src/Repository/NamedDBElementRepository.php +++ b/src/Repository/NamedDBElementRepository.php @@ -26,6 +26,11 @@ use App\Entity\Base\AbstractNamedDBElement; use App\Entity\UserSystem\User; use App\Helpers\Trees\TreeViewNode; +/** + * @template TEntityClass of AbstractNamedDBElement + * @extends DBElementRepository + * @see \App\Tests\Repository\NamedDBElementRepositoryTest + */ class NamedDBElementRepository extends DBElementRepository { /** @@ -38,19 +43,40 @@ class NamedDBElementRepository extends DBElementRepository { $result = []; - $entities = $this->findBy([], ['name' => 'ASC']); + $entities = $this->getFlatList(); foreach ($entities as $entity) { /** @var AbstractNamedDBElement $entity */ $node = new TreeViewNode($entity->getName(), null, null); $node->setId($entity->getID()); $result[] = $node; - if ($entity instanceof User && $entity->isDisabled()) { - //If this is an user, then add a badge when it is disabled - $node->setIcon('fa-fw fa-treeview fa-solid fa-user-lock text-muted'); + if ($entity instanceof User) { + if ($entity->isDisabled()) { + //If this is a user, then add a badge when it is disabled + $node->setIcon('fa-fw fa-treeview fa-solid fa-user-lock text-muted'); + } + if ($entity->isSamlUser()) { + $node->setIcon('fa-fw fa-treeview fa-solid fa-house-user text-muted'); + } } + } return $result; } + + /** + * Returns a flattened list of all nodes, sorted by name in natural order. + * @return AbstractNamedDBElement[] + * @phpstan-return array + */ + public function getFlatList(): array + { + $qb = $this->createQueryBuilder('e'); + $q = $qb->select('e') + ->orderBy('NATSORT(e.name)', 'ASC') + ->getQuery(); + + return $q->getResult(); + } } diff --git a/src/Repository/ParameterRepository.php b/src/Repository/ParameterRepository.php index 37420306..6c6c867d 100644 --- a/src/Repository/ParameterRepository.php +++ b/src/Repository/ParameterRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository; +use App\Entity\Parameters\AbstractParameter; + +/** + * @template TEntityClass of AbstractParameter + * @extends DBElementRepository + */ class ParameterRepository extends DBElementRepository { /** * Find parameters using a parameter name * @param string $name The name to search for * @param bool $exact True, if only exact names should match. False, if the name just needs to be contained in the parameter name - * @param int $max_results - * @return array + * @phpstan-return array */ public function autocompleteParamName(string $name, bool $exact = false, int $max_results = 50): array { @@ -37,7 +44,7 @@ class ParameterRepository extends DBElementRepository ->select('parameter.name') ->addSelect('parameter.symbol') ->addSelect('parameter.unit') - ->where('parameter.name LIKE :name'); + ->where('ILIKE(parameter.name, :name) = TRUE'); if ($exact) { $qb->setParameter('name', $name); } else { @@ -48,4 +55,4 @@ class ParameterRepository extends DBElementRepository return $qb->getQuery()->getArrayResult(); } -} \ No newline at end of file +} diff --git a/src/Repository/PartRepository.php b/src/Repository/PartRepository.php index 5dfb8f45..edccd74b 100644 --- a/src/Repository/PartRepository.php +++ b/src/Repository/PartRepository.php @@ -22,15 +22,19 @@ declare(strict_types=1); namespace App\Repository; +use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\NoResultException; use Doctrine\ORM\QueryBuilder; +/** + * @extends NamedDBElementRepository + */ class PartRepository extends NamedDBElementRepository { /** - * Gets the summed up instock of all parts (only parts without an measurent unit). + * Gets the summed up instock of all parts (only parts without a measurement unit). * * @throws NoResultException * @throws NonUniqueResultException @@ -49,7 +53,7 @@ class PartRepository extends NamedDBElementRepository } /** - * Gets the number of parts that has price informations. + * Gets the number of parts that has price information. * * @throws NoResultException * @throws NonUniqueResultException @@ -67,6 +71,9 @@ class PartRepository extends NamedDBElementRepository return (int) ($query->getSingleScalarResult() ?? 0); } + /** + * @return Part[] + */ public function autocompleteSearch(string $query, int $max_limits = 50): array { $qb = $this->createQueryBuilder('part'); @@ -74,16 +81,16 @@ class PartRepository extends NamedDBElementRepository ->leftJoin('part.category', 'category') ->leftJoin('part.footprint', 'footprint') - ->where('part.name LIKE :query') - ->orWhere('part.description LIKE :query') - ->orWhere('category.name LIKE :query') - ->orWhere('footprint.name LIKE :query') + ->where('ILIKE(part.name, :query) = TRUE') + ->orWhere('ILIKE(part.description, :query) = TRUE') + ->orWhere('ILIKE(category.name, :query) = TRUE') + ->orWhere('ILIKE(footprint.name, :query) = TRUE') ; $qb->setParameter('query', '%'.$query.'%'); $qb->setMaxResults($max_limits); - $qb->orderBy('part.name', 'ASC'); + $qb->orderBy('NATSORT(part.name)', 'ASC'); return $qb->getQuery()->getResult(); } diff --git a/src/Repository/Parts/CategoryRepository.php b/src/Repository/Parts/CategoryRepository.php index c472d8d6..9bbb1e37 100644 --- a/src/Repository/Parts/CategoryRepository.php +++ b/src/Repository/Parts/CategoryRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository\Parts; use App\Entity\Parts\Category; @@ -26,13 +28,13 @@ use InvalidArgumentException; class CategoryRepository extends AbstractPartsContainingRepository { - public function getParts(object $element, array $order_by = ['name' => 'ASC']): array + public function getParts(object $element, string $nameOrderDirection = "ASC"): array { if (!$element instanceof Category) { throw new InvalidArgumentException('$element must be an Category!'); } - return $this->getPartsByField($element, $order_by, 'category'); + return $this->getPartsByField($element, $nameOrderDirection, 'category'); } public function getPartsCount(object $element): int diff --git a/src/Repository/Parts/DeviceRepository.php b/src/Repository/Parts/DeviceRepository.php index dc5d5acc..442c91e5 100644 --- a/src/Repository/Parts/DeviceRepository.php +++ b/src/Repository/Parts/DeviceRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository\Parts; -use App\Entity\Base\AbstractPartsContainingDBElement; use App\Entity\ProjectSystem\Project; -use App\Entity\Parts\Category; -use App\Entity\Parts\Part; -use App\Repository\AbstractPartsContainingRepository; use App\Repository\StructuralDBElementRepository; use InvalidArgumentException; @@ -53,4 +51,4 @@ class DeviceRepository extends StructuralDBElementRepository //Prevent user from deleting devices, to not accidentally remove filled devices from old versions return 1; } -} \ No newline at end of file +} diff --git a/src/Repository/Parts/FootprintRepository.php b/src/Repository/Parts/FootprintRepository.php index 72c25003..8934831a 100644 --- a/src/Repository/Parts/FootprintRepository.php +++ b/src/Repository/Parts/FootprintRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository\Parts; use App\Entity\Parts\Footprint; @@ -26,13 +28,13 @@ use InvalidArgumentException; class FootprintRepository extends AbstractPartsContainingRepository { - public function getParts(object $element, array $order_by = ['name' => 'ASC']): array + public function getParts(object $element, string $nameOrderDirection = "ASC"): array { if (!$element instanceof Footprint) { throw new InvalidArgumentException('$element must be an Footprint!'); } - return $this->getPartsByField($element, $order_by, 'footprint'); + return $this->getPartsByField($element, $nameOrderDirection, 'footprint'); } public function getPartsCount(object $element): int diff --git a/src/Repository/Parts/ManufacturerRepository.php b/src/Repository/Parts/ManufacturerRepository.php index aa4d8fec..1a838710 100644 --- a/src/Repository/Parts/ManufacturerRepository.php +++ b/src/Repository/Parts/ManufacturerRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository\Parts; use App\Entity\Parts\Manufacturer; @@ -26,13 +28,13 @@ use InvalidArgumentException; class ManufacturerRepository extends AbstractPartsContainingRepository { - public function getParts(object $element, array $order_by = ['name' => 'ASC']): array + public function getParts(object $element, string $nameOrderDirection = "ASC"): array { if (!$element instanceof Manufacturer) { throw new InvalidArgumentException('$element must be an Manufacturer!'); } - return $this->getPartsByField($element, $order_by, 'manufacturer'); + return $this->getPartsByField($element, $nameOrderDirection, 'manufacturer'); } public function getPartsCount(object $element): int diff --git a/src/Repository/Parts/MeasurementUnitRepository.php b/src/Repository/Parts/MeasurementUnitRepository.php index 80d32743..c581f751 100644 --- a/src/Repository/Parts/MeasurementUnitRepository.php +++ b/src/Repository/Parts/MeasurementUnitRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository\Parts; use App\Entity\Parts\MeasurementUnit; @@ -26,13 +28,13 @@ use InvalidArgumentException; class MeasurementUnitRepository extends AbstractPartsContainingRepository { - public function getParts(object $element, array $order_by = ['name' => 'ASC']): array + public function getParts(object $element, string $nameOrderDirection = "ASC"): array { if (!$element instanceof MeasurementUnit) { throw new InvalidArgumentException('$element must be an MeasurementUnit!'); } - return $this->getPartsByField($element, $order_by, 'partUnit'); + return $this->getPartsByField($element, $nameOrderDirection, 'partUnit'); } public function getPartsCount(object $element): int diff --git a/src/Repository/Parts/StorelocationRepository.php b/src/Repository/Parts/StorelocationRepository.php index c0c432be..82317868 100644 --- a/src/Repository/Parts/StorelocationRepository.php +++ b/src/Repository/Parts/StorelocationRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository\Parts; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Repository\AbstractPartsContainingRepository; use Doctrine\ORM\QueryBuilder; use InvalidArgumentException; class StorelocationRepository extends AbstractPartsContainingRepository { - /** - * @param object $element - * @param array $order_by - * @return array - */ - public function getParts(object $element, array $order_by = ['name' => 'ASC']): array + public function getParts(object $element, string $nameOrderDirection = "ASC"): array { - if (!$element instanceof Storelocation) { + if (!$element instanceof StorageLocation) { throw new InvalidArgumentException('$element must be an Storelocation!'); } @@ -45,18 +42,16 @@ class StorelocationRepository extends AbstractPartsContainingRepository ->from(Part::class, 'part') ->leftJoin('part.partLots', 'lots') ->where('lots.storage_location = ?1') - ->setParameter(1, $element); - - foreach ($order_by as $field => $order) { - $qb->addOrderBy('part.'.$field, $order); - } + ->setParameter(1, $element) + ->orderBy('NATSORT(part.name)', $nameOrderDirection) + ; return $qb->getQuery()->getResult(); } public function getPartsCount(object $element): int { - if (!$element instanceof Storelocation) { + if (!$element instanceof StorageLocation) { throw new InvalidArgumentException('$element must be an Storelocation!'); } diff --git a/src/Repository/Parts/SupplierRepository.php b/src/Repository/Parts/SupplierRepository.php index 0a2e2c8f..393ae593 100644 --- a/src/Repository/Parts/SupplierRepository.php +++ b/src/Repository/Parts/SupplierRepository.php @@ -1,4 +1,7 @@ . */ - namespace App\Repository\Parts; use App\Entity\Parts\Part; @@ -28,7 +30,7 @@ use InvalidArgumentException; class SupplierRepository extends AbstractPartsContainingRepository { - public function getParts(object $element, array $order_by = ['name' => 'ASC']): array + public function getParts(object $element, string $nameOrderDirection = "ASC"): array { if (!$element instanceof Supplier) { throw new InvalidArgumentException('$element must be an Supplier!'); @@ -40,11 +42,9 @@ class SupplierRepository extends AbstractPartsContainingRepository ->from(Part::class, 'part') ->leftJoin('part.orderdetails', 'orderdetail') ->where('orderdetail.supplier = ?1') - ->setParameter(1, $element); - - foreach ($order_by as $field => $order) { - $qb->addOrderBy('part.'.$field, $order); - } + ->setParameter(1, $element) + ->orderBy('NATSORT(part.name)', $nameOrderDirection) + ; return $qb->getQuery()->getResult(); } diff --git a/src/Repository/StructuralDBElementRepository.php b/src/Repository/StructuralDBElementRepository.php index e23eda8f..781c7622 100644 --- a/src/Repository/StructuralDBElementRepository.php +++ b/src/Repository/StructuralDBElementRepository.php @@ -27,8 +27,41 @@ use App\Helpers\Trees\StructuralDBElementIterator; use App\Helpers\Trees\TreeViewNode; use RecursiveIteratorIterator; -class StructuralDBElementRepository extends NamedDBElementRepository +/** + * @see \App\Tests\Repository\StructuralDBElementRepositoryTest + * @template TEntityClass of AbstractStructuralDBElement + * @extends AttachmentContainingDBElementRepository + */ +class StructuralDBElementRepository extends AttachmentContainingDBElementRepository { + /** + * @var array An array containing all new entities created by getNewEntityByPath. + * This is used to prevent creating multiple entities for the same path. + */ + private array $new_entity_cache = []; + + /** + * Finds all nodes for the given parent node, ordered by name in a natural sort way + * @param AbstractStructuralDBElement|null $parent + * @param string $nameOrdering The ordering of the names. Either ASC or DESC + * @return array + */ + public function findNodesForParent(?AbstractStructuralDBElement $parent, string $nameOrdering = "ASC"): array + { + $qb = $this->createQueryBuilder('e'); + $qb->select('e') + ->orderBy('NATSORT(e.name)', $nameOrdering); + + if ($parent !== null) { + $qb->where('e.parent = :parent') + ->setParameter('parent', $parent); + } else { + $qb->where('e.parent IS NULL'); + } + //@phpstan-ignore-next-line [parent is only defined by the sub classes] + return $qb->getQuery()->getResult(); + } + /** * Finds all nodes without a parent node. They are our root nodes. * @@ -36,14 +69,15 @@ class StructuralDBElementRepository extends NamedDBElementRepository */ public function findRootNodes(): array { - return $this->findBy(['parent' => null], ['name' => 'ASC']); + return $this->findNodesForParent(null); } /** * Gets a tree of TreeViewNode elements. The root elements has $parent as parent. * The treeview is generic, that means the href are null and ID values are set. * - * @param AbstractStructuralDBElement|null $parent the parent the root elements should have + * @param AbstractStructuralDBElement|null $parent the parent the root elements should have + * @phpstan-param TEntityClass|null $parent * * @return TreeViewNode[] */ @@ -51,7 +85,7 @@ class StructuralDBElementRepository extends NamedDBElementRepository { $result = []; - $entities = $this->findBy(['parent' => $parent], ['name' => 'ASC']); + $entities = $this->findNodesForParent($parent); foreach ($entities as $entity) { /** @var AbstractStructuralDBElement $entity */ //Make a recursive call to find all children nodes @@ -69,20 +103,21 @@ class StructuralDBElementRepository extends NamedDBElementRepository * Gets a flattened hierarchical tree. Useful for generating option lists. * * @param AbstractStructuralDBElement|null $parent This entity will be used as root element. Set to null, to use global root - * + * @phpstan-param TEntityClass|null $parent * @return AbstractStructuralDBElement[] a flattened list containing the tree elements + * @phpstan-return array */ - public function toNodesList(?AbstractStructuralDBElement $parent = null): array + public function getFlatList(?AbstractStructuralDBElement $parent = null): array { $result = []; - $entities = $this->findBy(['parent' => $parent], ['name' => 'ASC']); + $entities = $this->findNodesForParent($parent); $elementIterator = new StructuralDBElementIterator($entities); $recursiveIterator = new RecursiveIteratorIterator($elementIterator, RecursiveIteratorIterator::SELF_FIRST); //$result = iterator_to_array($recursiveIterator); - //We can not use iterator_to_array here or we get only the parent elements + //We can not use iterator_to_array here, or we get only the parent elements foreach ($recursiveIterator as $item) { $result[] = $item; } @@ -91,12 +126,11 @@ class StructuralDBElementRepository extends NamedDBElementRepository } /** - * Creates a structure of AbsstractStructuralDBElements from a path separated by $separator, which splits the various levels. + * Creates a structure of AbstractStructuralDBElements from a path separated by $separator, which splits the various levels. * This function will try to use existing elements, if they are already in the database. If not, they will be created. * An array of the created elements will be returned, with the last element being the deepest element. - * @param string $path - * @param string $separator * @return AbstractStructuralDBElement[] + * @phpstan-return array */ public function getNewEntityFromPath(string $path, string $separator = '->'): array { @@ -108,14 +142,63 @@ class StructuralDBElementRepository extends NamedDBElementRepository continue; } + //Use the cache to prevent creating multiple entities for the same path + $entity = $this->getNewEntityFromCache($name, $parent); + + //See if we already have an element with this name and parent in the database + if (!$entity instanceof AbstractStructuralDBElement) { + $entity = $this->findOneBy(['name' => $name, 'parent' => $parent]); + } + if (null === $entity) { + $class = $this->getClassName(); + /** @var TEntityClass $entity */ + $entity = new $class; + $entity->setName($name); + $entity->setParent($parent); + + $this->setNewEntityToCache($entity); + } + + $result[] = $entity; + $parent = $entity; + } + + return $result; + } + + private function getNewEntityFromCache(string $name, ?AbstractStructuralDBElement $parent): ?AbstractStructuralDBElement + { + $key = $parent instanceof AbstractStructuralDBElement ? $parent->getFullPath('%->%').'%->%'.$name : $name; + return $this->new_entity_cache[$key] ?? null; + } + + private function setNewEntityToCache(AbstractStructuralDBElement $entity): void + { + $key = $entity->getFullPath('%->%'); + $this->new_entity_cache[$key] = $entity; + } + + /** + * Returns an element of AbstractStructuralDBElements queried from a path separated by $separator, which splits the various levels. + * An array of the created elements will be returned, with the last element being the deepest element. + * If no element was found, an empty array will be returned. + * @return AbstractStructuralDBElement[] + * @phpstan-return array + */ + public function getEntityByPath(string $path, string $separator = '->'): array + { + $parent = null; + $result = []; + foreach (explode($separator, $path) as $name) { + $name = trim($name); + if ('' === $name) { + continue; + } + //See if we already have an element with this name and parent $entity = $this->findOneBy(['name' => $name, 'parent' => $parent]); if (null === $entity) { - $class = $this->getClassName(); - /** @var AbstractStructuralDBElement $entity */ - $entity = new $class; - $entity->setName($name); - $entity->setParent($parent); + return []; } $result[] = $entity; @@ -124,4 +207,74 @@ class StructuralDBElementRepository extends NamedDBElementRepository return $result; } + + /** + * Finds the element with the given name for the use with the InfoProvider System + * The name search is a bit more fuzzy than the normal findByName, because it is case-insensitive and ignores special characters. + * Also, it will try to find the element using the additional names field, of the elements. + * @param string $name + * @return AbstractStructuralDBElement|null + * @phpstan-return TEntityClass|null + */ + public function findForInfoProvider(string $name): ?AbstractStructuralDBElement + { + //First try to find the element by name + $qb = $this->createQueryBuilder('e'); + //Use lowercase conversion to be case-insensitive + $qb->where($qb->expr()->like('LOWER(e.name)', 'LOWER(:name)')); + + $qb->setParameter('name', $name); + + $result = $qb->getQuery()->getResult(); + + if (count($result) === 1) { + return $result[0]; + } + + //If we have no result, try to find the element by alternative names + $qb = $this->createQueryBuilder('e'); + //Use lowercase conversion to be case-insensitive + $qb->where($qb->expr()->like('LOWER(e.alternative_names)', 'LOWER(:name)')); + $qb->setParameter('name', '%'.$name.',%'); + + $result = $qb->getQuery()->getResult(); + + if (count($result) >= 1) { + return $result[0]; + } + + //If we find nothing, return null + return null; + } + + /** + * Similar to findForInfoProvider, but will create a new element with the given name if none was found. + * @param string $name + * @return AbstractStructuralDBElement + * @phpstan-return TEntityClass + */ + public function findOrCreateForInfoProvider(string $name): AbstractStructuralDBElement + { + $entity = $this->findForInfoProvider($name); + if (null === $entity) { + + //Try to find if we already have an element cached for this name + $entity = $this->getNewEntityFromCache($name, null); + if ($entity !== null) { + return $entity; + } + + $class = $this->getClassName(); + /** @var TEntityClass $entity */ + $entity = new $class; + $entity->setName($name); + + //Set the found name to the alternative names, so the entity can be easily renamed later + $entity->setAlternativeNames($name); + + $this->setNewEntityToCache($entity); + } + + return $entity; + } } diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index 2d4fea12..bbaa2b39 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -23,7 +23,10 @@ declare(strict_types=1); namespace App\Repository; use App\Entity\UserSystem\User; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\NonUniqueResultException; +use Doctrine\ORM\Query\Parameter; +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -32,18 +35,21 @@ use Symfony\Component\Security\Core\User\UserInterface; * @method User|null findOneBy(array $criteria, array $orderBy = null) * @method User[] findAll() * @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + * @extends NamedDBElementRepository + * @see \App\Tests\Repository\UserRepositoryTest */ final class UserRepository extends NamedDBElementRepository implements PasswordUpgraderInterface { - protected $anonymous_user; + protected ?User $anonymous_user = null; /** * Returns the anonymous user. * The result is cached, so the database is only called once, after the anonymous user was found. + * @return User|null The user if it is existing, null if no one matched the criteria */ public function getAnonymousUser(): ?User { - if (null === $this->anonymous_user) { + if (!$this->anonymous_user instanceof User) { $this->anonymous_user = $this->findOneBy([ 'id' => User::ID_ANONYMOUS, ]); @@ -52,6 +58,30 @@ final class UserRepository extends NamedDBElementRepository implements PasswordU return $this->anonymous_user; } + /** + * Find a user by its username. + * @param string $username + * @return User|null + */ + public function findByUsername(string $username): ?User + { + if ($username === '') { + return null; + } + + $qb = $this->createQueryBuilder('u'); + $qb->select('u') + ->where('u.name = (:name)'); + + $qb->setParameter('name', $username); + + try { + return $qb->getQuery()->getOneOrNullResult(); + } catch (NonUniqueResultException) { + return null; + } + } + /** * Find a user by its name or its email. Useful for login or password reset purposes. * @@ -61,7 +91,7 @@ final class UserRepository extends NamedDBElementRepository implements PasswordU */ public function findByEmailOrName(string $name_or_password): ?User { - if (empty($name_or_password)) { + if ($name_or_password === '') { return null; } @@ -70,23 +100,43 @@ final class UserRepository extends NamedDBElementRepository implements PasswordU ->where('u.name = (:name)') ->orWhere('u.email = (:email)'); - $qb->setParameters([ - 'email' => $name_or_password, - 'name' => $name_or_password, - ]); + $qb->setParameter('email', $name_or_password); + $qb->setParameter('name', $name_or_password); try { return $qb->getQuery()->getOneOrNullResult(); - } catch (NonUniqueResultException $nonUniqueResultException) { + } catch (NonUniqueResultException) { return null; } } - public function upgradePassword(UserInterface $user, string $newHashedPassword): void + public function upgradePassword(UserInterface|PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void { if ($user instanceof User) { $user->setPassword($newHashedPassword); $this->getEntityManager()->flush(); } } + + /** + * Returns the list of all local users (not SAML users). + * @return User[] + */ + public function onlyLocalUsers(): array + { + return $this->findBy([ + 'saml_user' => false, + ]); + } + + /** + * Returns the list of all SAML users. + * @return User[] + */ + public function onlySAMLUsers(): array + { + return $this->findBy([ + 'saml_user' => true, + ]); + } } diff --git a/assets/controllers/pages/u2f_register_controller.js b/src/Repository/UserSystem/ApiTokenRepository.php similarity index 74% rename from assets/controllers/pages/u2f_register_controller.js rename to src/Repository/UserSystem/ApiTokenRepository.php index ffa4c3f2..609014f3 100644 --- a/assets/controllers/pages/u2f_register_controller.js +++ b/src/Repository/UserSystem/ApiTokenRepository.php @@ -1,7 +1,8 @@ +. */ -import {Controller} from "@hotwired/stimulus"; +declare(strict_types=1); -export default class extends Controller + +namespace App\Repository\UserSystem; + +use Doctrine\ORM\EntityRepository; + +class ApiTokenRepository extends EntityRepository { - connect() { - this.element.onclick = function() { - window.u2fauth.register(); - } - } -} +} \ No newline at end of file diff --git a/src/Security/ApiTokenAuthenticatedToken.php b/src/Security/ApiTokenAuthenticatedToken.php new file mode 100644 index 00000000..2f186e63 --- /dev/null +++ b/src/Security/ApiTokenAuthenticatedToken.php @@ -0,0 +1,52 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Security; + +use App\Entity\UserSystem\ApiToken; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken; + +class ApiTokenAuthenticatedToken extends PostAuthenticationToken +{ + public function __construct(UserInterface $user, string $firewallName, array $roles, private readonly ApiToken $apiToken) + { + //Add roles for the API + $roles[] = 'ROLE_API_AUTHENTICATED'; + + //Add roles based on the token level + $roles = array_merge($roles, $apiToken->getLevel()->getAdditionalRoles()); + + + parent::__construct($user, $firewallName, array_unique($roles)); + } + + /** + * Returns the API token that was used to authenticate the user. + * @return ApiToken + */ + public function getApiToken(): ApiToken + { + return $this->apiToken; + } +} \ No newline at end of file diff --git a/src/Security/ApiTokenAuthenticator.php b/src/Security/ApiTokenAuthenticator.php new file mode 100644 index 00000000..a52b1f7c --- /dev/null +++ b/src/Security/ApiTokenAuthenticator.php @@ -0,0 +1,156 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Security; + +use App\Entity\UserSystem\ApiToken; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; +use Symfony\Component\Security\Http\AccessToken\AccessTokenExtractorInterface; +use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Passport; +use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * Authenticator similar to the builtin AccessTokenAuthenticator, but we return a Token here which contains information + * about the used token. + */ +class ApiTokenAuthenticator implements AuthenticatorInterface +{ + public function __construct( + #[Autowire(service: 'security.access_token_extractor.main')] + private readonly AccessTokenExtractorInterface $accessTokenExtractor, + private readonly TranslatorInterface $translator, + private readonly EntityManagerInterface $entityManager, + private readonly string $realm = 'api', + ) { + } + + /** + * Gets the ApiToken belonging to the given accessToken string. + * If the token is invalid or expired, an exception is thrown and authentication fails. + * @param string $accessToken + * @return ApiToken + */ + private function getTokenFromString(#[\SensitiveParameter] string $accessToken): ApiToken + { + $repo = $this->entityManager->getRepository(ApiToken::class); + $token = $repo->findOneBy(['token' => $accessToken]); + + if (!$token instanceof ApiToken) { + throw new BadCredentialsException(); + } + + if (!$token->isValid()) { + throw new CustomUserMessageAuthenticationException('Token expired'); + } + + $old_time = $token->getLastTimeUsed(); + //Set the last used date of the token + $token->setLastTimeUsed(new \DateTimeImmutable()); + //Only flush the token if the last used date change is more than 10 minutes + //For performance reasons we don't want to flush the token every time it is used, but only if it is used more than 10 minutes after the last time it was used + //If a flush is later in the code we don't want to flush the token again + if ($old_time === null || $old_time->diff($token->getLastTimeUsed())->i > 10) { + $this->entityManager->flush(); + } + + return $token; + } + + public function supports(Request $request): ?bool + { + return null === $this->accessTokenExtractor->extractAccessToken($request) ? false : null; + } + + public function authenticate(Request $request): Passport + { + $accessToken = $this->accessTokenExtractor->extractAccessToken($request); + if (!$accessToken) { + throw new BadCredentialsException('Invalid credentials.'); + } + + $apiToken = $this->getTokenFromString($accessToken); + $userBadge = new UserBadge($apiToken->getUser()?->getUserIdentifier() ?? throw new BadCredentialsException('Invalid credentials.')); + $apiBadge = new ApiTokenBadge($apiToken); + + return new SelfValidatingPassport($userBadge, [$apiBadge]); + } + + public function createToken(Passport $passport, string $firewallName): TokenInterface + { + return new ApiTokenAuthenticatedToken( + $passport->getUser(), + $firewallName, + $passport->getUser()->getRoles(), + $passport->getBadge(ApiTokenBadge::class)?->getApiToken() ?? throw new \LogicException('Passport does not contain an API token.') + ); + } + + + public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response + { + $errorMessage = $this->translator->trans($exception->getMessageKey(), $exception->getMessageData(), + 'security'); + + return new Response( + null, + Response::HTTP_UNAUTHORIZED, + ['WWW-Authenticate' => $this->getAuthenticateHeader($errorMessage)] + ); + } + + /** + * @see https://datatracker.ietf.org/doc/html/rfc6750#section-3 + */ + private function getAuthenticateHeader(?string $errorDescription = null): string + { + $data = [ + 'realm' => $this->realm, + 'error' => 'invalid_token', + 'error_description' => $errorDescription, + ]; + $values = []; + foreach ($data as $k => $v) { + if (null === $v || '' === $v) { + continue; + } + $values[] = sprintf('%s="%s"', $k, $v); + } + + return sprintf('Bearer %s', implode(',', $values)); + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response + { + return null; + } +} \ No newline at end of file diff --git a/src/Security/ApiTokenBadge.php b/src/Security/ApiTokenBadge.php new file mode 100644 index 00000000..d2429a06 --- /dev/null +++ b/src/Security/ApiTokenBadge.php @@ -0,0 +1,51 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Security; + +use App\Entity\UserSystem\ApiToken; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface; + +class ApiTokenBadge implements BadgeInterface +{ + + /** + * @param ApiToken $apiToken + */ + public function __construct(private readonly ApiToken $apiToken) + { + } + + /** + * @return ApiToken The token that was used to authenticate the user + */ + public function getApiToken(): ApiToken + { + return $this->apiToken; + } + + public function isResolved(): bool + { + return true; + } +} \ No newline at end of file diff --git a/src/Security/AuthenticationEntryPoint.php b/src/Security/AuthenticationEntryPoint.php new file mode 100644 index 00000000..41f624b2 --- /dev/null +++ b/src/Security/AuthenticationEntryPoint.php @@ -0,0 +1,89 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Security; + +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; + +use function Symfony\Component\Translation\t; + +/** + * This class decides, what to do, when a user tries to access a page, that requires authentication and he is not + * authenticated / logged in yet. + * For browser requests, the user is redirected to the login page, for API requests, a 401 response with a JSON encoded + * message is returned. + */ +class AuthenticationEntryPoint implements AuthenticationEntryPointInterface +{ + public function __construct( + private readonly UrlGeneratorInterface $urlGenerator, + ) { + } + + public function start(Request $request, ?AuthenticationException $authException = null): Response + { + //Check if the request is an API request + if ($this->isJSONRequest($request)) { + //If it is, we return a 401 response with a JSON body + return new JsonResponse([ + 'title' => 'Unauthorized', + 'detail' => 'Authentication is required. Please pass a valid API token in the Authorization header.', + ], Response::HTTP_UNAUTHORIZED); + } + + //Otherwise we redirect to the login page + + //Add a nice flash message to make it clear what happened + if ($request->getSession() instanceof Session) { + $request->getSession()->getFlashBag()->add('error', t('login.flash.access_denied_please_login')); + } + + return new RedirectResponse($this->urlGenerator->generate('login')); + } + + private function isJSONRequest(Request $request): bool + { + //If either the content type or accept header is a json type, we assume it is an API request + $contentType = $request->headers->get('Content-Type'); + $accept = $request->headers->get('Accept'); + + $tmp = false; + + if ($contentType !== null) { + $tmp = str_contains($contentType, 'json'); + } + + if ($accept !== null) { + $tmp = $tmp || str_contains($accept, 'json'); + } + + return $tmp; + } +} \ No newline at end of file diff --git a/src/Security/EnsureSAMLUserForSAMLLoginChecker.php b/src/Security/EnsureSAMLUserForSAMLLoginChecker.php new file mode 100644 index 00000000..0ebf893c --- /dev/null +++ b/src/Security/EnsureSAMLUserForSAMLLoginChecker.php @@ -0,0 +1,71 @@ +. + */ +namespace App\Security; + +use App\Entity\UserSystem\User; +use Nbgrp\OneloginSamlBundle\Security\Http\Authenticator\Token\SamlToken; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; +use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * @see \App\Tests\Security\EnsureSAMLUserForSAMLLoginCheckerTest + */ +class EnsureSAMLUserForSAMLLoginChecker implements EventSubscriberInterface +{ + public function __construct(private readonly TranslatorInterface $translator) + { + } + + public static function getSubscribedEvents(): array + { + return [ + AuthenticationSuccessEvent::class => 'onAuthenticationSuccess', + ]; + } + + public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void + { + $token = $event->getAuthenticationToken(); + $user = $token->getUser(); + + //Do not check for anonymous users + if (!$user instanceof User) { + return; + } + + //Do not allow SAML users to login as local user + if ($token instanceof SamlToken && !$user->isSamlUser()) { + throw new CustomUserMessageAccountStatusException($this->translator->trans('saml.error.cannot_login_local_user_per_saml', + [], 'security')); + } + + //Do not allow local users to login as SAML user via local username and password + if ($token instanceof UsernamePasswordToken && $user->isSamlUser()) { + //Ensure that you can not login locally with a SAML user (even though this should not happen, as the password is not set) + throw new CustomUserMessageAccountStatusException($this->translator->trans('saml.error.cannot_login_saml_user_locally', [], 'security')); + } + } +} diff --git a/src/Security/SamlUserFactory.php b/src/Security/SamlUserFactory.php new file mode 100644 index 00000000..312be859 --- /dev/null +++ b/src/Security/SamlUserFactory.php @@ -0,0 +1,153 @@ +. + */ +namespace App\Security; + +use App\Entity\UserSystem\Group; +use App\Entity\UserSystem\User; +use Doctrine\ORM\EntityManagerInterface; +use Nbgrp\OneloginSamlBundle\Security\Http\Authenticator\Token\SamlToken; +use Nbgrp\OneloginSamlBundle\Security\User\SamlUserFactoryInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * @see \App\Tests\Security\SamlUserFactoryTest + */ +class SamlUserFactory implements SamlUserFactoryInterface, EventSubscriberInterface +{ + private readonly array $saml_role_mapping; + + public function __construct(private readonly EntityManagerInterface $em, ?array $saml_role_mapping, private readonly bool $update_group_on_login) + { + $this->saml_role_mapping = $saml_role_mapping ?: []; + } + + final public const SAML_PASSWORD_PLACEHOLDER = '!!SAML!!'; + + public function createUser($username, array $attributes = []): UserInterface + { + $user = new User(); + $user->setName($username); + $user->setNeedPwChange(false); + $user->setPassword(self::SAML_PASSWORD_PLACEHOLDER); + //This is a SAML user now! + $user->setSamlUser(true); + + //Update basic user information + $user->setSamlAttributes($attributes); + + //Check if we can find a group for this user based on the SAML attributes + $group = $this->mapSAMLAttributesToLocalGroup($attributes); + $user->setGroup($group); + + return $user; + } + + /** + * This method is called after a successful authentication. It is used to update the group of the user, + * based on the new SAML attributes. + */ + public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void + { + if (! $this->update_group_on_login) { + return; + } + + $token = $event->getAuthenticationToken(); + $user = $token->getUser(); + //Only update the group if the user is a SAML user + if (! $token instanceof SamlToken || ! $user instanceof User) { + return; + } + + //Check if we can find a group for this user based on the SAML attributes + $group = $this->mapSAMLAttributesToLocalGroup($token->getAttributes()); + //If needed update the group of the user and save it to DB + if ($group !== $user->getGroup()) { + $user->setGroup($group); + $this->em->flush(); + } + } + + /** + * Maps the given SAML attributes to a local group. + * @param array $attributes The SAML attributes + */ + public function mapSAMLAttributesToLocalGroup(array $attributes): ?Group + { + //Extract the roles from the SAML attributes + $roles = $attributes['group'] ?? []; + $group_id = $this->mapSAMLRolesToLocalGroupID($roles); + + //Check if we can find a group with the given ID + if ($group_id !== null) { + $group = $this->em->find(Group::class, $group_id); + if ($group instanceof Group) { + return $group; + } + } + + //If no group was found, return null + return null; + } + + /** + * Maps a list of SAML roles to a local group ID. + * The first available mapping will be used (so the order of the $map is important, first match wins). + * @param array $roles The list of SAML roles + * @param array|null $map The mapping from SAML roles. If null, the global mapping will be used. + * @return int|null The ID of the local group or null if no mapping was found. + */ + public function mapSAMLRolesToLocalGroupID(array $roles, ?array $map = null): ?int + { + $map ??= $this->saml_role_mapping; + + //Iterate over the mapping (from first to last) and check if we have a match + foreach ($map as $saml_role => $group_id) { + //Skip wildcard + if ($saml_role === '*') { + continue; + } + if (in_array($saml_role, $roles, true)) { + return (int) $group_id; + } + } + + + //If no applicable mapping was found, check if we have a default mapping + if (array_key_exists('*', $map)) { + return (int) $map['*']; + } + + //If no mapping was found, return null + return null; + } + + public static function getSubscribedEvents(): array + { + return [ + AuthenticationSuccessEvent::class => 'onAuthenticationSuccess', + ]; + } +} diff --git a/src/Security/TwoFactor/WebauthnKeyLastUseTwoFactorProvider.php b/src/Security/TwoFactor/WebauthnKeyLastUseTwoFactorProvider.php new file mode 100644 index 00000000..9bfa691d --- /dev/null +++ b/src/Security/TwoFactor/WebauthnKeyLastUseTwoFactorProvider.php @@ -0,0 +1,106 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Security\TwoFactor; + +use App\Entity\UserSystem\WebauthnKey; +use Doctrine\ORM\EntityManagerInterface; +use Jbtronics\TFAWebauthn\Services\UserPublicKeyCredentialSourceRepository; +use Jbtronics\TFAWebauthn\Services\WebauthnProvider; +use Scheb\TwoFactorBundle\Security\TwoFactor\AuthenticationContextInterface; +use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\TwoFactorFormRendererInterface; +use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\TwoFactorProviderInterface; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated; + +/** + * This class decorates the Webauthn TwoFactorProvider and adds additional logic which allows us to set a last used date + * on the used webauthn key, which can be viewed in the user settings. + */ +#[AsDecorator('jbtronics_webauthn_tfa.two_factor_provider')] +class WebauthnKeyLastUseTwoFactorProvider implements TwoFactorProviderInterface +{ + + public function __construct( + #[AutowireDecorated] + private readonly TwoFactorProviderInterface $decorated, + private readonly EntityManagerInterface $entityManager, + #[Autowire(service: 'jbtronics_webauthn_tfa.user_public_key_source_repo')] + private readonly UserPublicKeyCredentialSourceRepository $publicKeyCredentialSourceRepository, + #[Autowire(service: 'jbtronics_webauthn_tfa.webauthn_provider')] + private readonly WebauthnProvider $webauthnProvider, + ) + { + } + + public function beginAuthentication(AuthenticationContextInterface $context): bool + { + return $this->decorated->beginAuthentication($context); + } + + public function prepareAuthentication(object $user): void + { + $this->decorated->prepareAuthentication($user); + } + + public function validateAuthenticationCode(object $user, string $authenticationCode): bool + { + //Try to extract the used webauthn key from the code + $webauthnKey = $this->getWebauthnKeyFromCode($authenticationCode); + + //Perform the actual validation like normal + $tmp = $this->decorated->validateAuthenticationCode($user, $authenticationCode); + + //Update the last used date of the webauthn key, if the validation was successful + if($tmp && $webauthnKey !== null) { + $webauthnKey->updateLastTimeUsed(); + $this->entityManager->flush(); + } + + return $tmp; + } + + public function getFormRenderer(): TwoFactorFormRendererInterface + { + return $this->decorated->getFormRenderer(); + } + + private function getWebauthnKeyFromCode(string $authenticationCode): ?WebauthnKey + { + $publicKeyCredentialLoader = $this->webauthnProvider->getPublicKeyCredentialLoader(); + + //Try to load the public key credential from the code + $publicKeyCredential = $publicKeyCredentialLoader->load($authenticationCode); + + //Find the credential source for the given credential id + $publicKeyCredentialSource = $this->publicKeyCredentialSourceRepository->findOneByCredentialId($publicKeyCredential->rawId); + + //If the credential source is not an instance of WebauthnKey, return null + if(!($publicKeyCredentialSource instanceof WebauthnKey)) { + return null; + } + + return $publicKeyCredentialSource; + } +} \ No newline at end of file diff --git a/src/Security/UserChecker.php b/src/Security/UserChecker.php index d42d3390..16afb37e 100644 --- a/src/Security/UserChecker.php +++ b/src/Security/UserChecker.php @@ -25,28 +25,25 @@ namespace App\Security; use App\Entity\UserSystem\User; use Symfony\Component\Security\Core\Exception\AccountStatusException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; -use Symfony\Component\Security\Core\Exception\DisabledException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Contracts\Translation\TranslatorInterface; +/** + * @see \App\Tests\Security\UserCheckerTest + */ final class UserChecker implements UserCheckerInterface { - private TranslatorInterface $translator; - - public function __construct(TranslatorInterface $translator) + public function __construct(private readonly TranslatorInterface $translator) { - $this->translator = $translator; } /** * Checks the user account before authentication. - * - * @throws AccountStatusException */ public function checkPreAuth(UserInterface $user): void { - // TODO: Implement checkPreAuth() method. + //We don't need to check the user before authentication, just implemented to fulfill the interface } /** @@ -60,10 +57,10 @@ final class UserChecker implements UserCheckerInterface return; } - //Check if user is disabled. Then dont allow login + //Check if user is disabled. Then don't allow login if ($user->isDisabled()) { //throw new DisabledException(); - throw new CustomUserMessageAccountStatusException($this->translator->trans('user.login_error.user_disabled')); + throw new CustomUserMessageAccountStatusException($this->translator->trans('user.login_error.user_disabled', [], 'security')); } } } diff --git a/src/Security/Voter/AttachmentVoter.php b/src/Security/Voter/AttachmentVoter.php index 135ba57f..c2b17053 100644 --- a/src/Security/Voter/AttachmentVoter.php +++ b/src/Security/Voter/AttachmentVoter.php @@ -22,57 +22,109 @@ declare(strict_types=1); namespace App\Security\Voter; +use App\Services\UserSystem\VoterHelper; +use Symfony\Bundle\SecurityBundle\Security; +use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\Attachment; -use App\Entity\UserSystem\User; -use App\Services\UserSystem\PermissionManager; -use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\Security\Core\Security; +use App\Entity\Attachments\AttachmentTypeAttachment; +use App\Entity\Attachments\CategoryAttachment; +use App\Entity\Attachments\CurrencyAttachment; +use App\Entity\Attachments\FootprintAttachment; +use App\Entity\Attachments\GroupAttachment; +use App\Entity\Attachments\ManufacturerAttachment; +use App\Entity\Attachments\MeasurementUnitAttachment; +use App\Entity\Attachments\PartAttachment; +use App\Entity\Attachments\ProjectAttachment; +use App\Entity\Attachments\StorageLocationAttachment; +use App\Entity\Attachments\SupplierAttachment; +use App\Entity\Attachments\UserAttachment; +use RuntimeException; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; use function in_array; -class AttachmentVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class AttachmentVoter extends Voter { - protected $security; + private const ALLOWED_ATTRIBUTES = ['read', 'view', 'edit', 'delete', 'create', 'show_private', 'show_history']; - public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security) + public function __construct(private readonly Security $security, private readonly VoterHelper $helper) { - parent::__construct($resolver, $entityManager); - $this->security = $security; } - /** - * Similar to voteOnAttribute, but checking for the anonymous user is already done. - * The current user (or the anonymous user) is passed by $user. - * - * @param string $attribute - */ - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { - //return $this->resolver->inherit($user, 'attachments', $attribute) ?? false; - //If the attachment has no element (which should not happen), we deny access, as we can not determine if the user is allowed to access the associated element - $target_element = $subject->getElement(); - if (! $subject instanceof Attachment || null === $target_element) { + //This voter only works for attachments + if (!is_a($subject, Attachment::class, true)) { return false; } - //Depending on the operation delegate either to the attachments element or to the attachment permission - switch ($attribute) { - //We can view the attachment if we can view the element - case 'read': - case 'view': - return $this->security->isGranted('read', $target_element); - //We can edit/create/delete the attachment if we can edit the element - case 'edit': - case 'create': - case 'delete': - return $this->security->isGranted('edit', $target_element); - - case 'show_private': - return $this->resolver->inherit($user, 'attachments', 'show_private') ?? false; + if ($attribute === 'show_private') { + return $this->helper->isGranted($token, 'attachments', 'show_private'); } - throw new \RuntimeException('Encountered unknown attribute "'.$attribute.'" in AttachmentVoter!'); + + if (is_object($subject)) { + //If the attachment has no element (which should not happen), we deny access, as we can not determine if the user is allowed to access the associated element + $target_element = $subject->getElement(); + if ($target_element instanceof AttachmentContainingDBElement) { + return $this->security->isGranted($this->mapOperation($attribute), $target_element); + } + } + + if (is_string($subject)) { + //If we do not have a concrete element (or we just got a string as value), we delegate to the different categories + if (is_a($subject, AttachmentTypeAttachment::class, true)) { + $param = 'attachment_types'; + } elseif (is_a($subject, CategoryAttachment::class, true)) { + $param = 'categories'; + } elseif (is_a($subject, CurrencyAttachment::class, true)) { + $param = 'currencies'; + } elseif (is_a($subject, ProjectAttachment::class, true)) { + $param = 'projects'; + } elseif (is_a($subject, FootprintAttachment::class, true)) { + $param = 'footprints'; + } elseif (is_a($subject, GroupAttachment::class, true)) { + $param = 'groups'; + } elseif (is_a($subject, ManufacturerAttachment::class, true)) { + $param = 'manufacturers'; + } elseif (is_a($subject, MeasurementUnitAttachment::class, true)) { + $param = 'measurement_units'; + } elseif (is_a($subject, PartAttachment::class, true)) { + $param = 'parts'; + } elseif (is_a($subject, StorageLocationAttachment::class, true)) { + $param = 'storelocations'; + } elseif (is_a($subject, SupplierAttachment::class, true)) { + $param = 'suppliers'; + } elseif (is_a($subject, UserAttachment::class, true)) { + $param = 'users'; + } elseif ($subject === Attachment::class) { + //If the subject was deleted, we can not determine the type properly, so we just use the parts permission + $param = 'parts'; + } + else { + throw new RuntimeException('Encountered unknown Parameter type: ' . $subject); + } + + return $this->helper->isGranted($token, $param, $this->mapOperation($attribute)); + } + + return false; + } + + private function mapOperation(string $attribute): string + { + return match ($attribute) { + 'read', 'view' => 'read', + 'edit', 'create', 'delete' => 'edit', + 'show_history' => 'show_history', + default => throw new \RuntimeException('Encountered unknown attribute "'.$attribute.'" in AttachmentVoter!'), + }; } /** @@ -83,14 +135,24 @@ class AttachmentVoter extends ExtendedVoter * * @return bool True if the attribute and subject are supported, false otherwise */ - protected function supports(string $attribute, $subject): bool + protected function supports(string $attribute, mixed $subject): bool { if (is_a($subject, Attachment::class, true)) { //These are the allowed attributes - return in_array($attribute, ['read', 'view', 'edit', 'delete', 'create', 'show_private'], true); + return in_array($attribute, self::ALLOWED_ATTRIBUTES, true); } //Allow class name as subject return false; } + + public function supportsAttribute(string $attribute): bool + { + return in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, Attachment::class, true); + } } diff --git a/src/Security/Voter/BOMEntryVoter.php b/src/Security/Voter/BOMEntryVoter.php new file mode 100644 index 00000000..121c8172 --- /dev/null +++ b/src/Security/Voter/BOMEntryVoter.php @@ -0,0 +1,90 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Security\Voter; + +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; + +/** + * @phpstan-extends Voter + */ +class BOMEntryVoter extends Voter +{ + + private const ALLOWED_ATTRIBUTES = ['read', 'view', 'edit', 'delete', 'create', 'show_history']; + + public function __construct(private readonly Security $security) + { + } + + protected function supports(string $attribute, mixed $subject): bool + { + return $this->supportsAttribute($attribute) && is_a($subject, ProjectBOMEntry::class, true); + } + + protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool + { + if (!is_a($subject, ProjectBOMEntry::class, true)) { + return false; + } + + if (is_object($subject)) { + $project = $subject->getProject(); + + //Allow everything if the project was not set yet + if ($project === null) { + return true; + } + } else { + //If a string was given, use the general project permissions to resolve permissions + $project = Project::class; + } + + //Entry can be read if the user has read access to the project + if ($attribute === 'read') { + return $this->security->isGranted('read', $project); + } + + //History can be shown if the user has show_history access to the project + if ($attribute === 'show_history') { + return $this->security->isGranted('show_history', $project); + } + + //Everything else can be done if the user has edit access to the project + return $this->security->isGranted('edit', $project); + } + + public function supportsAttribute(string $attribute): bool + { + return in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, ProjectBOMEntry::class, true); + } +} \ No newline at end of file diff --git a/src/Security/Voter/ExtendedVoter.php b/src/Security/Voter/ExtendedVoter.php deleted file mode 100644 index 825d768c..00000000 --- a/src/Security/Voter/ExtendedVoter.php +++ /dev/null @@ -1,75 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace App\Security\Voter; - -use App\Entity\UserSystem\User; -use App\Repository\UserRepository; -use App\Services\UserSystem\PermissionManager; -use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authorization\Voter\Voter; - -/** - * The purpose of this class is, to use the anonymous user from DB in the case, that nobody is logged in. - */ -abstract class ExtendedVoter extends Voter -{ - protected EntityManagerInterface $entityManager; - protected PermissionManager $resolver; - - public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager) - { - $this->resolver = $resolver; - $this->entityManager = $entityManager; - } - - final protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool - { - $user = $token->getUser(); - - //An allowed user is not allowed to do anything... - if ($user instanceof User && $user->isDisabled()) { - return false; - } - - // if the user is anonymous (meaning $user is null), we use the anonymous user. - if (!$user instanceof User) { - /** @var UserRepository $repo */ - $repo = $this->entityManager->getRepository(User::class); - $user = $repo->getAnonymousUser(); - if (null === $user) { - return false; - } - } - - return $this->voteOnUser($attribute, $subject, $user); - } - - /** - * Similar to voteOnAttribute, but checking for the anonymous user is already done. - * The current user (or the anonymous user) is passed by $user. - * - * @param string $attribute - */ - abstract protected function voteOnUser(string $attribute, $subject, User $user): bool; -} diff --git a/src/Security/Voter/GroupVoter.php b/src/Security/Voter/GroupVoter.php index e1e21543..34839d38 100644 --- a/src/Security/Voter/GroupVoter.php +++ b/src/Security/Voter/GroupVoter.php @@ -23,19 +23,29 @@ declare(strict_types=1); namespace App\Security\Voter; use App\Entity\UserSystem\Group; -use App\Entity\UserSystem\User; +use App\Services\UserSystem\VoterHelper; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; -class GroupVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class GroupVoter extends Voter { + + public function __construct(private readonly VoterHelper $helper) + { + } + /** * Similar to voteOnAttribute, but checking for the anonymous user is already done. * The current user (or the anonymous user) is passed by $user. * * @param string $attribute */ - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { - return $this->resolver->inherit($user, 'groups', $attribute) ?? false; + return $this->helper->isGranted($token, 'groups', $attribute); } /** @@ -46,12 +56,22 @@ class GroupVoter extends ExtendedVoter * * @return bool True if the attribute and subject are supported, false otherwise */ - protected function supports(string $attribute, $subject): bool + protected function supports(string $attribute, mixed $subject): bool { if (is_a($subject, Group::class, true)) { - return $this->resolver->isValidOperation('groups', $attribute); + return $this->helper->isValidOperation('groups', $attribute); } return false; } + + public function supportsAttribute(string $attribute): bool + { + return $this->helper->isValidOperation('groups', $attribute); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, Group::class, true); + } } diff --git a/src/Security/Voter/HasAccessPermissionsVoter.php b/src/Security/Voter/HasAccessPermissionsVoter.php new file mode 100644 index 00000000..bd466d07 --- /dev/null +++ b/src/Security/Voter/HasAccessPermissionsVoter.php @@ -0,0 +1,59 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Security\Voter; + +use App\Services\UserSystem\PermissionManager; +use App\Services\UserSystem\VoterHelper; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; + +/** + * This voter implements a virtual role, which can be used if the user has any permission set to allowed. + * We use this to restrict access to the homepage. + * @phpstan-extends Voter + */ +final class HasAccessPermissionsVoter extends Voter +{ + public const ROLE = "HAS_ACCESS_PERMISSIONS"; + + public function __construct(private readonly PermissionManager $permissionManager, private readonly VoterHelper $helper) + { + } + + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool + { + $user = $this->helper->resolveUser($token); + return $this->permissionManager->hasAnyPermissionSetToAllowInherited($user); + } + + protected function supports(string $attribute, mixed $subject): bool + { + return $attribute === self::ROLE; + } + + public function supportsAttribute(string $attribute): bool + { + return $attribute === self::ROLE; + } +} \ No newline at end of file diff --git a/src/Security/Voter/ImpersonateUserVoter.php b/src/Security/Voter/ImpersonateUserVoter.php new file mode 100644 index 00000000..edf55c62 --- /dev/null +++ b/src/Security/Voter/ImpersonateUserVoter.php @@ -0,0 +1,64 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Security\Voter; + +use App\Entity\UserSystem\User; +use App\Services\UserSystem\VoterHelper; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * This voter implements a virtual role, which can be used if the user has any permission set to allowed. + * We use this to restrict access to the homepage. + * @phpstan-extends Voter + */ +final class ImpersonateUserVoter extends Voter +{ + + public function __construct(private readonly VoterHelper $helper) + { + } + + protected function supports(string $attribute, mixed $subject): bool + { + return $attribute === 'CAN_SWITCH_USER' + && $subject instanceof UserInterface; + } + + protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool + { + return $this->helper->isGranted($token, 'users', 'impersonate'); + } + + public function supportsAttribute(string $attribute): bool + { + return $attribute === 'CAN_SWITCH_USER'; + } + + public function supportsType(string $subjectType): bool + { + return is_a($subjectType, User::class, true); + } +} \ No newline at end of file diff --git a/src/Security/Voter/LabelProfileVoter.php b/src/Security/Voter/LabelProfileVoter.php index 5a3699a2..47505bf9 100644 --- a/src/Security/Voter/LabelProfileVoter.php +++ b/src/Security/Voter/LabelProfileVoter.php @@ -42,9 +42,14 @@ declare(strict_types=1); namespace App\Security\Voter; use App\Entity\LabelSystem\LabelProfile; -use App\Entity\UserSystem\User; +use App\Services\UserSystem\VoterHelper; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; -class LabelProfileVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class LabelProfileVoter extends Voter { protected const MAPPING = [ 'read' => 'read_profiles', @@ -55,21 +60,34 @@ class LabelProfileVoter extends ExtendedVoter 'revert_element' => 'revert_element', ]; - protected function voteOnUser(string $attribute, $subject, User $user): bool + public function __construct(private readonly VoterHelper $helper) + {} + + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { - return $this->resolver->inherit($user, 'labels', self::MAPPING[$attribute]) ?? false; + return $this->helper->isGranted($token, 'labels', self::MAPPING[$attribute]); } protected function supports($attribute, $subject): bool { - if ($subject instanceof LabelProfile) { + if (is_a($subject, LabelProfile::class, true)) { if (!isset(self::MAPPING[$attribute])) { return false; } - return $this->resolver->isValidOperation('labels', self::MAPPING[$attribute]); + return $this->helper->isValidOperation('labels', self::MAPPING[$attribute]); } return false; } + + public function supportsAttribute(string $attribute): bool + { + return isset(self::MAPPING[$attribute]); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, LabelProfile::class, true); + } } diff --git a/src/Security/Voter/LogEntryVoter.php b/src/Security/Voter/LogEntryVoter.php index b03cd99f..08bc3b70 100644 --- a/src/Security/Voter/LogEntryVoter.php +++ b/src/Security/Voter/LogEntryVoter.php @@ -22,29 +22,56 @@ declare(strict_types=1); namespace App\Security\Voter; +use App\Services\UserSystem\VoterHelper; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\LogSystem\AbstractLogEntry; -use App\Entity\UserSystem\User; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; -class LogEntryVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class LogEntryVoter extends Voter { - public const ALLOWED_OPS = ['read', 'delete']; + final public const ALLOWED_OPS = ['read', 'show_details', 'delete']; - protected function voteOnUser(string $attribute, $subject, User $user): bool + public function __construct(private readonly Security $security, private readonly VoterHelper $helper) { + } + + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool + { + $user = $this->helper->resolveUser($token); + + if (!$subject instanceof AbstractLogEntry) { + throw new \InvalidArgumentException('The subject must be an instance of '.AbstractLogEntry::class); + } + if ('delete' === $attribute) { - return $this->resolver->inherit($user, 'system', 'delete_logs') ?? false; + return $this->helper->isGranted($token, 'system', 'delete_logs'); } if ('read' === $attribute) { //Allow read of the users own log entries if ( $subject->getUser() === $user - && $this->resolver->inherit($user, 'self', 'show_logs') + && $this->helper->isGranted($token, 'self', 'show_logs') ) { return true; } - return $this->resolver->inherit($user, 'system', 'show_logs') ?? false; + return $this->helper->isGranted($token, 'system', 'show_logs'); + } + + if ('show_details' === $attribute) { + //To view details of a element related log entry, the user needs to be able to view the history of this entity type + $targetClass = $subject->getTargetClass(); + if (null !== $targetClass) { + return $this->security->isGranted('show_history', $targetClass); + } + + //In other cases, this behaves like the read permission + return $this->voteOnAttribute('read', $subject, $token); } return false; @@ -53,9 +80,19 @@ class LogEntryVoter extends ExtendedVoter protected function supports($attribute, $subject): bool { if ($subject instanceof AbstractLogEntry) { - return in_array($subject, static::ALLOWED_OPS, true); + return in_array($attribute, static::ALLOWED_OPS, true); } return false; } + + public function supportsAttribute(string $attribute): bool + { + return in_array($attribute, static::ALLOWED_OPS, true); + } + + public function supportsType(string $subjectType): bool + { + return is_a($subjectType, AbstractLogEntry::class, true); + } } diff --git a/src/Security/Voter/OrderdetailVoter.php b/src/Security/Voter/OrderdetailVoter.php index eaeea11d..20843b9a 100644 --- a/src/Security/Voter/OrderdetailVoter.php +++ b/src/Security/Voter/OrderdetailVoter.php @@ -41,52 +41,41 @@ declare(strict_types=1); namespace App\Security\Voter; +use App\Services\UserSystem\VoterHelper; +use Symfony\Bundle\SecurityBundle\Security; +use App\Entity\Parts\Part; use App\Entity\PriceInformations\Orderdetail; -use App\Entity\UserSystem\User; -use App\Services\UserSystem\PermissionManager; -use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; -class OrderdetailVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class OrderdetailVoter extends Voter { - protected Security $security; - - public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security) + public function __construct(private readonly Security $security, private readonly VoterHelper $helper) { - parent::__construct($resolver, $entityManager); - $this->security = $security; } protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element']; - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { if (! is_a($subject, Orderdetail::class, true)) { throw new \RuntimeException('This voter can only handle Orderdetail objects!'); } - switch ($attribute) { - case 'read': - $operation = 'read'; - break; - case 'edit': //As long as we can edit, we can also edit orderdetails - case 'create': - case 'delete': - $operation = 'edit'; - break; - case 'show_history': - $operation = 'show_history'; - break; - case 'revert_element': - $operation = 'revert_element'; - break; - default: - throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!'); - } + $operation = match ($attribute) { + 'read' => 'read', + 'edit', 'create', 'delete' => 'edit', + 'show_history' => 'show_history', + 'revert_element' => 'revert_element', + default => throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!'), + }; //If we have no part associated use the generic part permission - if (is_string($subject) || $subject->getPart() === null) { - return $this->resolver->inherit($user, 'parts', $operation) ?? false; + if (is_string($subject) || !$subject->getPart() instanceof Part) { + return $this->helper->isGranted($token, 'parts', $operation); } //Otherwise vote on the part @@ -101,4 +90,14 @@ class OrderdetailVoter extends ExtendedVoter return false; } + + public function supportsAttribute(string $attribute): bool + { + return in_array($attribute, self::ALLOWED_PERMS, true); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, Orderdetail::class, true); + } } diff --git a/src/Security/Voter/ParameterVoter.php b/src/Security/Voter/ParameterVoter.php index 525a75b6..8ee2b9f5 100644 --- a/src/Security/Voter/ParameterVoter.php +++ b/src/Security/Voter/ParameterVoter.php @@ -1,4 +1,7 @@ . */ - namespace App\Security\Voter; +use App\Services\UserSystem\VoterHelper; +use Symfony\Bundle\SecurityBundle\Security; +use App\Entity\Base\AbstractDBElement; use App\Entity\Parameters\AbstractParameter; use App\Entity\Parameters\AttachmentTypeParameter; use App\Entity\Parameters\CategoryParameter; @@ -30,102 +35,101 @@ use App\Entity\Parameters\GroupParameter; use App\Entity\Parameters\ManufacturerParameter; use App\Entity\Parameters\MeasurementUnitParameter; use App\Entity\Parameters\PartParameter; -use App\Entity\Parameters\StorelocationParameter; +use App\Entity\Parameters\StorageLocationParameter; use App\Entity\Parameters\SupplierParameter; -use App\Entity\UserSystem\User; -use App\Services\UserSystem\PermissionManager; -use Doctrine\ORM\EntityManagerInterface; use RuntimeException; -use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; -class ParameterVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class ParameterVoter extends Voter { - protected Security $security; + private const ALLOWED_ATTRIBUTES = ['read', 'edit', 'delete', 'create', 'show_history', 'revert_element']; - public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security) + public function __construct(private readonly Security $security, private readonly VoterHelper $helper) { - $this->security = $security; - parent::__construct($resolver, $entityManager); } - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { //return $this->resolver->inherit($user, 'attachments', $attribute) ?? false; - if (!$subject instanceof AbstractParameter) { + if (!is_a($subject, AbstractParameter::class, true)) { return false; } - //If the attachment has no element (which should not happen), we deny access, as we can not determine if the user is allowed to access the associated element - $target_element = $subject->getElement(); - if ($target_element !== null) { - //Depending on the operation delegate either to the attachments element or to the attachment permission + if (is_object($subject)) { + //If the attachment has no element (which should not happen), we deny access, as we can not determine if the user is allowed to access the associated element + $target_element = $subject->getElement(); + if ($target_element instanceof AbstractDBElement) { + $operation = match ($attribute) { + 'read', 'view' => 'read', + 'edit', 'create', 'delete' => 'edit', + 'show_history' => 'show_history', + 'revert_element' => 'revert_element', + default => throw new RuntimeException('Unknown operation: '.$attribute), + }; - - switch ($attribute) { - //We can view the attachment if we can view the element - case 'read': - case 'view': - $operation = 'read'; - break; - //We can edit/create/delete the attachment if we can edit the element - case 'edit': - case 'create': - case 'delete': - $operation = 'edit'; - break; - case 'show_history': - $operation = 'show_history'; - break; - case 'revert_element': - $operation = 'revert_element'; - break; - default: - throw new RuntimeException('Unknown operation: '.$attribute); + return $this->security->isGranted($operation, $target_element); } - - return $this->security->isGranted($operation, $target_element); } - //If we do not have a concrete element, we delegate to the different categories - if ($subject instanceof AttachmentTypeParameter) { + //If we do not have a concrete element (or we just got a string as value), we delegate to the different categories + if (is_a($subject, AttachmentTypeParameter::class, true)) { $param = 'attachment_types'; - } elseif ($subject instanceof CategoryParameter) { + } elseif (is_a($subject, CategoryParameter::class, true)) { $param = 'categories'; - } elseif ($subject instanceof CurrencyParameter) { + } elseif (is_a($subject, CurrencyParameter::class, true)) { $param = 'currencies'; - } elseif ($subject instanceof ProjectParameter) { + } elseif (is_a($subject, ProjectParameter::class, true)) { $param = 'projects'; - } elseif ($subject instanceof FootprintParameter) { + } elseif (is_a($subject, FootprintParameter::class, true)) { $param = 'footprints'; - } elseif ($subject instanceof GroupParameter) { + } elseif (is_a($subject, GroupParameter::class, true)) { $param = 'groups'; - } elseif ($subject instanceof ManufacturerParameter) { + } elseif (is_a($subject, ManufacturerParameter::class, true)) { $param = 'manufacturers'; - } elseif ($subject instanceof MeasurementUnitParameter) { + } elseif (is_a($subject, MeasurementUnitParameter::class, true)) { $param = 'measurement_units'; - } elseif ($subject instanceof PartParameter) { + } elseif (is_a($subject, PartParameter::class, true)) { $param = 'parts'; - } elseif ($subject instanceof StorelocationParameter) { + } elseif (is_a($subject, StorageLocationParameter::class, true)) { $param = 'storelocations'; - } elseif ($subject instanceof SupplierParameter) { + } elseif (is_a($subject, SupplierParameter::class, true)) { $param = 'suppliers'; - } else { - throw new RuntimeException('Encountered unknown Parameter type: ' . get_class($subject)); + } elseif ($subject === AbstractParameter::class) { + //If the subject was deleted, we can not determine the type properly, so we just use the parts permission + $param = 'parts'; + } + else { + throw new RuntimeException('Encountered unknown Parameter type: ' . (is_object($subject) ? $subject::class : $subject)); } - return $this->resolver->inherit($user, $param, $attribute) ?? false; + return $this->helper->isGranted($token, $param, $attribute); } - protected function supports(string $attribute, $subject) + protected function supports(string $attribute, $subject): bool { if (is_a($subject, AbstractParameter::class, true)) { //These are the allowed attributes - return in_array($attribute, ['read', 'edit', 'delete', 'create', 'show_history', 'revert_element'], true); + return in_array($attribute, self::ALLOWED_ATTRIBUTES, true); } //Allow class name as subject return false; } -} \ No newline at end of file + + public function supportsAttribute(string $attribute): bool + { + return in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, AbstractParameter::class, true); + } + +} diff --git a/src/Security/Voter/PartAssociationVoter.php b/src/Security/Voter/PartAssociationVoter.php new file mode 100644 index 00000000..7678b67a --- /dev/null +++ b/src/Security/Voter/PartAssociationVoter.php @@ -0,0 +1,105 @@ +. + */ + +declare(strict_types=1); + +/** + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2022 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 . + */ + +namespace App\Security\Voter; + +use App\Entity\Parts\PartAssociation; +use App\Services\UserSystem\VoterHelper; +use Symfony\Bundle\SecurityBundle\Security; +use App\Entity\Parts\Part; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; + +/** + * This voter handles permissions for part associations. + * The permissions are inherited from the part. + * @phpstan-extends Voter + */ +final class PartAssociationVoter extends Voter +{ + public function __construct(private readonly Security $security, private readonly VoterHelper $helper) + { + } + + protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element']; + + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool + { + if (!is_string($subject) && !$subject instanceof PartAssociation) { + throw new \RuntimeException('Invalid subject type!'); + } + + $operation = match ($attribute) { + 'read' => 'read', + 'edit', 'create', 'delete' => 'edit', + 'show_history' => 'show_history', + 'revert_element' => 'revert_element', + default => throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!'), + }; + + //If we have no part associated use the generic part permission + if (is_string($subject) || !$subject->getOwner() instanceof Part) { + return $this->helper->isGranted($token, 'parts', $operation); + } + + //Otherwise vote on the part + return $this->security->isGranted($attribute, $subject->getOwner()); + } + + protected function supports($attribute, $subject): bool + { + if (is_a($subject, PartAssociation::class, true)) { + return in_array($attribute, self::ALLOWED_PERMS, true); + } + + return false; + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, PartAssociation::class, true); + } + + public function supportsAttribute(string $attribute): bool + { + return in_array($attribute, self::ALLOWED_PERMS, true); + } +} diff --git a/src/Security/Voter/PartLotVoter.php b/src/Security/Voter/PartLotVoter.php index da05070b..a64473c8 100644 --- a/src/Security/Voter/PartLotVoter.php +++ b/src/Security/Voter/PartLotVoter.php @@ -41,57 +41,52 @@ declare(strict_types=1); namespace App\Security\Voter; +use App\Services\UserSystem\VoterHelper; +use Symfony\Bundle\SecurityBundle\Security; +use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\UserSystem\User; -use App\Services\UserSystem\PermissionManager; -use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; -class PartLotVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class PartLotVoter extends Voter { - protected Security $security; - - public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security) + public function __construct(private readonly Security $security, private readonly VoterHelper $helper) { - parent::__construct($resolver, $entityManager); - $this->security = $security; } protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element', 'withdraw', 'add', 'move']; - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { - if (! is_a($subject, PartLot::class, true)) { - throw new \RuntimeException('This voter can only handle PartLot objects!'); - } + $user = $this->helper->resolveUser($token); - if (in_array($attribute, ['withdraw', 'add', 'move'])) + if (in_array($attribute, ['withdraw', 'add', 'move'], true)) { - return $this->resolver->inherit($user, 'parts_stock', $attribute) ?? false; + $base_permission = $this->helper->isGranted($token, 'parts_stock', $attribute); + + $lot_permission = true; + //If the lot has an owner, we need to check if the user is the owner of the lot to be allowed to withdraw it. + if ($subject instanceof PartLot && $subject->getOwner()) { + $lot_permission = $subject->getOwner() === $user || $subject->getOwner()->getID() === $user->getID(); + } + + return $base_permission && $lot_permission; } - switch ($attribute) { - case 'read': - $operation = 'read'; - break; - case 'edit': //As long as we can edit, we can also edit orderdetails - case 'create': - case 'delete': - $operation = 'edit'; - break; - case 'show_history': - $operation = 'show_history'; - break; - case 'revert_element': - $operation = 'revert_element'; - break; - default: - throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!'); - } + $operation = match ($attribute) { + 'read' => 'read', + 'edit', 'create', 'delete' => 'edit', + 'show_history' => 'show_history', + 'revert_element' => 'revert_element', + default => throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!'), + }; //If we have no part associated use the generic part permission - if (is_string($subject) || $subject->getPart() === null) { - return $this->resolver->inherit($user, 'parts', $operation) ?? false; + if (is_string($subject) || !$subject->getPart() instanceof Part) { + return $this->helper->isGranted($token, 'parts', $operation); } //Otherwise vote on the part @@ -106,4 +101,14 @@ class PartLotVoter extends ExtendedVoter return false; } + + public function supportsAttribute(string $attribute): bool + { + return in_array($attribute, self::ALLOWED_PERMS, true); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, PartLot::class, true); + } } diff --git a/src/Security/Voter/PartVoter.php b/src/Security/Voter/PartVoter.php index fb1e3a38..ef70b6ce 100644 --- a/src/Security/Voter/PartVoter.php +++ b/src/Security/Voter/PartVoter.php @@ -23,30 +23,48 @@ declare(strict_types=1); namespace App\Security\Voter; use App\Entity\Parts\Part; -use App\Entity\UserSystem\User; +use App\Services\UserSystem\VoterHelper; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; /** * A Voter that votes on Part entities. * * See parts permissions for valid operations. + * + * @phpstan-extends Voter */ -class PartVoter extends ExtendedVoter +final class PartVoter extends Voter { - public const READ = 'read'; + final public const READ = 'read'; + + public function __construct(private readonly VoterHelper $helper) + { + } protected function supports($attribute, $subject): bool { if (is_a($subject, Part::class, true)) { - return $this->resolver->isValidOperation('parts', $attribute); + return $this->helper->isValidOperation('parts', $attribute); } //Allow class name as subject return false; } - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { //Null concealing operator means, that no - return $this->resolver->inherit($user, 'parts', $attribute) ?? false; + return $this->helper->isGranted($token, 'parts', $attribute); + } + + public function supportsAttribute(string $attribute): bool + { + return $this->helper->isValidOperation('parts', $attribute); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, Part::class, true); } } diff --git a/src/Security/Voter/PermissionVoter.php b/src/Security/Voter/PermissionVoter.php index 018c4f92..c6ec1b3d 100644 --- a/src/Security/Voter/PermissionVoter.php +++ b/src/Security/Voter/PermissionVoter.php @@ -22,27 +22,35 @@ declare(strict_types=1); namespace App\Security\Voter; -use App\Entity\UserSystem\User; +use App\Services\UserSystem\VoterHelper; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; /** * This voter allows you to directly check permissions from the permission structure, without passing an object. * This use the syntax like "@permission.op" * However you should use the "normal" object based voters if possible, because they are needed for a future ACL system. + * @phpstan-extends Voter */ -class PermissionVoter extends ExtendedVoter +final class PermissionVoter extends Voter { - /** - * Similar to voteOnAttribute, but checking for the anonymous user is already done. - * The current user (or the anonymous user) is passed by $user. - * - * @param string $attribute - */ - protected function voteOnUser(string $attribute, $subject, User $user): bool + public function __construct(private readonly VoterHelper $helper) + { + + } + + protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { $attribute = ltrim($attribute, '@'); [$perm, $op] = explode('.', $attribute); - return $this->resolver->inherit($user, $perm, $op) ?? false; + return $this->helper->isGranted($token, $perm, $op); + } + + public function supportsAttribute(string $attribute): bool + { + //Check if the attribute has the form '@permission.operation' + return preg_match('#^@\\w+\\.\\w+$#', $attribute) === 1; } /** @@ -53,14 +61,14 @@ class PermissionVoter extends ExtendedVoter * * @return bool True if the attribute and subject are supported, false otherwise */ - protected function supports(string $attribute, $subject): bool + protected function supports(string $attribute, mixed $subject): bool { //Check if the attribute has the form @permission.operation if (preg_match('#^@\\w+\\.\\w+$#', $attribute)) { $attribute = ltrim($attribute, '@'); [$perm, $op] = explode('.', $attribute); - $valid = $this->resolver->isValidOperation($perm, $op); + $valid = $this->helper->isValidOperation($perm, $op); //if an invalid operation is encountered, throw an exception so the developer knows it if(!$valid) { diff --git a/src/Security/Voter/PricedetailVoter.php b/src/Security/Voter/PricedetailVoter.php index eb4a81aa..681b73b7 100644 --- a/src/Security/Voter/PricedetailVoter.php +++ b/src/Security/Voter/PricedetailVoter.php @@ -41,52 +41,38 @@ declare(strict_types=1); namespace App\Security\Voter; +use App\Services\UserSystem\VoterHelper; +use Symfony\Bundle\SecurityBundle\Security; +use App\Entity\PriceInformations\Orderdetail; +use App\Entity\Parts\Part; use App\Entity\PriceInformations\Pricedetail; -use App\Entity\UserSystem\User; -use App\Services\UserSystem\PermissionManager; -use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; -class PricedetailVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class PricedetailVoter extends Voter { - protected Security $security; - - public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security) + public function __construct(private readonly Security $security, private readonly VoterHelper $helper) { - parent::__construct($resolver, $entityManager); - $this->security = $security; } protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element']; - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { - if (!is_a($subject, Pricedetail::class, true)) { - throw new \RuntimeException('This voter can only handle Pricedetails objects!'); - } - - switch ($attribute) { - case 'read': - $operation = 'read'; - break; - case 'edit': //As long as we can edit, we can also edit orderdetails - case 'create': - case 'delete': - $operation = 'edit'; - break; - case 'show_history': - $operation = 'show_history'; - break; - case 'revert_element': - $operation = 'revert_element'; - break; - default: - throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!'); - } + $operation = match ($attribute) { + 'read' => 'read', + 'edit', 'create', 'delete' => 'edit', + 'show_history' => 'show_history', + 'revert_element' => 'revert_element', + default => throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!'), + }; //If we have no part associated use the generic part permission - if (is_string($subject) || $subject->getOrderdetail() === null || $subject->getOrderdetail()->getPart() === null) { - return $this->resolver->inherit($user, 'parts', $operation) ?? false; + if (is_string($subject) || !$subject->getOrderdetail() instanceof Orderdetail || !$subject->getOrderdetail()->getPart() instanceof Part) { + return $this->helper->isGranted($token, 'parts', $operation); } //Otherwise vote on the part @@ -101,4 +87,14 @@ class PricedetailVoter extends ExtendedVoter return false; } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, Pricedetail::class, true); + } + + public function supportsAttribute(string $attribute): bool + { + return in_array($attribute, self::ALLOWED_PERMS, true); + } } diff --git a/src/Security/Voter/StructureVoter.php b/src/Security/Voter/StructureVoter.php index df88e113..2417b796 100644 --- a/src/Security/Voter/StructureVoter.php +++ b/src/Security/Voter/StructureVoter.php @@ -28,14 +28,19 @@ use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; -use App\Entity\UserSystem\User; -use function get_class; +use App\Services\UserSystem\VoterHelper; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; + use function is_object; -class StructureVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class StructureVoter extends Voter { protected const OBJ_PERM_MAP = [ AttachmentType::class => 'attachment_types', @@ -43,12 +48,16 @@ class StructureVoter extends ExtendedVoter Project::class => 'projects', Footprint::class => 'footprints', Manufacturer::class => 'manufacturers', - Storelocation::class => 'storelocations', + StorageLocation::class => 'storelocations', Supplier::class => 'suppliers', Currency::class => 'currencies', MeasurementUnit::class => 'measurement_units', ]; + public function __construct(private readonly VoterHelper $helper) + { + } + /** * Determines if the attribute and subject are supported by this voter. * @@ -57,31 +66,32 @@ class StructureVoter extends ExtendedVoter * * @return bool True if the attribute and subject are supported, false otherwise */ - protected function supports(string $attribute, $subject): bool + protected function supports(string $attribute, mixed $subject): bool { if (is_object($subject) || is_string($subject)) { $permission_name = $this->instanceToPermissionName($subject); //If permission name is null, then the subject is not supported - return (null !== $permission_name) && $this->resolver->isValidOperation($permission_name, $attribute); + return (null !== $permission_name) && $this->helper->isValidOperation($permission_name, $attribute); } return false; } + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || $this->instanceToPermissionName($subjectType) !== null; + } + /** - * Maps a instance type to the permission name. + * Maps an instance type to the permission name. * - * @param object|string $subject The subject for which the permission name should be generated + * @param object|string $subject The subject for which the permission name should be generated * * @return string|null the name of the permission for the subject's type or null, if the subject is not supported */ - protected function instanceToPermissionName($subject): ?string + protected function instanceToPermissionName(object|string $subject): ?string { - if (!is_string($subject)) { - $class_name = get_class($subject); - } else { - $class_name = $subject; - } + $class_name = is_string($subject) ? $subject : $subject::class; //If it is existing in index, we can skip the loop if (isset(static::OBJ_PERM_MAP[$class_name])) { @@ -103,10 +113,10 @@ class StructureVoter extends ExtendedVoter * * @param string $attribute */ - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { $permission_name = $this->instanceToPermissionName($subject); //Just resolve the permission - return $this->resolver->inherit($user, $permission_name, $attribute) ?? false; + return $this->helper->isGranted($token, $permission_name, $attribute); } } diff --git a/src/Security/Voter/UserVoter.php b/src/Security/Voter/UserVoter.php index dcd7cb20..b41c1a40 100644 --- a/src/Security/Voter/UserVoter.php +++ b/src/Security/Voter/UserVoter.php @@ -23,10 +23,22 @@ declare(strict_types=1); namespace App\Security\Voter; use App\Entity\UserSystem\User; +use App\Services\UserSystem\PermissionManager; +use App\Services\UserSystem\VoterHelper; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; + use function in_array; -class UserVoter extends ExtendedVoter +/** + * @phpstan-extends Voter + */ +final class UserVoter extends Voter { + public function __construct(private readonly VoterHelper $helper, private readonly PermissionManager $resolver) + { + } + /** * Determines if the attribute and subject are supported by this voter. * @@ -35,41 +47,66 @@ class UserVoter extends ExtendedVoter * * @return bool True if the attribute and subject are supported, false otherwise */ - protected function supports(string $attribute, $subject): bool + protected function supports(string $attribute, mixed $subject): bool { if (is_a($subject, User::class, true)) { - return in_array($attribute, array_merge( - $this->resolver->listOperationsForPermission('users'), - $this->resolver->listOperationsForPermission('self')), - false + return in_array($attribute, + array_merge( + $this->resolver->listOperationsForPermission('users'), + $this->resolver->listOperationsForPermission('self'), + ['info'] + ), + true ); } return false; } + public function supportsAttribute(string $attribute): bool + { + return $this->helper->isValidOperation('users', $attribute) || $this->helper->isValidOperation('self', $attribute) || $attribute === 'info'; + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'string' || is_a($subjectType, User::class, true); + } + /** * Similar to voteOnAttribute, but checking for the anonymous user is already done. * The current user (or the anonymous user) is passed by $user. * * @param string $attribute */ - protected function voteOnUser(string $attribute, $subject, User $user): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool { + $user = $this->helper->resolveUser($token); + + if ($attribute === 'info') { + //Every logged-in user (non-anonymous) can see the info pages of other users + if (!$user->isAnonymousUser()) { + return true; + } + + //For the anonymous user, use the user read permission + $attribute = 'read'; + } + //Check if the checked user is the user itself if (($subject instanceof User) && $subject->getID() === $user->getID() && - $this->resolver->isValidOperation('self', $attribute)) { + $this->helper->isValidOperation('self', $attribute)) { //Then we also need to check the self permission - $tmp = $this->resolver->inherit($user, 'self', $attribute) ?? false; + $tmp = $this->helper->isGranted($token, 'self', $attribute); //But if the self value is not allowed then use just the user value: if ($tmp) { return $tmp; } } - //Else just check users permission: - if ($this->resolver->isValidOperation('users', $attribute)) { - return $this->resolver->inherit($user, 'users', $attribute) ?? false; + //Else just check user permission: + if ($this->helper->isValidOperation('users', $attribute)) { + return $this->helper->isGranted($token, 'users', $attribute); } return false; diff --git a/src/Serializer/APIPlatform/DetermineTypeFromElementIRIDenormalizer.php b/src/Serializer/APIPlatform/DetermineTypeFromElementIRIDenormalizer.php new file mode 100644 index 00000000..8283dbbe --- /dev/null +++ b/src/Serializer/APIPlatform/DetermineTypeFromElementIRIDenormalizer.php @@ -0,0 +1,124 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Serializer\APIPlatform; + +use ApiPlatform\Metadata\Exception\ResourceClassNotFoundException; +use ApiPlatform\Api\IriConverterInterface; +use ApiPlatform\Metadata\Operation; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; +use App\Entity\Attachments\Attachment; +use App\Entity\Parameters\AbstractParameter; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; + +/** + * The purpose of this normalizer is to automatically add the _type discriminator field for the Attachment and AbstractParameter classes + * based on the element IRI. + * So that for a request pointing for a part element, an PartAttachment is automatically created. + * This highly improves UX and is the expected behavior. + */ +class DetermineTypeFromElementIRIDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface +{ + private const SUPPORTED_CLASSES = [ + Attachment::class, + AbstractParameter::class + ]; + + use DenormalizerAwareTrait; + + private const ALREADY_CALLED = self::class . '::ALREADY_CALLED'; + + public function __construct(private readonly IriConverterInterface $iriConverter, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory) + { + } + + /** + * This functions add the _type discriminator to the input array if necessary automatically from the given element IRI. + * @param array $input + * @param Operation $operation + * @return array + * @throws ResourceClassNotFoundException + */ + private function addTypeDiscriminatorIfNecessary(array $input, Operation $operation): array + { + + //We only want to modify POST requests + if (!$operation instanceof Post) { + return $input; + } + + + //Ignore if the _type variable is already set + if (isset($input['_type'])) { + return $input; + } + + if (!isset($input['element']) || !is_string($input['element'])) { + return $input; + } + + //Retrieve the element + $element = $this->iriConverter->getResourceFromIri($input['element']); + + //Retrieve the short name of the operation + $type = $this->resourceMetadataCollectionFactory->create($element::class)->getOperation()->getShortName(); + $input['_type'] = $type; + + return $input; + } + + public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): object + { + //If we are on API platform, we want to add the type discriminator if necessary + if (!isset($data['_type']) && isset($context['operation'])) { + $data = $this->addTypeDiscriminatorIfNecessary($data, $context['operation']); + } + + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool + { + //Only denormalize if the _type discriminator is not set and the class is supported and we not have already called this function + return !isset($context[self::ALREADY_CALLED]) + && is_array($data) + && !isset($data['_type']) + && in_array($type, self::SUPPORTED_CLASSES, true); + } + + public function getSupportedTypes(?string $format): array + { + $tmp = []; + + foreach (self::SUPPORTED_CLASSES as $class) { + $tmp[$class] = false; + } + + return $tmp; + } +} \ No newline at end of file diff --git a/src/Serializer/APIPlatform/OverrideClassDenormalizer.php b/src/Serializer/APIPlatform/OverrideClassDenormalizer.php new file mode 100644 index 00000000..c8155abc --- /dev/null +++ b/src/Serializer/APIPlatform/OverrideClassDenormalizer.php @@ -0,0 +1,61 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Serializer\APIPlatform; + +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; + +/** + * The idea of this denormalizer is to allow to override the type of the object created using a certain context key. + * This is required to resolve the issue of the serializer/API platform not correctly being able to determine the type + * of the "element" properties of the Attachment and Parameter subclasses. + */ +class OverrideClassDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface +{ + use DenormalizerAwareTrait; + + public const CONTEXT_KEY = '__override_type__'; + + public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed + { + //Deserialize the data with the overridden type + $overrideType = $context[self::CONTEXT_KEY]; + unset($context[self::CONTEXT_KEY]); + + return $this->denormalizer->denormalize($data, $overrideType, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool + { + return isset($context[self::CONTEXT_KEY]); + } + + public function getSupportedTypes(?string $format): array + { + return [ + '*' => false, + ]; + } +} \ No newline at end of file diff --git a/src/Serializer/APIPlatform/SkippableItemNormalizer.php b/src/Serializer/APIPlatform/SkippableItemNormalizer.php new file mode 100644 index 00000000..5568c4cb --- /dev/null +++ b/src/Serializer/APIPlatform/SkippableItemNormalizer.php @@ -0,0 +1,90 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Serializer\APIPlatform; + +use ApiPlatform\Serializer\ItemNormalizer; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\SerializerAwareInterface; +use Symfony\Component\Serializer\SerializerInterface; + +/** + * This class decorates API Platform's ItemNormalizer to allow skipping the normalization process by setting the + * DISABLE_ITEM_NORMALIZER context key to true. This is useful for all kind of serialization operations, where the API + * Platform subsystem should not be used. + */ +#[AsDecorator("api_platform.serializer.normalizer.item")] +class SkippableItemNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface +{ + + public const DISABLE_ITEM_NORMALIZER = 'DISABLE_ITEM_NORMALIZER'; + + public function __construct(private readonly ItemNormalizer $inner) + { + + } + + public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed + { + return $this->inner->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool + { + if ($context[self::DISABLE_ITEM_NORMALIZER] ?? false) { + return false; + } + + return $this->inner->supportsDenormalization($data, $type, $format, $context); + } + + public function normalize(mixed $object, ?string $format = null, array $context = []): float|int|bool|\ArrayObject|array|string|null + { + return $this->inner->normalize($object, $format, $context); + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + if ($context[self::DISABLE_ITEM_NORMALIZER] ?? false) { + return false; + } + + return $this->inner->supportsNormalization($data, $format, $context); + } + + public function setSerializer(SerializerInterface $serializer): void + { + $this->inner->setSerializer($serializer); + } + + public function getSupportedTypes(?string $format): array + { + //Don't cache results, as we check for the context + return [ + 'object' => false + ]; + } +} \ No newline at end of file diff --git a/src/Serializer/AttachmentNormalizer.php b/src/Serializer/AttachmentNormalizer.php new file mode 100644 index 00000000..bd791d04 --- /dev/null +++ b/src/Serializer/AttachmentNormalizer.php @@ -0,0 +1,84 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Serializer; + +use App\Entity\Attachments\Attachment; +use App\Services\Attachments\AttachmentURLGenerator; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +class AttachmentNormalizer implements NormalizerInterface, NormalizerAwareInterface +{ + + use NormalizerAwareTrait; + + private const ALREADY_CALLED = 'ATTACHMENT_NORMALIZER_ALREADY_CALLED'; + + public function __construct( + private readonly AttachmentURLGenerator $attachmentURLGenerator, + ) + { + } + + public function normalize(mixed $object, ?string $format = null, array $context = []): array|null + { + if (!$object instanceof Attachment) { + throw new \InvalidArgumentException('This normalizer only supports Attachment objects!'); + } + + //Prevent loops, by adding a flag to the context + $context[self::ALREADY_CALLED] = true; + + $data = $this->normalizer->normalize($object, $format, $context); + $data['internal_path'] = $this->attachmentURLGenerator->getInternalViewURL($object); + + //Add thumbnail url if the attachment is a picture + $data['thumbnail_url'] = $object->isPicture() ? $this->attachmentURLGenerator->getThumbnailURL($object) : null; + + //For backwards compatibility reasons + //Deprecated: Use internal_path and external_path instead + $data['media_url'] = $data['internal_path'] ?? $object->getExternalPath(); + + return $data; + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + // avoid recursion: only call once per object + if (isset($context[self::ALREADY_CALLED])) { + return false; + } + + return $data instanceof Attachment; + } + + public function getSupportedTypes(?string $format): array + { + return [ + //We depend on the context to determine if we should normalize or not + Attachment::class => false, + ]; + } +} \ No newline at end of file diff --git a/src/Serializer/BigNumberNormalizer.php b/src/Serializer/BigNumberNormalizer.php new file mode 100644 index 00000000..10cedfa5 --- /dev/null +++ b/src/Serializer/BigNumberNormalizer.php @@ -0,0 +1,75 @@ +. + */ +namespace App\Serializer; + +use Brick\Math\BigDecimal; +use Brick\Math\BigNumber; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * @see \App\Tests\Serializer\BigNumberNormalizerTest + */ +class BigNumberNormalizer implements NormalizerInterface, DenormalizerInterface +{ + + public function supportsNormalization($data, ?string $format = null, array $context = []): bool + { + return $data instanceof BigNumber; + } + + public function normalize($object, ?string $format = null, array $context = []): string + { + if (!$object instanceof BigNumber) { + throw new \InvalidArgumentException('This normalizer only supports BigNumber objects!'); + } + + return (string) $object; + } + + /** + * @return bool[] + */ + public function getSupportedTypes(?string $format): array + { + return [ + BigNumber::class => true, + BigDecimal::class => true, + ]; + } + + public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): BigNumber|null + { + if (!is_a($type, BigNumber::class, true)) { + throw new \InvalidArgumentException('This normalizer only supports BigNumber objects!'); + } + + return $type::of($data); + } + + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool + { + //data must be a string or a number (int, float, etc.) and the type must be BigNumber or BigDecimal + return (is_string($data) || is_numeric($data)) && (is_subclass_of($type, BigNumber::class)); + } +} diff --git a/src/Serializer/PartNormalizer.php b/src/Serializer/PartNormalizer.php new file mode 100644 index 00000000..9050abfc --- /dev/null +++ b/src/Serializer/PartNormalizer.php @@ -0,0 +1,206 @@ +. + */ +namespace App\Serializer; + +use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; +use App\Entity\Parts\StorageLocation; +use App\Entity\Parts\Supplier; +use App\Entity\PriceInformations\Orderdetail; +use App\Entity\PriceInformations\Pricedetail; +use Brick\Math\BigDecimal; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * @see \App\Tests\Serializer\PartNormalizerTest + */ +class PartNormalizer implements NormalizerInterface, DenormalizerInterface, NormalizerAwareInterface, DenormalizerAwareInterface +{ + + use NormalizerAwareTrait; + use DenormalizerAwareTrait; + + private const ALREADY_CALLED = 'PART_NORMALIZER_ALREADY_CALLED'; + + private const DENORMALIZE_KEY_MAPPING = [ + 'notes' => 'comment', + 'quantity' => 'instock', + 'amount' => 'instock', + 'mpn' => 'manufacturer_product_number', + 'spn' => 'supplier_part_number', + 'supplier_product_number' => 'supplier_part_number', + 'storage_location' => 'storelocation', + ]; + + public function __construct( + private readonly StructuralElementFromNameDenormalizer $locationDenormalizer, + ) + { + } + + public function supportsNormalization($data, ?string $format = null, array $context = []): bool + { + //We only remove the type field for CSV export + return !isset($context[self::ALREADY_CALLED]) && $format === 'csv' && $data instanceof Part ; + } + + public function normalize($object, ?string $format = null, array $context = []): array + { + if (!$object instanceof Part) { + throw new \InvalidArgumentException('This normalizer only supports Part objects!'); + } + + $context[self::ALREADY_CALLED] = true; + + //Prevent exception in API Platform + if ($object->getID() === null) { + $context['iri'] = 'not-persisted'; + } + + $data = $this->normalizer->normalize($object, $format, $context); + + //Remove type field for CSV export + if ($format === 'csv') { + unset($data['type']); + } + + return $data; + } + + public function supportsDenormalization($data, string $type, string $format = null, array $context = []): bool + { + //Only denormalize if we are doing a file import operation + if (!($context['partdb_import'] ?? false)) { + return false; + } + + //Only make the denormalizer available on import operations + return !isset($context[self::ALREADY_CALLED]) + && is_array($data) && is_a($type, Part::class, true); + } + + private function normalizeKeys(array &$data): array + { + //Rename keys based on the mapping, while leaving the data untouched + foreach ($data as $key => $value) { + if (isset(self::DENORMALIZE_KEY_MAPPING[$key])) { + $data[self::DENORMALIZE_KEY_MAPPING[$key]] = $value; + unset($data[$key]); + } + } + + return $data; + } + + public function denormalize($data, string $type, ?string $format = null, array $context = []): ?Part + { + $this->normalizeKeys($data); + + //Empty IPN should be null, or we get a constraint error + if (isset($data['ipn']) && $data['ipn'] === '') { + $data['ipn'] = null; + } + + //Fill empty needs_review and needs_review_comment fields with false + if (empty($data['needs_review'])) { + $data['needs_review'] = false; + } + if (empty($data['favorite'])) { + $data['favorite'] = false; + } + if (empty($data['minamount'])) { + $data['minamount'] = 0.0; + } + + $context[self::ALREADY_CALLED] = true; + + $object = $this->denormalizer->denormalize($data, $type, $format, $context); + + if (!$object instanceof Part) { + throw new \InvalidArgumentException('This normalizer only supports Part objects!'); + } + + if ((isset($data['instock']) && trim((string) $data['instock']) !== "") || (isset($data['storelocation']) && trim((string) $data['storelocation']) !== "")) { + $partLot = new PartLot(); + + if (isset($data['instock']) && $data['instock'] !== "") { + //Replace comma with dot + $instock = (float) str_replace(',', '.', (string) $data['instock']); + + $partLot->setAmount($instock); + } else { + $partLot->setInstockUnknown(true); + } + + if (isset($data['storelocation']) && $data['storelocation'] !== "") { + $location = $this->locationDenormalizer->denormalize($data['storelocation'], StorageLocation::class, $format, $context); + $partLot->setStorageLocation($location); + } + + $object->addPartLot($partLot); + } + + if (isset($data['supplier']) && $data['supplier'] !== "") { + $supplier = $this->locationDenormalizer->denormalize($data['supplier'], Supplier::class, $format, $context); + + if ($supplier !== null) { + $orderdetail = new Orderdetail(); + $orderdetail->setSupplier($supplier); + + if (isset($data['supplier_part_number']) && $data['supplier_part_number'] !== "") { + $orderdetail->setSupplierpartnr($data['supplier_part_number']); + } + + $object->addOrderdetail($orderdetail); + + if (isset($data['price']) && $data['price'] !== "") { + $pricedetail = new Pricedetail(); + $pricedetail->setMinDiscountQuantity(1); + $pricedetail->setPriceRelatedQuantity(1); + $price = BigDecimal::of(str_replace(',', '.', (string) $data['price'])); + $pricedetail->setPrice($price); + + $orderdetail->addPricedetail($pricedetail); + } + } + } + + return $object; + } + + /** + * @return bool[] + */ + public function getSupportedTypes(?string $format): array + { + //Must be false, because we rely on is_array($data) in supportsDenormalization() + return [ + Part::class => false, + ]; + } +} diff --git a/src/Serializer/StructuralElementDenormalizer.php b/src/Serializer/StructuralElementDenormalizer.php new file mode 100644 index 00000000..d9b03ae7 --- /dev/null +++ b/src/Serializer/StructuralElementDenormalizer.php @@ -0,0 +1,132 @@ +. + */ +namespace App\Serializer; + +use App\Entity\Base\AbstractStructuralDBElement; +use App\Repository\StructuralDBElementRepository; +use App\Serializer\APIPlatform\SkippableItemNormalizer; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + +/** + * @see \App\Tests\Serializer\StructuralElementDenormalizerTest + */ +class StructuralElementDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface +{ + + use DenormalizerAwareTrait; + + private const ALREADY_CALLED = 'STRUCTURAL_DENORMALIZER_ALREADY_CALLED'; + + private array $object_cache = []; + + public function __construct( + private readonly EntityManagerInterface $entityManager) + { + } + + public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool + { + //Only denormalize if we are doing a file import operation + if (!($context['partdb_import'] ?? false)) { + return false; + } + + //If we already handled this object, skip it + if (isset($context[self::ALREADY_CALLED]) + && is_array($context[self::ALREADY_CALLED]) + && in_array($data, $context[self::ALREADY_CALLED], true)) { + return false; + } + + return is_array($data) + && is_subclass_of($type, AbstractStructuralDBElement::class) + //Only denormalize if we are doing a file import operation + && in_array('import', $context['groups'] ?? [], true); + } + + /** + * @template T of AbstractStructuralDBElement + * @param $data + * @phpstan-param class-string $type + * @param string|null $format + * @param array $context + * @return AbstractStructuralDBElement|null + * @phpstan-return T|null + */ + public function denormalize($data, string $type, ?string $format = null, array $context = []): ?AbstractStructuralDBElement + { + //Do not use API Platform's denormalizer + $context[SkippableItemNormalizer::DISABLE_ITEM_NORMALIZER] = true; + + if (!isset($context[self::ALREADY_CALLED])) { + $context[self::ALREADY_CALLED] = []; + } + + $context[self::ALREADY_CALLED][] = $data; + + + /** @var AbstractStructuralDBElement $deserialized_entity */ + $deserialized_entity = $this->denormalizer->denormalize($data, $type, $format, $context); + + //Check if we already have the entity in the database (via path) + /** @var StructuralDBElementRepository $repo */ + $repo = $this->entityManager->getRepository($type); + + $path = $deserialized_entity->getFullPath(AbstractStructuralDBElement::PATH_DELIMITER_ARROW); + $db_elements = $repo->getEntityByPath($path, AbstractStructuralDBElement::PATH_DELIMITER_ARROW); + if ($db_elements !== []) { + //We already have the entity in the database, so we can return it + return end($db_elements); + } + + + //Check if we have created the entity in this request before (so we don't create multiple entities for the same path) + //Entities get saved in the cache by type and path + //We use a different cache for this then the objects created by a string value (saved in repo). However, that should not be a problem + //unless the user data has mixed structure between json data and a string path + if (isset($this->object_cache[$type][$path])) { + return $this->object_cache[$type][$path]; + } + + //Save the entity in the cache + $this->object_cache[$type][$path] = $deserialized_entity; + + //We don't have the entity in the database, so we have to persist it + $this->entityManager->persist($deserialized_entity); + + return $deserialized_entity; + } + + public function getSupportedTypes(): array + { + //Must be false, because we use in_array in supportsDenormalization + return [ + AbstractStructuralDBElement::class => false, + ]; + } +} diff --git a/src/Serializer/StructuralElementFromNameDenormalizer.php b/src/Serializer/StructuralElementFromNameDenormalizer.php new file mode 100644 index 00000000..1d7255b7 --- /dev/null +++ b/src/Serializer/StructuralElementFromNameDenormalizer.php @@ -0,0 +1,91 @@ +. + */ +namespace App\Serializer; + +use App\Entity\Base\AbstractStructuralDBElement; +use App\Repository\StructuralDBElementRepository; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; + +/** + * @see \App\Tests\Serializer\StructuralElementFromNameDenormalizerTest + */ +class StructuralElementFromNameDenormalizer implements DenormalizerInterface +{ + public function __construct(private readonly EntityManagerInterface $em) + { + } + + public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool + { + //Only denormalize if we are doing a file import operation + if (!($context['partdb_import'] ?? false)) { + return false; + } + + return is_string($data) && is_subclass_of($type, AbstractStructuralDBElement::class); + } + + /** + * @template T of AbstractStructuralDBElement + * @phpstan-param class-string $type + * @phpstan-return T|null + */ + public function denormalize($data, string $type, ?string $format = null, array $context = []): AbstractStructuralDBElement|null + { + //Retrieve the repository for the given type + /** @var StructuralDBElementRepository $repo */ + $repo = $this->em->getRepository($type); + + $path_delimiter = $context['path_delimiter'] ?? '->'; + + if ($context['create_unknown_datastructures'] ?? false) { + $elements = $repo->getNewEntityFromPath($data, $path_delimiter); + //Persist all new elements + foreach ($elements as $element) { + $this->em->persist($element); + } + if ($elements === []) { + return null; + } + return end($elements); + } + + $elements = $repo->getEntityByPath($data, $path_delimiter); + if ($elements === []) { + return null; + } + return end($elements); + } + + /** + * @return bool[] + */ + public function getSupportedTypes(?string $format): array + { + //Cachable value Must be false, because we do an is_string check on data in supportsDenormalization + return [ + AbstractStructuralDBElement::class => false + ]; + } +} diff --git a/src/Serializer/StructuralElementNormalizer.php b/src/Serializer/StructuralElementNormalizer.php new file mode 100644 index 00000000..e73f69be --- /dev/null +++ b/src/Serializer/StructuralElementNormalizer.php @@ -0,0 +1,83 @@ +. + */ +namespace App\Serializer; + +use App\Entity\Base\AbstractStructuralDBElement; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + +/** + * @see \App\Tests\Serializer\StructuralElementNormalizerTest + */ +class StructuralElementNormalizer implements NormalizerInterface +{ + public function __construct( + #[Autowire(service: ObjectNormalizer::class)]private readonly NormalizerInterface $normalizer + ) + { + } + + public function supportsNormalization($data, ?string $format = null, array $context = []): bool + { + //Only normalize if we are doing a file export operation + if (!($context['partdb_export'] ?? false)) { + return false; + } + + return $data instanceof AbstractStructuralDBElement; + } + + public function normalize($object, ?string $format = null, array $context = []): mixed + { + if (!$object instanceof AbstractStructuralDBElement) { + throw new \InvalidArgumentException('This normalizer only supports AbstractStructural objects!'); + } + + $data = $this->normalizer->normalize($object, $format, $context); + + //If the data is not an array, we can't do anything with it + if (!is_array($data)) { + return $data; + } + + //Remove type field for CSV export + if ($format === 'csv') { + unset($data['type']); + } + + $data['full_name'] = $object->getFullPath('->'); + + return $data; + } + + /** + * @return bool[] + */ + public function getSupportedTypes(?string $format): array + { + return [ + AbstractStructuralDBElement::class => true, + ]; + } +} diff --git a/src/Services/Attachments/AttachmentManager.php b/src/Services/Attachments/AttachmentManager.php index f789d3de..1075141b 100644 --- a/src/Services/Attachments/AttachmentManager.php +++ b/src/Services/Attachments/AttachmentManager.php @@ -35,11 +35,8 @@ use function strlen; */ class AttachmentManager { - protected AttachmentPathResolver $pathResolver; - - public function __construct(AttachmentPathResolver $pathResolver) + public function __construct(protected AttachmentPathResolver $pathResolver) { - $this->pathResolver = $pathResolver; } /** @@ -47,35 +44,31 @@ class AttachmentManager * * @param Attachment $attachment The attachment for which the file should be generated * - * @return SplFileInfo|null The fileinfo for the attachment file. Null, if the attachment is external or has + * @return SplFileInfo|null The fileinfo for the attachment file. Null, if the attachment is only external or has * invalid file. */ public function attachmentToFile(Attachment $attachment): ?SplFileInfo { - if ($attachment->isExternal() || !$this->isFileExisting($attachment)) { + if (!$this->isInternalFileExisting($attachment)) { return null; } - return new SplFileInfo($this->toAbsoluteFilePath($attachment)); + return new SplFileInfo($this->toAbsoluteInternalFilePath($attachment)); } /** - * Returns the absolute filepath of the attachment. Null is returned, if the attachment is externally saved, - * or is not existing. + * Returns the absolute filepath to the internal copy of the attachment. Null is returned, if the attachment is + * only externally saved, or is not existing. * * @param Attachment $attachment The attachment for which the filepath should be determined */ - public function toAbsoluteFilePath(Attachment $attachment): ?string + public function toAbsoluteInternalFilePath(Attachment $attachment): ?string { - if (empty($attachment->getPath())) { + if (!$attachment->hasInternal()){ return null; } - if ($attachment->isExternal()) { - return null; - } - - $path = $this->pathResolver->placeholderToRealPath($attachment->getPath()); + $path = $this->pathResolver->placeholderToRealPath($attachment->getInternalPath()); //realpath does not work with null as argument if (null === $path) { @@ -92,8 +85,8 @@ class AttachmentManager } /** - * Checks if the file in this attachement is existing. This works for files on the HDD, and for URLs - * (it's not checked if the ressource behind the URL is really existing, so for every external attachment true is returned). + * Checks if the file in this attachment is existing. This works for files on the HDD, and for URLs + * (it's not checked if the resource behind the URL is really existing, so for every external attachment true is returned). * * @param Attachment $attachment The attachment for which the existence should be checked * @@ -101,15 +94,23 @@ class AttachmentManager */ public function isFileExisting(Attachment $attachment): bool { - if (empty($attachment->getPath())) { - return false; - } - - if ($attachment->isExternal()) { + if($attachment->hasExternal()){ return true; } + return $this->isInternalFileExisting($attachment); + } - $absolute_path = $this->toAbsoluteFilePath($attachment); + /** + * Checks if the internal file in this attachment is existing. Returns false if the attachment doesn't have an + * internal file. + * + * @param Attachment $attachment The attachment for which the existence should be checked + * + * @return bool true if the file is existing + */ + public function isInternalFileExisting(Attachment $attachment): bool + { + $absolute_path = $this->toAbsoluteInternalFilePath($attachment); if (null === $absolute_path) { return false; @@ -120,27 +121,23 @@ class AttachmentManager /** * Returns the filesize of the attachments in bytes. - * For external attachments or not existing attachments, null is returned. + * For purely external attachments or inexistent attachments, null is returned. * * @param Attachment $attachment the filesize for which the filesize should be calculated */ public function getFileSize(Attachment $attachment): ?int { - if ($attachment->isExternal()) { + if (!$this->isInternalFileExisting($attachment)) { return null; } - if (!$this->isFileExisting($attachment)) { - return null; - } - - $tmp = filesize($this->toAbsoluteFilePath($attachment)); + $tmp = filesize($this->toAbsoluteInternalFilePath($attachment)); return false !== $tmp ? $tmp : null; } /** - * Returns a human readable version of the attachment file size. + * Returns a human-readable version of the attachment file size. * For external attachments, null is returned. * * @param int $decimals The number of decimals numbers that should be printed @@ -160,7 +157,7 @@ class AttachmentManager $sz = 'BKMGTP'; $factor = (int) floor((strlen((string) $bytes) - 1) / 3); - - return sprintf("%.{$decimals}f", $bytes / 1024 ** $factor).@$sz[$factor]; + //Use real (10 based) SI prefixes + return sprintf("%.{$decimals}f", $bytes / 1000 ** $factor).@$sz[$factor]; } } diff --git a/src/Services/Attachments/AttachmentPathResolver.php b/src/Services/Attachments/AttachmentPathResolver.php index 9617024e..1b52c89b 100644 --- a/src/Services/Attachments/AttachmentPathResolver.php +++ b/src/Services/Attachments/AttachmentPathResolver.php @@ -22,24 +22,22 @@ declare(strict_types=1); namespace App\Services\Attachments; -use FontLib\Table\Type\maxp; use const DIRECTORY_SEPARATOR; use Symfony\Component\Filesystem\Filesystem; /** * This service converts the relative pathes for attachments saved in database (like %MEDIA%/img.jpg) to real pathes * an vice versa. + * @see \App\Tests\Services\Attachments\AttachmentPathResolverTest */ class AttachmentPathResolver { - protected string $project_dir; - - protected ?string $media_path; - protected ?string $footprints_path; + protected string $media_path; + protected string $footprints_path; protected ?string $models_path; protected ?string $secure_path; - protected array $placeholders; + protected array $placeholders = ['%MEDIA%', '%BASE%/data/media', '%FOOTPRINTS%', '%FOOTPRINTS_3D%', '%SECURE%']; protected array $pathes; protected array $placeholders_regex; protected array $pathes_regex; @@ -53,18 +51,13 @@ class AttachmentPathResolver * Set to null if this ressource should be disabled. * @param string|null $models_path set to null if this ressource should be disabled */ - public function __construct(string $project_dir, string $media_path, string $secure_path, ?string $footprints_path, ?string $models_path) + public function __construct(protected string $project_dir, string $media_path, string $secure_path, ?string $footprints_path, ?string $models_path) { - $this->project_dir = $project_dir; - - //Determine the path for our ressources - $this->media_path = $this->parameterToAbsolutePath($media_path); - $this->footprints_path = $this->parameterToAbsolutePath($footprints_path); + //Determine the path for our resources + $this->media_path = $this->parameterToAbsolutePath($media_path) ?? throw new \InvalidArgumentException('The media path must be set and valid!'); + $this->secure_path = $this->parameterToAbsolutePath($secure_path) ?? throw new \InvalidArgumentException('The secure path must be set and valid!'); + $this->footprints_path = $this->parameterToAbsolutePath($footprints_path) ; $this->models_path = $this->parameterToAbsolutePath($models_path); - $this->secure_path = $this->parameterToAbsolutePath($secure_path); - - //Here we define the valid placeholders and their replacement values - $this->placeholders = ['%MEDIA%', '%BASE%/data/media', '%FOOTPRINTS%', '%FOOTPRINTS_3D%', '%SECURE%']; $this->pathes = [$this->media_path, $this->media_path, $this->footprints_path, $this->models_path, $this->secure_path]; //Remove all disabled placeholders @@ -122,19 +115,23 @@ class AttachmentPathResolver * Converts an relative placeholder filepath (with %MEDIA% or older %BASE%) to an absolute filepath on disk. * The directory separator is always /. Relative pathes are not realy possible (.. is striped). * - * @param string $placeholder_path the filepath with placeholder for which the real path should be determined + * @param string|null $placeholder_path the filepath with placeholder for which the real path should be determined * * @return string|null The absolute real path of the file, or null if the placeholder path is invalid */ - public function placeholderToRealPath(string $placeholder_path): ?string + public function placeholderToRealPath(?string $placeholder_path): ?string { + if (null === $placeholder_path) { + return null; + } + //The new attachments use %MEDIA% as placeholders, which is the directory set in media_directory //Older path entries are given via %BASE% which was the project root $count = 0; //When path is a footprint we have to first run the string through our lecagy german mapping functions - if (strpos($placeholder_path, '%FOOTPRINTS%') !== false) { + if (str_contains($placeholder_path, '%FOOTPRINTS%')) { $placeholder_path = $this->convertOldFootprintPath($placeholder_path); } @@ -146,12 +143,12 @@ class AttachmentPathResolver } //If we have now have a placeholder left, the string is invalid: - if (preg_match('#%\w+%#', $placeholder_path)) { + if (preg_match('#%\w+%#', (string) $placeholder_path)) { return null; } //Path is invalid if path is directory traversal - if (false !== strpos($placeholder_path, '..')) { + if (str_contains((string) $placeholder_path, '..')) { return null; } @@ -190,7 +187,7 @@ class AttachmentPathResolver } //If the new string does not begin with a placeholder, it is invalid - if (!preg_match('#^%\w+%#', $real_path)) { + if (!preg_match('#^%\w+%#', (string) $real_path)) { return null; } @@ -198,7 +195,7 @@ class AttachmentPathResolver } /** - * The path where uploaded attachments is stored. + * The path where uploaded attachments is stored. * * @return string the absolute path to the media folder */ @@ -208,8 +205,8 @@ class AttachmentPathResolver } /** - * The path where secured attachments are stored. Must not be located in public/ folder, so it can only be accessed - * via the attachment controller. + * The path where secured attachments are stored. Must not be located in public/ folder, so it can only be accessed + * via the attachment controller. * * @return string the absolute path to the secure path */ @@ -221,7 +218,7 @@ class AttachmentPathResolver /** * The string where the builtin footprints are stored. * - * @return string|null The absolute path to the footprints folder. Null if built footprints were disabled. + * @return string|null The absolute path to the footprints' folder. Null if built footprints were disabled. */ public function getFootprintsPath(): ?string { @@ -231,7 +228,7 @@ class AttachmentPathResolver /** * The string where the builtin 3D models are stored. * - * @return string|null The absolute path to the models folder. Null if builtin models were disabled. + * @return string|null The absolute path to the models' folder. Null if builtin models were disabled. */ public function getModelsPath(): ?string { @@ -248,7 +245,7 @@ class AttachmentPathResolver $ret = []; foreach ($array as $item) { - $item = str_replace(['\\'], ['/'], $item); + $item = str_replace(['\\'], ['/'], (string) $item); $ret[] = '/'.preg_quote($item, '/').'/'; } diff --git a/src/Services/Attachments/AttachmentReverseSearch.php b/src/Services/Attachments/AttachmentReverseSearch.php index 34a6b929..e05192d0 100644 --- a/src/Services/Attachments/AttachmentReverseSearch.php +++ b/src/Services/Attachments/AttachmentReverseSearch.php @@ -30,22 +30,12 @@ use SplFileInfo; use Symfony\Component\Filesystem\Filesystem; /** - * This service provides functions to find attachments via an reverse search based on a file. + * This service provides functions to find attachments via a reverse search based on a file. */ class AttachmentReverseSearch { - protected EntityManagerInterface $em; - protected AttachmentPathResolver $pathResolver; - protected CacheManager $cacheManager; - protected AttachmentURLGenerator $attachmentURLGenerator; - - public function __construct(EntityManagerInterface $em, AttachmentPathResolver $pathResolver, - CacheManager $cacheManager, AttachmentURLGenerator $attachmentURLGenerator) + public function __construct(protected EntityManagerInterface $em, protected AttachmentPathResolver $pathResolver, protected CacheManager $cacheManager, protected AttachmentURLGenerator $attachmentURLGenerator) { - $this->em = $em; - $this->pathResolver = $pathResolver; - $this->cacheManager = $cacheManager; - $this->attachmentURLGenerator = $attachmentURLGenerator; } /** @@ -53,7 +43,7 @@ class AttachmentReverseSearch * * @param SplFileInfo $file The file for which is searched * - * @return Attachment[] an list of attachments that use the given file + * @return Attachment[] a list of attachments that use the given file */ public function findAttachmentsByFile(SplFileInfo $file): array { @@ -65,7 +55,7 @@ class AttachmentReverseSearch $repo = $this->em->getRepository(Attachment::class); return $repo->findBy([ - 'path' => [$relative_path_new, $relative_path_old], + 'internal_path' => [$relative_path_new, $relative_path_old], ]); } @@ -75,11 +65,11 @@ class AttachmentReverseSearch * @param SplFileInfo $file The file that should be removed * @param int $threshold the threshold used, to determine if a file should be deleted or not * - * @return bool True, if the file was delete. False if not. + * @return bool True, if the file was deleted. False if not. */ public function deleteIfNotUsed(SplFileInfo $file, int $threshold = 1): bool { - /* When the file is used more then $threshold times, don't delete it */ + /* When the file is used more than $threshold times, don't delete it */ if (count($this->findAttachmentsByFile($file)) > $threshold) { return false; } diff --git a/src/Services/Attachments/AttachmentSubmitHandler.php b/src/Services/Attachments/AttachmentSubmitHandler.php index cd3afcac..89457cea 100644 --- a/src/Services/Attachments/AttachmentSubmitHandler.php +++ b/src/Services/Attachments/AttachmentSubmitHandler.php @@ -26,6 +26,7 @@ use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\AttachmentType; use App\Entity\Attachments\AttachmentTypeAttachment; +use App\Entity\Attachments\AttachmentUpload; use App\Entity\Attachments\CategoryAttachment; use App\Entity\Attachments\CurrencyAttachment; use App\Entity\Attachments\LabelAttachment; @@ -35,18 +36,18 @@ use App\Entity\Attachments\GroupAttachment; use App\Entity\Attachments\ManufacturerAttachment; use App\Entity\Attachments\MeasurementUnitAttachment; use App\Entity\Attachments\PartAttachment; -use App\Entity\Attachments\StorelocationAttachment; +use App\Entity\Attachments\StorageLocationAttachment; use App\Entity\Attachments\SupplierAttachment; use App\Entity\Attachments\UserAttachment; use App\Exceptions\AttachmentDownloadException; +use Hshn\Base64EncodedFile\HttpFoundation\File\Base64EncodedFile; +use Hshn\Base64EncodedFile\HttpFoundation\File\UploadedBase64EncodedFile; use const DIRECTORY_SEPARATOR; -use function get_class; use InvalidArgumentException; use RuntimeException; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Mime\MimeTypesInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -55,28 +56,21 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; */ class AttachmentSubmitHandler { - protected AttachmentPathResolver $pathResolver; protected array $folder_mapping; - protected bool $allow_attachments_downloads; - protected HttpClientInterface $httpClient; - protected MimeTypesInterface $mimeTypes; - protected FileTypeFilterTools $filterTools; + + private ?int $max_upload_size_bytes = null; protected const BLACKLISTED_EXTENSIONS = ['php', 'phtml', 'php3', 'ph3', 'php4', 'ph4', 'php5', 'ph5', 'phtm', 'sh', 'asp', 'cgi', 'py', 'pl', 'exe', 'aspx', 'js', 'mjs', 'jsp', 'css', 'jar', 'html', 'htm', 'shtm', 'shtml', 'htaccess', 'htpasswd', '']; - public function __construct(AttachmentPathResolver $pathResolver, bool $allow_attachments_downloads, - HttpClientInterface $httpClient, MimeTypesInterface $mimeTypes, - FileTypeFilterTools $filterTools) + 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) { - $this->pathResolver = $pathResolver; - $this->allow_attachments_downloads = $allow_attachments_downloads; - $this->httpClient = $httpClient; - $this->mimeTypes = $mimeTypes; - - $this->filterTools = $filterTools; - //The mapping used to determine which folder will be used for an attachment type $this->folder_mapping = [ PartAttachment::class => 'part', @@ -88,7 +82,7 @@ class AttachmentSubmitHandler GroupAttachment::class => 'group', ManufacturerAttachment::class => 'manufacturer', MeasurementUnitAttachment::class => 'measurement_unit', - StorelocationAttachment::class => 'storelocation', + StorageLocationAttachment::class => 'storelocation', SupplierAttachment::class => 'supplier', UserAttachment::class => 'user', LabelAttachment::class => 'label_profile', @@ -101,8 +95,8 @@ class AttachmentSubmitHandler */ public function isValidFileExtension(AttachmentType $attachment_type, UploadedFile $uploadedFile): bool { - //Only validate if the attachment type has specified an filetype filter: - if (empty($attachment_type->getFiletypeFilter())) { + //Only validate if the attachment type has specified a filetype filter: + if ($attachment_type->getFiletypeFilter() === '') { return true; } @@ -114,10 +108,10 @@ class AttachmentSubmitHandler /** * Generates a filename for the given attachment and extension. - * The filename contains a random id, so every time this function is called you get an unique name. + * The filename contains a random id, so every time this function is called you get a unique name. * * @param Attachment $attachment The attachment that should be used for generating an attachment - * @param string $extension The extension that the new file should have (must only contain chars allowed in pathes) + * @param string $extension The extension that the new file should have (must only contain chars allowed in paths) * * @return string the new filename */ @@ -129,7 +123,7 @@ class AttachmentSubmitHandler $extension ); - //Use the (sanatized) attachment name as an filename part + //Use the (sanatized) attachment name as a filename part $safeName = transliterator_transliterate( 'Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $attachment->getName() @@ -148,84 +142,118 @@ class AttachmentSubmitHandler */ public function generateAttachmentPath(Attachment $attachment, bool $secure_upload = false): string { - if ($secure_upload) { - $base_path = $this->pathResolver->getSecurePath(); + $base_path = $secure_upload ? $this->pathResolver->getSecurePath() : $this->pathResolver->getMediaPath(); + + //Ensure the attachment has an assigned element + if (!$attachment->getElement() instanceof AttachmentContainingDBElement) { + throw new InvalidArgumentException('The given attachment is not assigned to an element! An element is needed to generate a path!'); + } + + //Determine the folder prefix for the given attachment class: + $prefix = null; + //Check if we can use the class name dire + if (isset($this->folder_mapping[$attachment::class])) { + $prefix = $this->folder_mapping[$attachment::class]; } else { - $base_path = $this->pathResolver->getMediaPath(); + //If not, check for instance of: + foreach ($this->folder_mapping as $class => $folder) { + if ($attachment instanceof $class) { + $prefix = $folder; + break; + } + } } //Ensure the given attachment class is known to mapping - if (!isset($this->folder_mapping[get_class($attachment)])) { - throw new InvalidArgumentException('The given attachment class is not known! The passed class was: '.get_class($attachment)); - } - //Ensure the attachment has an assigned element - if (null === $attachment->getElement()) { - throw new InvalidArgumentException('The given attachment is not assigned to an element! An element is needed to generate a path!'); + if (!$prefix) { + throw new InvalidArgumentException('The given attachment class is not known! The passed class was: '.$attachment::class); } //Build path return $base_path.DIRECTORY_SEPARATOR //Base path - .$this->folder_mapping[get_class($attachment)].DIRECTORY_SEPARATOR.$attachment->getElement()->getID(); + .$prefix.DIRECTORY_SEPARATOR.$attachment->getElement()->getID(); } /** - * Handle the submit of an attachment form. + * Handle submission of an attachment form. * This function will move the uploaded file or download the URL file to server, if needed. * * @param Attachment $attachment the attachment that should be used for handling - * @param UploadedFile|null $file If given, that file will be moved to the right location - * @param array $options The options to use with the upload. Here you can specify that an URL should be downloaded, - * or an file should be moved to a secure location. + * @param AttachmentUpload|null $upload The upload options DTO. If it is null, it will be tried to get from the attachment option * * @return Attachment The attachment with the new filename (same instance as passed $attachment) */ - public function handleFormSubmit(Attachment $attachment, ?UploadedFile $file, array $options = []): Attachment + public function handleUpload(Attachment $attachment, ?AttachmentUpload $upload): Attachment { - $resolver = new OptionsResolver(); - $this->configureOptions($resolver); - $options = $resolver->resolve($options); + if ($upload === null) { + $upload = $attachment->getUpload(); + if ($upload === null) { + throw new InvalidArgumentException('No upload options given and no upload options set in attachment!'); + } + } + + $file = $upload->file; + + //If no file was uploaded, but we have base64 encoded data, create a file from it + if (!$file && $upload->data !== null) { + $file = new UploadedBase64EncodedFile(new Base64EncodedFile($upload->data), $upload->filename ?? 'base64'); + } + + //By default we assume a public upload + $secure_attachment = $upload->private ?? false; //When a file is given then upload it, otherwise check if we need to download the URL - if ($file) { - $this->upload($attachment, $file, $options); - } elseif ($options['download_url'] && $attachment->isExternal()) { - $this->downloadURL($attachment, $options); + if ($file instanceof UploadedFile) { + + $this->upload($attachment, $file, $secure_attachment); + } elseif ($upload->downloadUrl && $attachment->hasExternal()) { + $this->downloadURL($attachment, $secure_attachment); } //Move the attachment files to secure location (and back) if needed - $this->moveFile($attachment, $options['secure_attachment']); + $this->moveFile($attachment, $secure_attachment); + + //Sanitize the SVG if needed + $this->sanitizeSVGAttachment($attachment); //Rename blacklisted (unsecure) files to a better extension $this->renameBlacklistedExtensions($attachment); - //Check if we should assign this attachment to master picture - //this is only possible if the attachment is new (not yet persisted to DB) - if ($options['become_preview_if_empty'] && null === $attachment->getID() && $attachment->isPicture()) { - $element = $attachment->getElement(); - if ($element instanceof AttachmentContainingDBElement && null === $element->getMasterPictureAttachment()) { + //Set / Unset the master picture attachment / preview image + $element = $attachment->getElement(); + if ($element instanceof AttachmentContainingDBElement) { + //Make this attachment the master picture if needed and this was requested + if ($upload->becomePreviewIfEmpty + && $element->getMasterPictureAttachment() === null //Element must not have an preview image yet + && null === $attachment->getID() //Attachment must be null + && $attachment->isPicture() //Attachment must be a picture + ) { $element->setMasterPictureAttachment($attachment); } + + //If this attachment is the master picture, but is not a picture anymore, dont use it as master picture anymore + if ($element->getMasterPictureAttachment() === $attachment && !$attachment->isPicture()) { + $element->setMasterPictureAttachment(null); + } } return $attachment; } /** - * Rename attachments with an unsafe extension (meaning files which would be runned by a to a safe one. - * @param Attachment $attachment - * @return Attachment + * Rename attachments with an unsafe extension (meaning files which would be run by a to a safe one). */ protected function renameBlacklistedExtensions(Attachment $attachment): Attachment { //We can not do anything on builtins or external ressources - if ($attachment->isBuiltIn() || $attachment->isExternal()) { + if ($attachment->isBuiltIn() || !$attachment->hasInternal()) { return $attachment; } //Determine the old filepath - $old_path = $this->pathResolver->placeholderToRealPath($attachment->getPath()); - if (empty($old_path) || !file_exists($old_path)) { + $old_path = $this->pathResolver->placeholderToRealPath($attachment->getInternalPath()); + if ($old_path === null || $old_path === '' || !file_exists($old_path)) { return $attachment; } $filename = basename($old_path); @@ -233,45 +261,34 @@ class AttachmentSubmitHandler //Check if the extension is blacklisted and replace the file extension with txt if needed - if(in_array($ext, self::BLACKLISTED_EXTENSIONS)) { + if(in_array($ext, self::BLACKLISTED_EXTENSIONS, true)) { $new_path = $this->generateAttachmentPath($attachment, $attachment->isSecure()) - .DIRECTORY_SEPARATOR.$this->generateAttachmentFilename($attachment, 'txt'); + .DIRECTORY_SEPARATOR.$this->generateAttachmentFilename($attachment, 'txt'); //Move file to new directory $fs = new Filesystem(); $fs->rename($old_path, $new_path); //Update the attachment - $attachment->setPath($this->pathResolver->realPathToPlaceholder($new_path)); + $attachment->setInternalPath($this->pathResolver->realPathToPlaceholder($new_path)); } return $attachment; } - protected function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - //If no preview image was set yet, the new uploaded file will become the preview image - 'become_preview_if_empty' => true, - //When an URL is given download the URL - 'download_url' => false, - 'secure_attachment' => false, - ]); - } - /** - * Move the given attachment to secure location (or back to public folder) if needed. + * Move the internal copy of the given attachment to a secure location (or back to public folder) if needed. * * @param Attachment $attachment the attachment for which the file should be moved * @param bool $secure_location this value determines, if the attachment is moved to the secure or public folder * - * @return Attachment The attachment with the updated filepath + * @return Attachment The attachment with the updated internal filepath */ protected function moveFile(Attachment $attachment, bool $secure_location): Attachment { //We can not do anything on builtins or external ressources - if ($attachment->isBuiltIn() || $attachment->isExternal()) { + if ($attachment->isBuiltIn() || !$attachment->hasInternal()) { return $attachment; } @@ -281,12 +298,12 @@ class AttachmentSubmitHandler } //Determine the old filepath - $old_path = $this->pathResolver->placeholderToRealPath($attachment->getPath()); + $old_path = $this->pathResolver->placeholderToRealPath($attachment->getInternalPath()); if (!file_exists($old_path)) { return $attachment; } - $filename = basename($old_path); + $filename = basename((string) $old_path); //If the basename is not one of the new unique on, we have to save the old filename if (!preg_match('#\w+-\w{13}\.#', $filename)) { //Save filename to attachment field @@ -305,7 +322,7 @@ class AttachmentSubmitHandler //Save info to attachment entity $new_path = $this->pathResolver->realPathToPlaceholder($new_path); - $attachment->setPath($new_path); + $attachment->setInternalPath($new_path); return $attachment; } @@ -313,27 +330,46 @@ class AttachmentSubmitHandler /** * Download the URL set in the attachment and save it on the server. * - * @param array $options The options from the handleFormSubmit function + * @param bool $secureAttachment True if the file should be moved to the secure attachment storage * - * @return Attachment The attachment with the new filepath + * @return Attachment The attachment with the downloaded copy */ - protected function downloadURL(Attachment $attachment, array $options): Attachment + protected function downloadURL(Attachment $attachment, bool $secureAttachment): Attachment { //Check if we are allowed to download files if (!$this->allow_attachments_downloads) { throw new RuntimeException('Download of attachments is not allowed!'); } - $url = $attachment->getURL(); + $url = $attachment->getExternalPath(); $fs = new Filesystem(); - $attachment_folder = $this->generateAttachmentPath($attachment, $options['secure_attachment']); + $attachment_folder = $this->generateAttachmentPath($attachment, $secureAttachment); $tmp_path = $attachment_folder.DIRECTORY_SEPARATOR.$this->generateAttachmentFilename($attachment, 'tmp'); try { - $response = $this->httpClient->request('GET', $url, [ + $opts = [ 'buffer' => false, - ]); + //Use user-agent and other headers to make the server think we are a browser + 'headers' => [ + "sec-ch-ua" => "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"", + "sec-ch-ua-mobile" => "?0", + "sec-ch-ua-platform" => "\"Windows\"", + "user-agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36", + "sec-fetch-site" => "none", + "sec-fetch-mode" => "navigate", + ], + + ]; + $response = $this->httpClient->request('GET', $url, $opts); + //Digikey wants TLSv1.3, so try again with that if we get a 403 + if ($response->getStatusCode() === 403) { + $opts['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT; + $response = $this->httpClient->request('GET', $url, $opts); + } + # if you have these changes and downloads still fail, check if it's due to an unknown certificate. Curl by + # default uses the systems ca store and that doesn't contain all the intermediate certificates needed to + # verify the leafs if (200 !== $response->getStatusCode()) { throw new AttachmentDownloadException('Status code: '.$response->getStatusCode()); @@ -350,27 +386,29 @@ class AttachmentSubmitHandler //File download should be finished here, so determine the new filename and extension $headers = $response->getHeaders(); - //Try to determine an filename + //Try to determine a filename $filename = ''; - //If an content disposition header was set try to extract the filename out of it + //If a content disposition header was set try to extract the filename out of it if (isset($headers['content-disposition'])) { $tmp = []; - preg_match('/[^;\\n=]*=([\'\"])*(.*)(?(1)\1|)/', $headers['content-disposition'][0], $tmp); - $filename = $tmp[2]; + //Only use the filename if the regex matches properly + if (preg_match('/[^;\\n=]*=([\'\"])*(.*)(?(1)\1|)/', $headers['content-disposition'][0], $tmp)) { + $filename = $tmp[2]; + } } - //If we dont know filename yet, try to determine it out of url + //If we don't know filename yet, try to determine it out of url if ('' === $filename) { - $filename = basename(parse_url($url, PHP_URL_PATH)); + $filename = basename(parse_url((string) $url, PHP_URL_PATH)); } //Set original file $attachment->setFilename($filename); - //Check if we have a extension given + //Check if we have an extension given $pathinfo = pathinfo($filename); - if (!empty($pathinfo['extension'])) { + if (isset($pathinfo['extension']) && $pathinfo['extension'] !== '') { $new_ext = $pathinfo['extension']; } else { //Otherwise we have to guess the extension for the new file, based on its content $new_ext = $this->mimeTypes->getExtensions($this->mimeTypes->guessMimeType($tmp_path))[0] ?? 'tmp'; @@ -383,8 +421,8 @@ class AttachmentSubmitHandler //Make our file path relative to %BASE% $new_path = $this->pathResolver->realPathToPlaceholder($new_path); //Save the path to the attachment - $attachment->setPath($new_path); - } catch (TransportExceptionInterface $transportExceptionInterface) { + $attachment->setInternalPath($new_path); + } catch (TransportExceptionInterface) { throw new AttachmentDownloadException('Transport error!'); } @@ -396,25 +434,99 @@ class AttachmentSubmitHandler * * @param Attachment $attachment The attachment in which the file should be saved * @param UploadedFile $file The file which was uploaded - * @param array $options The options from the handleFormSubmit function + * @param bool $secureAttachment True if the file should be moved to the secure attachment storage * * @return Attachment The attachment with the new filepath */ - protected function upload(Attachment $attachment, UploadedFile $file, array $options): Attachment + protected function upload(Attachment $attachment, UploadedFile $file, bool $secureAttachment): Attachment { - //Move our temporay attachment to its final location + //Move our temporary attachment to its final location $file_path = $file->move( - $this->generateAttachmentPath($attachment, $options['secure_attachment']), + $this->generateAttachmentPath($attachment, $secureAttachment), $this->generateAttachmentFilename($attachment, $file->getClientOriginalExtension()) )->getRealPath(); //Make our file path relative to %BASE% $file_path = $this->pathResolver->realPathToPlaceholder($file_path); //Save the path to the attachment - $attachment->setPath($file_path); + $attachment->setInternalPath($file_path); + //reset any external paths the attachment might have had + $attachment->setExternalPath(null); //And save original filename $attachment->setFilename($file->getClientOriginalName()); return $attachment; } + + /** + * Parses the given file size string and returns the size in bytes. + * Taken from https://github.com/symfony/symfony/blob/6.2/src/Symfony/Component/Validator/Constraints/File.php + */ + private function parseFileSizeString(string $maxSize): int + { + $factors = [ + 'k' => 1000, + 'ki' => 1 << 10, + 'm' => 1000 * 1000, + 'mi' => 1 << 20, + 'g' => 1000 * 1000 * 1000, + 'gi' => 1 << 30, + ]; + if (ctype_digit($maxSize)) { + return (int) $maxSize; + } + + if (preg_match('/^(\d++)('.implode('|', array_keys($factors)).')$/i', $maxSize, $matches)) { + return (((int) $matches[1]) * $factors[strtolower($matches[2])]); + } + + throw new RuntimeException(sprintf('"%s" is not a valid maximum size.', $maxSize)); + } + + /* + * Returns the maximum allowed upload size in bytes. + * This is the minimum value of Part-DB max_file_size, and php.ini's post_max_size and upload_max_filesize. + */ + public function getMaximumAllowedUploadSize(): int + { + if ($this->max_upload_size_bytes) { + return $this->max_upload_size_bytes; + } + + $this->max_upload_size_bytes = min( + $this->parseFileSizeString(ini_get('post_max_size')), + $this->parseFileSizeString(ini_get('upload_max_filesize')), + $this->parseFileSizeString($this->max_upload_size), + ); + + return $this->max_upload_size_bytes; + } + + /** + * Sanitizes the given SVG file, if the attachment is an internal SVG file. + * @param Attachment $attachment + * @return Attachment + */ + public function sanitizeSVGAttachment(Attachment $attachment): Attachment + { + //We can not do anything on builtins or external ressources + if ($attachment->isBuiltIn() || !$attachment->hasInternal()) { + return $attachment; + } + + //Resolve the path to the file + $path = $this->pathResolver->placeholderToRealPath($attachment->getInternalPath()); + + //Check if the file exists + if (!file_exists($path)) { + return $attachment; + } + + //Check if the file is an SVG + if ($attachment->getExtension() === "svg") { + $this->SVGSanitizer->sanitizeFile($path); + } + + return $attachment; + } } diff --git a/src/Services/Attachments/AttachmentURLGenerator.php b/src/Services/Attachments/AttachmentURLGenerator.php index c66d76fd..c22cefe4 100644 --- a/src/Services/Attachments/AttachmentURLGenerator.php +++ b/src/Services/Attachments/AttachmentURLGenerator.php @@ -22,43 +22,32 @@ declare(strict_types=1); namespace App\Services\Attachments; +use Imagine\Exception\RuntimeException; use App\Entity\Attachments\Attachment; use InvalidArgumentException; -use Liip\ImagineBundle\Service\FilterService; +use Liip\ImagineBundle\Imagine\Cache\CacheManager; use Psr\Log\LoggerInterface; -use RuntimeException; use function strlen; use Symfony\Component\Asset\Packages; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +/** + * @see \App\Tests\Services\Attachments\AttachmentURLGeneratorTest + */ class AttachmentURLGenerator { - protected Packages $assets; protected string $public_path; - protected AttachmentPathResolver $pathResolver; - protected UrlGeneratorInterface $urlGenerator; - protected AttachmentManager $attachmentHelper; - protected FilterService $filterService; - protected LoggerInterface $logger; - - public function __construct(Packages $assets, AttachmentPathResolver $pathResolver, - UrlGeneratorInterface $urlGenerator, AttachmentManager $attachmentHelper, - FilterService $filterService, LoggerInterface $logger) + public function __construct(protected Packages $assets, protected AttachmentPathResolver $pathResolver, + protected UrlGeneratorInterface $urlGenerator, protected AttachmentManager $attachmentHelper, + protected CacheManager $thumbnailManager, protected LoggerInterface $logger) { - $this->assets = $assets; - $this->pathResolver = $pathResolver; - $this->urlGenerator = $urlGenerator; - $this->attachmentHelper = $attachmentHelper; - $this->filterService = $filterService; - $this->logger = $logger; - //Determine a normalized path to the public folder (assets are relative to this folder) $this->public_path = $this->pathResolver->parameterToAbsolutePath('public'); } /** - * Converts the absolute file path to a version relative to the public folder, that can be passed to asset + * Converts the absolute file path to a version relative to the public folder, that can be passed to the * Asset Component functions. * * @param string $absolute_path the absolute path that should be converted @@ -78,8 +67,8 @@ class AttachmentURLGenerator $public_path = $this->public_path; } - //Our absolute path must begin with public path or we can not use it for asset pathes. - if (0 !== strpos($absolute_path, $public_path)) { + //Our absolute path must begin with public path, or we can not use it for asset pathes. + if (!str_starts_with($absolute_path, $public_path)) { return null; } @@ -88,7 +77,7 @@ class AttachmentURLGenerator } /** - * Converts a placeholder path to a path to a image path. + * Converts a placeholder path to a path to an image path. * * @param string $placeholder_path the placeholder path that should be converted */ @@ -103,9 +92,9 @@ class AttachmentURLGenerator * Returns a URL under which the attachment file can be viewed. * @return string|null The URL or null if the attachment file is not existing */ - public function getViewURL(Attachment $attachment): ?string + public function getInternalViewURL(Attachment $attachment): ?string { - $absolute_path = $this->attachmentHelper->toAbsoluteFilePath($attachment); + $absolute_path = $this->attachmentHelper->toAbsoluteInternalFilePath($attachment); if (null === $absolute_path) { return null; } @@ -121,7 +110,8 @@ class AttachmentURLGenerator } /** - * Returns a URL to an thumbnail of the attachment file. + * 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 */ public function getThumbnailURL(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string @@ -130,11 +120,14 @@ class AttachmentURLGenerator throw new InvalidArgumentException('Thumbnail creation only works for picture attachments!'); } - if ($attachment->isExternal() && !empty($attachment->getURL())) { - return $attachment->getURL(); + if (!$attachment->hasInternal()){ + if($attachment->hasExternal()) { + return $attachment->getExternalPath(); + } + return null; } - $absolute_path = $this->attachmentHelper->toAbsoluteFilePath($attachment); + $absolute_path = $this->attachmentHelper->toAbsoluteInternalFilePath($attachment); if (null === $absolute_path) { return null; } @@ -145,19 +138,21 @@ class AttachmentURLGenerator return $this->urlGenerator->generate('attachment_view', ['id' => $attachment->getID()]); } - //For builtin ressources it is not useful to create a thumbnail - //because the footprints images are small and highly optimized already. - if (('thumbnail_md' === $filter_name && $attachment->isBuiltIn()) - //GD can not work with SVG, so serve it directly... - || 'svg' === $attachment->getExtension()) { + //GD can not work with SVG, so serve it directly... + //We can not use getExtension here, because it uses the original filename and not the real extension + //Instead we use the logic, which is also used to determine if the attachment is a picture + $extension = pathinfo(parse_url($attachment->getInternalPath(), PHP_URL_PATH) ?? '', PATHINFO_EXTENSION); + if ('svg' === $extension) { return $this->assets->getUrl($asset_path); } try { - //Otherwise we can serve the relative path via Asset component - return $this->filterService->getUrlOfFilteredImage($asset_path, $filter_name); - } catch (\Imagine\Exception\RuntimeException $e) { - //If the filter fails, we can not serve the thumbnail and fall back to the original image and log an warning + //We try to get network path here (so no schema), but this param might just get ignored by the cache manager + $tmp = $this->thumbnailManager->getBrowserPath($asset_path, $filter_name, [], null, UrlGeneratorInterface::NETWORK_PATH); + //So we remove the schema manually + return preg_replace('/^https?:/', '', $tmp); + } catch (RuntimeException $e) { + //If the filter fails, we can not serve the thumbnail and fall back to the original image and log a warning $this->logger->warning('Could not open thumbnail for attachment with ID ' . $attachment->getID() . ': ' . $e->getMessage()); return $this->assets->getUrl($asset_path); } @@ -166,7 +161,7 @@ class AttachmentURLGenerator /** * Returns a download link to the file associated with the attachment. */ - public function getDownloadURL(Attachment $attachment): string + public function getInternalDownloadURL(Attachment $attachment): string { //Redirect always to download controller, which sets the correct headers for downloading: return $this->urlGenerator->generate('attachment_download', ['id' => $attachment->getID()]); diff --git a/src/Services/Attachments/BuiltinAttachmentsFinder.php b/src/Services/Attachments/BuiltinAttachmentsFinder.php index 7c3c8f4b..b009cf60 100644 --- a/src/Services/Attachments/BuiltinAttachmentsFinder.php +++ b/src/Services/Attachments/BuiltinAttachmentsFinder.php @@ -30,16 +30,12 @@ use Symfony\Contracts\Cache\CacheInterface; /** * This service is used to find builtin attachment ressources. + * @see \App\Tests\Services\Attachments\BuiltinAttachmentsFinderTest */ class BuiltinAttachmentsFinder { - protected AttachmentPathResolver $pathResolver; - protected CacheInterface $cache; - - public function __construct(CacheInterface $cache, AttachmentPathResolver $pathResolver) + public function __construct(protected CacheInterface $cache, protected AttachmentPathResolver $pathResolver) { - $this->pathResolver = $pathResolver; - $this->cache = $cache; } /** @@ -49,7 +45,6 @@ class BuiltinAttachmentsFinder * '%FOOTPRINTS%/path/to/folder/file1.png', * '%FOOTPRINTS%/path/to/folder/file2.png', * ] - * @return array */ public function getListOfFootprintsGroupedByFolder(): array { @@ -63,7 +58,7 @@ class BuiltinAttachmentsFinder foreach($finder as $file) { $folder = $file->getRelativePath(); //Normalize path (replace \ with /) - $folder = str_replace('\\', '/', $folder); + $folder = str_replace('\\', '/', (string) $folder); if(!isset($output[$folder])) { $output[$folder] = []; @@ -109,7 +104,7 @@ class BuiltinAttachmentsFinder return $results; }); - } catch (InvalidArgumentException $invalidArgumentException) { + } catch (InvalidArgumentException) { return []; } } @@ -125,7 +120,7 @@ class BuiltinAttachmentsFinder */ public function find(string $keyword, array $options = [], ?array $base_list = []): array { - if (empty($base_list)) { + if ($base_list === null || $base_list === []) { $base_list = $this->getListOfRessources(); } diff --git a/src/Services/Attachments/FileTypeFilterTools.php b/src/Services/Attachments/FileTypeFilterTools.php index bf44cbe1..d689fda3 100644 --- a/src/Services/Attachments/FileTypeFilterTools.php +++ b/src/Services/Attachments/FileTypeFilterTools.php @@ -29,9 +29,10 @@ use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; /** - * An servive that helps working with filetype filters (based on the format accept uses. + * A service that helps to work with filetype filters (based on the format accept uses). * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers for * more details. + * @see \App\Tests\Services\Attachments\FileTypeFilterToolsTest */ class FileTypeFilterTools { @@ -43,13 +44,8 @@ class FileTypeFilterTools protected const AUDIO_EXTS = ['mp3', 'flac', 'ogg', 'oga', 'wav', 'm4a', 'opus']; protected const ALLOWED_MIME_PLACEHOLDERS = ['image/*', 'audio/*', 'video/*']; - protected MimeTypesInterface $mimeTypes; - protected CacheInterface $cache; - - public function __construct(MimeTypesInterface $mimeTypes, CacheInterface $cache) + public function __construct(protected MimeTypesInterface $mimeTypes, protected CacheInterface $cache) { - $this->mimeTypes = $mimeTypes; - $this->cache = $cache; } /** @@ -73,7 +69,7 @@ class FileTypeFilterTools $element = trim($element); if (!preg_match('#^\.\w+$#', $element) // .ext is allowed && !preg_match('#^[-\w.]+/[-\w.]+#', $element) //Explicit MIME type is allowed - && !in_array($element, static::ALLOWED_MIME_PLACEHOLDERS, false)) { //image/* is allowed + && !in_array($element, static::ALLOWED_MIME_PLACEHOLDERS, true)) { //image/* is allowed return false; } } @@ -108,7 +104,7 @@ class FileTypeFilterTools } //Convert *.jpg to .jpg - if (0 === strpos($element, '*.')) { + if (str_starts_with($element, '*.')) { $element = str_replace('*.', '.', $element); } @@ -119,11 +115,13 @@ class FileTypeFilterTools $element = 'video/*'; } elseif ('audio' === $element || 'audio/' === $element) { $element = 'audio/*'; - } elseif (!preg_match('#^[-\w.]+/[-\w.*]+#', $element) && 0 !== strpos($element, '.')) { + } elseif (!preg_match('#^[-\w.]+/[-\w.*]+#', $element) && !str_starts_with($element, '.')) { //Convert jpg to .jpg $element = '.'.$element; } } + //Prevent weird side effects + unset($element); $elements = array_unique($elements); @@ -147,7 +145,7 @@ class FileTypeFilterTools foreach ($elements as $element) { $element = trim($element); - if (0 === strpos($element, '.')) { + if (str_starts_with($element, '.')) { //We found an explicit specified file extension -> add it to list $extensions[] = substr($element, 1); } elseif ('image/*' === $element) { @@ -177,6 +175,6 @@ class FileTypeFilterTools { $extension = strtolower($extension); - return empty($filter) || in_array($extension, $this->resolveFileExtensions($filter), false); + return $filter === '' || in_array($extension, $this->resolveFileExtensions($filter), true); } } diff --git a/src/Services/Attachments/PartPreviewGenerator.php b/src/Services/Attachments/PartPreviewGenerator.php index 39d1c65c..ba6e5db0 100644 --- a/src/Services/Attachments/PartPreviewGenerator.php +++ b/src/Services/Attachments/PartPreviewGenerator.php @@ -22,16 +22,19 @@ declare(strict_types=1); namespace App\Services\Attachments; +use App\Entity\Parts\Footprint; +use App\Entity\ProjectSystem\Project; +use App\Entity\Parts\Category; +use App\Entity\Parts\StorageLocation; +use App\Entity\Parts\MeasurementUnit; +use App\Entity\Parts\Manufacturer; use App\Entity\Attachments\Attachment; use App\Entity\Parts\Part; class PartPreviewGenerator { - protected AttachmentManager $attachmentHelper; - - public function __construct(AttachmentManager $attachmentHelper) + public function __construct(protected AttachmentManager $attachmentHelper) { - $this->attachmentHelper = $attachmentHelper; } /** @@ -55,21 +58,29 @@ class PartPreviewGenerator $list[] = $attachment; } - if (null !== $part->getFootprint()) { + //Then comes the other images of the part + foreach ($part->getAttachments() as $attachment) { + //Dont show the master attachment twice + if ($this->isAttachmentValidPicture($attachment) && $attachment !== $part->getMasterPictureAttachment()) { + $list[] = $attachment; + } + } + + if ($part->getFootprint() instanceof Footprint) { $attachment = $part->getFootprint()->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { $list[] = $attachment; } } - if (null !== $part->getBuiltProject()) { + if ($part->getBuiltProject() instanceof Project) { $attachment = $part->getBuiltProject()->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { $list[] = $attachment; } } - if (null !== $part->getCategory()) { + if ($part->getCategory() instanceof Category) { $attachment = $part->getCategory()->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { $list[] = $attachment; @@ -77,7 +88,7 @@ class PartPreviewGenerator } foreach ($part->getPartLots() as $lot) { - if (null !== $lot->getStorageLocation()) { + if ($lot->getStorageLocation() instanceof StorageLocation) { $attachment = $lot->getStorageLocation()->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { $list[] = $attachment; @@ -85,14 +96,14 @@ class PartPreviewGenerator } } - if (null !== $part->getPartUnit()) { + if ($part->getPartUnit() instanceof MeasurementUnit) { $attachment = $part->getPartUnit()->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { $list[] = $attachment; } } - if (null !== $part->getManufacturer()) { + if ($part->getManufacturer() instanceof Manufacturer) { $attachment = $part->getManufacturer()->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { $list[] = $attachment; @@ -117,7 +128,7 @@ class PartPreviewGenerator } //Otherwise check if the part has a footprint with a valid master attachment - if (null !== $part->getFootprint()) { + if ($part->getFootprint() instanceof Footprint) { $attachment = $part->getFootprint()->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { return $attachment; @@ -125,7 +136,7 @@ class PartPreviewGenerator } //With lowest priority use the master attachment of the project this part represents (when existing) - if (null !== $part->getBuiltProject()) { + if ($part->getBuiltProject() instanceof Project) { $attachment = $part->getBuiltProject()->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { return $attachment; @@ -145,7 +156,7 @@ class PartPreviewGenerator */ protected function isAttachmentValidPicture(?Attachment $attachment): bool { - return null !== $attachment + return $attachment instanceof Attachment && $attachment->isPicture() && $this->attachmentHelper->isFileExisting($attachment); } diff --git a/src/Services/Attachments/SVGSanitizer.php b/src/Services/Attachments/SVGSanitizer.php new file mode 100644 index 00000000..9ac5956b --- /dev/null +++ b/src/Services/Attachments/SVGSanitizer.php @@ -0,0 +1,58 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\Attachments; + +use Rhukster\DomSanitizer\DOMSanitizer; + +class SVGSanitizer +{ + + /** + * Sanitizes the given SVG string by removing any potentially harmful content (like inline scripts). + * @param string $input + * @return string + */ + public function sanitizeString(string $input): string + { + return (new DOMSanitizer(DOMSanitizer::SVG))->sanitize($input); + } + + /** + * Sanitizes the given SVG file by removing any potentially harmful content (like inline scripts). + * The sanitized content is written back to the file. + * @param string $filepath + */ + public function sanitizeFile(string $filepath): void + { + //Open the file and read the content + $content = file_get_contents($filepath); + if ($content === false) { + throw new \RuntimeException('Could not read file: ' . $filepath); + } + //Sanitize the content + $sanitizedContent = $this->sanitizeString($content); + //Write the sanitized content back to the file + file_put_contents($filepath, $sanitizedContent); + } +} \ No newline at end of file diff --git a/src/Services/Cache/ElementCacheTagGenerator.php b/src/Services/Cache/ElementCacheTagGenerator.php new file mode 100644 index 00000000..88fca09f --- /dev/null +++ b/src/Services/Cache/ElementCacheTagGenerator.php @@ -0,0 +1,69 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\Cache; + +use Doctrine\Persistence\Proxy; + +/** + * The purpose of this class is to generate cache tags for elements. + * E.g. to easily invalidate all caches for a given element type. + */ +class ElementCacheTagGenerator +{ + private array $cache = []; + + public function __construct() + { + } + + /** + * Returns a cache tag for the given element type, which can be used to invalidate all caches for this element type. + * @param string|object $element + * @return string + */ + public function getElementTypeCacheTag(string|object $element): string + { + //Ensure that the given element is a class name + if (is_object($element)) { + $element = $element::class; + } elseif (!class_exists($element)) { + //And that the class exists + throw new \InvalidArgumentException("The given class '$element' does not exist!"); + } + + //Check if the tag is already cached + if (isset($this->cache[$element])) { + return $this->cache[$element]; + } + + //If the element is a proxy, then get the real class name of the underlying object + if (is_a($element, Proxy::class, true) || str_starts_with($element, 'Proxies\\')) { + $element = get_parent_class($element); + } + + //Replace all backslashes with underscores to prevent problems with the cache and save the result + $this->cache[$element] = str_replace('\\', '_', $element); + return $this->cache[$element]; + } +} \ No newline at end of file diff --git a/src/Services/UserSystem/UserCacheKeyGenerator.php b/src/Services/Cache/UserCacheKeyGenerator.php similarity index 78% rename from src/Services/UserSystem/UserCacheKeyGenerator.php rename to src/Services/Cache/UserCacheKeyGenerator.php index c7c9e737..ac5487a5 100644 --- a/src/Services/UserSystem/UserCacheKeyGenerator.php +++ b/src/Services/Cache/UserCacheKeyGenerator.php @@ -20,25 +20,21 @@ declare(strict_types=1); -namespace App\Services\UserSystem; +namespace App\Services\Cache; use App\Entity\UserSystem\User; use Locale; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\Security\Core\Security; /** * Purpose of this service is to generate a key unique for a user, to use in Cache keys and tags. */ class UserCacheKeyGenerator { - protected Security $security; - protected RequestStack $requestStack; - - public function __construct(Security $security, RequestStack $requestStack) + public function __construct(protected Security $security, protected RequestStack $requestStack) { - $this->security = $security; - $this->requestStack = $requestStack; } /** @@ -51,10 +47,10 @@ class UserCacheKeyGenerator { $request = $this->requestStack->getCurrentRequest(); //Retrieve the locale from the request, if possible, otherwise use the default locale - $locale = $request ? $request->getLocale() : Locale::getDefault(); + $locale = $request instanceof Request ? $request->getLocale() : Locale::getDefault(); //If no user was specified, use the currently used one. - if (null === $user) { + if (!$user instanceof User) { $user = $this->security->getUser(); } @@ -64,7 +60,7 @@ class UserCacheKeyGenerator return 'user$_'.User::ID_ANONYMOUS; } - //In the most cases we can just use the username (its unique) - return 'user_'.$user->getUsername().'_'.$locale; + //Use the unique user id and the locale to generate the key + return 'user_'.$user->getID().'_'.$locale; } } diff --git a/src/Services/CustomEnvVarProcessor.php b/src/Services/CustomEnvVarProcessor.php index 8969b765..f269cc7d 100644 --- a/src/Services/CustomEnvVarProcessor.php +++ b/src/Services/CustomEnvVarProcessor.php @@ -35,7 +35,7 @@ final class CustomEnvVarProcessor implements EnvVarProcessorInterface $env = $getEnv($name); return !empty($env) && 'null://null' !== $env; - } catch (EnvNotFoundException $envNotFoundException) { + } catch (EnvNotFoundException) { return false; } } diff --git a/src/Services/Misc/DBInfoHelper.php b/src/Services/Doctrine/DBInfoHelper.php similarity index 56% rename from src/Services/Misc/DBInfoHelper.php rename to src/Services/Doctrine/DBInfoHelper.php index 896c0637..160e2d89 100644 --- a/src/Services/Misc/DBInfoHelper.php +++ b/src/Services/Doctrine/DBInfoHelper.php @@ -1,4 +1,25 @@ . + */ + +declare(strict_types=1); + /* * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). * @@ -17,12 +38,13 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - -namespace App\Services\Misc; +namespace App\Services\Doctrine; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\ORM\EntityManagerInterface; /** @@ -31,11 +53,9 @@ use Doctrine\ORM\EntityManagerInterface; class DBInfoHelper { protected Connection $connection; - protected EntityManagerInterface $entityManager; - public function __construct(EntityManagerInterface $entityManager) + public function __construct(protected EntityManagerInterface $entityManager) { - $this->entityManager = $entityManager; $this->connection = $entityManager->getConnection(); } @@ -49,17 +69,20 @@ class DBInfoHelper return 'mysql'; } - if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { return 'sqlite'; } + if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + return 'postgresql'; + } + return null; } /** * Returns the database version of the used database. - * @return string|null - * @throws \Doctrine\DBAL\Exception + * @throws Exception */ public function getDatabaseVersion(): ?string { @@ -67,32 +90,44 @@ class DBInfoHelper return $this->connection->fetchOne('SELECT VERSION()'); } - if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { return $this->connection->fetchOne('SELECT sqlite_version()'); } + if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + return $this->connection->fetchOne('SELECT version()'); + } + return null; } /** * Returns the database size in bytes. * @return int|null The database size in bytes or null if unknown - * @throws \Doctrine\DBAL\Exception + * @throws Exception */ public function getDatabaseSize(): ?int { if ($this->connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) { try { - return $this->connection->fetchOne('SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = DATABASE()'); - } catch (\Doctrine\DBAL\Exception $e) { + return (int) $this->connection->fetchOne('SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = DATABASE()'); + } catch (Exception) { return null; } } - if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { try { - return $this->connection->fetchOne('SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();'); - } catch (\Doctrine\DBAL\Exception $e) { + return (int) $this->connection->fetchOne('SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();'); + } catch (Exception) { + return null; + } + } + + if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + try { + return (int) $this->connection->fetchOne('SELECT pg_database_size(current_database())'); + } catch (Exception) { return null; } } @@ -102,30 +137,36 @@ class DBInfoHelper /** * Returns the name of the database. - * @return string|null */ public function getDatabaseName(): ?string { - return $this->connection->getDatabase() ?? null; + return $this->connection->getDatabase(); } /** * Returns the name of the database user. - * @return string|null */ public function getDatabaseUsername(): ?string { if ($this->connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) { try { return $this->connection->fetchOne('SELECT USER()'); - } catch (\Doctrine\DBAL\Exception $e) { + } catch (Exception) { return null; } } - if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { return 'sqlite'; } + + if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + try { + return $this->connection->fetchOne('SELECT current_user'); + } catch (Exception) { + return null; + } + } return null; } diff --git a/src/Services/Doctrine/NatsortDebugHelper.php b/src/Services/Doctrine/NatsortDebugHelper.php new file mode 100644 index 00000000..fe5b77aa --- /dev/null +++ b/src/Services/Doctrine/NatsortDebugHelper.php @@ -0,0 +1,86 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\Doctrine; + +use App\Doctrine\Functions\Natsort; +use App\Entity\Parts\Part; +use Doctrine\ORM\EntityManagerInterface; + +/** + * This service allows to debug the natsort function by showing various information about the current state of + * the natsort function. + */ +class NatsortDebugHelper +{ + public function __construct(private readonly EntityManagerInterface $entityManager) + { + // This is a dummy constructor + } + + /** + * Check if the slow natural sort is allowed on the Natsort function. + * If it is not, then the request handler might need to be adjusted. + * @return bool + */ + public function isSlowNaturalSortAllowed(): bool + { + return Natsort::isSlowNaturalSortAllowed(); + } + + public function getNaturalSortMethod(): string + { + //Construct a dummy query which uses the Natsort function + $query = $this->entityManager->createQuery('SELECT natsort(1) FROM ' . Part::class . ' p'); + $sql = $query->getSQL(); + //Remove the leading SELECT and the trailing semicolon + $sql = substr($sql, 7, -1); + + //Remove AS and everything afterwards + $sql = preg_replace('/\s+AS\s+.*/', '', $sql); + + //If just 1 is returned, then we use normal (non-natural sorting) + if ($sql === '1') { + return 'Disabled'; + } + + if (str_contains( $sql, 'COLLATE numeric')) { + return 'Native (PostgreSQL)'; + } + + if (str_contains($sql, 'NATURAL_SORT_KEY')) { + return 'Native (MariaDB)'; + } + + if (str_contains($sql, 'COLLATE NATURAL_CMP')) { + return 'Emulation via PHP (SQLite)'; + } + + if (str_contains($sql, 'NatSortKey')) { + return 'Emulation via custom function (MySQL)'; + } + + + return 'Unknown ('. $sql . ')'; + } +} \ No newline at end of file diff --git a/src/Services/EDA/KiCadHelper.php b/src/Services/EDA/KiCadHelper.php new file mode 100644 index 00000000..d4cbab34 --- /dev/null +++ b/src/Services/EDA/KiCadHelper.php @@ -0,0 +1,347 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\EDA; + +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; +use App\Entity\Parts\Part; +use App\Services\Cache\ElementCacheTagGenerator; +use App\Services\EntityURLGenerator; +use App\Services\Trees\NodesListBuilder; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Contracts\Cache\ItemInterface; +use Symfony\Contracts\Cache\TagAwareCacheInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +class KiCadHelper +{ + + public function __construct( + private readonly NodesListBuilder $nodesListBuilder, + private readonly TagAwareCacheInterface $kicadCache, + private readonly EntityManagerInterface $em, + private readonly ElementCacheTagGenerator $tagGenerator, + private readonly UrlGeneratorInterface $urlGenerator, + private readonly EntityURLGenerator $entityURLGenerator, + private readonly TranslatorInterface $translator, + /** 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, + ) { + } + + /** + * Returns an array of objects containing all categories in the database in the format required by KiCAD. + * The categories are flattened and sorted by their full path. + * Categories, which contain no parts, are filtered out. + * The result is cached for performance and invalidated on category changes. + * @return array + */ + public function getCategories(): array + { + return $this->kicadCache->get('kicad_categories_' . $this->category_depth, function (ItemInterface $item) { + //Invalidate the cache on category changes + $secure_class_name = $this->tagGenerator->getElementTypeCacheTag(Category::class); + $item->tag($secure_class_name); + + //Invalidate the cache on part changes (as the visibility depends on parts, and the parts can change) + $secure_class_name = $this->tagGenerator->getElementTypeCacheTag(Part::class); + $item->tag($secure_class_name); + + //If the category depth is smaller than 0, create only one dummy category + if ($this->category_depth < 0) { + return [ + [ + 'id' => '0', + 'name' => 'Part-DB', + ] + ]; + } + + //Otherwise just get the categories and filter them + + $categories = $this->nodesListBuilder->typeToNodesList(Category::class); + $repo = $this->em->getRepository(Category::class); + $result = []; + foreach ($categories as $category) { + //Skip invisible categories + if ($category->getEdaInfo()->getVisibility() === false) { + continue; + } + + //Skip categories with a depth greater than the configured one + if ($category->getLevel() > $this->category_depth) { + continue; + } + + //Ensure that the category contains parts + //For the last level, we need to use a recursive query, otherwise we can use a simple query + /** @var Category $category */ + $parts_count = $category->getLevel() >= $this->category_depth ? $repo->getPartsCountRecursive($category) : $repo->getPartsCount($category); + + if ($parts_count < 1) { + continue; + } + + //Check if the category should be visible + if (!$this->shouldCategoryBeVisible($category)) { + continue; + } + + //Format the category for KiCAD + $result[] = [ + 'id' => (string)$category->getId(), + 'name' => $category->getFullPath('/'), + //Show the category link as the category description, this also fixes an segfault in KiCad see issue #878 + 'description' => $this->entityURLGenerator->listPartsURL($category), + ]; + } + + return $result; + }); + } + + /** + * Returns an array of objects containing all parts for the given category in the format required by KiCAD. + * The result is cached for performance and invalidated on category or part changes. + * @param Category|null $category + * @return array + */ + public function getCategoryParts(?Category $category): array + { + return $this->kicadCache->get('kicad_category_parts_'.($category?->getID() ?? 0) . '_' . $this->category_depth, + function (ItemInterface $item) use ($category) { + $item->tag([ + $this->tagGenerator->getElementTypeCacheTag(Category::class), + $this->tagGenerator->getElementTypeCacheTag(Part::class), + //Visibility can change based on the footprint + $this->tagGenerator->getElementTypeCacheTag(Footprint::class) + ]); + + if ($this->category_depth >= 0) { + //Ensure that the category is set + if ($category === null) { + throw new NotFoundHttpException('Category must be set, if category_depth is greater than 1!'); + } + + $category_repo = $this->em->getRepository(Category::class); + if ($category->getLevel() >= $this->category_depth) { + //Get all parts for the category and its children + $parts = $category_repo->getPartsRecursive($category); + } else { + //Get only direct parts for the category (without children), as the category is not collapsed + $parts = $category_repo->getParts($category); + } + } else { + //Get all parts + $parts = $this->em->getRepository(Part::class)->findAll(); + } + + $result = []; + foreach ($parts as $part) { + //If the part is invisible, then skip it + if (!$this->shouldPartBeVisible($part)) { + continue; + } + + $result[] = [ + 'id' => (string)$part->getId(), + 'name' => $part->getName(), + 'description' => $part->getDescription(), + ]; + } + + return $result; + }); + } + + public function getKiCADPart(Part $part): array + { + $result = [ + 'id' => (string)$part->getId(), + 'name' => $part->getName(), + "symbolIdStr" => $part->getEdaInfo()->getKicadSymbol() ?? $part->getCategory()?->getEdaInfo()->getKicadSymbol() ?? "", + "exclude_from_bom" => $this->boolToKicadBool($part->getEdaInfo()->getExcludeFromBom() ?? $part->getCategory()?->getEdaInfo()->getExcludeFromBom() ?? false), + "exclude_from_board" => $this->boolToKicadBool($part->getEdaInfo()->getExcludeFromBoard() ?? $part->getCategory()?->getEdaInfo()->getExcludeFromBoard() ?? false), + "exclude_from_sim" => $this->boolToKicadBool($part->getEdaInfo()->getExcludeFromSim() ?? $part->getCategory()?->getEdaInfo()->getExcludeFromSim() ?? true), + "fields" => [] + ]; + + $result["fields"]["footprint"] = $this->createField($part->getEdaInfo()->getKicadFootprint() ?? $part->getFootprint()?->getEdaInfo()->getKicadFootprint() ?? ""); + $result["fields"]["reference"] = $this->createField($part->getEdaInfo()->getReferencePrefix() ?? $part->getCategory()?->getEdaInfo()->getReferencePrefix() ?? 'U', true); + $result["fields"]["value"] = $this->createField($part->getEdaInfo()->getValue() ?? $part->getName(), true); + $result["fields"]["keywords"] = $this->createField($part->getTags()); + + //Use the part info page as datasheet link. It must be an absolute URL. + $result["fields"]["datasheet"] = $this->createField( + $this->urlGenerator->generate( + 'part_info', + ['id' => $part->getId()], + UrlGeneratorInterface::ABSOLUTE_URL) + ); + + //Add basic fields + $result["fields"]["description"] = $this->createField($part->getDescription()); + if ($part->getCategory() !== null) { + $result["fields"]["Category"] = $this->createField($part->getCategory()->getFullPath('/')); + } + if ($part->getManufacturer() !== null) { + $result["fields"]["Manufacturer"] = $this->createField($part->getManufacturer()->getName()); + } + if ($part->getManufacturerProductNumber() !== "") { + $result['fields']["MPN"] = $this->createField($part->getManufacturerProductNumber()); + } + if ($part->getManufacturingStatus() !== null) { + $result["fields"]["Manufacturing Status"] = $this->createField( + //Always use the english translation + $this->translator->trans($part->getManufacturingStatus()->toTranslationKey(), locale: 'en') + ); + } + if ($part->getFootprint() !== null) { + $result["fields"]["Part-DB Footprint"] = $this->createField($part->getFootprint()->getName()); + } + if ($part->getPartUnit() !== null) { + $unit = $part->getPartUnit()->getName(); + if ($part->getPartUnit()->getUnit() !== "") { + $unit .= ' ('.$part->getPartUnit()->getUnit().')'; + } + $result["fields"]["Part-DB Unit"] = $this->createField($unit); + } + if ($part->getMass()) { + $result["fields"]["Mass"] = $this->createField($part->getMass() . ' g'); + } + $result["fields"]["Part-DB ID"] = $this->createField($part->getId()); + if ($part->getIpn() !== null && $part->getIpn() !== '' && $part->getIpn() !== '0') { + $result["fields"]["Part-DB IPN"] = $this->createField($part->getIpn()); + } + + + return $result; + } + + /** + * Determine if the given part should be visible for the EDA. + * @param Category $category + * @return bool + */ + private function shouldCategoryBeVisible(Category $category): bool + { + $eda_info = $category->getEdaInfo(); + + //If the category visibility is explicitly set, then use it + if ($eda_info->getVisibility() !== null) { + return $eda_info->getVisibility(); + } + + //try to check if the fields were set + if ($eda_info->getKicadSymbol() !== null + || $eda_info->getReferencePrefix() !== null) { + return true; + } + + //Check if there is any part in this category, which should be visible + $category_repo = $this->em->getRepository(Category::class); + if ($category->getLevel() >= $this->category_depth) { + //Get all parts for the category and its children + $parts = $category_repo->getPartsRecursive($category); + } else { + //Get only direct parts for the category (without children), as the category is not collapsed + $parts = $category_repo->getParts($category); + } + + foreach ($parts as $part) { + if ($this->shouldPartBeVisible($part)) { + return true; + } + } + + //Otherwise the category should be not visible + return false; + } + + /** + * Determine if the given part should be visible for the EDA. + * @param Part $part + * @return bool + */ + private function shouldPartBeVisible(Part $part): bool + { + $eda_info = $part->getEdaInfo(); + $category = $part->getCategory(); + + //If the user set a visibility, then use it + if ($eda_info->getVisibility() !== null) { + return $part->getEdaInfo()->getVisibility(); + } + + //If the part has a category, then use the category visibility if possible + if ($category && $category->getEdaInfo()->getVisibility() !== null) { + return $category->getEdaInfo()->getVisibility(); + } + + //If both are null, then we try to determine the visibility based on if fields are set + if ($eda_info->getKicadSymbol() !== null + || $eda_info->getKicadFootprint() !== null + || $eda_info->getReferencePrefix() !== null + || $eda_info->getValue() !== null) { + return true; + } + + //Check also if the fields are set for the category (if it exists) + if ($category && ( + $category->getEdaInfo()->getKicadSymbol() !== null + || $category->getEdaInfo()->getReferencePrefix() !== null + )) { + return true; + } + //And on the footprint + //Otherwise the part should be not visible + return $part->getFootprint() && $part->getFootprint()->getEdaInfo()->getKicadFootprint() !== null; + } + + /** + * Converts a boolean value to the format required by KiCAD. + * @param bool $value + * @return string + */ + private function boolToKicadBool(bool $value): string + { + return $value ? 'True' : 'False'; + } + + /** + * Creates a field array for KiCAD + * @param string|int|float $value + * @param bool $visible + * @return array + */ + private function createField(string|int|float $value, bool $visible = false): array + { + return [ + 'value' => (string)$value, + 'visible' => $this->boolToKicadBool($visible), + ]; + } +} \ No newline at end of file diff --git a/src/Services/ElementTypeNameGenerator.php b/src/Services/ElementTypeNameGenerator.php index adee61ad..14247145 100644 --- a/src/Services/ElementTypeNameGenerator.php +++ b/src/Services/ElementTypeNameGenerator.php @@ -22,9 +22,12 @@ declare(strict_types=1); namespace App\Services; +use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentType; +use App\Entity\Base\AbstractDBElement; use App\Entity\Contracts\NamedElementInterface; +use App\Entity\Parts\PartAssociation; use App\Entity\ProjectSystem\Project; use App\Entity\LabelSystem\LabelProfile; use App\Entity\Parameters\AbstractParameter; @@ -34,7 +37,7 @@ use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\PriceInformations\Orderdetail; @@ -43,18 +46,17 @@ use App\Entity\ProjectSystem\ProjectBOMEntry; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use App\Exceptions\EntityNotSupportedException; -use function get_class; use Symfony\Contracts\Translation\TranslatorInterface; +/** + * @see \App\Tests\Services\ElementTypeNameGeneratorTest + */ class ElementTypeNameGenerator { - protected TranslatorInterface $translator; protected array $mapping; - public function __construct(TranslatorInterface $translator) + public function __construct(protected TranslatorInterface $translator, private readonly EntityURLGenerator $entityURLGenerator) { - $this->translator = $translator; - //Child classes has to become before parent classes $this->mapping = [ Attachment::class => $this->translator->trans('attachment.label'), @@ -67,7 +69,7 @@ class ElementTypeNameGenerator MeasurementUnit::class => $this->translator->trans('measurement_unit.label'), Part::class => $this->translator->trans('part.label'), PartLot::class => $this->translator->trans('part_lot.label'), - Storelocation::class => $this->translator->trans('storelocation.label'), + StorageLocation::class => $this->translator->trans('storelocation.label'), Supplier::class => $this->translator->trans('supplier.label'), Currency::class => $this->translator->trans('currency.label'), Orderdetail::class => $this->translator->trans('orderdetail.label'), @@ -76,11 +78,12 @@ class ElementTypeNameGenerator User::class => $this->translator->trans('user.label'), AbstractParameter::class => $this->translator->trans('parameter.label'), LabelProfile::class => $this->translator->trans('label_profile.label'), + PartAssociation::class => $this->translator->trans('part_association.label'), ]; } /** - * Gets an localized label for the type of the entity. + * Gets a localized label for the type of the entity. * A part element becomes "Part" ("Bauteil" in german) and a category object becomes "Category". * Useful when the type should be shown to user. * Throws an exception if the class is not supported. @@ -91,24 +94,24 @@ class ElementTypeNameGenerator * * @throws EntityNotSupportedException when the passed entity is not supported */ - public function getLocalizedTypeLabel($entity): string + public function getLocalizedTypeLabel(object|string $entity): string { - $class = is_string($entity) ? $entity : get_class($entity); + $class = is_string($entity) ? $entity : $entity::class; - //Check if we have an direct array entry for our entity class, then we can use it + //Check if we have a direct array entry for our entity class, then we can use it if (isset($this->mapping[$class])) { return $this->mapping[$class]; } //Otherwise iterate over array and check for inheritance (needed when the proxy element from doctrine are passed) - foreach ($this->mapping as $class => $translation) { - if (is_a($entity, $class, true)) { + foreach ($this->mapping as $class_to_check => $translation) { + if (is_a($entity, $class_to_check, true)) { return $translation; } } //When nothing was found throw an exception - throw new EntityNotSupportedException(sprintf('No localized label for the element with type %s was found!', is_object($entity) ? get_class($entity) : (string) $entity)); + throw new EntityNotSupportedException(sprintf('No localized label for the element with type %s was found!', is_object($entity) ? $entity::class : (string) $entity)); } /** @@ -117,7 +120,7 @@ class ElementTypeNameGenerator * It uses getLocalizedLabel to determine the type. * * @param NamedElementInterface $entity the entity for which the string should be generated - * @param bool $use_html If set to true, a html string is returned, where the type is set italic + * @param bool $use_html If set to true, a html string is returned, where the type is set italic, and the name is escaped * * @return string The localized string * @@ -132,4 +135,77 @@ class ElementTypeNameGenerator return $type.': '.$entity->getName(); } + + + /** + * Returns a HTML formatted label for the given enitity in the format "Type: Name" (on elements with a name) and + * "Type: ID" (on elements without a name). If possible the value is given as a link to the element. + * @param AbstractDBElement $entity The entity for which the label should be generated + * @param bool $include_associated If set to true, the associated entity (like the part belonging to a part lot) is included in the label to give further information + */ + public function formatLabelHTMLForEntity(AbstractDBElement $entity, bool $include_associated = false): string + { + //The element is existing + if ($entity instanceof NamedElementInterface && $entity->getName() !== '') { + try { + $tmp = sprintf( + '%s', + $this->entityURLGenerator->infoURL($entity), + $this->getTypeNameCombination($entity, true) + ); + } catch (EntityNotSupportedException) { + $tmp = $this->getTypeNameCombination($entity, true); + } + } else { //Target does not have a name + $tmp = sprintf( + '%s: %s', + $this->getLocalizedTypeLabel($entity), + $entity->getID() + ); + } + + //Add a hint to the associated element if possible + if ($include_associated) { + if ($entity instanceof Attachment && $entity->getElement() instanceof AttachmentContainingDBElement) { + $on = $entity->getElement(); + } elseif ($entity instanceof AbstractParameter && $entity->getElement() instanceof AbstractDBElement) { + $on = $entity->getElement(); + } elseif ($entity instanceof PartLot && $entity->getPart() instanceof Part) { + $on = $entity->getPart(); + } elseif ($entity instanceof Orderdetail && $entity->getPart() instanceof Part) { + $on = $entity->getPart(); + } elseif ($entity instanceof Pricedetail && $entity->getOrderdetail() instanceof Orderdetail && $entity->getOrderdetail()->getPart() instanceof Part) { + $on = $entity->getOrderdetail()->getPart(); + } elseif ($entity instanceof ProjectBOMEntry && $entity->getProject() instanceof Project) { + $on = $entity->getProject(); + } + + if (isset($on) && $on instanceof NamedElementInterface) { + try { + $tmp .= sprintf( + ' (%s)', + $this->entityURLGenerator->infoURL($on), + $this->getTypeNameCombination($on, true) + ); + } catch (EntityNotSupportedException) { + } + } + } + + return $tmp; + } + + /** + * Create a HTML formatted label for a deleted element of which we only know the class and the ID. + * Please note that it is not checked if the element really not exists anymore, so you have to do this yourself. + */ + public function formatElementDeletedHTML(string $class, int $id): string + { + return sprintf( + '%s: %s [%s]', + $this->getLocalizedTypeLabel($class), + $id, + $this->translator->trans('log.target_deleted') + ); + } } diff --git a/src/Services/EntityMergers/EntityMerger.php b/src/Services/EntityMergers/EntityMerger.php new file mode 100644 index 00000000..c0be84ee --- /dev/null +++ b/src/Services/EntityMergers/EntityMerger.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\EntityMergers; + +use App\Services\EntityMergers\Mergers\EntityMergerInterface; +use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; + +/** + * This service is used to merge two entities together. + * It automatically finds the correct merger (implementing EntityMergerInterface) for the two entities if one exists. + */ +class EntityMerger +{ + public function __construct(#[TaggedIterator('app.entity_merger')] protected iterable $mergers) + { + } + + /** + * This function finds the first merger that supports merging the other entity into the target entity. + * @param object $target + * @param object $other + * @param array $context + * @return EntityMergerInterface|null + */ + public function findMergerForObject(object $target, object $other, array $context = []): ?EntityMergerInterface + { + foreach ($this->mergers as $merger) { + if ($merger->supports($target, $other, $context)) { + return $merger; + } + } + return null; + } + + /** + * This function merges the other entity into the target entity. If no merger is found an exception is thrown. + * The target entity will be modified and returned. + * @param object $target + * @param object $other + * @param array $context + * @template T of object + * @phpstan-param T $target + * @phpstan-param T $other + * @phpstan-return T + * @return object + */ + public function merge(object $target, object $other, array $context = []): object + { + $merger = $this->findMergerForObject($target, $other, $context); + if ($merger === null) { + throw new \RuntimeException('No merger found for merging '.$other::class.' into '.$target::class); + } + return $merger->merge($target, $other, $context); + } +} \ No newline at end of file diff --git a/src/Services/EntityMergers/Mergers/EntityMergerHelperTrait.php b/src/Services/EntityMergers/Mergers/EntityMergerHelperTrait.php new file mode 100644 index 00000000..64c952a9 --- /dev/null +++ b/src/Services/EntityMergers/Mergers/EntityMergerHelperTrait.php @@ -0,0 +1,358 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\EntityMergers\Mergers; + +use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\AttachmentContainingDBElement; +use App\Entity\Base\AbstractNamedDBElement; +use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Parameters\AbstractParameter; +use App\Entity\Parts\Part; +use Doctrine\Common\Collections\Collection; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Contracts\Service\Attribute\Required; + +use function Symfony\Component\String\u; + +/** + * This trait provides helper methods for entity mergers. + * By default, it uses the value from the target entity, unless it not fullfills a condition. + */ +trait EntityMergerHelperTrait +{ + protected PropertyAccessorInterface $property_accessor; + + #[Required] + public function setPropertyAccessor(PropertyAccessorInterface $property_accessor): void + { + $this->property_accessor = $property_accessor; + } + + /** + * Choice the value to use from the target or the other entity by using a callback function. + * + * @param callable $callback The callback to use. The signature is: function($target_value, $other_value, $target, $other, $field). The callback should return the value to use. + * @param object $target The target entity + * @param object $other The other entity + * @param string $field The field to use + * @return object The target entity with the value set + */ + protected function useCallback(callable $callback, object $target, object $other, string $field): object + { + //Get the values from the entities + $target_value = $this->property_accessor->getValue($target, $field); + $other_value = $this->property_accessor->getValue($other, $field); + + //Call the callback, with the signature: function($target_value, $other_value, $target, $other, $field) + //The callback should return the value to use + $value = $callback($target_value, $other_value, $target, $other, $field); + + //Set the value + $this->property_accessor->setValue($target, $field, $value); + + return $target; + } + + /** + * Use the value from the other entity, if the value from the target entity is null. + * + * @param object $target The target entity + * @param object $other The other entity + * @param string $field The field to use + * @return object The target entity with the value set + */ + protected function useOtherValueIfNotNull(object $target, object $other, string $field): object + { + return $this->useCallback( + fn($target_value, $other_value) => $target_value ?? $other_value, + $target, + $other, + $field + ); + + } + + /** + * Use the value from the other entity, if the value from the target entity is empty. + * + * @param object $target The target entity + * @param object $other The other entity + * @param string $field The field to use + * @return object The target entity with the value set + */ + protected function useOtherValueIfNotEmtpy(object $target, object $other, string $field): object + { + return $this->useCallback( + fn($target_value, $other_value) => empty($target_value) ? $other_value : $target_value, + $target, + $other, + $field + ); + } + + /** + * Use the larger value from the target and the other entity for the given field. + * + * @param object $target + * @param object $other + * @param string $field + * @return object + */ + protected function useLargerValue(object $target, object $other, string $field): object + { + return $this->useCallback( + fn($target_value, $other_value) => max($target_value, $other_value), + $target, + $other, + $field + ); + } + + /** + * Use the smaller value from the target and the other entity for the given field. + * + * @param object $target + * @param object $other + * @param string $field + * @return object + */ + protected function useSmallerValue(object $target, object $other, string $field): object + { + return $this->useCallback( + fn($target_value, $other_value) => min($target_value, $other_value), + $target, + $other, + $field + ); + } + + /** + * Perform an OR operation on the boolean values from the target and the other entity for the given field. + * This effectively means that the value is true, if it is true in at least one of the entities. + * @param object $target + * @param object $other + * @param string $field + * @return object + */ + protected function useTrueValue(object $target, object $other, string $field): object + { + return $this->useCallback( + fn(bool $target_value, bool $other_value): bool => $target_value || $other_value, + $target, + $other, + $field + ); + } + + /** + * Perform a merge of comma separated lists from the target and the other entity for the given field. + * The values are merged and duplicates are removed. + * @param object $target + * @param object $other + * @param string $field + * @return object + */ + protected function mergeTags(object $target, object $other, string $field, string $separator = ','): object + { + return $this->useCallback( + function (string|null $t, string|null $o) use ($separator): string { + //Explode the strings into arrays + $t_array = explode($separator, $t ?? ''); + $o_array = explode($separator, $o ?? ''); + + //Merge the arrays and remove duplicates + $tmp = array_unique(array_merge($t_array, $o_array)); + + //Implode the array back to a string + return implode($separator, $tmp); + }, + $target, + $other, + $field + ); + } + + /** + * Merge the collections from the target and the other entity for the given field and put all items into the target collection. + * @param object $target + * @param object $other + * @param string $field + * @param callable|null $equal_fn A function, which checks if two items are equal. The signature is: function(object $target, object other): bool. + * Return true if the items are equal, false otherwise. If two items are equal, the item from the other collection is not added to the target collection. + * If null, the items are compared by (instance) identity. + * @return object + */ + protected function mergeCollections(object $target, object $other, string $field, ?callable $equal_fn = null): object + { + $target_collection = $this->property_accessor->getValue($target, $field); + $other_collection = $this->property_accessor->getValue($other, $field); + + if (!$target_collection instanceof Collection) { + throw new \InvalidArgumentException("The target field $field is not a collection"); + } + + //Clone the items from the other collection + $clones = []; + foreach ($other_collection as $item) { + //Check if the item is already in the target collection + if ($equal_fn !== null) { + foreach ($target_collection as $target_item) { + if ($equal_fn($target_item, $item)) { + continue 2; + } + } + } elseif ($target_collection->contains($item)) { + continue; + } + + $clones[] = clone $item; + } + + $tmp = array_merge($target_collection->toArray(), $clones); + + //Create a new collection with the clones and merge it into the target collection + $this->property_accessor->setValue($target, $field, $tmp); + + return $target; + } + + /** + * Merge the attachments from the target and the other entity. + * @param AttachmentContainingDBElement $target + * @param AttachmentContainingDBElement $other + * @return object + */ + protected function mergeAttachments(AttachmentContainingDBElement $target, AttachmentContainingDBElement $other): object + { + return $this->mergeCollections($target, $other, 'attachments', fn(Attachment $t, Attachment $o): bool => $t->getName() === $o->getName() + && $t->getAttachmentType() === $o->getAttachmentType() + && $t->getExternalPath() === $o->getExternalPath() + && $t->getInternalPath() === $o->getInternalPath()); + } + + /** + * Merge the parameters from the target and the other entity. + * @param AbstractStructuralDBElement|Part $target + * @param AbstractStructuralDBElement|Part $other + * @return object + */ + protected function mergeParameters(AbstractStructuralDBElement|Part $target, AbstractStructuralDBElement|Part $other): object + { + return $this->mergeCollections($target, $other, 'parameters', fn(AbstractParameter $t, AbstractParameter $o): bool => $t->getName() === $o->getName() + && $t->getSymbol() === $o->getSymbol() + && $t->getUnit() === $o->getUnit() + && $t->getValueMax() === $o->getValueMax() + && $t->getValueMin() === $o->getValueMin() + && $t->getValueTypical() === $o->getValueTypical() + && $t->getValueText() === $o->getValueText() + && $t->getGroup() === $o->getGroup()); + } + + /** + * Check if the two strings have equal content. + * This method is case-insensitive and ignores whitespace. + * @param string|\Stringable $t + * @param string|\Stringable $o + * @return bool + */ + protected function areStringsEqual(string|\Stringable $t, string|\Stringable $o): bool + { + $t_str = u($t)->trim()->folded(); + $o_str = u($o)->trim()->folded(); + + return $t_str->equalsTo($o_str); + } + + /** + * Merge the text from the target and the other entity for the given field by attaching the other text to the target text via the given separator. + * For example, if the target text is "Hello" and the other text is "World", the result is "Hello / World". + * If the text is the same in both entities, the target text is returned. + * @param object $target + * @param object $other + * @param string $field + * @param string $separator + * @return object + */ + protected function mergeTextWithSeparator(object $target, object $other, string $field, string $separator = ' / '): object + { + return $this->useCallback( + function (string $t, string $o) use ($separator): string { + //Check if the strings are equal + if ($this->areStringsEqual($t, $o)) { + return $t; + } + + //Skip empty strings + if (trim($t) === '') { + return trim($o); + } + if (trim($o) === '') { + return trim($t); + } + + return trim($t) . $separator . trim($o); + }, + $target, + $other, + $field + ); + } + + /** + * Merge two comments from the target and the other entity for the given field. + * The comments of the both entities get concated, while the second part get a headline with the name of the old part. + * @param AbstractNamedDBElement $target + * @param AbstractNamedDBElement $other + * @param string $field + * @return object + */ + protected function mergeComment(AbstractNamedDBElement $target, AbstractNamedDBElement $other, string $field = 'comment'): object + { + return $this->useCallback( + function (string $t, string $o) use ($other): string { + //Check if the strings are equal + if ($this->areStringsEqual($t, $o)) { + return $t; + } + + //Skip empty strings + if (trim($t) === '') { + return trim($o); + } + if (trim($o) === '') { + return trim($t); + } + + return sprintf("%s\n\n%s:\n%s", + trim($t), + $other->getName(), + trim($o) + ); + }, + $target, + $other, + $field + ); + } +} \ No newline at end of file diff --git a/src/Services/EntityMergers/Mergers/EntityMergerInterface.php b/src/Services/EntityMergers/Mergers/EntityMergerInterface.php new file mode 100644 index 00000000..046fc0ea --- /dev/null +++ b/src/Services/EntityMergers/Mergers/EntityMergerInterface.php @@ -0,0 +1,58 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\EntityMergers\Mergers; + + +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; + +/** + * @template T of object + */ +#[AutoconfigureTag('app.entity_merger')] +interface EntityMergerInterface +{ + /** + * Determines if this merger supports merging the other entity into the target entity. + * @param object $target + * @phpstan-param T $target + * @param object $other + * @phpstan-param T $other + * @param array $context + * @return bool True if this merger supports merging the other entity into the target entity, false otherwise + */ + public function supports(object $target, object $other, array $context = []): bool; + + /** + * Merge the other entity into the target entity. + * The target entity will be modified and returned. + * @param object $target + * @phpstan-param T $target + * @param object $other + * @phpstan-param T $other + * @param array $context + * @phpstan-return T + * @return object + */ + public function merge(object $target, object $other, array $context = []): object; +} \ No newline at end of file diff --git a/src/Services/EntityMergers/Mergers/PartMerger.php b/src/Services/EntityMergers/Mergers/PartMerger.php new file mode 100644 index 00000000..4ce779e8 --- /dev/null +++ b/src/Services/EntityMergers/Mergers/PartMerger.php @@ -0,0 +1,186 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\EntityMergers\Mergers; + +use App\Entity\Parts\InfoProviderReference; +use App\Entity\Parts\ManufacturingStatus; +use App\Entity\Parts\Part; +use App\Entity\Parts\PartAssociation; +use App\Entity\PriceInformations\Orderdetail; + +/** + * This class merges two parts together. + * + * @implements EntityMergerInterface + * @see \App\Tests\Services\EntityMergers\Mergers\PartMergerTest + */ +class PartMerger implements EntityMergerInterface +{ + + use EntityMergerHelperTrait; + + public function supports(object $target, object $other, array $context = []): bool + { + return $target instanceof Part && $other instanceof Part; + } + + public function merge(object $target, object $other, array $context = []): Part + { + if (!$target instanceof Part || !$other instanceof Part) { + throw new \InvalidArgumentException('The target and the other entity must be instances of Part'); + } + + //Merge basic fields + $this->mergeTextWithSeparator($target, $other, 'name'); + $this->mergeTextWithSeparator($target, $other, 'description'); + $this->mergeComment($target, $other); + $this->useOtherValueIfNotEmtpy($target, $other, 'manufacturer_product_url'); + $this->useOtherValueIfNotEmtpy($target, $other, 'manufacturer_product_number'); + $this->useOtherValueIfNotEmtpy($target, $other, 'mass'); + $this->useOtherValueIfNotEmtpy($target, $other, 'ipn'); + + //Merge relations to other entities + $this->useOtherValueIfNotNull($target, $other, 'manufacturer'); + $this->useOtherValueIfNotNull($target, $other, 'footprint'); + $this->useOtherValueIfNotNull($target, $other, 'category'); + $this->useOtherValueIfNotNull($target, $other, 'partUnit'); + + //We assume that the higher value is the correct one for minimum instock + $this->useLargerValue($target, $other, 'minamount'); + + //We assume that a part needs review and is a favorite if one of the parts is + $this->useTrueValue($target, $other, 'needs_review'); + $this->useTrueValue($target, $other, 'favorite'); + + //Merge the tags using the tag merger + $this->mergeTags($target, $other, 'tags'); + + //Merge manufacturing status + $this->useCallback(function (?ManufacturingStatus $t, ?ManufacturingStatus $o): ManufacturingStatus { + //Use the other value, if the target value is not set + if ($t === ManufacturingStatus::NOT_SET || $t === null) { + return $o ?? ManufacturingStatus::NOT_SET; + } + + return $t; + }, $target, $other, 'manufacturing_status'); + + //Merge provider reference + $this->useCallback(function (InfoProviderReference $t, InfoProviderReference $o): InfoProviderReference { + if (!$t->isProviderCreated() && $o->isProviderCreated()) { + return $o; + } + return $t; + }, $target, $other, 'providerReference'); + + //Merge the collections + $this->mergeCollectionFields($target, $other, $context); + + return $target; + } + + private function comparePartAssociations(PartAssociation $t, PartAssociation $o): bool { + //We compare the translation keys, as it contains info about the type and other type info + return $t->getOther() === $o->getOther() + && $t->getTypeTranslationKey() === $o->getTypeTranslationKey(); + } + + private function mergeCollectionFields(Part $target, Part $other, array $context): void + { + /******************************************************************************** + * Merge collections + ********************************************************************************/ + + //Lots from different parts are never considered equal, so we just merge them together + $this->mergeCollections($target, $other, 'partLots'); + $this->mergeAttachments($target, $other); + $this->mergeParameters($target, $other); + + //Merge the associations + $this->mergeCollections($target, $other, 'associated_parts_as_owner', $this->comparePartAssociations(...)); + + //We have to recreate the associations towards the other part, as they are not created by the merger + foreach ($other->getAssociatedPartsAsOther() as $association) { + //Clone the association + $clone = clone $association; + //Set the target part as the other part + $clone->setOther($target); + $owner = $clone->getOwner(); + if (!$owner) { + continue; + } + //Ensure that the association is not already present + foreach ($owner->getAssociatedPartsAsOwner() as $existing_association) { + if ($this->comparePartAssociations($existing_association, $clone)) { + continue 2; + } + } + + //Add the association to the owner + $owner->addAssociatedPartsAsOwner($clone); + } + + $this->mergeCollections($target, $other, 'orderdetails', function (Orderdetail $t, Orderdetail $o) { + //First check that the orderdetails infos are equal + $tmp = $t->getSupplier() === $o->getSupplier() + && $t->getSupplierPartNr() === $o->getSupplierPartNr() + && $t->getSupplierProductUrl(false) === $o->getSupplierProductUrl(false); + + if (!$tmp) { + return false; + } + + //Check if the pricedetails are equal + $t_pricedetails = $t->getPricedetails(); + $o_pricedetails = $o->getPricedetails(); + //Ensure that both pricedetails have the same length + if (count($t_pricedetails) !== count($o_pricedetails)) { + return false; + } + + //Check if all pricedetails are equal + for ($n=0, $nMax = count($t_pricedetails); $n< $nMax; $n++) { + $t_price = $t_pricedetails->get($n); + $o_price = $o_pricedetails->get($n); + + if (!$t_price->getPrice()->isEqualTo($o_price->getPrice()) + || $t_price->getCurrency() !== $o_price->getCurrency() + || $t_price->getPriceRelatedQuantity() !== $o_price->getPriceRelatedQuantity() + || $t_price->getMinDiscountQuantity() !== $o_price->getMinDiscountQuantity() + ) { + return false; + } + } + + //If all pricedetails are equal, the orderdetails are equal + return true; + }); + //The pricedetails are not correctly assigned to the new orderdetails, so fix that + foreach ($target->getOrderdetails() as $orderdetail) { + foreach ($orderdetail->getPricedetails() as $pricedetail) { + $pricedetail->setOrderdetail($orderdetail); + } + } + } +} \ No newline at end of file diff --git a/src/Services/EntityURLGenerator.php b/src/Services/EntityURLGenerator.php index 1ed659e4..78db06f0 100644 --- a/src/Services/EntityURLGenerator.php +++ b/src/Services/EntityURLGenerator.php @@ -26,6 +26,7 @@ use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentType; use App\Entity\Attachments\PartAttachment; use App\Entity\Base\AbstractDBElement; +use App\Entity\Parameters\PartParameter; use App\Entity\ProjectSystem\Project; use App\Entity\LabelSystem\LabelProfile; use App\Entity\Parts\Category; @@ -34,7 +35,7 @@ use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\PriceInformations\Orderdetail; @@ -43,9 +44,7 @@ use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use App\Exceptions\EntityNotSupportedException; use App\Services\Attachments\AttachmentURLGenerator; -use DateTime; use function array_key_exists; -use function get_class; use InvalidArgumentException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -56,13 +55,8 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; */ class EntityURLGenerator { - protected UrlGeneratorInterface $urlGenerator; - protected AttachmentURLGenerator $attachmentURLGenerator; - - public function __construct(UrlGeneratorInterface $urlGenerator, AttachmentURLGenerator $attachmentURLGenerator) + public function __construct(protected UrlGeneratorInterface $urlGenerator, protected AttachmentURLGenerator $attachmentURLGenerator) { - $this->urlGenerator = $urlGenerator; - $this->attachmentURLGenerator = $attachmentURLGenerator; } /** @@ -78,35 +72,25 @@ class EntityURLGenerator * @throws EntityNotSupportedException thrown if the entity is not supported for the given type * @throws InvalidArgumentException thrown if the givent type is not existing */ - public function getURL($entity, string $type): string + public function getURL(mixed $entity, string $type): string { - switch ($type) { - case 'info': - return $this->infoURL($entity); - case 'edit': - return $this->editURL($entity); - case 'create': - return $this->createURL($entity); - case 'clone': - return $this->cloneURL($entity); - case 'list': - case 'list_parts': - return $this->listPartsURL($entity); - case 'delete': - return $this->deleteURL($entity); - case 'file_download': - return $this->downloadURL($entity); - case 'file_view': - return $this->viewURL($entity); - } - - throw new InvalidArgumentException('Method is not supported!'); + return match ($type) { + 'info' => $this->infoURL($entity), + 'edit' => $this->editURL($entity), + 'create' => $this->createURL($entity), + 'clone' => $this->cloneURL($entity), + 'list', 'list_parts' => $this->listPartsURL($entity), + 'delete' => $this->deleteURL($entity), + 'file_download' => $this->downloadURL($entity), + 'file_view' => $this->viewURL($entity), + default => throw new InvalidArgumentException('Method is not supported!'), + }; } /** * Gets the URL to view the given element at a given timestamp. */ - public function timeTravelURL(AbstractDBElement $entity, DateTime $dateTime): string + public function timeTravelURL(AbstractDBElement $entity, \DateTimeInterface $dateTime): string { $map = [ Part::class => 'part_info', @@ -116,7 +100,7 @@ class EntityURLGenerator Project::class => 'project_edit', Supplier::class => 'supplier_edit', Manufacturer::class => 'manufacturer_edit', - Storelocation::class => 'store_location_edit', + StorageLocation::class => 'store_location_edit', Footprint::class => 'footprint_edit', User::class => 'user_edit', Currency::class => 'currency_edit', @@ -133,7 +117,7 @@ class EntityURLGenerator 'timestamp' => $dateTime->getTimestamp(), ] ); - } catch (EntityNotSupportedException $exception) { + } catch (EntityNotSupportedException) { if ($entity instanceof PartLot) { return $this->urlGenerator->generate('part_info', [ 'id' => $entity->getPart()->getID(), @@ -158,33 +142,48 @@ class EntityURLGenerator 'timestamp' => $dateTime->getTimestamp(), ]); } - } - - //Otherwise throw an error - throw new EntityNotSupportedException('The given entity is not supported yet!'); - } - - public function viewURL(Attachment $entity): ?string - { - if ($entity->isExternal()) { //For external attachments, return the link to external path - return $entity->getURL(); - } - //return $this->urlGenerator->generate('attachment_view', ['id' => $entity->getID()]); - return $this->attachmentURLGenerator->getViewURL($entity); - } - - public function downloadURL($entity): ?string - { - if ($entity instanceof Attachment) { - if ($entity->isExternal()) { //For external attachments, return the link to external path - return $entity->getURL(); + if ($entity instanceof PartParameter) { + return $this->urlGenerator->generate('part_info', [ + 'id' => $entity->getElement()->getID(), + 'timestamp' => $dateTime->getTimestamp(), + ]); } - - return $this->attachmentURLGenerator->getDownloadURL($entity); } //Otherwise throw an error - throw new EntityNotSupportedException(sprintf('The given entity is not supported yet! Passed class type: %s', get_class($entity))); + throw new EntityNotSupportedException('The given entity is not supported yet! Passed class type: '.$entity::class); + } + + public function viewURL(Attachment $entity): string + { + //If the underlying file path is invalid, null gets returned, which is not allowed here. + //We still have the chance to use an external path, if it is set. + if ($entity->hasInternal() && ($url = $this->attachmentURLGenerator->getInternalViewURL($entity)) !== null) { + return $url; + } + + if($entity->hasExternal()) { + return $entity->getExternalPath(); + } + + throw new \RuntimeException('Attachment has no internal nor external path!'); + } + + public function downloadURL($entity): string + { + if (!($entity instanceof Attachment)) { + throw new EntityNotSupportedException(sprintf('The given entity is not supported yet! Passed class type: %s', $entity::class)); + } + + if ($entity->hasInternal()) { + return $this->attachmentURLGenerator->getInternalDownloadURL($entity); + } + + if($entity->hasExternal()) { + return $entity->getExternalPath(); + } + + throw new \RuntimeException('Attachment has not internal or external path!'); } /** @@ -207,7 +206,7 @@ class EntityURLGenerator Project::class => 'project_info', Supplier::class => 'supplier_edit', Manufacturer::class => 'manufacturer_edit', - Storelocation::class => 'store_location_edit', + StorageLocation::class => 'store_location_edit', Footprint::class => 'footprint_edit', User::class => 'user_edit', Currency::class => 'currency_edit', @@ -222,13 +221,13 @@ class EntityURLGenerator /** * Generates an URL to a page, where this entity can be edited. * - * @param mixed $entity The entity for which the edit link should be generated + * @param AbstractDBElement $entity The entity for which the edit link should be generated * * @return string the URL to the edit page * * @throws EntityNotSupportedException If the method is not supported for the given Entity */ - public function editURL($entity): string + public function editURL(AbstractDBElement $entity): string { $map = [ Part::class => 'part_edit', @@ -237,7 +236,7 @@ class EntityURLGenerator Project::class => 'project_edit', Supplier::class => 'supplier_edit', Manufacturer::class => 'manufacturer_edit', - Storelocation::class => 'store_location_edit', + StorageLocation::class => 'store_location_edit', Footprint::class => 'footprint_edit', User::class => 'user_edit', Currency::class => 'currency_edit', @@ -252,13 +251,14 @@ class EntityURLGenerator /** * Generates an URL to a page, where a entity of this type can be created. * - * @param mixed $entity The entity for which the link should be generated + * @param AbstractDBElement|string $entity The entity (or the entity class) for which the link should be generated + * @phpstan-param AbstractDBElement|class-string $entity * * @return string the URL to the page * * @throws EntityNotSupportedException If the method is not supported for the given Entity */ - public function createURL($entity): string + public function createURL(AbstractDBElement|string $entity): string { $map = [ Part::class => 'part_new', @@ -267,7 +267,7 @@ class EntityURLGenerator Project::class => 'project_new', Supplier::class => 'supplier_new', Manufacturer::class => 'manufacturer_new', - Storelocation::class => 'store_location_new', + StorageLocation::class => 'store_location_new', Footprint::class => 'footprint_new', User::class => 'user_new', Currency::class => 'currency_new', @@ -298,7 +298,7 @@ class EntityURLGenerator Project::class => 'device_clone', Supplier::class => 'supplier_clone', Manufacturer::class => 'manufacturer_clone', - Storelocation::class => 'store_location_clone', + StorageLocation::class => 'store_location_clone', Footprint::class => 'footprint_clone', User::class => 'user_clone', Currency::class => 'currency_clone', @@ -328,7 +328,7 @@ class EntityURLGenerator Footprint::class => 'part_list_footprint', Manufacturer::class => 'part_list_manufacturer', Supplier::class => 'part_list_supplier', - Storelocation::class => 'part_list_store_location', + StorageLocation::class => 'part_list_store_location', ]; return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]); @@ -343,7 +343,7 @@ class EntityURLGenerator Project::class => 'project_delete', Supplier::class => 'supplier_delete', Manufacturer::class => 'manufacturer_delete', - Storelocation::class => 'store_location_delete', + StorageLocation::class => 'store_location_delete', Footprint::class => 'footprint_delete', User::class => 'user_delete', Currency::class => 'currency_delete', @@ -360,26 +360,27 @@ class EntityURLGenerator * Throws an exception if the entity class is not known to the map. * * @param array $map The map that should be used for determing the controller - * @param mixed $entity The entity for which the controller name should be determined + * @param AbstractDBElement|string $entity The entity for which the controller name should be determined + * @phpstan-param AbstractDBElement|class-string $entity * * @return string The name of the controller fitting the entity class * * @throws EntityNotSupportedException */ - protected function mapToController(array $map, $entity): string + protected function mapToController(array $map, string|AbstractDBElement $entity): string { - $class = get_class($entity); + $class = is_string($entity) ? $entity : $entity::class; //Check if we have an direct mapping for the given class if (!array_key_exists($class, $map)) { //Check if we need to check inheritance by looping through our map foreach (array_keys($map) as $key) { - if (is_a($entity, $key)) { + if (is_a($entity, $key, true)) { return $map[$key]; } } - throw new EntityNotSupportedException(sprintf('The given entity is not supported yet! Passed class type: %s', get_class($entity))); + throw new EntityNotSupportedException(sprintf('The given entity is not supported yet! Passed class type: %s', $entity::class)); } return $map[$class]; diff --git a/src/Services/Formatters/AmountFormatter.php b/src/Services/Formatters/AmountFormatter.php index 18d52a44..73d59113 100644 --- a/src/Services/Formatters/AmountFormatter.php +++ b/src/Services/Formatters/AmountFormatter.php @@ -23,35 +23,30 @@ declare(strict_types=1); namespace App\Services\Formatters; use App\Entity\Parts\MeasurementUnit; -use App\Services\Formatters\SIFormatter; use InvalidArgumentException; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; /** - * This service formats an part amout using a Measurement Unit. + * This service formats a part amount using a Measurement Unit. + * @see \App\Tests\Services\Formatters\AmountFormatterTest */ class AmountFormatter { - protected SIFormatter $siFormatter; - - public function __construct(SIFormatter $siFormatter) + public function __construct(protected SIFormatter $siFormatter) { - $this->siFormatter = $siFormatter; } /** * Formats the given value using the measurement unit and options. * - * @param float|string|int $value * @param MeasurementUnit|null $unit The measurement unit, whose unit symbol should be used for formatting. * If set to null, it is assumed that the part amount is measured in pieces. * * @return string The formatted string - * * @throws InvalidArgumentException thrown if $value is not numeric */ - public function format($value, ?MeasurementUnit $unit = null, array $options = []): string + public function format(float|string|int $value, ?MeasurementUnit $unit = null, array $options = []): string { if (!is_numeric($value)) { throw new InvalidArgumentException('$value must be an numeric value!'); @@ -77,7 +72,7 @@ class AmountFormatter //Otherwise just output it if (!empty($options['unit'])) { $format_string = '%.'.$options['decimals'].'f '.$options['unit']; - } else { //Dont add space after number if no unit was specified + } else { //Don't add space after number if no unit was specified $format_string = '%.'.$options['decimals'].'f'; } @@ -127,7 +122,7 @@ class AmountFormatter $resolver->setAllowedTypes('decimals', 'int'); $resolver->setNormalizer('decimals', static function (Options $options, $value) { - // If the unit is integer based, then dont show any decimals + // If the unit is integer based, then don't show any decimals if ($options['is_integer']) { return 0; } diff --git a/src/Services/Formatters/MarkdownParser.php b/src/Services/Formatters/MarkdownParser.php index 805fd4bf..f3ef07df 100644 --- a/src/Services/Formatters/MarkdownParser.php +++ b/src/Services/Formatters/MarkdownParser.php @@ -25,22 +25,19 @@ namespace App\Services\Formatters; use Symfony\Contracts\Translation\TranslatorInterface; /** - * This class allows you to convert markdown text to HTML. + * This class allows you to convert Markdown text to HTML. */ class MarkdownParser { - protected TranslatorInterface $translator; - - public function __construct(TranslatorInterface $translator) + public function __construct(protected TranslatorInterface $translator) { - $this->translator = $translator; } /** * Mark the markdown for rendering. * The rendering of markdown is done on client side. * - * @param string $markdown the markdown text that should be parsed to html + * @param string $markdown the Markdown text that should be parsed to html * @param bool $inline_mode When true, p blocks will have no margins behind them * * @return string the markdown in a version that can be parsed on client side diff --git a/src/Services/Formatters/MoneyFormatter.php b/src/Services/Formatters/MoneyFormatter.php index ee8a189a..44a49cb5 100644 --- a/src/Services/Formatters/MoneyFormatter.php +++ b/src/Services/Formatters/MoneyFormatter.php @@ -28,27 +28,25 @@ use NumberFormatter; class MoneyFormatter { - protected string $base_currency; protected string $locale; - public function __construct(string $base_currency) + public function __construct(protected string $base_currency) { - $this->base_currency = $base_currency; $this->locale = Locale::getDefault(); } /** - * Format the the given value in the given currency. + * Format the given value in the given currency. * * @param string|float $value the value that should be formatted * @param Currency|null $currency The currency that should be used for formatting. If null the global one is used * @param int $decimals the number of decimals that should be shown * @param bool $show_all_digits if set to true, all digits are shown, even if they are null */ - public function format($value, ?Currency $currency = null, int $decimals = 5, bool $show_all_digits = false): string + public function format(string|float $value, ?Currency $currency = null, int $decimals = 5, bool $show_all_digits = false): string { $iso_code = $this->base_currency; - if (null !== $currency && !empty($currency->getIsoCode())) { + if ($currency instanceof Currency && ($currency->getIsoCode() !== '')) { $iso_code = $currency->getIsoCode(); } diff --git a/src/Services/Formatters/SIFormatter.php b/src/Services/Formatters/SIFormatter.php index 288641a3..a6325987 100644 --- a/src/Services/Formatters/SIFormatter.php +++ b/src/Services/Formatters/SIFormatter.php @@ -24,6 +24,7 @@ namespace App\Services\Formatters; /** * A service that helps you to format values using the SI prefixes. + * @see \App\Tests\Services\Formatters\SIFormatterTest */ class SIFormatter { @@ -45,7 +46,7 @@ class SIFormatter * * @param int $magnitude the magnitude for which the prefix should be determined * - * @return array A array, containing the divisor in first element, and the prefix symbol in second. For example, [1000, "k"]. + * @return array An array, containing the divisor in first element, and the prefix symbol in second. For example, [1000, "k"]. */ public function getPrefixByMagnitude(int $magnitude): array { @@ -93,11 +94,7 @@ class SIFormatter [$divisor, $symbol] = $this->getPrefixByMagnitude($this->getMagnitude($value)); $value /= $divisor; //Build the format string, e.g.: %.2d km - if ('' !== $unit || '' !== $symbol) { - $format_string = '%.'.$decimals.'f '.$symbol.$unit; - } else { - $format_string = '%.'.$decimals.'f'; - } + $format_string = '' !== $unit || '' !== $symbol ? '%.'.$decimals.'f '.$symbol.$unit : '%.'.$decimals.'f'; return sprintf($format_string, $value); } diff --git a/src/Services/ImportExportSystem/BOMImporter.php b/src/Services/ImportExportSystem/BOMImporter.php new file mode 100644 index 00000000..d4876445 --- /dev/null +++ b/src/Services/ImportExportSystem/BOMImporter.php @@ -0,0 +1,163 @@ +. + */ +namespace App\Services\ImportExportSystem; + +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use InvalidArgumentException; +use League\Csv\Reader; +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * @see \App\Tests\Services\ImportExportSystem\BOMImporterTest + */ +class BOMImporter +{ + + private const MAP_KICAD_PCB_FIELDS = [ + 0 => 'Id', + 1 => 'Designator', + 2 => 'Package', + 3 => 'Quantity', + 4 => 'Designation', + 5 => 'Supplier and ref', + ]; + + public function __construct() + { + } + + protected function configureOptions(OptionsResolver $resolver): OptionsResolver + { + $resolver->setRequired('type'); + $resolver->setAllowedValues('type', ['kicad_pcbnew']); + + return $resolver; + } + + /** + * Converts the given file into an array of BOM entries using the given options and save them into the given project. + * The changes are not saved into the database yet. + * @return ProjectBOMEntry[] + */ + public function importFileIntoProject(File $file, Project $project, array $options): array + { + $bom_entries = $this->fileToBOMEntries($file, $options); + + //Assign the bom_entries to the project + foreach ($bom_entries as $bom_entry) { + $project->addBomEntry($bom_entry); + } + + return $bom_entries; + } + + /** + * Converts the given file into an array of BOM entries using the given options. + * @return ProjectBOMEntry[] + */ + public function fileToBOMEntries(File $file, array $options): array + { + return $this->stringToBOMEntries($file->getContent(), $options); + } + + /** + * Import string data into an array of BOM entries, which are not yet assigned to a project. + * @param string $data The data to import + * @param array $options An array of options + * @return ProjectBOMEntry[] An array of imported entries + */ + public function stringToBOMEntries(string $data, array $options): array + { + $resolver = new OptionsResolver(); + $resolver = $this->configureOptions($resolver); + $options = $resolver->resolve($options); + + return match ($options['type']) { + 'kicad_pcbnew' => $this->parseKiCADPCB($data, $options), + default => throw new InvalidArgumentException('Invalid import type!'), + }; + } + + private function parseKiCADPCB(string $data, array $options = []): array + { + $csv = Reader::createFromString($data); + $csv->setDelimiter(';'); + $csv->setHeaderOffset(0); + + $bom_entries = []; + + foreach ($csv->getRecords() as $offset => $entry) { + //Translate the german field names to english + $entry = $this->normalizeColumnNames($entry); + + //Ensure that the entry has all required fields + if (!isset ($entry['Designator'])) { + throw new \UnexpectedValueException('Designator missing at line '.($offset + 1).'!'); + } + if (!isset ($entry['Package'])) { + throw new \UnexpectedValueException('Package missing at line '.($offset + 1).'!'); + } + if (!isset ($entry['Designation'])) { + throw new \UnexpectedValueException('Designation missing at line '.($offset + 1).'!'); + } + if (!isset ($entry['Quantity'])) { + throw new \UnexpectedValueException('Quantity missing at line '.($offset + 1).'!'); + } + + $bom_entry = new ProjectBOMEntry(); + $bom_entry->setName($entry['Designation'] . ' (' . $entry['Package'] . ')'); + $bom_entry->setMountnames($entry['Designator'] ?? ''); + $bom_entry->setComment($entry['Supplier and ref'] ?? ''); + $bom_entry->setQuantity((float) ($entry['Quantity'] ?? 1)); + + $bom_entries[] = $bom_entry; + } + + return $bom_entries; + } + + /** + * This function uses the order of the fields in the CSV files to make them locale independent. + * @param array $entry + * @return array + */ + private function normalizeColumnNames(array $entry): array + { + $out = []; + + //Map the entry order to the correct column names + foreach (array_values($entry) as $index => $field) { + if ($index > 5) { + break; + } + + //@phpstan-ignore-next-line We want to keep this check just to be safe when something changes + $new_index = self::MAP_KICAD_PCB_FIELDS[$index] ?? throw new \UnexpectedValueException('Invalid field index!'); + $out[$new_index] = $field; + } + + return $out; + } +} diff --git a/src/Services/ImportExportSystem/EntityExporter.php b/src/Services/ImportExportSystem/EntityExporter.php index 2d85097c..c37db50c 100644 --- a/src/Services/ImportExportSystem/EntityExporter.php +++ b/src/Services/ImportExportSystem/EntityExporter.php @@ -23,8 +23,13 @@ declare(strict_types=1); namespace App\Services\ImportExportSystem; use App\Entity\Base\AbstractNamedDBElement; -use function in_array; +use App\Entity\Base\AbstractStructuralDBElement; +use App\Helpers\FilenameSanatizer; +use App\Serializer\APIPlatform\SkippableItemNormalizer; +use Symfony\Component\OptionsResolver\OptionsResolver; use InvalidArgumentException; +use Symfony\Component\Serializer\Exception\CircularReferenceException; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use function is_array; use ReflectionClass; use ReflectionException; @@ -32,114 +37,165 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\Serializer\SerializerInterface; +use function Symfony\Component\String\u; /** * Use this class to export an entity to multiple file formats. + * @see \App\Tests\Services\ImportExportSystem\EntityExporterTest */ class EntityExporter { - protected SerializerInterface $serializer; - - public function __construct(SerializerInterface $serializer) + public function __construct(protected SerializerInterface $serializer) { - /*$encoders = [new XmlEncoder(), new JsonEncoder(), new CSVEncoder(), new YamlEncoder()]; - $normalizers = [new ObjectNormalizer(), new DateTimeNormalizer()]; - $this->serializer = new Serializer($normalizers, $encoders); - $this->serializer-> */ - $this->serializer = $serializer; + } + + protected function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefault('format', 'csv'); + $resolver->setAllowedValues('format', ['csv', 'json', 'xml', 'yaml']); + + $resolver->setDefault('csv_delimiter', ';'); + $resolver->setAllowedTypes('csv_delimiter', 'string'); + + $resolver->setDefault('level', 'extended'); + $resolver->setAllowedValues('level', ['simple', 'extended', 'full']); + + $resolver->setDefault('include_children', false); + $resolver->setAllowedTypes('include_children', 'bool'); } /** - * Exports an Entity or an array of entities to multiple file formats. + * Export the given entities using the given options. + * @param AbstractNamedDBElement|AbstractNamedDBElement[] $entities The data to export + * @param array $options The options to use for exporting + * @return string The serialized data + */ + public function exportEntities(AbstractNamedDBElement|array $entities, array $options): string + { + if (!is_array($entities)) { + $entities = [$entities]; + } + + //Ensure that all entities are of type AbstractNamedDBElement + foreach ($entities as $entity) { + if (!$entity instanceof AbstractNamedDBElement) { + throw new InvalidArgumentException('All entities must be of type AbstractNamedDBElement!'); + } + } + + $resolver = new OptionsResolver(); + $this->configureOptions($resolver); + + $options = $resolver->resolve($options); + + //If include children is set, then we need to add the include_children group + $groups = [$options['level']]; + if ($options['include_children']) { + $groups[] = 'include_children'; + } + + return $this->serializer->serialize($entities, $options['format'], + [ + 'groups' => $groups, + 'as_collection' => true, + 'csv_delimiter' => $options['csv_delimiter'], + 'xml_root_node_name' => 'PartDBExport', + 'partdb_export' => true, + //Skip the item normalizer, so that we dont get IRIs in the output + SkippableItemNormalizer::DISABLE_ITEM_NORMALIZER => true, + //Handle circular references + AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => $this->handleCircularReference(...), + ] + ); + } + + private function handleCircularReference(object $object, string $format, array $context): string + { + if ($object instanceof AbstractStructuralDBElement) { + return $object->getFullPath("->"); + } elseif ($object instanceof AbstractNamedDBElement) { + return $object->getName(); + } elseif ($object instanceof \Stringable) { + return $object->__toString(); + } + + throw new CircularReferenceException('Circular reference detected for object of type '.get_class($object)); + } + + /** + * Exports an Entity or an array of entities to multiple file formats. * * @param Request $request the request that should be used for option resolving - * @param AbstractNamedDBElement|object[] $entity + * @param AbstractNamedDBElement|object[] $entities * * @return Response the generated response containing the exported data * * @throws ReflectionException */ - public function exportEntityFromRequest($entity, Request $request): Response + public function exportEntityFromRequest(AbstractNamedDBElement|array $entities, Request $request): Response { - $format = $request->get('format') ?? 'json'; + $options = [ + 'format' => $request->get('format') ?? 'json', + 'level' => $request->get('level') ?? 'extended', + 'include_children' => $request->request->getBoolean('include_children') ?? false, + ]; - //Check if we have one of the supported formats - if (!in_array($format, ['json', 'csv', 'yaml', 'xml'], true)) { - throw new InvalidArgumentException('Given format is not supported!'); + if (!is_array($entities)) { + $entities = [$entities]; } - //Check export verbosity level - $level = $request->get('level') ?? 'extended'; - if (!in_array($level, ['simple', 'extended', 'full'], true)) { - throw new InvalidArgumentException('Given level is not supported!'); - } + //Do the serialization with the given options + $serialized_data = $this->exportEntities($entities, $options); - //Check for include children option - $include_children = $request->get('include_children') ?? false; + $response = new Response($serialized_data); - //Check which groups we need to export, based on level and include_children - $groups = [$level]; - if ($include_children) { - $groups[] = 'include_children'; - } + //Resolve the format + $optionsResolver = new OptionsResolver(); + $this->configureOptions($optionsResolver); + $options = $optionsResolver->resolve($options); + + //Determine the content type for the response //Plain text should work for all types $content_type = 'text/plain'; //Try to use better content types based on the format + $format = $options['format']; switch ($format) { case 'xml': $content_type = 'application/xml'; - break; case 'json': $content_type = 'application/json'; - break; } - - //Ensure that we always serialize an array. This makes it easier to import the data again. - if (is_array($entity)) { - $entity_array = $entity; - } else { - $entity_array = [$entity]; - } - - $serialized_data = $this->serializer->serialize($entity_array, $format, - [ - 'groups' => $groups, - 'as_collection' => true, - 'csv_delimiter' => ';', //Better for Excel - 'xml_root_node_name' => 'PartDBExport', - ]); - - $response = new Response($serialized_data); - $response->headers->set('Content-Type', $content_type); //If view option is not specified, then download the file. if (!$request->get('view')) { - if ($entity instanceof AbstractNamedDBElement) { - $entity_name = $entity->getName(); - } elseif (is_array($entity)) { - if (empty($entity)) { - throw new InvalidArgumentException('$entity must not be empty!'); - } - //Use the class name of the first element for the filename - $reflection = new ReflectionClass($entity[0]); - $entity_name = $reflection->getShortName(); + //Determine the filename + //When we only have one entity, then we can use the name of the entity + if (count($entities) === 1) { + $entity_name = $entities[0]->getName(); } else { - throw new InvalidArgumentException('$entity type is not supported!'); + //Use the class name of the first element for the filename otherwise + $reflection = new ReflectionClass($entities[0]); + $entity_name = $reflection->getShortName(); } + $level = $options['level']; + $filename = 'export_'.$entity_name.'_'.$level.'.'.$format; + //Sanitize the filename + $filename = FilenameSanatizer::sanitizeFilename($filename); + // Create the disposition of the file $disposition = $response->headers->makeDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, - $string = preg_replace('![^'.preg_quote('-', '!').'a-z0-_9\s]+!', '', strtolower($filename)) + u($filename)->ascii()->toString(), ); // Set the content disposition $response->headers->set('Content-Disposition', $disposition); diff --git a/src/Services/ImportExportSystem/EntityImporter.php b/src/Services/ImportExportSystem/EntityImporter.php index d6e00570..cecab12d 100644 --- a/src/Services/ImportExportSystem/EntityImporter.php +++ b/src/Services/ImportExportSystem/EntityImporter.php @@ -24,6 +24,12 @@ namespace App\Services\ImportExportSystem; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Parts\Category; +use App\Entity\Parts\Part; +use App\Repository\StructuralDBElementRepository; +use App\Serializer\APIPlatform\SkippableItemNormalizer; +use Symfony\Component\Validator\ConstraintViolationList; +use Symfony\Component\Validator\ConstraintViolationListInterface; use function count; use Doctrine\ORM\EntityManagerInterface; use InvalidArgumentException; @@ -33,42 +39,58 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; +/** + * @see \App\Tests\Services\ImportExportSystem\EntityImporterTest + */ class EntityImporter { - protected SerializerInterface $serializer; - protected EntityManagerInterface $em; - protected ValidatorInterface $validator; - public function __construct(SerializerInterface $serializer, EntityManagerInterface $em, ValidatorInterface $validator) + /** + * The encodings that are supported by the importer, and that should be autodeceted. + */ + private const ENCODINGS = ["ASCII", "UTF-8", "ISO-8859-1", "ISO-8859-15", "Windows-1252", "UTF-16", "UTF-32"]; + + public function __construct(protected SerializerInterface $serializer, protected EntityManagerInterface $em, protected ValidatorInterface $validator) { - $this->serializer = $serializer; - $this->em = $em; - $this->validator = $validator; } /** * Creates many entries at once, based on a (text) list of name. - * The created enties are not persisted to database yet, so you have to do it yourself. + * The created entities are not persisted to database yet, so you have to do it yourself. * + * @template T of AbstractNamedDBElement * @param string $lines The list of names seperated by \n * @param string $class_name The name of the class for which the entities should be created + * @phpstan-param class-string $class_name * @param AbstractStructuralDBElement|null $parent the element which will be used as parent element for new elements * @param array $errors an associative array containing all validation errors + * @param-out list $errors * - * @return AbstractStructuralDBElement[] An array containing all valid imported entities (with the type $class_name) + * @return AbstractNamedDBElement[] An array containing all valid imported entities (with the type $class_name) + * @return T[] */ public function massCreation(string $lines, string $class_name, ?AbstractStructuralDBElement $parent = null, array &$errors = []): array { + //Try to detect the text encoding of the data and convert it to UTF-8 + $lines = mb_convert_encoding($lines, 'UTF-8', mb_detect_encoding($lines, self::ENCODINGS)); + //Expand every line to a single entry: $names = explode("\n", $lines); if (!is_a($class_name, AbstractNamedDBElement::class, true)) { throw new InvalidArgumentException('$class_name must be a StructuralDBElement type!'); } - if (null !== $parent && !is_a($parent, $class_name)) { + if ($parent instanceof AbstractStructuralDBElement && !$parent instanceof $class_name) { throw new InvalidArgumentException('$parent must have the same type as specified in $class_name!'); } + //Ensure that parent is already persisted. Otherwise the getNewEntityFromPath function will not work. + if ($parent !== null && $parent->getID() === null) { + throw new InvalidArgumentException('The parent must persisted to database!'); + } + + $repo = $this->em->getRepository($class_name); + $errors = []; $valid_entities = []; @@ -78,10 +100,10 @@ class EntityImporter $indentations = [0]; foreach ($names as $name) { - //Count intendation level (whitespace characters at the beginning of the line) + //Count indentation level (whitespace characters at the beginning of the line) $identSize = strlen($name)-strlen(ltrim($name)); - //If the line is intendet more than the last line, we have a new parent element + //If the line is intended more than the last line, we have a new parent element if ($identSize > end($indentations)) { $current_parent = $last_element; //Add the new indentation level to the stack @@ -89,11 +111,7 @@ class EntityImporter } while ($identSize < end($indentations)) { //If the line is intendet less than the last line, we have to go up in the tree - if ($current_parent instanceof AbstractStructuralDBElement) { - $current_parent = $current_parent->getParent(); - } else { - $current_parent = null; - } + $current_parent = $current_parent instanceof AbstractStructuralDBElement ? $current_parent->getParent() : null; array_pop($indentations); } @@ -102,14 +120,27 @@ class EntityImporter //Skip empty lines (StrucuralDBElements must have a name) continue; } + /** @var AbstractStructuralDBElement $entity */ - //Create new element with given name - $entity = new $class_name(); - $entity->setName($name); - //Only set the parent if the entity is a StructuralDBElement - if ($entity instanceof AbstractStructuralDBElement) { - $entity->setParent($current_parent); + //Create new element with given name. Using the function from the repository, to correctly reuse existing elements + + if ($current_parent instanceof AbstractStructuralDBElement) { + $new_path = $current_parent->getFullPath("->") . '->' . $name; + } else { + $new_path = $name; } + //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) { + throw new InvalidArgumentException('getNewEntityFromPath returned an empty array!'); + } + } else { //Otherwise just create a new entity + $entity = new $class_name; + $entity->setName($name); + } + //Validate entity $tmp = $this->validator->validate($entity); @@ -130,87 +161,45 @@ class EntityImporter } /** - * This methods deserializes the given file and saves it database. - * The imported elements will be checked (validated) before written to database. - * - * @param File $file the file that should be used for importing - * @param string $class_name the class name of the enitity that should be imported - * @param array $options options for the import process - * - * @return array An associative array containing an ConstraintViolationList and the entity name as key are returned, - * if an error happened during validation. When everything was successfull, the array should be empty. + * Import data from a string. + * @param string $data The serialized data which should be imported + * @param array $options The options for the import process + * @param array $errors An array which will be filled with the validation errors, if any occurs during import + * @param-out array $errors + * @return array An array containing all valid imported entities */ - public function fileToDBEntities(File $file, string $class_name, array $options = []): array + public function importString(string $data, array $options = [], array &$errors = []): array { + //Try to detect the text encoding of the data and convert it to UTF-8 + $data = mb_convert_encoding($data, 'UTF-8', mb_detect_encoding($data, self::ENCODINGS)); + $resolver = new OptionsResolver(); $this->configureOptions($resolver); - $options = $resolver->resolve($options); - $entities = $this->fileToEntityArray($file, $class_name, $options); - - $errors = []; - - //Iterate over each $entity write it to DB. - foreach ($entities as $entity) { - /** @var AbstractStructuralDBElement $entity */ - //Move every imported entity to the selected parent - $entity->setParent($options['parent']); - - //Validate entity - $tmp = $this->validator->validate($entity); - - //When no validation error occured, persist entity to database (cascade must be set in entity) - if (null === $tmp) { - $this->em->persist($entity); - } else { //Log validation errors to global log. - $errors[$entity->getFullPath()] = $tmp; - } + if (!is_a($options['class'], AbstractNamedDBElement::class, true)) { + throw new InvalidArgumentException('$class_name must be an AbstractNamedDBElement type!'); } - //Save changes to database, when no error happened, or we should continue on error. - if (empty($errors) || false === $options['abort_on_validation_error']) { - $this->em->flush(); - } - - return $errors; - } - - /** - * This method converts (deserialize) a (uploaded) file to an array of entities with the given class. - * - * The imported elements will NOT be validated. If you want to use the result array, you have to validate it by yourself. - * - * @param File $file the file that should be used for importing - * @param string $class_name the class name of the enitity that should be imported - * @param array $options options for the import process - * - * @return array an array containing the deserialized elements - */ - public function fileToEntityArray(File $file, string $class_name, array $options = []): array - { - $resolver = new OptionsResolver(); - $this->configureOptions($resolver); - - $options = $resolver->resolve($options); - - //Read file contents - $content = file_get_contents($file->getRealPath()); - - $groups = ['simple']; + $groups = ['import']; //We can only import data, that is marked with the group "import" //Add group when the children should be preserved if ($options['preserve_children']) { $groups[] = 'include_children'; } //The [] behind class_name denotes that we expect an array. - $entities = $this->serializer->deserialize($content, $class_name.'[]', $options['format'], + $entities = $this->serializer->deserialize($data, $options['class'].'[]', $options['format'], [ 'groups' => $groups, - 'csv_delimiter' => $options['csv_separator'], + 'csv_delimiter' => $options['csv_delimiter'], + 'create_unknown_datastructures' => $options['create_unknown_datastructures'], + 'path_delimiter' => $options['path_delimiter'], + 'partdb_import' => true, + //Disable API Platform normalizer, as we don't want to use it here + SkippableItemNormalizer::DISABLE_ITEM_NORMALIZER => true, ]); - //Ensure we have an array of entitity elements. + //Ensure we have an array of entity elements. if (!is_array($entities)) { $entities = [$entities]; } @@ -220,18 +209,146 @@ class EntityImporter $this->correctParentEntites($entities, null); } + //Set the parent of the imported elements to the given options + foreach ($entities as $entity) { + if ($entity instanceof AbstractStructuralDBElement) { + $entity->setParent($options['parent']); + } + if ($entity instanceof Part) { + if ($options['part_category']) { + $entity->setCategory($options['part_category']); + } + if ($options['part_needs_review']) { + $entity->setNeedsReview(true); + } + } + } + + //Validate the entities + $errors = []; + + //Iterate over each $entity write it to DB. + foreach ($entities as $key => $entity) { + //Ensure that entity is a NamedDBElement + if (!$entity instanceof AbstractNamedDBElement) { + throw new \RuntimeException("Encountered an entity that is not a NamedDBElement!"); + } + + //Validate entity + $tmp = $this->validator->validate($entity); + + if (count($tmp) > 0) { //Log validation errors to global log. + $name = $entity instanceof AbstractStructuralDBElement ? $entity->getFullPath() : $entity->getName(); + + if (trim($name) === '') { + $name = 'Row ' . (string) $key; + } + + $errors[$name] = [ + 'violations' => $tmp, + 'entity' => $entity, + ]; + + //Remove the invalid entity from the array + unset($entities[$key]); + } + } + return $entities; } - protected function configureOptions(OptionsResolver $resolver): void + protected function configureOptions(OptionsResolver $resolver): OptionsResolver { $resolver->setDefaults([ - 'csv_separator' => ';', - 'format' => 'json', + 'csv_delimiter' => ';', //The separator to use when importing csv files + 'format' => 'json', //The format of the file that should be imported + 'class' => AbstractNamedDBElement::class, 'preserve_children' => true, - 'parent' => null, + 'parent' => null, //The parent element to which the imported elements should be added 'abort_on_validation_error' => true, + 'part_category' => null, + 'part_needs_review' => false, //If true, the imported parts will be marked as "needs review", otherwise the value from the file will be used + 'create_unknown_datastructures' => true, //If true, unknown datastructures (categories, footprints, etc.) will be created on the fly + 'path_delimiter' => '->', //The delimiter used to separate the path elements in the name of a structural element ]); + + $resolver->setAllowedValues('format', ['csv', 'json', 'xml', 'yaml']); + $resolver->setAllowedTypes('csv_delimiter', 'string'); + $resolver->setAllowedTypes('preserve_children', 'bool'); + $resolver->setAllowedTypes('class', 'string'); + $resolver->setAllowedTypes('part_category', [Category::class, 'null']); + $resolver->setAllowedTypes('part_needs_review', 'bool'); + + return $resolver; + } + + /** + * This method deserializes the given file and writes the entities to the database (and flush the db). + * The imported elements will be checked (validated) before written to database. + * + * @param File $file the file that should be used for importing + * @param array $options options for the import process + * @param-out AbstractNamedDBElement[] $entities The imported entities are returned in this array + * + * @return array An associative array containing an ConstraintViolationList and the entity name as key are returned, + * if an error happened during validation. When everything was successfully, the array should be empty. + */ + public function importFileAndPersistToDB(File $file, array $options = [], array &$entities = []): array + { + $options = $this->configureOptions(new OptionsResolver())->resolve($options); + + $errors = []; + $entities = $this->importFile($file, $options, $errors); + + //When we should abort on validation error, do nothing and return the errors + if (!empty($errors) && $options['abort_on_validation_error']) { + return $errors; + } + + //Iterate over each $entity write it to DB (the invalid entities were already filtered out). + foreach ($entities as $entity) { + $this->em->persist($entity); + } + + //Save changes to database, when no error happened, or we should continue on error. + $this->em->flush(); + + return $errors; + } + + /** + * This method converts (deserialize) a (uploaded) file to an array of entities with the given class. + * The imported elements are not persisted to database yet, so you have to do it yourself. + * + * @param File $file the file that should be used for importing + * @param array $options options for the import process + * @param-out array $errors + * + * @return AbstractNamedDBElement[] an array containing the deserialized elements + */ + public function importFile(File $file, array $options = [], array &$errors = []): array + { + return $this->importString($file->getContent(), $options, $errors); + } + + + /** + * Determines the format to import based on the file extension. + * @param string $extension The file extension to use + * @return string The format to use (json, xml, csv, yaml), or null if the extension is unknown + */ + public function determineFormat(string $extension): ?string + { + //Convert the extension to lower case + $extension = strtolower($extension); + + return match ($extension) { + 'json' => 'json', + 'xml' => 'xml', + 'csv', 'tsv' => 'csv', + 'yaml', 'yml' => 'yaml', + default => null, + }; } /** @@ -240,7 +357,7 @@ class EntityImporter * @param iterable $entities the list of entities that should be fixed * @param AbstractStructuralDBElement|null $parent the parent, to which the entity should be set */ - protected function correctParentEntites(iterable $entities, AbstractStructuralDBElement $parent = null): void + protected function correctParentEntites(iterable $entities, ?AbstractStructuralDBElement $parent = null): void { foreach ($entities as $entity) { /** @var AbstractStructuralDBElement $entity */ diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/MySQLDumpXMLConverter.php b/src/Services/ImportExportSystem/PartKeeprImporter/MySQLDumpXMLConverter.php new file mode 100644 index 00000000..f221ee89 --- /dev/null +++ b/src/Services/ImportExportSystem/PartKeeprImporter/MySQLDumpXMLConverter.php @@ -0,0 +1,112 @@ +. + */ +namespace App\Services\ImportExportSystem\PartKeeprImporter; + +class MySQLDumpXMLConverter +{ + + /** + * Converts a MySQL dump XML file to an associative array structure in the following form + * [ + * 'table_name' => [ + * [ + * 'column_name' => 'value', + * 'column_name' => 'value', + * ... + * ], + * [ + * 'column_name' => 'value', + * 'column_name' => 'value', + * ... + * ], + * ... + * ], + * + * @param string $xml_string The XML string to convert + * @return array The associative array structure + */ + public function convertMySQLDumpXMLDataToArrayStructure(string $xml_string): array + { + $dom = new \DOMDocument(); + $dom->loadXML($xml_string); + + //Check that the root node is a node + $root = $dom->documentElement; + if ($root->nodeName !== 'mysqldump') { + throw new \InvalidArgumentException('The given XML string is not a valid MySQL dump XML file!'); + } + + //Get all nodes (there must be exactly one) + $databases = $root->getElementsByTagName('database'); + if ($databases->length !== 1) { + throw new \InvalidArgumentException('The given XML string is not a valid MySQL dump XML file!'); + } + + //Get the node + $database = $databases->item(0); + + //Get all nodes + $tables = $database->getElementsByTagName('table_data'); + $table_data = []; + + //Iterate over all nodes and convert them to arrays + foreach ($tables as $table) { + //Normalize the table name to lowercase. On Linux filesystems the tables sometimes contain uppercase letters + //However we expect the table names to be lowercase in the further steps + $table_name = strtolower($table->getAttribute('name')); + $table_data[$table_name] = $this->convertTableToArray($table); + } + + return $table_data; + } + + private function convertTableToArray(\DOMElement $table): array + { + $table_data = []; + + //Get all nodes + $rows = $table->getElementsByTagName('row'); + + //Iterate over all nodes and convert them to arrays + foreach ($rows as $row) { + $table_data[] = $this->convertTableRowToArray($row); + } + + return $table_data; + } + + private function convertTableRowToArray(\DOMElement $table_row): array + { + $row_data = []; + + //Get all nodes + $fields = $table_row->getElementsByTagName('field'); + + //Iterate over all nodes + foreach ($fields as $field) { + $row_data[$field->getAttribute('name')] = $field->nodeValue; + } + + return $row_data; + } +} diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKDatastructureImporter.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKDatastructureImporter.php new file mode 100644 index 00000000..1f842c23 --- /dev/null +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKDatastructureImporter.php @@ -0,0 +1,264 @@ +. + */ +namespace App\Services\ImportExportSystem\PartKeeprImporter; + +use App\Entity\Attachments\FootprintAttachment; +use App\Entity\Attachments\ManufacturerAttachment; +use App\Entity\Attachments\StorageLocationAttachment; +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; +use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\MeasurementUnit; +use App\Entity\Parts\StorageLocation; +use App\Entity\Parts\Supplier; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +use function count; + +/** + * This service is used to import the datastructures (categories, manufacturers, etc.) from a PartKeepr export. + */ +class PKDatastructureImporter +{ + + use PKImportHelperTrait; + + public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor) + { + $this->em = $em; + $this->propertyAccessor = $propertyAccessor; + } + + + /** + * Imports the distributors from the given data. + * @param array $data The data to import (associated array, containing a 'distributor' key + * @return int The number of imported distributors + */ + public function importDistributors(array $data): int + { + if (!isset($data['distributor'])) { + throw new \RuntimeException('$data must contain a "distributor" key!'); + } + + $distributor_data = $data['distributor']; + + foreach ($distributor_data as $distributor) { + $supplier = new Supplier(); + $supplier->setName($distributor['name']); + $supplier->setWebsite($distributor['url'] ?? ''); + $supplier->setAddress($distributor['address'] ?? ''); + $supplier->setPhoneNumber($distributor['phone'] ?? ''); + $supplier->setFaxNumber($distributor['fax'] ?? ''); + $supplier->setEmailAddress($distributor['email'] ?? ''); + $supplier->setComment($distributor['comment']); + $supplier->setAutoProductUrl($distributor['skuurl'] ?? ''); + + $this->setIDOfEntity($supplier, $distributor['id']); + $this->em->persist($supplier); + } + + $this->em->flush(); + + return is_countable($distributor_data) ? count($distributor_data) : 0; + } + + public function importManufacturers(array $data): int + { + if (!isset($data['manufacturer'])) { + throw new \RuntimeException('$data must contain a "manufacturer" key!'); + } + + $manufacturer_data = $data['manufacturer']; + + $max_id = 0; + + //Assign a parent manufacturer to all manufacturers, as partkeepr has a lot of manufacturers by default + $parent_manufacturer = new Manufacturer(); + $parent_manufacturer->setName('PartKeepr'); + $parent_manufacturer->setNotSelectable(true); + + foreach ($manufacturer_data as $manufacturer) { + $entity = new Manufacturer(); + $entity->setName($manufacturer['name']); + $entity->setWebsite($manufacturer['url'] ?? ''); + $entity->setAddress($manufacturer['address'] ?? ''); + $entity->setPhoneNumber($manufacturer['phone'] ?? ''); + $entity->setFaxNumber($manufacturer['fax'] ?? ''); + $entity->setEmailAddress($manufacturer['email'] ?? ''); + $entity->setComment($manufacturer['comment']); + $entity->setParent($parent_manufacturer); + + $this->setIDOfEntity($entity, $manufacturer['id']); + $this->em->persist($entity); + + $max_id = max($max_id, $manufacturer['id']); + } + + //Set the ID of the parent manufacturer to the max ID + 1, to avoid trouble with the auto increment + $this->setIDOfEntity($parent_manufacturer, $max_id + 1); + $this->em->persist($parent_manufacturer); + + $this->em->flush(); + + $this->importAttachments($data, 'manufacturericlogo', Manufacturer::class, 'manufacturer_id', ManufacturerAttachment::class); + + return is_countable($manufacturer_data) ? count($manufacturer_data) : 0; + } + + public function importPartUnits(array $data): int + { + if (!isset($data['partunit'])) { + throw new \RuntimeException('$data must contain a "partunit" key!'); + } + + $partunit_data = $data['partunit']; + foreach ($partunit_data as $partunit) { + $unit = new MeasurementUnit(); + $unit->setName($partunit['name']); + $unit->setUnit($partunit['shortName'] ?? null); + + $this->setIDOfEntity($unit, $partunit['id']); + $this->em->persist($unit); + } + + $this->em->flush(); + + return is_countable($partunit_data) ? count($partunit_data) : 0; + } + + public function importCategories(array $data): int + { + if (!isset($data['partcategory'])) { + throw new \RuntimeException('$data must contain a "partcategory" key!'); + } + + $partcategory_data = $data['partcategory']; + + //In a first step, create all categories like they were a flat structure (so ignore the parent) + foreach ($partcategory_data as $partcategory) { + $category = new Category(); + $category->setName($partcategory['name']); + $category->setComment($partcategory['description']); + + $this->setIDOfEntity($category, $partcategory['id']); + $this->em->persist($category); + } + + $this->em->flush(); + + //In a second step, set the correct parent element + foreach ($partcategory_data as $partcategory) { + $this->setParent(Category::class, $partcategory['id'], $partcategory['parent_id']); + } + $this->em->flush(); + + return is_countable($partcategory_data) ? count($partcategory_data) : 0; + } + + /** + * The common import functions for footprints and storeloactions + */ + private function importElementsWithCategory(array $data, string $target_class, string $data_prefix): int + { + $key = $data_prefix; + $category_key = $data_prefix.'category'; + + if (!isset($data[$key])) { + throw new \RuntimeException('$data must contain a "'. $key .'" key!'); + } + if (!isset($data[$category_key])) { + throw new \RuntimeException('$data must contain a "'. $category_key .'" key!'); + } + + //We import the footprints first, as we need the IDs of the footprints be our real DBs later (as we match the part import by ID) + //As the footprints category is not existing yet, we just skip the parent field for now + $footprint_data = $data[$key]; + $max_footprint_id = 0; + foreach ($footprint_data as $footprint) { + $entity = new $target_class(); + $entity->setName($footprint['name']); + $entity->setComment($footprint['description'] ?? ''); + + $this->setIDOfEntity($entity, $footprint['id']); + $this->em->persist($entity); + $max_footprint_id = max($max_footprint_id, (int) $footprint['id']); + } + + //Import the footprint categories ignoring the parents for now + //Their IDs are $max_footprint_id + $ID + $footprintcategory_data = $data[$category_key]; + foreach ($footprintcategory_data as $footprintcategory) { + $entity = new $target_class(); + $entity->setName($footprintcategory['name']); + $entity->setComment($footprintcategory['description']); + //Categories are not assignable to parts, so we set them to not selectable + $entity->setNotSelectable(true); + + $this->setIDOfEntity($entity, $max_footprint_id + (int) $footprintcategory['id']); + $this->em->persist($entity); + } + + $this->em->flush(); + + //Now we can correct the parents and category IDs of the parts + foreach ($footprintcategory_data as $footprintcategory) { + //We have to use the mapped IDs here, as the imported ID is not the effective ID + if ($footprintcategory['parent_id']) { + $this->setParent($target_class, $max_footprint_id + (int)$footprintcategory['id'], + $max_footprint_id + (int)$footprintcategory['parent_id']); + } + } + foreach ($footprint_data as $footprint) { + if ($footprint['category_id']) { + $this->setParent($target_class, $footprint['id'], + $max_footprint_id + (int)$footprint['category_id']); + } + } + + $this->em->flush(); + + return (is_countable($footprint_data) ? count($footprint_data) : 0) + (is_countable($footprintcategory_data) ? count($footprintcategory_data) : 0); + } + + public function importFootprints(array $data): int + { + $count = $this->importElementsWithCategory($data, Footprint::class, 'footprint'); + + //Footprints have both attachments and images + $this->importAttachments($data, 'footprintattachment', Footprint::class, 'footprint_id', FootprintAttachment::class); + $this->importAttachments($data, 'footprintimage', Footprint::class, 'footprint_id', FootprintAttachment::class); + + return $count; + } + + public function importStorelocations(array $data): int + { + $count = $this->importElementsWithCategory($data, StorageLocation::class, 'storagelocation'); + + $this->importAttachments($data, 'storagelocationimage', StorageLocation::class, 'storageLocation_id', StorageLocationAttachment::class); + + return $count; + } +} diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelper.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelper.php new file mode 100644 index 00000000..f36e48ce --- /dev/null +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelper.php @@ -0,0 +1,69 @@ +. + */ +namespace App\Services\ImportExportSystem\PartKeeprImporter; + +use App\Doctrine\Purger\ResetAutoIncrementORMPurger; +use Doctrine\ORM\EntityManagerInterface; + +/** + * This service contains various helper functions for the PartKeeprImporter (like purging the database). + */ +class PKImportHelper +{ + public function __construct(protected EntityManagerInterface $em) + { + } + + /** + * Purges the database tables for the import, so that all data can be created from scratch. + * Existing users and groups are not purged. + * This is needed to avoid ID collisions. + */ + public function purgeDatabaseForImport(): void + { + //We use the ResetAutoIncrementORMPurger to reset the auto increment values of the tables. Also it normalizes table names before checking for exclusion. + $purger = new ResetAutoIncrementORMPurger($this->em, ['users', 'groups', 'u2f_keys', 'internal', 'migration_versions']); + $purger->purge(); + } + + /** + * Extracts the current database schema version from the PartKeepr XML dump. + */ + public function getDatabaseSchemaVersion(array $data): string + { + if (!isset($data['schemaversions'])) { + throw new \RuntimeException('Could not find schema version in XML dump!'); + } + + return end($data['schemaversions'])['version']; + } + + /** + * Checks that the database schema of the PartKeepr XML dump is compatible with the importer + * @return bool True if the schema is compatible, false otherwise + */ + public function checkVersion(array $data): bool + { + return $this->getDatabaseSchemaVersion($data) === '20170601175559'; + } +} diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php new file mode 100644 index 00000000..1e4cd3ba --- /dev/null +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php @@ -0,0 +1,257 @@ +. + */ +namespace App\Services\ImportExportSystem\PartKeeprImporter; + +use Doctrine\ORM\Id\AssignedGenerator; +use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\AttachmentContainingDBElement; +use App\Entity\Attachments\AttachmentType; +use App\Entity\Base\AbstractDBElement; +use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Contracts\TimeStampableInterface; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * This trait contains helper functions for the PartKeeprImporter. + */ +trait PKImportHelperTrait +{ + protected EntityManagerInterface $em; + protected PropertyAccessorInterface $propertyAccessor; + + private ?AttachmentType $import_attachment_type = null; + + /** + * Converts a PartKeepr attachment/image row to an Attachment entity. + * @param array $attachment_row The attachment row from the PartKeepr database + * @param string $target_class The target class for the attachment + * @param string $type The type of the attachment (attachment or image) + * @throws \Exception + */ + protected function convertAttachmentDataToEntity(array $attachment_row, string $target_class, string $type): Attachment + { + //By default, we use the cached version + if (!$this->import_attachment_type) { + //Get the import attachment type + $this->import_attachment_type = $this->em->getRepository(AttachmentType::class)->findOneBy([ + 'name' => 'PartKeepr Attachment' + ]); + if (!$this->import_attachment_type) { //If not existing in DB create it + $this->import_attachment_type = new AttachmentType(); + $this->import_attachment_type->setName('PartKeepr Attachment'); + $this->em->persist($this->import_attachment_type); + } + } + + if (!in_array($type, ['attachment', 'image'], true)) { + throw new \InvalidArgumentException(sprintf('The type %s is not a valid attachment type', $type)); + } + + if (!is_a($target_class, Attachment::class, true)) { + throw new \InvalidArgumentException(sprintf('The target class %s is not a subclass of %s', $target_class, Attachment::class)); + } + + /** @var Attachment $attachment */ + $attachment = new $target_class(); + if (!empty($attachment_row['description'])) { + $attachment->setName($attachment_row['description']); + } else { + $attachment->setName($attachment_row['originalname']); + } + $attachment->setFilename($attachment_row['originalname']); + $attachment->setAttachmentType($this->import_attachment_type); + $this->setCreationDate($attachment, $attachment_row['created']); + + //Determine file extension (if the extension is empty, we use the original extension) + if (empty($attachment_row['extension'])) { + //Use mime type to determine the extension like PartKeepr does in legacy implementation (just use the second part of the mime type) + //See UploadedFile.php:291 in PartKeepr (https://github.com/partkeepr/PartKeepr/blob/f6176c3354b24fa39ac8bc4328ee0df91de3d5b6/src/PartKeepr/UploadedFileBundle/Entity/UploadedFile.php#L291) + if (!empty ($attachment_row['mimetype'])) { + $attachment_row['extension'] = explode('/', (string) $attachment_row['mimetype'])[1]; + } else { + //If the mime type is empty, we use the original extension + $attachment_row['extension'] = pathinfo((string) $attachment_row['originalname'], PATHINFO_EXTENSION); + } + + } + + //Determine file path + //Images are stored in the (public) media folder, attachments in the (private) uploads/ folder + $path = $type === 'attachment' ? '%SECURE%' : '%MEDIA%'; + //The folder is the type of the attachment from the PartKeepr database + $path .= '/'.$attachment_row['type']; + //Next comes the filename plus extension + $path .= '/'.$attachment_row['filename'].'.'.$attachment_row['extension']; + + $attachment->setInternalPath($path); + + return $attachment; + } + + /** + * Imports the attachments from the given data + * @param array $data The PartKeepr database + * @param string $table_name The table name for the attachments (if it contains "image", it will be treated as an image) + * @param string $target_class The target class (e.g. Part) + * @param string $target_id_field The field name where the target ID is stored + * @param string $attachment_class The attachment class (e.g. PartAttachment) + */ + protected function importAttachments(array $data, string $table_name, string $target_class, string $target_id_field, string $attachment_class): void + { + //Determine if we have an image or an attachment + $type = str_contains($table_name, 'image') || str_contains($table_name, 'iclogo') ? 'image' : 'attachment'; + + if (!isset($data[$table_name])) { + throw new \RuntimeException(sprintf('The table %s does not exist in the PartKeepr database', $table_name)); + } + + if (!is_a($target_class, AttachmentContainingDBElement::class, true)) { + throw new \InvalidArgumentException(sprintf('The target class %s is not a subclass of %s', $target_class, AttachmentContainingDBElement::class)); + } + + if (!is_a($attachment_class, Attachment::class, true)) { + throw new \InvalidArgumentException(sprintf('The attachment class %s is not a subclass of %s', $attachment_class, Attachment::class)); + } + + //Get the table data + $table_data = $data[$table_name]; + foreach($table_data as $attachment_row) { + $attachment = $this->convertAttachmentDataToEntity($attachment_row, $attachment_class, $type); + + //Retrieve the target entity + $target_id = (int) $attachment_row[$target_id_field]; + /** @var AttachmentContainingDBElement $target */ + $target = $this->em->find($target_class, $target_id); + if (!$target) { + throw new \RuntimeException(sprintf('Could not find target entity with ID %s', $target_id)); + } + + $target->addAttachment($attachment); + $this->em->persist($attachment); + } + + $this->em->flush(); + } + + /** + * Assigns the parent to the given entity, using the numerical IDs from the imported data. + * @return AbstractStructuralDBElement The structural element that was modified (with $element_id) + */ + protected function setParent(string $class, int|string $element_id, int|string $parent_id): AbstractStructuralDBElement + { + $element = $this->em->find($class, (int) $element_id); + if (!$element) { + throw new \RuntimeException(sprintf('Could not find element with ID %s', $element_id)); + } + + //If the parent is null, we're done + if (!$parent_id) { + return $element; + } + + $parent = $this->em->find($class, (int) $parent_id); + if (!$parent) { + throw new \RuntimeException(sprintf('Could not find parent with ID %s', $parent_id)); + } + + $element->setParent($parent); + return $element; + } + + /** + * Sets the given field of the given entity to the entity with the given ID. + */ + protected function setAssociationField(AbstractDBElement $element, string $field, string $other_class, $other_id): AbstractDBElement + { + //If the parent is null, set the field to null and we're done + if (!$other_id) { + $this->propertyAccessor->setValue($element, $field, null); + return $element; + } + + $parent = $this->em->find($other_class, (int) $other_id); + if (!$parent) { + throw new \RuntimeException(sprintf('Could not find other_class with ID %s', $other_id)); + } + + $this->propertyAccessor->setValue($element, $field, $parent); + return $element; + } + + /** + * Set the ID of an entity to a specific value. Must be called before persisting the entity, but before flushing. + */ + protected function setIDOfEntity(AbstractDBElement $element, int|string $id): void + { + $id = (int) $id; + + $metadata = $this->em->getClassMetadata($element::class); + $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); + $metadata->setIdGenerator(new AssignedGenerator()); + $metadata->setIdentifierValues($element, ['id' => $id]); + } + + /** + * Sets the creation date of an entity to a specific value. + * @return void + * @throws \Exception + */ + protected function setCreationDate(TimeStampableInterface $entity, ?string $datetime_str): void + { + if ($datetime_str !== null && $datetime_str !== '' && $datetime_str !== '0000-00-00 00:00:00') { + $date = new \DateTimeImmutable($datetime_str); + } else { + $date = null; //Null means "now" at persist time + } + + $reflectionClass = new \ReflectionClass($entity); + $property = $reflectionClass->getProperty('addedDate'); + $property->setAccessible(true); + $property->setValue($entity, $date); + } + + /** + * Gets the SI prefix factor for the given prefix ID. + * Used to convert a value from the PartKeepr database to the PartDB database. + * @param array $data + * @param int $prefix_id + * @return float + */ + protected function getSIPrefixFactor(array $data, int $prefix_id): float + { + if ($prefix_id === 0) { + return 1.0; + } + + $prefixes = $data['siprefix']; + foreach ($prefixes as $prefix) { + if ((int) $prefix['id'] === $prefix_id) { + return (int)$prefix['base'] ** (int)$prefix['exponent']; + } + } + + throw new \RuntimeException(sprintf('Could not find SI prefix with ID %s', $prefix_id)); + } +} diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php new file mode 100644 index 00000000..fafde29a --- /dev/null +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php @@ -0,0 +1,149 @@ +. + */ +namespace App\Services\ImportExportSystem\PartKeeprImporter; + +use App\Entity\Attachments\ProjectAttachment; +use App\Entity\Parts\Part; +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use App\Entity\UserSystem\Group; +use App\Entity\UserSystem\User; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * This service is used to other non-mandatory data from a PartKeepr export. + * You have to import the datastructures and parts first to use project import! + */ +class PKOptionalImporter +{ + use PKImportHelperTrait; + + public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor) + { + $this->em = $em; + $this->propertyAccessor = $propertyAccessor; + } + + /** + * Import the projects from the given data. + * @return int The number of imported projects + */ + public function importProjects(array $data): int + { + if (!isset($data['project'])) { + throw new \RuntimeException('$data must contain a "project" key!'); + } + if (!isset($data['projectpart'])) { + throw new \RuntimeException('$data must contain a "projectpart" key!'); + } + + $projects_data = $data['project']; + $projectparts_data = $data['projectpart']; + + //First import the projects + foreach ($projects_data as $project_data) { + $project = new Project(); + $project->setName($project_data['name']); + $project->setDescription($project_data['description'] ?? ''); + + $this->setIDOfEntity($project, $project_data['id']); + $this->em->persist($project); + } + $this->em->flush(); + + //Then the project BOM entries + foreach ($projectparts_data as $projectpart_data) { + /** @var Project $project */ + $project = $this->em->find(Project::class, (int) $projectpart_data['project_id']); + if (!$project) { + throw new \RuntimeException('Could not find project with ID '.$projectpart_data['project_id']); + } + + $bom_entry = new ProjectBOMEntry(); + $bom_entry->setQuantity((float) $projectpart_data['quantity']); + $bom_entry->setName($projectpart_data['remarks']); + $this->setAssociationField($bom_entry, 'part', Part::class, $projectpart_data['part_id']); + + $comments = []; + if (!empty($projectpart_data['lotNumber'])) { + $comments[] = 'Lot number: '.$projectpart_data['lotNumber']; + } + if (!empty($projectpart_data['overage'])) { + $comments[] = 'Overage: '.$projectpart_data['overage'].($projectpart_data['overageType'] ? ' %' : ' pcs'); + } + $bom_entry->setComment(implode(',', $comments)); + + $project->addBomEntry($bom_entry); + } + $this->em->flush(); + + $this->importAttachments($data, 'projectattachment', Project::class, 'project_id', ProjectAttachment::class); + + return is_countable($projects_data) ? count($projects_data) : 0; + } + + /** + * Import the users from the given data. + * @return int The number of imported users + */ + public function importUsers(array $data): int + { + if (!isset($data['fosuser'])) { + throw new \RuntimeException('$data must contain a "fosuser" key!'); + } + + //All imported users get assigned to the "PartKeepr Users" group + $group_users = $this->em->find(Group::class, 3); + $group = $this->em->getRepository(Group::class)->findOneBy(['name' => 'PartKeepr Users', 'parent' => $group_users]); + if ($group === null) { + $group = new Group(); + $group->setName('PartKeepr Users'); + $group->setParent($group_users); + $this->em->persist($group); + } + + + $users_data = $data['fosuser']; + foreach ($users_data as $user_data) { + if (in_array($user_data['username'], ['admin', 'anonymous'], true)) { + continue; + } + + $user = new User(); + $user->setName($user_data['username']); + $user->setEmail($user_data['email']); + $user->setGroup($group); + + //User is disabled by default + $user->setDisabled(true); + + //We let doctrine generate a new ID for the user + $this->em->persist($user); + } + + $this->em->flush(); + + return is_countable($users_data) ? count($users_data) : 0; + } +} diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKPartImporter.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKPartImporter.php new file mode 100644 index 00000000..9dd67233 --- /dev/null +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKPartImporter.php @@ -0,0 +1,317 @@ +. + */ +namespace App\Services\ImportExportSystem\PartKeeprImporter; + +use App\Entity\Attachments\PartAttachment; +use App\Entity\Parameters\PartParameter; +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; +use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\MeasurementUnit; +use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; +use App\Entity\Parts\StorageLocation; +use App\Entity\Parts\Supplier; +use App\Entity\PriceInformations\Currency; +use App\Entity\PriceInformations\Orderdetail; +use App\Entity\PriceInformations\Pricedetail; +use Brick\Math\BigDecimal; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Intl\Currencies; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * This service is used to import parts from a PartKeepr export. You have to import the datastructures first! + */ +class PKPartImporter +{ + use PKImportHelperTrait; + + public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor, private readonly string $base_currency) + { + $this->em = $em; + $this->propertyAccessor = $propertyAccessor; + } + + public function importParts(array $data): int + { + if (!isset($data['part'])) { + throw new \RuntimeException('$data must contain a "part" key!'); + } + + + $part_data = $data['part']; + foreach ($part_data as $part) { + $entity = new Part(); + $entity->setName($part['name']); + $entity->setDescription($part['description'] ?? ''); + //All parts get a tag, that they were imported from PartKeepr + $entity->setTags('partkeepr-imported'); + $this->setAssociationField($entity, 'category', Category::class, $part['category_id']); + + //If the part is a metapart, write that in the description, and we can skip the rest + if ($part['metaPart'] === '1') { + $entity->setDescription('Metapart (Not supported in Part-DB)'); + $entity->setComment('This part represents a former metapart in PartKeepr. It is not supported in Part-DB yet. And you can most likely delete it.'); + $entity->setTags('partkeepr-imported,partkeepr-metapart'); + } else { + $entity->setMinAmount((float) ($part['minStockLevel'] ?? 0)); + if (!empty($part['internalPartNumber'])) { + $entity->setIpn($part['internalPartNumber']); + } + $entity->setComment($part['comment'] ?? ''); + $entity->setNeedsReview($part['needsReview'] === '1'); + $this->setCreationDate($entity, $part['createDate']); + + $this->setAssociationField($entity, 'footprint', Footprint::class, $part['footprint_id']); + + //Set partUnit (when it is not ID=1, which is Pieces in Partkeepr) + if ($part['partUnit_id'] !== '1') { + $this->setAssociationField($entity, 'partUnit', MeasurementUnit::class, $part['partUnit_id']); + } + + //Create a part lot to store the stock level and location + $lot = new PartLot(); + $lot->setAmount((float) ($part['stockLevel'] ?? 0)); + $this->setAssociationField($lot, 'storage_location', StorageLocation::class, $part['storageLocation_id']); + $entity->addPartLot($lot); + + //For partCondition, productionsRemarks and Status, create a custom parameter + if ($part['partCondition']) { + $partCondition = (new PartParameter())->setName('Part Condition')->setGroup('PartKeepr') + ->setValueText($part['partCondition']); + $entity->addParameter($partCondition); + } + if ($part['productionRemarks']) { + $partCondition = (new PartParameter())->setName('Production Remarks')->setGroup('PartKeepr') + ->setValueText($part['productionRemarks']); + $entity->addParameter($partCondition); + } + if ($part['status']) { + $partCondition = (new PartParameter())->setName('Status')->setGroup('PartKeepr') + ->setValueText($part['status']); + $entity->addParameter($partCondition); + } + } + + $this->setIDOfEntity($entity, $part['id']); + $this->em->persist($entity); + } + + $this->em->flush(); + + $this->importPartManufacturers($data); + $this->importPartParameters($data); + $this->importOrderdetails($data); + + //Import attachments + $this->importAttachments($data, 'partattachment', Part::class, 'part_id', PartAttachment::class); + + return is_countable($part_data) ? count($part_data) : 0; + } + + protected function importPartManufacturers(array $data): void + { + if (!isset($data['partmanufacturer'])) { + throw new \RuntimeException('$data must contain a "partmanufacturer" key!'); + } + + //Part-DB only supports one manufacturer per part, only the last one is imported + $partmanufacturer_data = $data['partmanufacturer']; + foreach ($partmanufacturer_data as $partmanufacturer) { + /** @var Part $part */ + $part = $this->em->find(Part::class, (int) $partmanufacturer['part_id']); + if (!$part) { + throw new \RuntimeException(sprintf('Could not find part with ID %s', $partmanufacturer['part_id'])); + } + $manufacturer = $this->em->find(Manufacturer::class, (int) $partmanufacturer['manufacturer_id']); + //The manufacturer is optional + if (!$manufacturer instanceof Manufacturer && !empty($partmanufacturer['manufacturer_id'])) { + throw new \RuntimeException(sprintf('Could not find manufacturer with ID %s', $partmanufacturer['manufacturer_id'])); + } + $part->setManufacturer($manufacturer); + $part->setManufacturerProductNumber($partmanufacturer['partNumber']); + } + + $this->em->flush(); + } + + protected function importPartParameters(array $data): void + { + if (!isset($data['partparameter'])) { + throw new \RuntimeException('$data must contain a "partparameter" key!'); + } + + foreach ($data['partparameter'] as $partparameter) { + $entity = new PartParameter(); + + //Name format: Name (Description) + $name = $partparameter['name']; + if (!empty($partparameter['description'])) { + $name .= ' ('.$partparameter['description'].')'; + } + $entity->setName($name); + + $entity->setValueText($partparameter['stringValue'] ?? ''); + if ($partparameter['unit_id'] !== null && (int) $partparameter['unit_id'] !== 0) { + $entity->setUnit($this->getUnitSymbol($data, (int)$partparameter['unit_id'])); + } else { + $entity->setUnit(""); + } + + if ($partparameter['value'] !== null) { + $entity->setValueTypical((float) $partparameter['value'] * $this->getSIPrefixFactor($data, (int) $partparameter['siPrefix_id'])); + } + if ($partparameter['minimumValue'] !== null) { + $entity->setValueMin((float) $partparameter['minimumValue'] * $this->getSIPrefixFactor($data, (int) $partparameter['minSiPrefix_id'])); + } + if ($partparameter['maximumValue'] !== null) { + $entity->setValueMax((float) $partparameter['maximumValue'] * $this->getSIPrefixFactor($data, (int) $partparameter['maxSiPrefix_id'])); + } + + $part = $this->em->find(Part::class, (int) $partparameter['part_id']); + if (!$part instanceof Part) { + throw new \RuntimeException(sprintf('Could not find part with ID %s', $partparameter['part_id'])); + } + + $part->addParameter($entity); + $this->em->persist($entity); + } + $this->em->flush(); + } + + /** + * Returns the currency for the given ISO code. If the currency does not exist, it is created. + * This function returns null if the ISO code is the base currency. + */ + protected function getOrCreateCurrency(string $currency_iso_code): ?Currency + { + //Normalize ISO code + $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->base_currency) { + return null; + } + + $currency = $this->em->getRepository(Currency::class)->findOneBy([ + 'iso_code' => $currency_iso_code, + ]); + + if ($currency === null) { + $currency = new Currency(); + $currency->setIsoCode($currency_iso_code); + $currency->setName(Currencies::getName($currency_iso_code)); + $this->em->persist($currency); + $this->em->flush(); + } + + return $currency; + } + + protected function importOrderdetails(array $data): void + { + if (!isset($data['partdistributor'])) { + throw new \RuntimeException('$data must contain a "partdistributor" key!'); + } + + foreach ($data['partdistributor'] as $partdistributor) { + //Retrieve the part + $part = $this->em->find(Part::class, (int) $partdistributor['part_id']); + if (!$part instanceof Part) { + throw new \RuntimeException(sprintf('Could not find part with ID %s', $partdistributor['part_id'])); + } + //Retrieve the distributor + $supplier = $this->em->find(Supplier::class, (int) $partdistributor['distributor_id']); + if (!$supplier instanceof Supplier) { + throw new \RuntimeException(sprintf('Could not find supplier with ID %s', $partdistributor['distributor_id'])); + } + + //Check if the part already has an orderdetail for this supplier and ordernumber + if (empty($partdistributor['orderNumber']) && !empty($partdistributor['sku'])) { + $spn = $partdistributor['sku']; + } elseif (!empty($partdistributor['orderNumber']) && empty($partdistributor['sku'])) { + $spn = $partdistributor['orderNumber']; + } elseif (!empty($partdistributor['orderNumber']) && !empty($partdistributor['sku'])) { + $spn = $partdistributor['orderNumber'] . ' (' . $partdistributor['sku'] . ')'; + } else { + $spn = ''; + } + + $orderdetail = $this->em->getRepository(Orderdetail::class)->findOneBy([ + 'part' => $part, + 'supplier' => $supplier, + 'supplierpartnr' => $spn, + ]); + + //When no orderdetail exists, create one + if ($orderdetail === null) { + $orderdetail = new Orderdetail(); + $orderdetail->setSupplier($supplier); + $orderdetail->setSupplierpartnr($spn); + $part->addOrderdetail($orderdetail); + $this->em->persist($orderdetail); + } + + //Add the price information to the orderdetail (only if the price is not zero, as this was a placeholder in PartKeepr) + if (!empty($partdistributor['price']) && !BigDecimal::of($partdistributor['price'])->isZero()) { + $pricedetail = new Pricedetail(); + $orderdetail->addPricedetail($pricedetail); + //Partkeepr stores the price per item, we need to convert it to the price per packaging unit + $price_per_item = BigDecimal::of($partdistributor['price']); + $packaging_unit = (float) ($partdistributor['packagingUnit'] ?? 1); + $pricedetail->setPrice($price_per_item->multipliedBy($packaging_unit)); + $pricedetail->setPriceRelatedQuantity($packaging_unit); + //We have to set the minimum discount quantity to the packaging unit (PartKeepr does not know this concept) + //But in Part-DB the minimum discount qty have to be unique across a orderdetail + $pricedetail->setMinDiscountQuantity($packaging_unit); + + //Set the currency of the price + if (!empty($partdistributor['currency'])) { + $currency = $this->getOrCreateCurrency($partdistributor['currency']); + $pricedetail->setCurrency($currency); + } + + $this->em->persist($pricedetail); + } + + $this->em->flush(); + //Clear the entity manager to improve performance + $this->em->clear(); + } + } + + /** + * Returns the (parameter) unit symbol for the given ID. + */ + protected function getUnitSymbol(array $data, int $id): string + { + foreach ($data['unit'] as $unit) { + if ((int) $unit['id'] === $id) { + return $unit['symbol']; + } + } + + throw new \RuntimeException(sprintf('Could not find unit with ID %s', $id)); + } +} diff --git a/src/Services/InfoProviderSystem/DTOs/FileDTO.php b/src/Services/InfoProviderSystem/DTOs/FileDTO.php new file mode 100644 index 00000000..0d1db76a --- /dev/null +++ b/src/Services/InfoProviderSystem/DTOs/FileDTO.php @@ -0,0 +1,53 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\DTOs; + +/** + * This DTO represents a file that can be downloaded from a URL. + * This could be a datasheet, a 3D model, a picture or similar. + * @see \App\Tests\Services\InfoProviderSystem\DTOs\FileDTOTest + */ +class FileDTO +{ + /** + * @var string The URL where to get this file + */ + public readonly string $url; + + /** + * @param string $url The URL where to get this file + * @param string|null $name Optionally the name of this file + */ + public function __construct( + string $url, + public readonly ?string $name = null, + ) { + //Find all occurrences of non URL safe characters and replace them with their URL encoded version. + //We only want to replace characters which can not have a valid meaning in a URL (what would break the URL). + //Digikey provided some wrong URLs with a ^ in them, which is not a valid URL character. (https://github.com/Part-DB/Part-DB-server/issues/521) + $this->url = preg_replace_callback('/[^a-zA-Z0-9_\-.$+!*();\/?:@=&#%]/', static fn($matches) => rawurlencode($matches[0]), $url); + } + + +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/DTOs/ParameterDTO.php b/src/Services/InfoProviderSystem/DTOs/ParameterDTO.php new file mode 100644 index 00000000..0b54d1a9 --- /dev/null +++ b/src/Services/InfoProviderSystem/DTOs/ParameterDTO.php @@ -0,0 +1,165 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\DTOs; + +/** + * This DTO represents a parameter of a part (similar to the AbstractParameter entity). + * This could be a voltage, a current, a temperature or similar. + * @see \App\Tests\Services\InfoProviderSystem\DTOs\ParameterDTOTest + */ +class ParameterDTO +{ + public function __construct( + public readonly string $name, + public readonly ?string $value_text = null, + public readonly ?float $value_typ = null, + public readonly ?float $value_min = null, + public readonly ?float $value_max = null, + public readonly ?string $unit = null, + public readonly ?string $symbol = null, + public readonly ?string $group = null, + ) { + + } + + /** + * This function tries to decide on the value, if it is a numerical value (which is then stored in one of the value_*) fields) or a text value (which is stored in value_text). + * It is possible to give ranges like 1...2 (or 1~2) here, which will be parsed as value_min: 1.0, value_max: 2.0. + * + * For certain expressions (like ranges) the unit is automatically extracted from the value, if no unit is given + * @TODO Rework that, so that the difference between parseValueField and parseValueIncludingUnit is clearer or merge them + * @param string $name + * @param string|float $value + * @param string|null $unit + * @param string|null $symbol + * @param string|null $group + * @return self + */ + public static function parseValueField(string $name, string|float $value, ?string $unit = null, ?string $symbol = null, ?string $group = null): self + { + //If we encounter something like 2.5@text, then put the "@text" into text_value and continue with the number parsing + if (is_string($value) && preg_match('/^(.+)(@.+)$/', $value, $matches) === 1) { + $value = $matches[1]; + $value_text = $matches[2]; + } else { + $value_text = null; + } + + //If the value is just a number, we assume thats the typical value + if (is_float($value) || is_numeric($value)) { + return new self($name, value_text: $value_text, value_typ: (float) $value, unit: $unit, symbol: $symbol, + group: $group); + } + + //If the attribute contains ".." or "..." or a tilde we assume it is a range + if (preg_match('/(\.{2,3}|~)/', $value) === 1) { + $parts = preg_split('/\s*(\.{2,3}|~)\s*/', $value); + if (count($parts) === 2) { + //Try to extract number and unit from value (allow leading +) + if ($unit === null || trim($unit) === '') { + [$number, $unit] = self::splitIntoValueAndUnit(ltrim($parts[0], " +")) ?? [$parts[0], null]; + } else { + $number = $parts[0]; + } + + // If the second part has some extra info, we'll save that into value_text + if (!empty($unit) && preg_match('/^(.+' . preg_quote($unit, '/') . ')\s*(.+)$/', $parts[1], $matches) > 0) { + $parts[1] = $matches[1]; + $value_text2 = $matches[2]; + } else { + $value_text2 = null; + } + [$number2, $unit2] = self::splitIntoValueAndUnit(ltrim($parts[1], " +")) ?? [$parts[1], $unit]; + + //If both parts have the same unit and both values are numerical, we'll save it as range + if ($unit === $unit2 && is_numeric($number) && is_numeric($number2)) { + return new self(name: $name, value_text: $value_text2, value_min: (float) $number, + value_max: (float) $number2, unit: $unit, symbol: $symbol, group: $group); + } + } + //If it's a plus/minus value, we'll also treat it as a range + } elseif (str_starts_with($value, '±')) { + [$number, $unit] = self::splitIntoValueAndUnit(ltrim($value, " ±")) ?? [ltrim($value, ' ±'), $unit]; + if (is_numeric($number)) { + return new self(name: $name, value_min: -abs((float) $number), value_max: abs((float) $number), unit: $unit, symbol: $symbol, group: $group); + } + } + + //If no unit was passed to us, try to extract it from the value + if (empty($unit)) { + [$value, $unit] = self::splitIntoValueAndUnit($value) ?? [$value, null]; + } + + //Were we successful in trying to reduce the value to a number? + if ($value_text !== null && is_numeric($value)) { + return new self($name, value_text: $value_text, value_typ: (float) $value, unit: $unit, symbol: $symbol, + group: $group); + } + + return new self($name, value_text: $value.$value_text, unit: $unit, symbol: $symbol, group: $group); + } + + /** + * This function tries to decide on the value, if it is a numerical value (which is then stored in one of the value_*) fields) or a text value (which is stored in value_text). + * It also tries to extract the unit from the value field (so 3kg will be parsed as value_typ: 3.0, unit: kg). + * Ranges like 1...2 will be parsed as value_min: 1.0, value_max: 2.0. + * @param string $name + * @param string|float $value + * @param string|null $symbol + * @param string|null $group + * @return self + */ + public static function parseValueIncludingUnit(string $name, string|float $value, ?string $symbol = null, ?string $group = null): self + { + //Try to extract unit from value + $unit = null; + if (is_string($value)) { + [$number, $unit] = self::splitIntoValueAndUnit($value) ?? [$value, null]; + + return self::parseValueField(name: $name, value: $number, unit: $unit, symbol: $symbol, group: $group); + } + + //Otherwise we assume that no unit is given + return self::parseValueField(name: $name, value: $value, unit: null, symbol: $symbol, group: $group); + } + + /** + * Splits the given value into a value and a unit part if possible. + * If the value is not in the expected format, null is returned. + * @param string $value The value to split + * @return array|null An array with the value and the unit part or null if the value is not in the expected format + * @phpstan-return array{0: string, 1: string}|null + */ + public static function splitIntoValueAndUnit(string $value): ?array + { + if (preg_match('/^(?-?[0-9\.]+)\s*(?[%Ωµ°℃a-z_\/]+\s?\w{0,4})$/iu', $value, $matches)) { + $value = $matches['value']; + $unit = $matches['unit']; + + return [$value, $unit]; + } + + return null; + } +} diff --git a/src/Services/InfoProviderSystem/DTOs/PartDetailDTO.php b/src/Services/InfoProviderSystem/DTOs/PartDetailDTO.php new file mode 100644 index 00000000..9f365f1e --- /dev/null +++ b/src/Services/InfoProviderSystem/DTOs/PartDetailDTO.php @@ -0,0 +1,73 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\DTOs; + +use App\Entity\Parts\ManufacturingStatus; + +/** + * This DTO represents a part with all its details. + */ +class PartDetailDTO extends SearchResultDTO +{ + public function __construct( + string $provider_key, + string $provider_id, + string $name, + string $description, + ?string $category = null, + ?string $manufacturer = null, + ?string $mpn = null, + ?string $preview_image_url = null, + ?ManufacturingStatus $manufacturing_status = null, + ?string $provider_url = null, + ?string $footprint = null, + public readonly ?string $notes = null, + /** @var FileDTO[]|null */ + public readonly ?array $datasheets = null, + /** @var FileDTO[]|null */ + public readonly ?array $images = null, + /** @var ParameterDTO[]|null */ + public readonly ?array $parameters = null, + /** @var PurchaseInfoDTO[]|null */ + public readonly ?array $vendor_infos = null, + /** The mass of the product in grams */ + public readonly ?float $mass = null, + /** The URL to the product on the website of the manufacturer */ + public readonly ?string $manufacturer_product_url = null, + ) { + parent::__construct( + provider_key: $provider_key, + provider_id: $provider_id, + name: $name, + description: $description, + category: $category, + manufacturer: $manufacturer, + mpn: $mpn, + preview_image_url: $preview_image_url, + manufacturing_status: $manufacturing_status, + provider_url: $provider_url, + footprint: $footprint, + ); + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/DTOs/PriceDTO.php b/src/Services/InfoProviderSystem/DTOs/PriceDTO.php new file mode 100644 index 00000000..f1eb28f7 --- /dev/null +++ b/src/Services/InfoProviderSystem/DTOs/PriceDTO.php @@ -0,0 +1,59 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\DTOs; + +use Brick\Math\BigDecimal; + +/** + * This DTO represents a price for a single unit in a certain discount range + */ +class PriceDTO +{ + private readonly BigDecimal $price_as_big_decimal; + + public function __construct( + /** @var float The minimum amount that needs to get ordered for this price to be valid */ + public readonly float $minimum_discount_amount, + /** @var string The price as string (with .) */ + public readonly string $price, + /** @var string The currency of the used ISO code of this price detail */ + public readonly ?string $currency_iso_code, + /** @var bool If the price includes tax */ + public readonly ?bool $includes_tax = true, + /** @var float the price related quantity */ + public readonly ?float $price_related_quantity = 1.0, + ) + { + $this->price_as_big_decimal = BigDecimal::of($this->price); + } + + /** + * Gets the price as BigDecimal + * @return BigDecimal + */ + public function getPriceAsBigDecimal(): BigDecimal + { + return $this->price_as_big_decimal; + } +} diff --git a/src/Services/InfoProviderSystem/DTOs/PurchaseInfoDTO.php b/src/Services/InfoProviderSystem/DTOs/PurchaseInfoDTO.php new file mode 100644 index 00000000..bcd8be43 --- /dev/null +++ b/src/Services/InfoProviderSystem/DTOs/PurchaseInfoDTO.php @@ -0,0 +1,48 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\DTOs; + +/** + * This DTO represents a purchase information for a part (supplier name, order number and prices). + * @see \App\Tests\Services\InfoProviderSystem\DTOs\PurchaseInfoDTOTest + */ +class PurchaseInfoDTO +{ + public function __construct( + public readonly string $distributor_name, + public readonly string $order_number, + /** @var PriceDTO[] */ + public readonly array $prices, + /** @var string|null An url to the product page of the vendor */ + public readonly ?string $product_url = null, + ) + { + //Ensure that the prices are PriceDTO instances + foreach ($this->prices as $price) { + if (!$price instanceof PriceDTO) { + throw new \InvalidArgumentException('The prices array must only contain PriceDTO instances'); + } + } + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/DTOs/SearchResultDTO.php b/src/Services/InfoProviderSystem/DTOs/SearchResultDTO.php new file mode 100644 index 00000000..28943702 --- /dev/null +++ b/src/Services/InfoProviderSystem/DTOs/SearchResultDTO.php @@ -0,0 +1,74 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\DTOs; + +use App\Entity\Parts\ManufacturingStatus; + +/** + * This DTO represents a search result for a part. + * @see \App\Tests\Services\InfoProviderSystem\DTOs\SearchResultDTOTest + */ +class SearchResultDTO +{ + /** @var string|null An URL to a preview image */ + public readonly ?string $preview_image_url; + /** @var FileDTO|null The preview image as FileDTO object */ + public readonly ?FileDTO $preview_image_file; + + public function __construct( + /** @var string The provider key (e.g. "digikey") */ + public readonly string $provider_key, + /** @var string The ID which identifies the part in the provider system */ + public readonly string $provider_id, + /** @var string The name of the part */ + public readonly string $name, + /** @var string A short description of the part */ + public readonly string $description, + /** @var string|null The category the distributor assumes for the part */ + public readonly ?string $category = null, + /** @var string|null The manufacturer of the part */ + public readonly ?string $manufacturer = null, + /** @var string|null The manufacturer part number */ + public readonly ?string $mpn = null, + /** @var string|null An URL to a preview image */ + ?string $preview_image_url = null, + /** @var ManufacturingStatus|null The manufacturing status of the part */ + public readonly ?ManufacturingStatus $manufacturing_status = null, + /** @var string|null A link to the part on the providers page */ + public readonly ?string $provider_url = null, + /** @var string|null A footprint representation of the providers page */ + public readonly ?string $footprint = null, + ) { + + if ($preview_image_url !== null) { + //Utilize the escaping mechanism of FileDTO to ensure that the preview image URL is correctly encoded + //See issue #521: https://github.com/Part-DB/Part-DB-server/issues/521 + $this->preview_image_file = new FileDTO($preview_image_url); + $this->preview_image_url = $this->preview_image_file->url; + } else { + $this->preview_image_file = null; + $this->preview_image_url = null; + } + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/DTOtoEntityConverter.php b/src/Services/InfoProviderSystem/DTOtoEntityConverter.php new file mode 100644 index 00000000..40f69498 --- /dev/null +++ b/src/Services/InfoProviderSystem/DTOtoEntityConverter.php @@ -0,0 +1,356 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem; + +use App\Entity\Attachments\AttachmentType; +use App\Entity\Attachments\PartAttachment; +use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Parameters\PartParameter; +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; +use App\Entity\Parts\InfoProviderReference; +use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\ManufacturingStatus; +use App\Entity\Parts\Part; +use App\Entity\Parts\Supplier; +use App\Entity\PriceInformations\Currency; +use App\Entity\PriceInformations\Orderdetail; +use App\Entity\PriceInformations\Pricedetail; +use App\Repository\Parts\CategoryRepository; +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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 Doctrine\ORM\EntityManagerInterface; + +/** + * This class converts DTOs to entities which can be persisted in the DB + * @see \App\Tests\Services\InfoProviderSystem\DTOtoEntityConverterTest + */ +final class DTOtoEntityConverter +{ + private const TYPE_DATASHEETS_NAME = 'Datasheet'; + private const TYPE_IMAGE_NAME = 'Image'; + + public function __construct(private readonly EntityManagerInterface $em, private readonly string $base_currency) + { + } + + /** + * Converts the given DTO to a PartParameter entity. + * @param ParameterDTO $dto + * @param PartParameter $entity The entity to apply the DTO on. If null a new entity will be created + * @return PartParameter + */ + public function convertParameter(ParameterDTO $dto, PartParameter $entity = new PartParameter()): PartParameter + { + $entity->setName($dto->name); + $entity->setValueText($dto->value_text ?? ''); + $entity->setValueTypical($dto->value_typ); + $entity->setValueMin($dto->value_min); + $entity->setValueMax($dto->value_max); + $entity->setUnit($dto->unit ?? ''); + $entity->setSymbol($dto->symbol ?? ''); + $entity->setGroup($dto->group ?? ''); + + return $entity; + } + + /** + * Converts the given DTO to a Pricedetail entity. + * @param PriceDTO $dto + * @param Pricedetail $entity + * @return Pricedetail + */ + public function convertPrice(PriceDTO $dto, Pricedetail $entity = new Pricedetail()): Pricedetail + { + $entity->setMinDiscountQuantity($dto->minimum_discount_amount); + $entity->setPrice($dto->getPriceAsBigDecimal()); + $entity->setPriceRelatedQuantity($dto->price_related_quantity); + + //Currency TODO + if ($dto->currency_iso_code !== null) { + $entity->setCurrency($this->getCurrency($dto->currency_iso_code)); + } else { + $entity->setCurrency(null); + } + + return $entity; + } + + /** + * Converts the given DTO to an orderdetail entity. + */ + public function convertPurchaseInfo(PurchaseInfoDTO $dto, Orderdetail $entity = new Orderdetail()): Orderdetail + { + $entity->setSupplierpartnr($dto->order_number); + $entity->setSupplierProductUrl($dto->product_url ?? ''); + + $entity->setSupplier($this->getOrCreateEntityNonNull(Supplier::class, $dto->distributor_name)); + foreach ($dto->prices as $price) { + $entity->addPricedetail($this->convertPrice($price)); + } + + return $entity; + } + + /** + * Converts the given DTO to an Attachment entity. + * @param FileDTO $dto + * @param AttachmentType $type The type which should be used for the attachment + * @param PartAttachment $entity + * @return PartAttachment + */ + public function convertFile(FileDTO $dto, AttachmentType $type, PartAttachment $entity = new PartAttachment()): PartAttachment + { + $entity->setURL($dto->url); + + $entity->setAttachmentType($type); + + //If no name is given, try to extract the name from the URL + if ($dto->name === null || $dto->name === '' || $dto->name === '0') { + $entity->setName($this->getAttachmentNameFromURL($dto->url)); + } else { + $entity->setName($dto->name); + } + + return $entity; + } + + private function getAttachmentNameFromURL(string $url): string + { + return basename(parse_url($url, PHP_URL_PATH)); + } + + /** + * Converts a PartDetailDTO to a Part entity + * @param PartDetailDTO $dto + * @param Part $entity The part entity to fill + * @return Part + */ + public function convertPart(PartDetailDTO $dto, Part $entity = new Part()): Part + { + $entity->setName($dto->name); + $entity->setDescription($dto->description ?? ''); + $entity->setComment($dto->notes ?? ''); + + $entity->setMass($dto->mass); + + //Try to map the category to an existing entity (but never create a new one) + if ($dto->category) { + //@phpstan-ignore-next-line For some reason php does not recognize the repo returns a category + $entity->setCategory($this->em->getRepository(Category::class)->findForInfoProvider($dto->category)); + } + + $entity->setManufacturer($this->getOrCreateEntity(Manufacturer::class, $dto->manufacturer)); + $entity->setFootprint($this->getOrCreateEntity(Footprint::class, $dto->footprint)); + + $entity->setManufacturerProductNumber($dto->mpn ?? ''); + $entity->setManufacturingStatus($dto->manufacturing_status ?? ManufacturingStatus::NOT_SET); + $entity->setManufacturerProductURL($dto->manufacturer_product_url ?? ''); + + //Set the provider reference on the part + $entity->setProviderReference(InfoProviderReference::fromPartDTO($dto)); + + $param_groups = []; + + //Add parameters + foreach ($dto->parameters ?? [] as $parameter) { + $new_param = $this->convertParameter($parameter); + + $key = $new_param->getName() . '##' . $new_param->getGroup(); + //If there is already an parameter with the same name and group, rename the new parameter, by suffixing a number + if (count($param_groups[$key] ?? []) > 0) { + $new_param->setName($new_param->getName() . ' (' . (count($param_groups[$key]) + 1) . ')'); + } + + $param_groups[$key][] = $new_param; + + $entity->addParameter($new_param); + } + + //Add preview image + $image_type = $this->getImageType(); + + if ($dto->preview_image_url) { + $preview_image = new PartAttachment(); + $preview_image->setURL($dto->preview_image_url); + $preview_image->setName('Main image'); + $preview_image->setAttachmentType($image_type); + + $entity->addAttachment($preview_image); + $entity->setMasterPictureAttachment($preview_image); + } + + $attachments_grouped = []; + + //Add other images + $images = $this->files_unique($dto->images ?? []); + foreach ($images as $image) { + //Ensure that the image is not the same as the preview image + if ($image->url === $dto->preview_image_url) { + continue; + } + + $attachment = $this->convertFile($image, $image_type); + + $attachments_grouped[$attachment->getName()][] = $attachment; + if (count($attachments_grouped[$attachment->getName()] ?? []) > 1) { + $attachment->setName($attachment->getName() . ' (' . (count($attachments_grouped[$attachment->getName()]) + 1) . ')'); + } + + + $entity->addAttachment($attachment); + } + + //Add datasheets + $datasheet_type = $this->getDatasheetType(); + $datasheets = $this->files_unique($dto->datasheets ?? []); + foreach ($datasheets as $datasheet) { + $attachment = $this->convertFile($datasheet, $datasheet_type); + + $attachments_grouped[$attachment->getName()][] = $attachment; + if (count($attachments_grouped[$attachment->getName()] ?? []) > 1) { + $attachment->setName($attachment->getName() . ' (' . (count($attachments_grouped[$attachment->getName()])) . ')'); + } + + $entity->addAttachment($attachment); + } + + //Add orderdetails and prices + foreach ($dto->vendor_infos ?? [] as $vendor_info) { + $entity->addOrderdetail($this->convertPurchaseInfo($vendor_info)); + } + + return $entity; + } + + /** + * Returns the given array of files with all duplicates removed. + * @param FileDTO[] $files + * @return FileDTO[] + */ + private function files_unique(array $files): array + { + $unique = []; + //We use the URL and name as unique identifier. If two file DTO have the same URL and name, they are considered equal + //and get filtered out, if it already exists in the array + foreach ($files as $file) { + //Skip already existing files, to preserve the order. The second condition ensure that we keep the version with a name over the one without a name + if (isset($unique[$file->url]) && $unique[$file->url]->name !== null) { + continue; + } + $unique[$file->url] = $file; + } + + return array_values($unique); + } + + /** + * Get the existing entity of the given class with the given name or create it if it does not exist. + * If the name is null, null is returned. + * @template T of AbstractStructuralDBElement + * @param string $class + * @phpstan-param class-string $class + * @param string|null $name + * @return AbstractStructuralDBElement|null + * @phpstan-return T|null + */ + private function getOrCreateEntity(string $class, ?string $name): ?AbstractStructuralDBElement + { + //Fall through to make converting easier + if ($name === null) { + return null; + } + + return $this->getOrCreateEntityNonNull($class, $name); + } + + /** + * Get the existing entity of the given class with the given name or create it if it does not exist. + * @template T of AbstractStructuralDBElement + * @param string $class The class of the entity to create + * @phpstan-param class-string $class + * @param string $name The name of the entity to create + * @return AbstractStructuralDBElement + * @phpstan-return T + */ + private function getOrCreateEntityNonNull(string $class, string $name): AbstractStructuralDBElement + { + return $this->em->getRepository($class)->findOrCreateForInfoProvider($name); + } + + /** + * Returns the currency entity for the given ISO code or create it if it does not exist + * @param string $iso_code + * @return Currency|null + */ + private function getCurrency(string $iso_code): ?Currency + { + //Check if the currency is the base currency (then we can just return null) + if ($iso_code === $this->base_currency) { + return null; + } + + return $this->em->getRepository(Currency::class)->findOrCreateByISOCode($iso_code); + } + + /** + * Returns the attachment type used for datasheets or creates it if it does not exist + * @return AttachmentType + */ + private function getDatasheetType(): AttachmentType + { + /** @var AttachmentType $tmp */ + $tmp = $this->em->getRepository(AttachmentType::class)->findOrCreateForInfoProvider(self::TYPE_DATASHEETS_NAME); + + //If the entity was newly created, set the file filter + if ($tmp->getID() === null) { + $tmp->setFiletypeFilter('application/pdf'); + $tmp->setAlternativeNames(self::TYPE_DATASHEETS_NAME); + } + + return $tmp; + } + + /** + * Returns the attachment type used for datasheets or creates it if it does not exist + * @return AttachmentType + */ + private function getImageType(): AttachmentType + { + /** @var AttachmentType $tmp */ + $tmp = $this->em->getRepository(AttachmentType::class)->findOrCreateForInfoProvider(self::TYPE_IMAGE_NAME); + + //If the entity was newly created, set the file filter + if ($tmp->getID() === null) { + $tmp->setFiletypeFilter('image/*'); + $tmp->setAlternativeNames(self::TYPE_IMAGE_NAME); + } + + return $tmp; + } + +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/ExistingPartFinder.php b/src/Services/InfoProviderSystem/ExistingPartFinder.php new file mode 100644 index 00000000..762c1517 --- /dev/null +++ b/src/Services/InfoProviderSystem/ExistingPartFinder.php @@ -0,0 +1,77 @@ +findAllExisting($dto); + return count($results) > 0 ? $results[0] : null; + } + + /** + * Returns all existing local parts that match the search result. + * If no part is found, return an empty array. + * @param SearchResultDTO $dto + * @return Part[] + */ + public function findAllExisting(SearchResultDTO $dto): array + { + $qb = $this->em->getRepository(Part::class)->createQueryBuilder('part'); + $qb->select('part') + ->leftJoin('part.manufacturer', 'manufacturer') + ->Orwhere($qb->expr()->andX( + 'part.providerReference.provider_key = :providerKey', + 'part.providerReference.provider_id = :providerId', + )) + + //Or the manufacturer (allowing for alternative names) and the MPN (or part name) must match + ->OrWhere( + $qb->expr()->andX( + $qb->expr()->orX( + "ILIKE(manufacturer.name, :manufacturerName) = TRUE", + "ILIKE(manufacturer.alternative_names, :manufacturerAltNames) = TRUE", + ), + $qb->expr()->orX( + "ILIKE(part.manufacturer_product_number, :mpn) = TRUE", + "ILIKE(part.name, :mpn) = TRUE", + ) + ) + ) + ; + + $qb->setParameter('providerKey', $dto->provider_key); + $qb->setParameter('providerId', $dto->provider_id); + + $qb->setParameter('manufacturerName', $dto->manufacturer); + $qb->setParameter('manufacturerAltNames', '%'.$dto->manufacturer.'%'); + $qb->setParameter('mpn', $dto->mpn); + + return $qb->getQuery()->getResult(); + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/PartInfoRetriever.php b/src/Services/InfoProviderSystem/PartInfoRetriever.php new file mode 100644 index 00000000..0eb74642 --- /dev/null +++ b/src/Services/InfoProviderSystem/PartInfoRetriever.php @@ -0,0 +1,148 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem; + +use App\Entity\Parts\Part; +use App\Services\InfoProviderSystem\DTOs\PartDetailDTO; +use App\Services\InfoProviderSystem\DTOs\SearchResultDTO; +use App\Services\InfoProviderSystem\Providers\InfoProviderInterface; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Cache\ItemInterface; + +final class PartInfoRetriever +{ + + private const CACHE_DETAIL_EXPIRATION = 60 * 60 * 24 * 4; // 4 days + private const CACHE_RESULT_EXPIRATION = 60 * 60 * 24 * 4; // 7 days + + public function __construct(private readonly ProviderRegistry $provider_registry, + private readonly DTOtoEntityConverter $dto_to_entity_converter, private readonly CacheInterface $partInfoCache, + #[Autowire(param: "kernel.debug")] + private readonly bool $debugMode = false) + { + } + + /** + * Search for a keyword in the given providers. The results can be cached + * @param string[]|InfoProviderInterface[] $providers A list of providers to search in, either as provider keys or as provider instances + * @param string $keyword The keyword to search for + * @return SearchResultDTO[] The search results + */ + public function searchByKeyword(string $keyword, array $providers): array + { + $results = []; + + foreach ($providers as $provider) { + if (is_string($provider)) { + $provider = $this->provider_registry->getProviderByKey($provider); + } + + //Ensure that the provider is active + if (!$provider->isActive()) { + throw new \RuntimeException("The provider with key {$provider->getProviderKey()} is not active!"); + } + + if (!$provider instanceof InfoProviderInterface) { + throw new \InvalidArgumentException("The provider must be either a provider key or a provider instance!"); + } + + /** @noinspection SlowArrayOperationsInLoopInspection */ + $results = array_merge($results, $this->searchInProvider($provider, $keyword)); + } + + return $results; + } + + /** + * Search for a keyword in the given provider. The result is cached for 7 days. + * @return SearchResultDTO[] + */ + protected function searchInProvider(InfoProviderInterface $provider, string $keyword): array + { + //Generate key and escape reserved characters from the provider id + $escaped_keyword = urlencode($keyword); + return $this->partInfoCache->get("search_{$provider->getProviderKey()}_{$escaped_keyword}", function (ItemInterface $item) use ($provider, $keyword) { + //Set the expiration time + $item->expiresAfter(!$this->debugMode ? self::CACHE_RESULT_EXPIRATION : 1); + + return $provider->searchByKeyword($keyword); + }); + } + + /** + * Retrieves the details for a part from the given provider with the given (provider) part id. + * The result is cached for 4 days. + * @param string $provider_key + * @param string $part_id + * @return PartDetailDTO + */ + public function getDetails(string $provider_key, string $part_id): PartDetailDTO + { + $provider = $this->provider_registry->getProviderByKey($provider_key); + + //Ensure that the provider is active + if (!$provider->isActive()) { + throw new \RuntimeException("The provider with key $provider_key is not active!"); + } + + //Generate key and escape reserved characters from the provider id + $escaped_part_id = urlencode($part_id); + return $this->partInfoCache->get("details_{$provider_key}_{$escaped_part_id}", function (ItemInterface $item) use ($provider, $part_id) { + //Set the expiration time + $item->expiresAfter(!$this->debugMode ? self::CACHE_DETAIL_EXPIRATION : 1); + + return $provider->getDetails($part_id); + }); + } + + /** + * Retrieves the details for a part, based on the given search result. + * @param SearchResultDTO $search_result + * @return PartDetailDTO + */ + public function getDetailsForSearchResult(SearchResultDTO $search_result): PartDetailDTO + { + return $this->getDetails($search_result->provider_key, $search_result->provider_id); + } + + /** + * Converts the given DTO to a part entity + * @return Part + */ + public function dtoToPart(PartDetailDTO $search_result): Part + { + return $this->createPart($search_result->provider_key, $search_result->provider_id); + } + + /** + * Use the given details to create a part entity + */ + public function createPart(string $provider_key, string $part_id): Part + { + $details = $this->getDetails($provider_key, $part_id); + + return $this->dto_to_entity_converter->convertPart($details); + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/ProviderRegistry.php b/src/Services/InfoProviderSystem/ProviderRegistry.php new file mode 100644 index 00000000..f6c398d2 --- /dev/null +++ b/src/Services/InfoProviderSystem/ProviderRegistry.php @@ -0,0 +1,142 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem; + +use App\Services\InfoProviderSystem\Providers\InfoProviderInterface; + +/** + * This class keeps track of all registered info providers and allows to find them by their key + * @see \App\Tests\Services\InfoProviderSystem\ProviderRegistryTest + */ +final class ProviderRegistry +{ + /** + * @var InfoProviderInterface[] The info providers index by their keys + * @phpstan-var array + */ + private array $providers_by_name = []; + + /** + * @var InfoProviderInterface[] The enabled providers indexed by their keys + */ + private array $providers_active = []; + + /** + * @var InfoProviderInterface[] The disabled providers indexed by their keys + */ + private array $providers_disabled = []; + + /** + * @var bool Whether the registry has been initialized + */ + private bool $initialized = false; + + /** + * @param iterable $providers + */ + public function __construct(private readonly iterable $providers) + { + //We do not initialize the structures here, because we do not want to do unnecessary work + //We do this lazy on the first call to getProviders() + } + + /** + * Initializes the registry, we do this lazy to avoid unnecessary work, on construction, which is always called + * even if the registry is not used + * @return void + */ + private function initStructures(): void + { + foreach ($this->providers as $provider) { + $key = $provider->getProviderKey(); + + if (isset($this->providers_by_name[$key])) { + throw new \LogicException("Provider with key $key already registered"); + } + + $this->providers_by_name[$key] = $provider; + if ($provider->isActive()) { + $this->providers_active[$key] = $provider; + } else { + $this->providers_disabled[$key] = $provider; + } + } + + $this->initialized = true; + } + + /** + * Returns an array of all registered providers (enabled and disabled) + * @return InfoProviderInterface[] + */ + public function getProviders(): array + { + if (!$this->initialized) { + $this->initStructures(); + } + + return $this->providers_by_name; + } + + /** + * Returns the provider identified by the given key + * @param string $key + * @return InfoProviderInterface + * @throws \InvalidArgumentException If the provider with the given key does not exist + */ + public function getProviderByKey(string $key): InfoProviderInterface + { + if (!$this->initialized) { + $this->initStructures(); + } + + return $this->providers_by_name[$key] ?? throw new \InvalidArgumentException("Provider with key $key not found"); + } + + /** + * Returns an array of all active providers + * @return InfoProviderInterface[] + */ + public function getActiveProviders(): array + { + if (!$this->initialized) { + $this->initStructures(); + } + + return $this->providers_active; + } + + /** + * Returns an array of all disabled providers + * @return InfoProviderInterface[] + */ + public function getDisabledProviders(): array + { + if (!$this->initialized) { + $this->initStructures(); + } + + return $this->providers_disabled; + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/DigikeyProvider.php b/src/Services/InfoProviderSystem/Providers/DigikeyProvider.php new file mode 100644 index 00000000..b20368ce --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/DigikeyProvider.php @@ -0,0 +1,314 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Entity\Parts\ManufacturingStatus; +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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\Services\InfoProviderSystem\DTOs\SearchResultDTO; +use App\Services\OAuth\OAuthTokenManager; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +class DigikeyProvider implements InfoProviderInterface +{ + + private const OAUTH_APP_NAME = 'ip_digikey_oauth'; + + //Sandbox:'https://sandbox-api.digikey.com'; (you need to change it in knpu/oauth2-client-bundle config too) + private const BASE_URI = 'https://api.digikey.com'; + + private const VENDOR_NAME = 'DigiKey'; + + private readonly HttpClientInterface $digikeyClient; + + /** + * A list of parameter IDs, that are always assumed as text only and will never be converted to a numerical value. + * This allows to fix issues like #682, where the "Supplier Device Package" was parsed as a numerical value. + */ + private const TEXT_ONLY_PARAMETERS = [ + 1291, //Supplier Device Package + 39246, //Package / Case + ]; + + public function __construct(HttpClientInterface $httpClient, private readonly OAuthTokenManager $authTokenManager, + 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" => $clientId, + "X-DIGIKEY-Locale-Site" => $this->country, + "X-DIGIKEY-Locale-Language" => $this->language, + "X-DIGIKEY-Locale-Currency" => $this->currency, + "X-DIGIKEY-Customer-Id" => 0, + ] + ]); + } + + public function getProviderInfo(): array + { + return [ + 'name' => 'DigiKey', + 'description' => 'This provider uses the DigiKey API to search for parts.', + 'url' => 'https://www.digikey.com/', + 'oauth_app_name' => self::OAUTH_APP_NAME, + 'disabled_help' => 'Set the PROVIDER_DIGIKEY_CLIENT_ID and PROVIDER_DIGIKEY_SECRET env option and connect OAuth to enable.' + ]; + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::FOOTPRINT, + ProviderCapabilities::PICTURE, + ProviderCapabilities::DATASHEET, + ProviderCapabilities::PRICE, + ]; + } + + public function getProviderKey(): string + { + return 'digikey'; + } + + public function isActive(): bool + { + //The client ID has to be set and a token has to be available (user clicked connect) + return $this->clientId !== '' && $this->authTokenManager->hasToken(self::OAUTH_APP_NAME); + } + + public function searchByKeyword(string $keyword): array + { + $request = [ + 'Keywords' => $keyword, + 'Limit' => 50, + 'Offset' => 0, + 'FilterOptionsRequest' => [ + 'MarketPlaceFilter' => 'ExcludeMarketPlace', + ], + ]; + + //$response = $this->digikeyClient->request('POST', '/Search/v3/Products/Keyword', [ + $response = $this->digikeyClient->request('POST', '/products/v4/search/keyword', [ + 'json' => $request, + 'auth_bearer' => $this->authTokenManager->getAlwaysValidTokenString(self::OAUTH_APP_NAME) + ]); + + $response_array = $response->toArray(); + + + $result = []; + $products = $response_array['Products']; + foreach ($products as $product) { + foreach ($product['ProductVariations'] as $variation) { + $result[] = new SearchResultDTO( + provider_key: $this->getProviderKey(), + provider_id: $variation['DigiKeyProductNumber'], + name: $product['ManufacturerProductNumber'], + description: $product['Description']['DetailedDescription'] ?? $product['Description']['ProductDescription'], + category: $this->getCategoryString($product), + manufacturer: $product['Manufacturer']['Name'] ?? null, + mpn: $product['ManufacturerProductNumber'], + preview_image_url: $product['PhotoUrl'] ?? null, + manufacturing_status: $this->productStatusToManufacturingStatus($product['ProductStatus']['Id']), + provider_url: $product['ProductUrl'], + footprint: $variation['PackageType']['Name'], //Use the footprint field, to show the user the package type (Tape & Reel, etc., as digikey has many different package types) + ); + } + } + + return $result; + } + + public function getDetails(string $id): PartDetailDTO + { + $response = $this->digikeyClient->request('GET', '/products/v4/search/' . urlencode($id) . '/productdetails', [ + 'auth_bearer' => $this->authTokenManager->getAlwaysValidTokenString(self::OAUTH_APP_NAME) + ]); + + $response_array = $response->toArray(); + $product = $response_array['Product']; + + $footprint = null; + $parameters = $this->parametersToDTOs($product['Parameters'] ?? [], $footprint); + $media = $this->mediaToDTOs($id); + + // Get the price_breaks of the selected variation + $price_breaks = []; + foreach ($product['ProductVariations'] as $variation) { + if ($variation['DigiKeyProductNumber'] == $id) { + $price_breaks = $variation['StandardPricing'] ?? []; + break; + } + } + + return new PartDetailDTO( + provider_key: $this->getProviderKey(), + provider_id: $id, + name: $product['ManufacturerProductNumber'], + description: $product['Description']['DetailedDescription'] ?? $product['Description']['ProductDescription'], + category: $this->getCategoryString($product), + manufacturer: $product['Manufacturer']['Name'] ?? null, + mpn: $product['ManufacturerProductNumber'], + preview_image_url: $product['PhotoUrl'] ?? null, + manufacturing_status: $this->productStatusToManufacturingStatus($product['ProductStatus']['Id']), + provider_url: $product['ProductUrl'], + footprint: $footprint, + datasheets: $media['datasheets'], + images: $media['images'], + parameters: $parameters, + vendor_infos: $this->pricingToDTOs($price_breaks, $id, $product['ProductUrl']), + ); + } + + /** + * Converts the product status from the Digikey API to the manufacturing status used in Part-DB + * @param int|null $dk_status + * @return ManufacturingStatus|null + */ + private function productStatusToManufacturingStatus(?int $dk_status): ?ManufacturingStatus + { + // The V4 can use strings to get the status, but if you have changed the PROVIDER_DIGIKEY_LANGUAGE it will not match. + // Using the Id instead which should be fixed. + // + // The API is not well documented and the ID are not there yet, so were extracted using "trial and error". + // The 'Preliminary' id was not found in several categories so I was unable to extract it. Disabled for now. + return match ($dk_status) { + null => null, + 0 => ManufacturingStatus::ACTIVE, + 1 => ManufacturingStatus::DISCONTINUED, + 2, 4 => ManufacturingStatus::EOL, + 7 => ManufacturingStatus::NRFND, + //'Preliminary' => ManufacturingStatus::ANNOUNCED, + default => ManufacturingStatus::NOT_SET, + }; + } + + private function getCategoryString(array $product): string + { + $category = $product['Category']['Name']; + $sub_category = current($product['Category']['ChildCategories']); + + if ($sub_category) { + //Replace the ' - ' category separator with ' -> ' + $category = $category . ' -> ' . str_replace(' - ', ' -> ', $sub_category["Name"]); + } + + return $category; + } + + /** + * This function converts the "Parameters" part of the Digikey API response to an array of ParameterDTOs + * @param array $parameters + * @param string|null $footprint_name You can pass a variable by reference, where the name of the footprint will be stored + * @return ParameterDTO[] + */ + private function parametersToDTOs(array $parameters, string|null &$footprint_name = null): array + { + $results = []; + + $footprint_name = null; + + foreach ($parameters as $parameter) { + if ($parameter['ParameterId'] === 1291) { //Meaning "Manufacturer given footprint" + $footprint_name = $parameter['ValueText']; + } + + if (in_array(trim((string) $parameter['ValueText']), ['', '-'], true)) { + continue; + } + + //If the parameter was marked as text only, then we do not try to parse it as a numerical value + if (in_array($parameter['ParameterId'], self::TEXT_ONLY_PARAMETERS, true)) { + $results[] = new ParameterDTO(name: $parameter['ParameterText'], value_text: $parameter['ValueText']); + } else { //Otherwise try to parse it as a numerical value + $results[] = ParameterDTO::parseValueIncludingUnit($parameter['ParameterText'], $parameter['ValueText']); + } + } + + return $results; + } + + /** + * Converts the pricing (StandardPricing field) from the Digikey API to an array of PurchaseInfoDTOs + * @param array $price_breaks + * @param string $order_number + * @param string $product_url + * @return PurchaseInfoDTO[] + */ + private function pricingToDTOs(array $price_breaks, string $order_number, string $product_url): array + { + $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->currency); + } + + return [ + new PurchaseInfoDTO(distributor_name: self::VENDOR_NAME, order_number: $order_number, prices: $prices, product_url: $product_url) + ]; + } + + /** + * @param string $id The Digikey product number, to get the media for + * @return FileDTO[][] + * @phpstan-return array + */ + private function mediaToDTOs(string $id): array + { + $datasheets = []; + $images = []; + + $response = $this->digikeyClient->request('GET', '/products/v4/search/' . urlencode($id) . '/media', [ + 'auth_bearer' => $this->authTokenManager->getAlwaysValidTokenString(self::OAUTH_APP_NAME) + ]); + + $media_array = $response->toArray(); + + foreach ($media_array['MediaLinks'] as $media_link) { + $file = new FileDTO(url: $media_link['Url'], name: $media_link['Title']); + + switch ($media_link['MediaType']) { + case 'Datasheets': + $datasheets[] = $file; + break; + case 'Product Photos': + $images[] = $file; + break; + } + } + + return [ + 'datasheets' => $datasheets, + 'images' => $images, + ]; + } + +} diff --git a/src/Services/InfoProviderSystem/Providers/Element14Provider.php b/src/Services/InfoProviderSystem/Providers/Element14Provider.php new file mode 100644 index 00000000..b942b929 --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/Element14Provider.php @@ -0,0 +1,310 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Entity\Parts\ManufacturingStatus; +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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 Composer\CaBundle\CaBundle; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +class Element14Provider implements InfoProviderInterface +{ + + private const ENDPOINT_URL = 'https://api.element14.com/catalog/products'; + private const API_VERSION_NUMBER = '1.4'; + private const NUMBER_OF_RESULTS = 20; + + public const DISTRIBUTOR_NAME = 'Farnell'; + + private const COMPLIANCE_ATTRIBUTES = ['euEccn', 'hazardous', 'MSL', 'productTraceability', 'rohsCompliant', + 'rohsPhthalatesCompliant', 'SVHC', 'tariffCode', 'usEccn', 'hazardCode']; + + private readonly HttpClientInterface $element14Client; + + 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 + * + * This is a workaround until the issue is resolved in debian (or never). + * As this only affects this provider, this should have no negative impact and the CA bundle is still secure. + */ + $this->element14Client = $element14Client->withOptions([ + 'cafile' => CaBundle::getBundledCaBundlePath(), + ]); + } + + public function getProviderInfo(): array + { + return [ + 'name' => 'Farnell element14', + 'description' => 'This provider uses the Farnell element14 API to search for parts.', + 'url' => 'https://www.element14.com/', + 'disabled_help' => 'Configure the API key in the PROVIDER_ELEMENT14_KEY environment variable to enable.' + ]; + } + + public function getProviderKey(): string + { + return 'element14'; + } + + public function isActive(): bool + { + return $this->api_key !== ''; + } + + /** + * @param string $term + * @return PartDetailDTO[] + */ + private function queryByTerm(string $term): array + { + $response = $this->element14Client->request('GET', self::ENDPOINT_URL, [ + 'query' => [ + 'term' => $term, + 'storeInfo.id' => $this->store_id, + 'resultsSettings.offset' => 0, + 'resultsSettings.numberOfResults' => self::NUMBER_OF_RESULTS, + 'resultsSettings.responseGroup' => 'large', + 'callInfo.apiKey' => $this->api_key, + 'callInfo.responseDataFormat' => 'json', + 'versionNumber' => self::API_VERSION_NUMBER, + ], + ]); + + $arr = $response->toArray(); + if (isset($arr['keywordSearchReturn'])) { + $products = $arr['keywordSearchReturn']['products'] ?? []; + } elseif (isset($arr['premierFarnellPartNumberReturn'])) { + $products = $arr['premierFarnellPartNumberReturn']['products'] ?? []; + } else { + throw new \RuntimeException('Unknown response format'); + } + + $result = []; + + foreach ($products as $product) { + $result[] = new PartDetailDTO( + provider_key: $this->getProviderKey(), provider_id: $product['sku'], + name: $product['translatedManufacturerPartNumber'], + description: $this->displayNameToDescription($product['displayName'], $product['translatedManufacturerPartNumber']), + manufacturer: $product['vendorName'] ?? $product['brandName'] ?? null, + mpn: $product['translatedManufacturerPartNumber'], + preview_image_url: $this->toImageUrl($product['image'] ?? null), + manufacturing_status: $this->releaseStatusCodeToManufacturingStatus($product['releaseStatusCode'] ?? null), + provider_url: $product['productURL'], + notes: $product['productOverview']['description'] ?? null, + datasheets: $this->parseDataSheets($product['datasheets'] ?? null), + parameters: $this->attributesToParameters($product['attributes'] ?? null), + vendor_infos: $this->pricesToVendorInfo($product['sku'], $product['prices'] ?? [], $product['productURL']), + + ); + } + + return $result; + } + + /** + * @param array|null $datasheets + * @return FileDTO[]|null Array of FileDTOs + */ + private function parseDataSheets(?array $datasheets): ?array + { + if ($datasheets === null || count($datasheets) === 0) { + return null; + } + + $result = []; + foreach ($datasheets as $datasheet) { + $result[] = new FileDTO(url: $datasheet['url'], name: $datasheet['description']); + } + + return $result; + } + + private function toImageUrl(?array $image): ?string + { + if ($image === null || count($image) === 0) { + return null; + } + + //See Constructing an Image URL: https://partner.element14.com/docs/Product_Search_API_REST__Description + $locale = 'en_GB'; + if ($image['vrntPath'] === 'nio/') { + $locale = 'en_US'; + } + + return 'https://' . $this->store_id . '/productimages/standard/' . $locale . $image['baseName']; + } + + /** + * Converts the price array to a VendorInfoDTO array to be used in the PartDetailDTO + * @param string $sku + * @param array $prices + * @return array + */ + private function pricesToVendorInfo(string $sku, array $prices, string $product_url): array + { + $price_dtos = []; + + foreach ($prices as $price) { + $price_dtos[] = new PriceDTO( + minimum_discount_amount: $price['from'], + price: (string) $price['cost'], + currency_iso_code: $this->getUsedCurrency(), + includes_tax: false, + ); + } + + return [ + new PurchaseInfoDTO( + distributor_name: self::DISTRIBUTOR_NAME, + order_number: $sku, + prices: $price_dtos, + product_url: $product_url + ) + ]; + } + + public function getUsedCurrency(): string + { + //Decide based on the shop ID + 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', + 'ch.farnell.com' => 'CHF', + 'cpc.farnell.com', 'uk.farnell.com', 'onecall.farnell.com', 'export.farnell.com' => 'GBP', + 'il.farnell.com', 'www.newark.com' => 'USD', + 'hu.farnell.com' => 'HUF', + 'no.farnell.com' => 'NOK', + 'pl.farnell.com' => 'PLN', + 'ru.farnell.com' => 'RUB', + 'se.farnell.com' => 'SEK', + 'tr.farnell.com' => 'TRY', + 'canada.newark.com' => 'CAD', + 'mexico.newark.com' => 'MXN', + 'cn.element14.com' => 'CNY', + 'au.element14.com' => 'AUD', + 'nz.element14.com' => 'NZD', + 'hk.element14.com' => 'HKD', + 'sg.element14.com' => 'SGD', + 'my.element14.com' => 'MYR', + 'ph.element14.com' => 'PHP', + 'th.element14.com' => 'THB', + 'in.element14.com' => 'INR', + 'tw.element14.com' => 'TWD', + 'kr.element14.com' => 'KRW', + 'vn.element14.com' => 'VND', + default => throw new \RuntimeException('Unknown store ID: ' . $this->store_id) + }; + } + + /** + * @param array|null $attributes + * @return ParameterDTO[] + */ + private function attributesToParameters(?array $attributes): array + { + $result = []; + + foreach ($attributes as $attribute) { + $group = null; + + //Check if the attribute is a compliance attribute, they get assigned to the compliance group + if (in_array($attribute['attributeLabel'], self::COMPLIANCE_ATTRIBUTES, true)) { + $group = 'Compliance'; + } + + //tariffCode is a special case, we prepend a # to prevent conversion to float + if (in_array($attribute['attributeLabel'], ['tariffCode', 'hazardCode'], true)) { + $attribute['attributeValue'] = '#' . $attribute['attributeValue']; + } + + $result[] = ParameterDTO::parseValueField(name: $attribute['attributeLabel'], value: $attribute['attributeValue'], unit: $attribute['attributeUnit'] ?? null, group: $group); + } + + return $result; + } + + private function displayNameToDescription(string $display_name, string $mpn): string + { + //Try to find the position of the '-' after the MPN + $pos = strpos($display_name, $mpn . ' - '); + if ($pos === false) { + return $display_name; + } + + //Remove the MPN and the '-' from the display name + return substr($display_name, $pos + strlen($mpn) + 3); + } + + private function releaseStatusCodeToManufacturingStatus(?int $releaseStatusCode): ?ManufacturingStatus + { + if ($releaseStatusCode === null) { + return null; + } + + return match ($releaseStatusCode) { + 1 => ManufacturingStatus::ANNOUNCED, + 2,4 => ManufacturingStatus::ACTIVE, + 6 => ManufacturingStatus::EOL, + 7 => ManufacturingStatus::DISCONTINUED, + default => ManufacturingStatus::NOT_SET + }; + } + + public function searchByKeyword(string $keyword): array + { + return $this->queryByTerm('any:' . $keyword); + } + + public function getDetails(string $id): PartDetailDTO + { + $tmp = $this->queryByTerm('id:' . $id); + if (count($tmp) === 0) { + throw new \RuntimeException('No part found with ID ' . $id); + } + + if (count($tmp) > 1) { + throw new \RuntimeException('Multiple parts found with ID ' . $id); + } + + return $tmp[0]; + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::PICTURE, + ProviderCapabilities::DATASHEET, + ]; + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/InfoProviderInterface.php b/src/Services/InfoProviderSystem/Providers/InfoProviderInterface.php new file mode 100644 index 00000000..30821bad --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/InfoProviderInterface.php @@ -0,0 +1,81 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Services\InfoProviderSystem\DTOs\PartDetailDTO; +use App\Services\InfoProviderSystem\DTOs\SearchResultDTO; + +interface InfoProviderInterface +{ + + /** + * Get information about this provider + * + * @return array An associative array with the following keys (? means optional): + * - name: The (user friendly) name of the provider (e.g. "Digikey"), will be translated + * - description?: A short description of the provider (e.g. "Digikey is a ..."), will be translated + * - logo?: The logo of the provider (e.g. "digikey.png") + * - url?: The url of the provider (e.g. "https://www.digikey.com") + * - disabled_help?: A help text which is shown when the provider is disabled, explaining how to enable it + * - oauth_app_name?: The name of the OAuth app which is used for authentication (e.g. "ip_digikey_oauth"). If this is set a connect button will be shown + * + * @phpstan-return array{ name: string, description?: string, logo?: string, url?: string, disabled_help?: string, oauth_app_name?: string } + */ + public function getProviderInfo(): array; + + /** + * Returns a unique key for this provider, which will be saved into the database + * and used to identify the provider + * @return string A unique key for this provider (e.g. "digikey") + */ + public function getProviderKey(): string; + + /** + * Checks if this provider is enabled or not (meaning that it can be used for searching) + * @return bool True if the provider is enabled, false otherwise + */ + public function isActive(): bool; + + /** + * Searches for a keyword and returns a list of search results + * @param string $keyword The keyword to search for + * @return SearchResultDTO[] A list of search results + */ + public function searchByKeyword(string $keyword): array; + + /** + * Returns detailed information about the part with the given id + * @param string $id + * @return PartDetailDTO + */ + public function getDetails(string $id): PartDetailDTO; + + /** + * A list of capabilities this provider supports (which kind of data it can provide). + * Not every part have to contain all of these data, but the provider should be able to provide them in general. + * Currently, this list is purely informational and not used in functional checks. + * @return ProviderCapabilities[] + */ + public function getCapabilities(): array; +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/LCSCProvider.php b/src/Services/InfoProviderSystem/Providers/LCSCProvider.php new file mode 100755 index 00000000..d903a8dd --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/LCSCProvider.php @@ -0,0 +1,366 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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 Symfony\Component\HttpFoundation\Cookie; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +class LCSCProvider implements InfoProviderInterface +{ + + private const ENDPOINT_URL = 'https://wmsc.lcsc.com/ftps/wm'; + + public const DISTRIBUTOR_NAME = 'LCSC'; + + public function __construct(private readonly HttpClientInterface $lcscClient, private readonly string $currency, private readonly bool $enabled = true) + { + + } + + public function getProviderInfo(): array + { + return [ + 'name' => 'LCSC', + 'description' => 'This provider uses the (unofficial) LCSC API to search for parts.', + 'url' => 'https://www.lcsc.com/', + 'disabled_help' => 'Set PROVIDER_LCSC_ENABLED to 1 (or true) in your environment variable config.' + ]; + } + + public function getProviderKey(): string + { + return 'lcsc'; + } + + // This provider is always active + public function isActive(): bool + { + return $this->enabled; + } + + /** + * @param string $id + * @return PartDetailDTO + */ + private function queryDetail(string $id): PartDetailDTO + { + $response = $this->lcscClient->request('GET', self::ENDPOINT_URL . "/product/detail", [ + 'headers' => [ + 'Cookie' => new Cookie('currencyCode', $this->currency) + ], + 'query' => [ + 'productCode' => $id, + ], + ]); + + $arr = $response->toArray(); + $product = $arr['result'] ?? null; + + if ($product === null) { + throw new \RuntimeException('Could not find product code: ' . $id); + } + + return $this->getPartDetail($product); + } + + /** + * @param string $url + * @return String + */ + private function getRealDatasheetUrl(?string $url): string + { + if ($url !== null && trim($url) !== '' && preg_match("/^https:\/\/(datasheet\.lcsc\.com|www\.lcsc\.com\/datasheet)\/.*(C\d+)\.pdf$/", $url, $matches) > 0) { + if (preg_match("/^https:\/\/datasheet\.lcsc\.com\/lcsc\/(.*\.pdf)$/", $url, $rewriteMatches) > 0) { + $url = 'https://www.lcsc.com/datasheet/lcsc_datasheet_' . $rewriteMatches[1]; + } + $response = $this->lcscClient->request('GET', $url, [ + 'headers' => [ + 'Referer' => 'https://www.lcsc.com/product-detail/_' . $matches[2] . '.html' + ], + ]); + if (preg_match('/(previewPdfUrl): ?("[^"]+wmsc\.lcsc\.com[^"]+\.pdf")/', $response->getContent(), $matches) > 0) { + //HACKY: The URL string contains escaped characters like \u002F, etc. To decode it, the JSON decoding is reused + //See https://github.com/Part-DB/Part-DB-server/pull/582#issuecomment-2033125934 + $jsonObj = json_decode('{"' . $matches[1] . '": ' . $matches[2] . '}'); + $url = $jsonObj->previewPdfUrl; + } + } + return $url; + } + + /** + * @param string $term + * @return PartDetailDTO[] + */ + private function queryByTerm(string $term): array + { + $response = $this->lcscClient->request('GET', self::ENDPOINT_URL . "/search/global", [ + 'headers' => [ + 'Cookie' => new Cookie('currencyCode', $this->currency) + ], + 'query' => [ + 'keyword' => $term, + ], + ]); + + $arr = $response->toArray(); + + // Get products list + $products = $arr['result']['productSearchResultVO']['productList'] ?? []; + // Get product tip + $tipProductCode = $arr['result']['tipProductDetailUrlVO']['productCode'] ?? null; + + $result = []; + + // LCSC does not display LCSC codes in the search, instead taking you directly to the + // detailed product listing. It does so utilizing a product tip field. + // If product tip exists and there are no products in the product list try a detail query + if (count($products) === 0 && $tipProductCode !== null) { + $result[] = $this->queryDetail($tipProductCode); + } + + foreach ($products as $product) { + $result[] = $this->getPartDetail($product); + } + + return $result; + } + + /** + * Sanitizes a field by removing any HTML tags and other unwanted characters + * @param string|null $field + * @return string|null + */ + private function sanitizeField(?string $field): ?string + { + if ($field === null) { + return null; + } + + return strip_tags($field); + } + + + /** + * Takes a deserialized json object of the product and returns a PartDetailDTO + * @param array $product + * @return PartDetailDTO + */ + private function getPartDetail(array $product): PartDetailDTO + { + // Get product images in advance + $product_images = $this->getProductImages($product['productImages'] ?? null); + $product['productImageUrl'] ??= null; + + // If the product does not have a product image but otherwise has attached images, use the first one. + if (count($product_images) > 0) { + $product['productImageUrl'] ??= $product_images[0]->url; + } + + // LCSC puts HTML in footprints and descriptions sometimes randomly + $footprint = $product["encapStandard"] ?? null; + //If the footprint just consists of a dash, we'll assume it's empty + if ($footprint === '-') { + $footprint = null; + } + + //Build category by concatenating the catalogName and parentCatalogName + $category = $product['parentCatalogName'] ?? null; + if (isset($product['catalogName'])) { + $category = ($category ?? '') . ' -> ' . $product['catalogName']; + + // Replace the / with a -> for better readability + $category = str_replace('/', ' -> ', $category); + } + + return new PartDetailDTO( + provider_key: $this->getProviderKey(), + provider_id: $product['productCode'], + name: $product['productModel'], + description: $this->sanitizeField($product['productIntroEn']), + category: $this->sanitizeField($category ?? null), + manufacturer: $this->sanitizeField($product['brandNameEn'] ?? null), + mpn: $this->sanitizeField($product['productModel'] ?? null), + preview_image_url: $product['productImageUrl'], + manufacturing_status: null, + provider_url: $this->getProductShortURL($product['productCode']), + footprint: $this->sanitizeField($footprint), + datasheets: $this->getProductDatasheets($product['pdfUrl'] ?? null), + images: $product_images, + parameters: $this->attributesToParameters($product['paramVOList'] ?? []), + vendor_infos: $this->pricesToVendorInfo($product['productCode'], $this->getProductShortURL($product['productCode']), $product['productPriceList'] ?? []), + mass: $product['weight'] ?? null, + ); + } + + /** + * Converts the price array to a VendorInfoDTO array to be used in the PartDetailDTO + * @param string $sku + * @param string $url + * @param array $prices + * @return array + */ + private function pricesToVendorInfo(string $sku, string $url, array $prices): array + { + $price_dtos = []; + + foreach ($prices as $price) { + $price_dtos[] = new PriceDTO( + minimum_discount_amount: $price['ladder'], + price: $price['productPrice'], + currency_iso_code: $this->getUsedCurrency($price['currencySymbol']), + includes_tax: false, + ); + } + + return [ + new PurchaseInfoDTO( + distributor_name: self::DISTRIBUTOR_NAME, + order_number: $sku, + prices: $price_dtos, + product_url: $url, + ) + ]; + } + + /** + * Converts LCSC currency symbol to an ISO code. + * @param string $currency + * @return string + */ + private function getUsedCurrency(string $currency): string + { + //Decide based on the currency symbol + return match ($currency) { + 'US$', '$' => 'USD', + '€' => 'EUR', + 'A$' => 'AUD', + 'C$' => 'CAD', + '£' => 'GBP', + 'HK$' => 'HKD', + 'JP¥' => 'JPY', + 'RM' => 'MYR', + 'S$' => 'SGD', + '₽' => 'RUB', + 'kr' => 'SEK', + 'kr.' => 'DKK', + '₹' => 'INR', + //Fallback to the configured currency + default => $this->currency, + }; + } + + /** + * Returns a valid LCSC product short URL from product code + * @param string $product_code + * @return string + */ + private function getProductShortURL(string $product_code): string + { + return 'https://www.lcsc.com/product-detail/' . $product_code .'.html'; + } + + /** + * Returns a product datasheet FileDTO array from a single pdf url + * @param string $url + * @return FileDTO[] + */ + private function getProductDatasheets(?string $url): array + { + if ($url === null) { + return []; + } + + $realUrl = $this->getRealDatasheetUrl($url); + + return [new FileDTO($realUrl, null)]; + } + + /** + * Returns a FileDTO array with a list of product images + * @param array|null $images + * @return FileDTO[] + */ + private function getProductImages(?array $images): array + { + return array_map(static fn($image) => new FileDTO($image), $images ?? []); + } + + /** + * @param array|null $attributes + * @return ParameterDTO[] + */ + private function attributesToParameters(?array $attributes): array + { + $result = []; + + foreach ($attributes as $attribute) { + + //Skip this attribute if it's empty + if (in_array(trim((string) $attribute['paramValueEn']), ['', '-'], true)) { + continue; + } + + $result[] = ParameterDTO::parseValueIncludingUnit(name: $attribute['paramNameEn'], value: $attribute['paramValueEn'], group: null); + } + + return $result; + } + + public function searchByKeyword(string $keyword): array + { + return $this->queryByTerm($keyword); + } + + public function getDetails(string $id): PartDetailDTO + { + $tmp = $this->queryByTerm($id); + if (count($tmp) === 0) { + throw new \RuntimeException('No part found with ID ' . $id); + } + + if (count($tmp) > 1) { + throw new \RuntimeException('Multiple parts found with ID ' . $id); + } + + return $tmp[0]; + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::PICTURE, + ProviderCapabilities::DATASHEET, + ProviderCapabilities::PRICE, + ProviderCapabilities::FOOTPRINT, + ]; + } +} diff --git a/src/Services/InfoProviderSystem/Providers/MouserProvider.php b/src/Services/InfoProviderSystem/Providers/MouserProvider.php new file mode 100644 index 00000000..90bad263 --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/MouserProvider.php @@ -0,0 +1,350 @@ +. + */ + +/* +* This file provide an interface with the Mouser API V2 (also compatible with the V1) +* +* Copyright (C) 2023 Pasquale D'Orsi (https://github.com/pdo59) +* +* TODO: Obtain an API keys with an US Mouser user (currency $) and test the result of prices +* +*/ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Entity\Parts\ManufacturingStatus; +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 Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; + + +class MouserProvider implements InfoProviderInterface +{ + + private const ENDPOINT_URL = 'https://api.mouser.com/api/v2/search'; + + public const DISTRIBUTOR_NAME = 'Mouser'; + + public function __construct( + private readonly HttpClientInterface $mouserClient, + private readonly string $api_key, + private readonly string $language, + private readonly string $options, + private readonly int $search_limit + ) { + } + + public function getProviderInfo(): array + { + return [ + 'name' => 'Mouser', + 'description' => 'This provider uses the Mouser API to search for parts.', + 'url' => 'https://www.mouser.com/', + 'disabled_help' => 'Configure the API key in the PROVIDER_MOUSER_KEY environment variable to enable.' + ]; + } + + public function getProviderKey(): string + { + return 'mouser'; + } + + public function isActive(): bool + { + return $this->api_key !== ''; + } + + public function searchByKeyword(string $keyword): array + { + /* + SearchByKeywordRequest description: + Search parts by keyword and return a maximum of 50 parts. + + keyword* string + Used for keyword part search. + + records integer($int32) + Used to specify how many records the method should return. + + startingRecord integer($int32) + Indicates where in the total recordset the return set should begin. + From the startingRecord, the number of records specified will be returned up to the end of the recordset. + This is useful for paging through the complete recordset of parts matching keyword. + + + searchOptions string + Optional. + If not provided, the default is None. + Refers to options supported by the search engine. + Only one value at a time is supported. + Available options: None | Rohs | InStock | RohsAndInStock - can use string representations or integer IDs: 1[None] | 2[Rohs] | 4[InStock] | 8[RohsAndInStock]. + + searchWithYourSignUpLanguage string + Optional. + If not provided, the default is false. + Used when searching for keywords in the language specified when you signed up for Search API. + Can use string representation: true. + { + "SearchByKeywordRequest": { + "keyword": "BC557", + "records": 0, + "startingRecord": 0, + "searchOptions": "", + "searchWithYourSignUpLanguage": "" + } + } + */ + + $response = $this->mouserClient->request('POST', self::ENDPOINT_URL."/keyword", [ + 'query' => [ + 'apiKey' => $this->api_key, + ], + 'json' => [ + 'SearchByKeywordRequest' => [ + 'keyword' => $keyword, + 'records' => $this->search_limit, //self::NUMBER_OF_RESULTS, + 'startingRecord' => 0, + 'searchOptions' => $this->options, + 'searchWithYourSignUpLanguage' => $this->language, + ] + ], + ]); + + return $this->responseToDTOArray($response); + } + + public function getDetails(string $id): PartDetailDTO + { + /* + SearchByPartRequest description: + Search parts by part number and return a maximum of 50 parts. + + mouserPartNumber string + Used to search parts by the specific Mouser part number with a maximum input of 10 part numbers, separated by a pipe symbol for the search. + Each part number must be a minimum of 3 characters and a maximum of 40 characters. For example: 494-JANTX2N2222A|610-2N2222-TL|637-2N2222A + + partSearchOptions string + Optional. + If not provided, the default is None. Refers to options supported by the search engine. Only one value at a time is supported. + The following values are valid: None | Exact - can use string representations or integer IDs: 1[None] | 2[Exact] + + { + "SearchByPartRequest": { + "mouserPartNumber": "string", + "partSearchOptions": "string" + } + } + */ + + $response = $this->mouserClient->request('POST', self::ENDPOINT_URL."/partnumber", [ + 'query' => [ + 'apiKey' => $this->api_key, + ], + 'json' => [ + 'SearchByPartRequest' => [ + 'mouserPartNumber' => $id, + 'partSearchOptions' => 2 + ] + ], + ]); + $tmp = $this->responseToDTOArray($response); + + //Ensure that we have exactly one result + if (count($tmp) === 0) { + throw new \RuntimeException('No part found with ID '.$id); + } + + //Manually filter out the part with the correct ID + $tmp = array_filter($tmp, fn(PartDetailDTO $part) => $part->provider_id === $id); + if (count($tmp) === 0) { + throw new \RuntimeException('No part found with ID '.$id); + } + if (count($tmp) > 1) { + throw new \RuntimeException('Multiple parts found with ID '.$id); + } + + return reset($tmp); + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::PICTURE, + ProviderCapabilities::DATASHEET, + ProviderCapabilities::PRICE, + ]; + } + + + /** + * @param ResponseInterface $response + * @return PartDetailDTO[] + */ + private function responseToDTOArray(ResponseInterface $response): array + { + $arr = $response->toArray(); + + if (isset($arr['SearchResults'])) { + $products = $arr['SearchResults']['Parts'] ?? []; + } else { + throw new \RuntimeException('Unknown response format: ' .json_encode($arr, JSON_THROW_ON_ERROR)); + } + + $result = []; + foreach ($products as $product) { + + //Check if we have a valid product number. We assume that a product number, must have at least 4 characters + //Otherwise filter it out + if (strlen($product['MouserPartNumber']) < 4) { + continue; + } + + //Check if we have a mass field available + $mass = null; + if (isset($product['UnitWeightKg']['UnitWeight'])) { + $mass = (float) $product['UnitWeightKg']['UnitWeight']; + //The mass is given in kg, we want it in g + $mass *= 1000; + } + + + $result[] = new PartDetailDTO( + provider_key: $this->getProviderKey(), + provider_id: $product['MouserPartNumber'], + name: $product['ManufacturerPartNumber'], + description: $product['Description'], + category: $product['Category'], + manufacturer: $product['Manufacturer'], + mpn: $product['ManufacturerPartNumber'], + preview_image_url: $product['ImagePath'], + manufacturing_status: $this->releaseStatusCodeToManufacturingStatus( + $product['LifecycleStatus'] ?? null, + (int) ($product['AvailabilityInStock'] ?? 0) + ), + provider_url: $product['ProductDetailUrl'], + datasheets: $this->parseDataSheets($product['DataSheetUrl'] ?? null, + $product['MouserPartNumber'] ?? null), + vendor_infos: $this->pricingToDTOs($product['PriceBreaks'] ?? [], $product['MouserPartNumber'], + $product['ProductDetailUrl']), + mass: $mass, + ); + } + return $result; + } + + + private function parseDataSheets(?string $sheetUrl, ?string $sheetName): ?array + { + if ($sheetUrl === null || $sheetUrl === '' || $sheetUrl === '0') { + return null; + } + $result = []; + $result[] = new FileDTO(url: $sheetUrl, name: $sheetName); + return $result; + } + + /* + * Mouser API price is a string in the form "n[.,]nnn[.,] currency" + * then this convert it to a number + * Austria has a format like "€ 2,10" + */ + private function priceStrToFloat($val): float + { + //Remove any character that is not a number, dot or comma (like currency symbols) + $val = preg_replace('/[^0-9.,]/', '', $val); + + //Trim the string + $val = trim($val); + + //Convert commas to dots + $val = str_replace(",", ".", $val); + //Remove any dot that is not the last one (to avoid problems with thousands separators) + $val = preg_replace('/\.(?=.*\.)/', '', $val); + return (float)$val; + } + + /** + * Converts the pricing (StandardPricing field) from the Mouser API to an array of PurchaseInfoDTOs + * @param array $price_breaks + * @param string $order_number + * @param string $product_url + * @return PurchaseInfoDTO[] + */ + private function pricingToDTOs(array $price_breaks, string $order_number, string $product_url): array + { + $prices = []; + + foreach ($price_breaks as $price_break) { + $number = $this->priceStrToFloat($price_break['Price']); + $prices[] = new PriceDTO( + minimum_discount_amount: $price_break['Quantity'], + price: (string)$number, + currency_iso_code: $price_break['Currency'] + ); + } + + return [ + new PurchaseInfoDTO(distributor_name: self::DISTRIBUTOR_NAME, order_number: $order_number, prices: $prices, + product_url: $product_url) + ]; + } + + + /* Converts the product status from the MOUSER API to the manufacturing status used in Part-DB: + Factory Special Order - Ordine speciale in fabbrica + Not Recommended for New Designs - Non raccomandato per nuovi progetti + New Product - Nuovo prodotto + End of Life - Fine vita + -vuoto- - Attivo + + TODO: Probably need to review the values of field Lifecyclestatus + */ + /** + * Converts the lifecycle status from the Mouser API to a ManufacturingStatus + * @param string|null $productStatus The lifecycle status from the Mouser API + * @param int $availableInStock The number of parts available in stock + * @return ManufacturingStatus|null + */ + private function releaseStatusCodeToManufacturingStatus(?string $productStatus, int $availableInStock = 0): ?ManufacturingStatus + { + $tmp = match ($productStatus) { + null => null, + "New Product" => ManufacturingStatus::ANNOUNCED, + "Not Recommended for New Designs" => ManufacturingStatus::NRFND, + "Factory Special Order", "Obsolete" => ManufacturingStatus::DISCONTINUED, + "End of Life" => ManufacturingStatus::EOL, + default => ManufacturingStatus::ACTIVE, + }; + + //If the part would be assumed to be announced, check if it is in stock, then it is active + if ($tmp === ManufacturingStatus::ANNOUNCED && $availableInStock > 0) { + $tmp = ManufacturingStatus::ACTIVE; + } + + return $tmp; + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/OEMSecretsProvider.php b/src/Services/InfoProviderSystem/Providers/OEMSecretsProvider.php new file mode 100644 index 00000000..ccf800f8 --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/OEMSecretsProvider.php @@ -0,0 +1,1471 @@ +. + */ + +/** + * OEMSecretsProvider Class + * + * This class is responsible for interfacing with the OEMSecrets API (version 3.0.1) to retrieve and manage information + * about electronic components. Since the API does not provide a unique identifier for each part, the class aggregates + * results based on "part_number" and "manufacturer_id". It also transforms unstructured descriptions into structured + * parameters and aggregates datasheets and images provided by multiple distributors. + * The OEMSecrets API returns results by matching the provided part number not only with the original part number + * but also with the distributor-assigned part number and/or the part description. + * + * Key functionalities: + * - Aggregation of results based on part_number and manufacturer_id to ensure unique identification of parts. + * - Conversion of component descriptions into structured parameters (ParameterDTO) for better clarity and searchability. + * - Aggregation of datasheets and images from multiple distributors, ensuring that all available resources are collected. + * - Price handling, including filtering of distributors that offer zero prices, controlled by the `zero_price` configuration variable. + * - A sorting algorithm that first prioritizes exact matches with the keyword, followed by alphabetical sorting of items + * with the same prefix (e.g., "BC546", "BC546A", "BC546B"), and finally, sorts by either manufacturer or completeness + * based on the specified criteria. + * - Sorting the distributors: + * 1. Environment's country_code first. + * 2. Region matching environment's country_code, prioritizing "Global" ('XX'). + * 3. Distributors with null country_code/region are placed last. + * 4. Final fallback is alphabetical sorting by region and country_code. + * + * Configuration: + * - The ZERO_PRICE variable must be set in the `.env.local` file. If is set to 0, the class will skip distributors + * that do not offer valid prices for the components. + * - Currency and country settings can also be specified for localized pricing and distributor filtering. + * - Generation of parameters: if 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" + * - Sorting is guided by SORT_CRITERIA variable. 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. + * Distributors within each item are further sorted based on country_code and region, following the rules explained + * in the previous comment. + * + * Data Handling: + * - The class divides and stores component information across multiple session arrays: + * - `basic_info_results`: Stores basic information like name, description, manufacturer, and category. + * - `datasheets_results`: Aggregates datasheets provided by distributors, ensuring no duplicates. + * - `images_results`: Collects images of components from various sources, preventing duplication. + * - `parameters_results`: Extracts and stores key parameters parsed from component descriptions. + * - `purchase_info_results`: Contains detailed purchasing information like pricing and distributor details. + * + * - By splitting the data into separate session arrays, the class optimizes memory usage and simplifies retrieval + * of specific details without loading the entire dataset at once. + * + * Technical Details: + * - Uses OEMSecrets API (version 3.0.1) to retrieve component data. + * - Data processing includes sanitizing input, avoiding duplicates, and dynamically adjusting information as new distributor + * data becomes available (e.g., adding missing datasheets or parameters from subsequent API responses). + * + * @package App\Services\InfoProviderSystem\Providers + * @author Pasquale D'Orsi (https://github.com/pdo59) + * @version 1.2.0 + * @since 2024 August + */ + + +declare(strict_types=1); + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Entity\Parts\ManufacturingStatus; +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\Services\InfoProviderSystem\DTOs\ParameterDTO; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Psr\Cache\CacheItemPoolInterface; + + +class OEMSecretsProvider implements InfoProviderInterface +{ + + private const ENDPOINT_URL = 'https://oemsecretsapi.com/partsearch'; + + public function __construct( + private readonly HttpClientInterface $oemsecretsClient, + 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 + ) + { + } + + private array $countryNameToCodeMap = [ + 'Andorra' => 'AD', + 'United Arab Emirates' => 'AE', + 'Antarctica' => 'AQ', + 'Argentina' => 'AR', + 'Austria' => 'AT', + 'Australia' => 'AU', + 'Belgium' => 'BE', + 'Bolivia' => 'BO', + 'Brazil' => 'BR', + 'Bouvet Island' => 'BV', + 'Belarus' => 'BY', + 'Canada' => 'CA', + 'Switzerland' => 'CH', + 'Chile' => 'CL', + 'China' => 'CN', + 'Colombia' => 'CO', + 'Czech Republic' => 'CZ', + 'Germany' => 'DE', + 'Denmark' => 'DK', + 'Ecuador' => 'EC', + 'Estonia' => 'EE', + 'Western Sahara' => 'EH', + 'Spain' => 'ES', + 'Finland' => 'FI', + 'Falkland Islands' => 'FK', + 'Faroe Islands' => 'FO', + 'France' => 'FR', + 'United Kingdom' => 'GB', + 'Georgia' => 'GE', + 'French Guiana' => 'GF', + 'Guernsey' => 'GG', + 'Gibraltar' => 'GI', + 'Greenland' => 'GL', + 'Greece' => 'GR', + 'South Georgia and the South Sandwich Islands' => 'GS', + 'Guyana' => 'GY', + 'Hong Kong' => 'HK', + 'Heard Island and McDonald Islands' => 'HM', + 'Croatia' => 'HR', + 'Hungary' => 'HU', + 'Ireland' => 'IE', + 'Isle of Man' => 'IM', + 'India' => 'IN', + 'Iceland' => 'IS', + 'Italy' => 'IT', + 'Jamaica' => 'JM', + 'Japan' => 'JP', + 'North Korea' => 'KP', + 'South Korea' => 'KR', + 'Kazakhstan' => 'KZ', + 'Liechtenstein' => 'LI', + 'Sri Lanka' => 'LK', + 'Lithuania' => 'LT', + 'Luxembourg' => 'LU', + 'Monaco' => 'MC', + 'Moldova' => 'MD', + 'Montenegro' => 'ME', + 'North Macedonia' => 'MK', + 'Malta' => 'MT', + 'Netherlands' => 'NL', + 'Norway' => 'NO', + 'New Zealand' => 'NZ', + 'Peru' => 'PE', + 'Philippines' => 'PH', + 'Poland' => 'PL', + 'Portugal' => 'PT', + 'Paraguay' => 'PY', + 'Romania' => 'RO', + 'Serbia' => 'RS', + 'Russia' => 'RU', + 'Solomon Islands' => 'SB', + 'Sudan' => 'SD', + 'Sweden' => 'SE', + 'Singapore' => 'SG', + 'Slovenia' => 'SI', + 'Svalbard and Jan Mayen' => 'SJ', + 'Slovakia' => 'SK', + 'San Marino' => 'SM', + 'Somalia' => 'SO', + 'Suriname' => 'SR', + 'Syria' => 'SY', + 'Eswatini' => 'SZ', + 'Turks and Caicos Islands' => 'TC', + 'French Southern Territories' => 'TF', + 'Togo' => 'TG', + 'Thailand' => 'TH', + 'Tajikistan' => 'TJ', + 'Tokelau' => 'TK', + 'Turkmenistan' => 'TM', + 'Tunisia' => 'TN', + 'Tonga' => 'TO', + 'Turkey' => 'TR', + 'Trinidad and Tobago' => 'TT', + 'Tuvalu' => 'TV', + 'Taiwan' => 'TW', + 'Tanzania' => 'TZ', + 'Ukraine' => 'UA', + 'Uganda' => 'UG', + 'United States Minor Outlying Islands' => 'UM', + 'United States' => 'US', + 'Uruguay' => 'UY', + 'Uzbekistan' => 'UZ', + 'Vatican City' => 'VA', + 'Venezuela' => 'VE', + 'British Virgin Islands' => 'VG', + 'U.S. Virgin Islands' => 'VI', + 'Vietnam' => 'VN', + 'Vanuatu' => 'VU', + 'Wallis and Futuna' => 'WF', + 'Yemen' => 'YE', + 'South Africa' => 'ZA', + 'Zambia' => 'ZM', + 'Zimbabwe' => 'ZW', + 'Global' => 'XX' + ]; + + private array $distributorCountryCodes = []; + private array $countryCodeToRegionMap = []; + + /** + * Get information about this provider + * + * @return array An associative array with the following keys (? means optional): + * - name: The (user friendly) name of the provider (e.g. "Digikey"), will be translated + * - description?: A short description of the provider (e.g. "Digikey is a ..."), will be translated + * - logo?: The logo of the provider (e.g. "digikey.png") + * - url?: The url of the provider (e.g. "https://www.digikey.com") + * - disabled_help?: A help text which is shown when the provider is disabled, explaining how to enable it + * - oauth_app_name?: The name of the OAuth app which is used for authentication (e.g. "ip_digikey_oauth"). If this is set a connect button will be shown + * + * @phpstan-return array{ name: string, description?: string, logo?: string, url?: string, disabled_help?: string, oauth_app_name?: string } + */ + public function getProviderInfo(): array + { + return [ + 'name' => 'OEMSecrets', + 'description' => 'This provider uses the OEMSecrets API to search for parts.', + 'url' => 'https://www.oemsecrets.com/', + 'disabled_help' => 'Configure the API key in the PROVIDER_OEMSECRETS_KEY environment variable to enable.' + ]; + } + /** + * Returns a unique key for this provider, which will be saved into the database + * and used to identify the provider + * @return string A unique key for this provider (e.g. "digikey") + */ + public function getProviderKey(): string + { + return 'oemsecrets'; + } + + /** + * Checks if this provider is enabled or not (meaning that it can be used for searching) + * @return bool True if the provider is enabled, false otherwise + */ + public function isActive(): bool + { + return $this->api_key !== ''; + } + + + /** + * Searches for products based on a given keyword using the OEMsecrets Part Search API. + * + * This method queries the OEMsecrets API to retrieve distributor data for the provided part number, + * including details such as pricing, compliance, and inventory. It supports both direct API queries + * and debugging with local JSON files. The results are processed, cached, and then sorted based + * on the keyword and specified criteria. + * + * @param string $keyword The part number to search for + * @return array An array of processed product details, sorted by relevance and additional criteria. + * + * @throws \Exception If the JSON file used for debugging is not found or contains errors. + */ + public function searchByKeyword(string $keyword): array + { + /* + oemsecrets Part Search API 3.0.1 + + "https://oemsecretsapi.com/partsearch? + searchTerm=BC547 + &apiKey=icawpb0bspoo2c6s64uv4vpdfp2vgr7e27bxw0yct2bzh87mpl027x353uelpq2x + ¤cy=EUR + &countryCode=IT" + + partsearch description: + Use the Part Search API to find distributor data for a full or partial manufacturer + part number including part details, pricing, compliance and inventory. + + Required Parameter Format Description + searchTerm string Part number you are searching for + apiKey string Your unique API key provided to you by OEMsecrets + + Additional Parameter Format Description + countryCode string The country you want to output for + currency string / array The currency you want the prices to be displayed as + + To display the output for GB and to view prices in USD, add [ countryCode=GB ] and [ currency=USD ] + as seen below: + oemsecretsapi.com/partsearch?apiKey=abcexampleapikey123&searchTerm=bd04&countryCode=GB¤cy=USD + + To view prices in both USD and GBP add [ currency[]=USD¤cy[]=GBP ] + oemsecretsapi.com/partsearch?searchTerm=bd04&apiKey=abcexampleapikey123¤cy[]=USD¤cy[]=GBP + + */ + + + // Activate this block when querying the real APIs + //------------------ + + $response = $this->oemsecretsClient->request('GET', self::ENDPOINT_URL, [ + 'query' => [ + 'searchTerm' => $keyword, + 'apiKey' => $this->api_key, + 'currency' => $this->currency, + 'countryCode' => $this->country_code, + ], + ]); + + $response_array = $response->toArray(); + //------------------*/ + + // Or activate this block when we use json file for debugging + /*/------------------ + $jsonFilePath = ''; + if (!file_exists($jsonFilePath)) { + throw new \Exception("JSON file not found."); + } + $jsonContent = file_get_contents($jsonFilePath); + $response_array = json_decode($jsonContent, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception("JSON file decode failed: " . json_last_error_msg()); + } + //------------------*/ + + $products = $response_array['stock'] ?? []; + + $results = []; + $basicInfoResults = []; + $datasheetsResults = []; + $imagesResults = []; + $parametersResults = []; + $purchaseInfoResults = []; + + foreach ($products as $product) { + if (!isset($product['part_number'], $product['manufacturer'])) { + continue; // Skip invalid product entries + } + $provider_id = $this->generateProviderId($product['part_number'], $product['manufacturer']); + + $partDetailDTO = $this->processBatch( + $product, + $provider_id, + $basicInfoResults, + $datasheetsResults, + $imagesResults, + $parametersResults, + $purchaseInfoResults + ); + + if ($partDetailDTO !== null) { + $results[$provider_id] = $partDetailDTO; + $cacheKey = $this->getCacheKey($provider_id); + $cacheItem = $this->partInfoCache->getItem($cacheKey); + $cacheItem->set($partDetailDTO); + $cacheItem->expiresAfter(3600 * 24); + $this->partInfoCache->save($cacheItem); + } + } + + //Force garbage collection to free up memory + gc_collect_cycles(); + + // Sort of the results + $this->sortResultsData($results, $keyword); + + //Force garbage collection to free up memory + gc_collect_cycles(); + + return $results; + + } + + /** + * Generates a cache key for storing part details based on the provided provider ID. + * + * This method creates a unique cache key by prefixing the provider ID with 'part_details_' + * and hashing the provider ID using MD5 to ensure a consistent and compact key format. + * + * @param string $provider_id The unique identifier of the provider or part. + * @return string The generated cache key. + */ + private function getCacheKey(string $provider_id): string { + return 'oemsecrets_part_' . md5($provider_id); + } + + + /** + * Retrieves detailed information about the part with the given provider ID from the cache. + * + * This method checks the cache for the details of the specified part. If the details are + * found in the cache, they are returned. If not, an exception is thrown indicating that + * the details could not be found. + * + * @param string $id The unique identifier of the provider or part. + * @return PartDetailDTO The detailed information about the part. + * + * @throws \Exception If no details are found for the given provider ID. + */ + public function getDetails(string $id): PartDetailDTO + { + $cacheKey = $this->getCacheKey($id); + $cacheItem = $this->partInfoCache->getItem($cacheKey); + + if ($cacheItem->isHit()) { + return $cacheItem->get(); + } + //If we have no cached result yet, we extract the part number (first part of our ID) and search for it + $partNumber = explode('|', $id)[0]; + + //The searchByKeyword method will write the results to cache, so we can just try it again afterwards + $this->searchByKeyword($partNumber); + + $cacheItem = $this->partInfoCache->getItem($cacheKey); + if ($cacheItem->isHit()) { + return $cacheItem->get(); + } + + // If the details still are not found in the cache, throw an exception + throw new \RuntimeException("Details not found for provider_id $id"); + } + + + /** + * A list of capabilities this provider supports (which kind of data it can provide). + * Not every part have to contain all of these data, but the provider should be able to provide them in general. + * Currently, this list is purely informational and not used in functional checks. + * @return ProviderCapabilities[] + */ + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::PICTURE, + ProviderCapabilities::DATASHEET, + ProviderCapabilities::PRICE, + ]; + } + + + /** + * Processes a single product and updates arrays for basic information, datasheets, images, parameters, + * and purchase information. Aggregates and organizes data received for a specific `part_number` and `manufacturer_id`. + * Distributors within the product are also sorted based on country_code and region. + * + * @param array $product The product data received from the OEMSecrets API. + * @param string $provider_id A string that contains the unique key created for the part + * @param array &$basicInfoResults Array containing the basic product information (e.g., name, description, category). + * @param array &$datasheetsResults Array containing datasheets collected from various distributors for the product. + * @param array &$imagesResults Array containing images of the product collected from various distributors. + * @param array &$parametersResults Array containing technical parameters extracted from the product descriptions. + * @param array &$purchaseInfoResults Array containing purchase information, including distributors and pricing details. + * + * @return PartDetailDTO|null Returns a PartDetailDTO object if the product is processed successfully, otherwise null. + * + * @throws \Exception If a required key in the product data is missing or if there is an issue creating the DTO. + * + * @see createOrUpdateBasicInfo() Creates or updates the basic product information. + * @see getPrices() Extracts the pricing information for the product. + * @see parseDataSheets() Parses and prevents duplication of datasheets. + * @see getImages() Extracts and avoids duplication of images. + * @see getParameters() Extracts technical parameters from the product description. + * @see createPurchaseInfoDTO() Creates a PurchaseInfoDTO containing distributor and price information. + * + * @note Distributors within the product are sorted by country_code and region: + * 1. Distributors with the environment's country_code come first. + * 2. Distributors in the same region as the environment's country_code are next, + * with "Global" ('XX') prioritized within this region. + * 3. Distributors with null country_code or region are placed last. + * 4. Remaining distributors are sorted alphabetically by region and country_code. + + */ + private function processBatch( + array $product, + string $provider_id, + array &$basicInfoResults, + array &$datasheetsResults, + array &$imagesResults, + array &$parametersResults, + array &$purchaseInfoResults + ): ?PartDetailDTO + { + if (!isset($product['manufacturer'], $product['part_number'])) { + throw new \InvalidArgumentException("Missing required product data: 'manufacturer' or 'part_number'"); + } + + // Retrieve the country_code associated with the distributor and store it in the $distributorCountryCodes array. + $distributorCountry = $product['distributor']['distributor_country'] ?? null; + $distributorName = $product['distributor']['distributor_name'] ?? null; + $distributorRegion = $product['distributor']['distributor_region'] ?? null; + + if ($distributorCountry && $distributorName) { + $countryCode = $this->mapCountryNameToCode($distributorCountry); + if ($countryCode) { + $this->distributorCountryCodes[$distributorName] = $countryCode; + } + if ($distributorRegion) { + $this->countryCodeToRegionMap[$countryCode] = $distributorRegion; + } + } + + // Truncate the description and handle notes + $thenotes = ''; + $description = $product['description'] ?? ''; + if (strlen($description) > 100) { + $thenotes = $description; // Save the complete description + $description = substr($description, 0, 100) . '...'; // Truncate the description + } + + // Extract prices + $priceDTOs = $this->getPrices($product); + if (empty($priceDTOs) && (int)$this->zero_price === 0) { + return null; // Skip products without valid prices + } + + $existingBasicInfo = isset($basicInfoResults[$provider_id]) && is_array($basicInfoResults[$provider_id]) + ? $basicInfoResults[$provider_id] + : []; + + $basicInfoResults[$provider_id] = $this->createOrUpdateBasicInfo( + $provider_id, + $product, + $description, + $thenotes, + $existingBasicInfo + ); + + // Update images, datasheets, and parameters + + $newDatasheets = $this->parseDataSheets($product['datasheet_url'] ?? null, null, $datasheetsResults[$provider_id] ?? []); + if ($newDatasheets !== null) { + $datasheetsResults[$provider_id] = array_merge($datasheetsResults[$provider_id] ?? [], $newDatasheets); + } + + $imagesResults[$provider_id] = $this->getImages($product, $imagesResults[$provider_id] ?? []); + if ($this->set_param == 1) { + $parametersResults[$provider_id] = $this->getParameters($product, $parametersResults[$provider_id] ?? []); + } else { + $parametersResults[$provider_id] = []; + } + + // Handle purchase information + $currentDistributor = $this->createPurchaseInfoDTO($product, $priceDTOs, $purchaseInfoResults[$provider_id] ?? []); + if ($currentDistributor !== null) { + $purchaseInfoResults[$provider_id][] = $currentDistributor; + } + + // If there is data in $purchaseInfoResults, sort it before creating the PartDetailDTO + if (!empty($purchaseInfoResults[$provider_id])) { + usort($purchaseInfoResults[$provider_id], function ($a, $b) { + $nameA = $a->distributor_name; + $nameB = $b->distributor_name; + + $countryCodeA = $this->distributorCountryCodes[$nameA] ?? null; + $countryCodeB = $this->distributorCountryCodes[$nameB] ?? null; + + $regionA = $this->countryCodeToRegionMap[$countryCodeA] ?? ''; + $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->country_code] ?? ''; + + // Convert to string before comparison to avoid mixed types + $countryCodeA = (string) $countryCodeA; + $countryCodeB = (string) $countryCodeB; + $regionA = (string) $regionA; + $regionB = (string) $regionB; + + + // Step 0: If either country code is null, place it at the end + if ($countryCodeA === '' || $regionA === '') { + return 1; // Metti A dopo B + } elseif ($countryCodeB === '' || $regionB === '') { + return -1; // Metti B dopo A + } + + // Step 1: country_code from the environment + if ($countryCodeA === $this->country_code && $countryCodeB !== $this->country_code) { + return -1; + } elseif ($countryCodeA !== $this->country_code && $countryCodeB === $this->country_code) { + return 1; + } + + // Step 2: Sort by environment's region, prioritizing "Global" (XX) + if ($regionA === $regionForEnvCountry && $regionB !== $regionForEnvCountry) { + return -1; + } elseif ($regionA !== $regionForEnvCountry && $regionB === $regionForEnvCountry) { + return 1; + } + + // Step 3: If regions are the same, prioritize "Global" (XX) + if ($regionA === $regionB) { + if ($countryCodeA === 'XX' && $countryCodeB !== 'XX') { + return -1; + } elseif ($countryCodeA !== 'XX' && $countryCodeB === 'XX') { + return 1; + } + } + + // Step 4: Alphabetical sorting by region and country_code + $regionComparison = strcasecmp($regionA , $regionB); + if ($regionComparison !== 0) { + return $regionComparison; + } + + // Alphabetical sorting as a fallback + return strcasecmp($countryCodeA, $countryCodeB); + }); + } + // Convert the gathered data into a PartDetailDTO + + $partDetailDTO = new PartDetailDTO( + provider_key: $basicInfoResults[$provider_id]['provider_key'], + provider_id: $provider_id, + name: $basicInfoResults[$provider_id]['name'], + description: $basicInfoResults[$provider_id]['description'], + category: $basicInfoResults[$provider_id]['category'], + manufacturer: $basicInfoResults[$provider_id]['manufacturer'], + mpn: $basicInfoResults[$provider_id]['mpn'], + preview_image_url: $basicInfoResults[$provider_id]['preview_image_url'], + manufacturing_status: $basicInfoResults[$provider_id]['manufacturing_status'], + provider_url: $basicInfoResults[$provider_id]['provider_url'], + footprint: $basicInfoResults[$provider_id]['footprint'] ?? null, + notes: $basicInfoResults[$provider_id]['notes'] ?? null, + datasheets: $datasheetsResults[$provider_id] ?? [], + images: $imagesResults[$provider_id] ?? [], + parameters: $parametersResults[$provider_id] ?? [], + vendor_infos: $purchaseInfoResults[$provider_id] ?? [] + ); + + return $partDetailDTO; + } + + + /** + * Extracts pricing information from the product data, converts it to PriceDTO objects, + * and returns them as an array. + * + * @param array{ + * prices?: array>, + * source_currency?: string + * } $product The product data from the OEMSecrets API containing price details. + * + * @return PriceDTO[] Array of PriceDTO objects representing different price tiers for the product. + */ + private function getPrices(array $product): array + { + $prices = $product['prices'] ?? []; + $sourceCurrency = $product['source_currency'] ?? null; + $priceDTOs = []; + + // Flag to check if we have added prices in the preferred currency + $foundPreferredCurrency = false; + + if (is_array($prices)) { + // Step 1: Check if prices exist in the preferred currency + if (isset($prices[$this->currency]) && is_array($prices[$this->currency])) { + $priceDetails = $prices[$this->currency]; + foreach ($priceDetails as $priceDetail) { + if ( + is_array($priceDetail) && + isset($priceDetail['unit_break'], $priceDetail['unit_price']) && + is_numeric($priceDetail['unit_break']) && + is_string($priceDetail['unit_price']) && + $priceDetail['unit_price'] !== "0.0000" + ) { + $priceDTOs[] = new PriceDTO( + minimum_discount_amount: (float)$priceDetail['unit_break'], + price: (string)$priceDetail['unit_price'], + currency_iso_code: $this->currency, + includes_tax: false, + price_related_quantity: 1.0 + ); + $foundPreferredCurrency = true; + } + } + } + + // Step 2: If no prices in the preferred currency, use source currency + if (!$foundPreferredCurrency && $sourceCurrency && isset($prices[$sourceCurrency]) && is_array($prices[$sourceCurrency])) { + $priceDetails = $prices[$sourceCurrency]; + foreach ($priceDetails as $priceDetail) { + if ( + is_array($priceDetail) && + isset($priceDetail['unit_break'], $priceDetail['unit_price']) && + is_numeric($priceDetail['unit_break']) && + is_string($priceDetail['unit_price']) && + $priceDetail['unit_price'] !== "0.0000" + ) { + $priceDTOs[] = new PriceDTO( + minimum_discount_amount: (float)$priceDetail['unit_break'], + price: (string)$priceDetail['unit_price'], + currency_iso_code: $sourceCurrency, + includes_tax: false, + price_related_quantity: 1.0 + ); + } + } + } + } + + return $priceDTOs; + } + + + /** + * Retrieves product images provided by the distributor. Prevents duplicates based on the image name. + * @param array{ + * image_url?: string + * } $product The product data from the OEMSecrets API containing image URLs. + * @param FileDTO[] $existingImages Optional. Existing images for the product to avoid duplicates. + * + * @return FileDTO[] Array of FileDTO objects representing the product images. + */ + private function getImages(array $product, array $existingImages = []): array + { + $images = $existingImages; + $imageUrl = $product['image_url'] ?? null; + + if ($imageUrl) { + $imageName = basename(parse_url($imageUrl, PHP_URL_PATH)); + if (!in_array($imageName, array_column($images, 'name'), true)) { + $images[] = new FileDTO(url: $imageUrl, name: $imageName); + } + } + return $images; + } + + /** + * Extracts technical parameters from the product description, ensures no duplicates, and returns them as an array. + * + * @param array{ + * description?: string + * } $product The product data from the OEMSecrets API containing product descriptions. + * @param ParameterDTO[] $existingParameters Optional. Existing parameters for the product to avoid duplicates. + * + * @return ParameterDTO[] Array of ParameterDTO objects representing technical parameters extracted from the product description. + */ + private function getParameters(array $product, array $existingParameters = []): array + { + $parameters = $existingParameters; + $description = $product['description'] ?? ''; + + // Logic to extract parameters from the description + $extractedParameters = $this->parseDescriptionToParameters($description) ?? []; + + foreach ($extractedParameters as $newParam) { + $isDuplicate = false; + foreach ($parameters as $existingParam) { + if ($existingParam->name === $newParam->name) { + $isDuplicate = true; + break; + } + } + if (!$isDuplicate) { + $parameters[] = $newParam; + } + } + + return $parameters; + } + + /** + * Creates a PurchaseInfoDTO object containing distributor and pricing information for a product. + * Ensures that the distributor name is valid and prices are available. + * + * @param array{ + * distributor?: array{ + * distributor_name?: string + * }, + * sku?: string, + * source_part_number: string, + * buy_now_url?: string, + * lead_time_weeks?: mixed + * } $product The product data from the OEMSecrets API. + * @param PriceDTO[] $priceDTOs Array of PriceDTO objects representing pricing tiers. + * @param PurchaseInfoDTO[] $existingPurchaseInfos Optional. Existing purchase information for the product to avoid duplicates. + * + * @return PurchaseInfoDTO|null A PurchaseInfoDTO object containing the distributor information, or null if invalid. + */ + private function createPurchaseInfoDTO(array $product, array $priceDTOs, array $existingPurchaseInfos = []): ?PurchaseInfoDTO + { + $distributor_name = $product['distributor']['distributor_name'] ?? null; + if ($distributor_name && !empty($priceDTOs)) { + $sku = isset($product['sku']) ? (string)$product['sku'] : null; + $order_number_base = $sku ?: (string)$product['source_part_number']; + $order_number = $order_number_base; + + // Remove duplicates from the quantity/price tiers + $uniquePriceDTOs = []; + foreach ($priceDTOs as $priceDTO) { + $key = $priceDTO->minimum_discount_amount . '-' . $priceDTO->price; + $uniquePriceDTOs[$key] = $priceDTO; + } + $priceDTOs = array_values($uniquePriceDTOs); + + // Differentiate $order_number if duplicated + if ($this->isDuplicateOrderNumber($order_number, $distributor_name, $existingPurchaseInfos)) { + $lead_time_weeks = isset($product['lead_time_weeks']) ? (string)$product['lead_time_weeks'] : ''; + $order_number = $order_number_base . '-' . $lead_time_weeks; + + // If there is still a duplicate after adding lead_time_weeks + $counter = 1; + while ($this->isDuplicateOrderNumber($order_number, $distributor_name, $existingPurchaseInfos)) { + $order_number = $order_number_base . '-' . $lead_time_weeks . '-' . $counter; + $counter++; + } + } + + return new PurchaseInfoDTO( + distributor_name: $distributor_name, + order_number: $order_number, + prices: $priceDTOs, + product_url: $this->unwrapURL($product['buy_now_url'] ?? null) + ); + } + return null; // Return null if no valid distributor exists + } + + /** + * Checks if an order number already exists for a given distributor in the existing purchase infos. + * + * @param string $order_number The order number to check. + * @param string $distributor_name The name of the distributor. + * @param PurchaseInfoDTO[] $existingPurchaseInfos The existing purchase information to check against. + * @return bool True if a duplicate order number is found, otherwise false. + */ + private function isDuplicateOrderNumber(string $order_number, string $distributor_name, array $existingPurchaseInfos): bool + { + foreach ($existingPurchaseInfos as $purchaseInfo) { + if ($purchaseInfo->distributor_name === $distributor_name && $purchaseInfo->order_number === $order_number) { + return true; + } + } + return false; + } + + /** + * Creates or updates the basic information of a product, including the description, category, manufacturer, + * and other metadata. This function manages the PartDetailDTO creation or update. + * + * @param string $provider_id The unique identifier for the product based on part_number and manufacturer. + * * @param array{ + * part_number: string, + * category: string, + * manufacturer: string, + * source_part_number: string, + * image_url?: string, + * life_cycle?: string, + * quantity_in_stock?: int + * } $product The product data from the OEMSecrets API. + * @param string $description The truncated description for the product. + * @param string $thenotes The full description saved as notes for the product. + * + * @return array The updated or newly created PartDetailDTO containing basic product information. + */ + private function createOrUpdateBasicInfo( + string $provider_id, + array $product, + string $description, + string $thenotes, + ?array $existingBasicInfo + ): array { + // If there is no existing basic info array, we create a new one + if (is_null($existingBasicInfo)) { + return [ + 'provider_key' => $this->getProviderKey(), + 'provider_id' => $provider_id, + 'name' => $product['part_number'], + 'description' => $description, + 'category' => $product['category'], + 'manufacturer' => $product['manufacturer'], + 'mpn' => $product['source_part_number'], + 'preview_image_url' => $product['image_url'] ?? null, + 'manufacturing_status' => $this->releaseStatusCodeToManufacturingStatus( + $product['life_cycle'] ?? null, + (int)($product['quantity_in_stock'] ?? 0) + ), + 'provider_url' => $this->generateInquiryUrl($product['part_number']), + 'notes' => $thenotes, + 'footprint' => null + ]; + } + + // Update fields only if empty or undefined, with additional check for preview_image_url + return [ + 'provider_key' => $existingBasicInfo['provider_key'] ?? $this->getProviderKey(), + 'provider_id' => $existingBasicInfo['provider_id'] ?? $provider_id, + 'name' => $existingBasicInfo['name'] ?? $product['part_number'], + // Update description if it's null/empty + 'description' => !empty($existingBasicInfo['description']) + ? $existingBasicInfo['description'] + : $description, + // Update category if it's null/empty + 'category' => !empty($existingBasicInfo['category']) + ? $existingBasicInfo['category'] + : $product['category'], + 'manufacturer' => $existingBasicInfo['manufacturer'] ?? $product['manufacturer'], + 'mpn' => $existingBasicInfo['mpn'] ?? $product['source_part_number'], + 'preview_image_url' => !empty($existingBasicInfo['preview_image_url']) + ? $existingBasicInfo['preview_image_url'] + : ($product['image_url'] ?? null), + 'manufacturing_status' => !empty($existingBasicInfo['manufacturing_status']) + ? $existingBasicInfo['manufacturing_status'] + : $this->releaseStatusCodeToManufacturingStatus( + $product['life_cycle'] ?? null, + (int)($product['quantity_in_stock'] ?? 0) + ), + 'provider_url' => $existingBasicInfo['provider_url'] ?? $this->generateInquiryUrl($product['part_number']), // ?? $product['buy_now_url'], + 'notes' => $existingBasicInfo['notes'] ?? $thenotes, + 'footprint' => null + ]; + } + + /** + * Parses the datasheet URL and returns an array of FileDTO objects representing the datasheets. + * If the datasheet name is not provided, it attempts to extract the file name from the URL. + * If multiple datasheets with the same default name are encountered, the function appends a + * numeric suffix to ensure uniqueness. + * The query parameter used to extract the event link can be customized. + * + * URL Requirements: + * - The URL should be a valid URL string. + * - The URL can include a query parameter named "event_link", which contains a sub-URL where the + * actual datasheet file name is located (e.g., a link to a PDF file). + * - If "event_link" is not present, the function attempts to extract the file name directly from + * the URL path. + * - The URL path should ideally end with a valid file extension (e.g., .pdf, .doc, .xls, etc.). + * + * Example 1: + * Given URL: `https://example.com/datasheet.php?event_link=https%3A%2F%2Ffiles.example.com%2Fdatasheet.pdf` + * Extracted name: `datasheet.pdf` + * + * Example 2: + * Given URL: `https://example.com/files/datasheet.pdf` + * Extracted name: `datasheet.pdf` + * + * Example 3 (default name fallback): + * Given URL: `https://example.com/files/noextensionfile` + * Extracted name: `datasheet.pdf` + * + * @param string|null $sheetUrl The URL of the datasheet. + * @param string|null $sheetName The optional name of the datasheet. If null, the name is extracted from the URL. + * @param array $existingDatasheets The array of existing datasheets to check for duplicates. + * + * @return FileDTO[]|null Returns an array containing the new datasheet if unique, or null if the datasheet is a duplicate or invalid. + * + * @see FileDTO Used to create datasheet objects with a URL and name. + */ + private function parseDataSheets(?string $sheetUrl, ?string $sheetName, array $existingDatasheets = []): ?array + { + if ($sheetUrl === null || $sheetUrl === '' || $sheetUrl === '0') { + return null; + } + + //Unwrap the URL (remove analytics part) + $sheetUrl = $this->unwrapURL($sheetUrl); + + // If the datasheet name is not provided, extract it from the URL + if ($sheetName === null) { + $urlPath = parse_url($sheetUrl, PHP_URL_PATH); + if ($urlPath === false) { + throw new \RuntimeException("Invalid URL path: $sheetUrl"); + } + + // If "event_link" does not exist, try to extract the name from the main URL path + $sheetName = basename($urlPath); + if (!str_contains($sheetName, '.') || !preg_match('/\.(pdf|doc|docx|xls|xlsx|ppt|pptx)$/i', $sheetName)) { + // If the name does not have a valid extension, assign a default name + $sheetName = 'datasheet_' . uniqid('', true) . '.pdf'; + } + } + + // Create an array of existing file names + $existingNames = array_map(static function ($existingDatasheet) { + return $existingDatasheet->name; + }, $existingDatasheets); + + // Check if the name already exists + if (in_array($sheetName, $existingNames, true)) { + // The name already exists, so do not add the datasheet + return null; + } + + // Create an array with the datasheet data if it does not already exist + $result = []; + $result[] = new FileDTO(url: $sheetUrl, name: $sheetName); + return $result; + } + + /** + * Converts the lifecycle status from the API to a ManufacturingStatus + * - "Factory Special Order" / "Ordine speciale in fabbrica" + * - "Not Recommended for New Designs" / "Non raccomandato per nuovi progetti" + * - "New Product" / "Nuovo prodotto" (if availableInStock > 0 else ANNOUNCED) + * - "End of Life" / "Fine vita" + * - vuoto / "Attivo" + * + * @param string|null $productStatus The lifecycle status from the Mouser API. Expected values are: + * - "Factory Special Order" + * - "Not Recommended for New Designs" + * - "New Product" + * - "End of Life" + * - "Obsolete" + * @param int $availableInStock The number of parts available in stock. + * @return ManufacturingStatus|null Returns the corresponding ManufacturingStatus or null if the status is unknown. + * + * @todo Probably need to review the values of field Lifecyclestatus. + */ + private function releaseStatusCodeToManufacturingStatus(?string $productStatus, int $availableInStock = 0): ?ManufacturingStatus + { + $tmp = match ($productStatus) { + null => null, + "New Product" => ManufacturingStatus::ANNOUNCED, + "Not Recommended for New Designs" => ManufacturingStatus::NRFND, + "Factory Special Order", "Obsolete" => ManufacturingStatus::DISCONTINUED, + "End of Life" => ManufacturingStatus::EOL, + default => null, //ManufacturingStatus::ACTIVE, + }; + + //If the part would be assumed to be announced, check if it is in stock, then it is active + if ($tmp === ManufacturingStatus::ANNOUNCED && $availableInStock > 0) { + $tmp = ManufacturingStatus::ACTIVE; + } + + return $tmp; + } + + /** + * Parses the given product description to extract parameters and convert them into `ParameterDTO` objects. + * If the description contains only a single `:`, it is considered unstructured and ignored. + * The function processes the description by searching for key-value pairs in the format `name: value`, + * ignoring any parts of the description that do not follow this format. Parameters are split using either + * `;` or `,` as separators. + * + * The extraction logic handles typical values, ranges, units, and textual information from the description. + * If the description is empty or cannot be processed into valid parameters, the function returns null. + * + * @param string|null $description The description text from which parameters are to be extracted. + * + * @return ParameterDTO[]|null Returns an array of `ParameterDTO` objects if parameters are successfully extracted, + * or null if no valid parameters can be extracted from the description. + */ + private function parseDescriptionToParameters(?string $description): ?array + { + // If the description is null or empty, return null + if ($description === null || trim($description) === '') { + return null; + } + + // If the description contains only a single ':', return null + if (substr_count($description, ':') === 1) { + return null; + } + + // Array to store parsed parameters + $parameters = []; + + // Split the description using the ';' separator + $parts = preg_split('/[;,]/', $description); //explode(';', $description); + + // Process each part of the description + foreach ($parts as $part) { + $part = trim($part); + + // Check if the part contains a key-value structure + if (str_contains($part, ':')) { + [$name, $value] = explode(':', $part, 2); + $name = trim($name); + $value = trim($value); + + // Attempt to parse the value, handling ranges, units, and additional information + $parsedValue = $this->customParseValueIncludingUnit($name, $value); + + // If the value was successfully parsed, create a ParameterDTO + if ($parsedValue) { + // Convert numeric values to float + $value_typ = isset($parsedValue['value_typ']) ? (float)$parsedValue['value_typ'] : null; + $value_min = isset($parsedValue['value_min']) ? (float)$parsedValue['value_min'] : null; + $value_max = isset($parsedValue['value_max']) ? (float)$parsedValue['value_max'] : null; + + $parameters[] = new ParameterDTO( + name: $parsedValue['name'], + value_text: $parsedValue['value_text'] ?? null, + value_typ: $value_typ, + value_min: $value_min, + value_max: $value_max, + unit: $parsedValue['unit'] ?? null, // Add extracted unit + symbol: $parsedValue['symbol'] ?? null // Add extracted symbol + ); + } + } + } + + return !empty($parameters) ? $parameters : null; + } + + /** + * Parses a value that may contain both a numerical value and its corresponding unit. + * This function splits the value into its numerical part and its unit, handling cases + * where the value includes symbols, ranges, or additional text. It also detects and + * processes plus/minus ranges, typical values, and other special formats. + * + * Example formats that can be handled: + * - "2.5V" + * - "±5%" + * - "1-10A" + * - "2.5 @text" + * - "~100 Ohm" + * + * @param string $value The value string to be parsed, which may contain a number, unit, or both. + * + * @return array An associative array with parsed components: + * - 'name' => string (the name of the parameter) + * - 'value_typ' => float|null (the typical or parsed value) + * - 'range_min' => float|null (the minimum value if it's a range) + * - 'range_max' => float|null (the maximum value if it's a range) + * - 'value_text' => string|null (any additional text or symbol) + * - 'unit' => string|null (the detected or default unit) + * - 'symbol' => string|null (any special symbol or additional text) + */ + private function customParseValueIncludingUnit(string $name, string $value): array + { + // Parse using logic for units, ranges, and other elements + $result = [ + 'name' => $name, + 'value_typ' => null, + 'value_min' => null, + 'value_max' => null, + 'value_text' => null, + 'unit' => null, + 'symbol' => null, + ]; + + // Trim any whitespace from the value + $value = trim($value); + + // Handle ranges and plus/minus signs + if (str_contains($value, '...') || str_contains($value, '~') || str_contains($value, '±')) { + // Handle ranges + $value = str_replace(['...', '~'], '...', $value); // Normalize range separators + $rangeParts = preg_split('/\s*[\.\~]\s*/', $value); + + if (count($rangeParts) === 2) { + // Splitting the values and units + $parsedMin = $this->customSplitIntoValueAndUnit($rangeParts[0]); + $parsedMax = $this->customSplitIntoValueAndUnit($rangeParts[1]); + + // Assigning the parsed values + $result['value_min'] = $parsedMin['value_typ']; + $result['value_max'] = $parsedMax['value_typ']; + + // Determine the unit + $result['unit'] = $parsedMax['unit'] ?? $parsedMin['unit']; + } + + } elseif (str_contains($value, '@')) { + // If we find "@", we treat it as additional textual information + [$numericValue, $textValue] = explode('@', $value); + $result['value_typ'] = (float) $numericValue; + $result['value_text'] = trim($textValue); + } else { + // Check if the value is numeric with a unit + if (preg_match('/^([\+\-]?\d+(\.\d+)?)([a-zA-Z%°]+)?$/u', $value, $matches)) { + // It is a number with or without a unit + $result['value_typ'] = (float) $matches[1]; + $result['unit'] = $matches[3] ?? null; + } else { + // It's not a number, so we treat it as text + $result['value_text'] = $value; + } + } + + return $result; + } + + /** + * Splits a string into a numerical value and its associated unit. The function attempts to separate + * a number from its unit, handling common formats where the unit follows the number (e.g., "50kHz", "10A"). + * The function assumes the unit is the non-numeric part of the string. + * + * Example formats that can be handled: + * - "100 Ohm" + * - "10 MHz" + * - "5kV" + * - "±5%" + * + * @param string $value1 The input string containing both a numerical value and a unit. + * @param string|null $value2 Optional. A second value string, typically used for ranges (e.g., "10-20A"). + * + * @return array An associative array with the following elements: + * - 'value_typ' => string|null The first numerical part of the string. + * - 'unit' => string|null The unit part of the string, or null if no unit is detected. + * - 'value_min' => string|null The minimum value in a range, if applicable. + * - 'value_max' => string|null The maximum value in a range, if applicable. + */ + private function customSplitIntoValueAndUnit(string $value1, ?string $value2 = null): array + { + // Separate numbers and units (basic parsing handling) + $unit = null; + $value_typ = null; + + // Search for the number + unit pattern + if (preg_match('/^([\+\-]?\d+(\.\d+)?)([a-zA-Z%°]+)?$/u', $value1, $matches)) { + $value_typ = $matches[1]; + $unit = $matches[3] ?? null; + } + + $result = [ + 'value_typ' => $value_typ, + 'unit' => $unit, + ]; + + if ($value2 !== null) { + if (preg_match('/^([\+\-]?\d+(\.\d+)?)([a-zA-Z%°]+)?$/u', $value2, $matches2)) { + $result['value_min'] = $value_typ; + $result['value_max'] = $matches2[1]; + $result['unit'] = $matches2[3] ?? $unit; // If both values have the same unit, we keep it + } + } + + return $result; + } + + /** + * Generates the API URL to fetch product information for the specified part number from OEMSecrets. + * Ensures that the base API URL and any query parameters are properly formatted. + * + * @param string $partNumber The part number to include in the URL. + * @param string $oemInquiry The inquiry path for the OEMSecrets API, with a default value of 'compare/'. + * This parameter represents the specific API endpoint to query. + * + * @return string The complete provider URL including the base provider URL, the inquiry path, and the part number. + * + * Example: + * If the base URL is "https://www.oemsecrets.com/", the inquiry path is "compare/", and the part number is "NE555", + * the resulting URL will be: "https://www.oemsecrets.com/compare/NE555" + */ + private function generateInquiryUrl(string $partNumber, string $oemInquiry = 'compare/'): string + { + $baseUrl = rtrim($this->getProviderInfo()['url'], '/') . '/'; + $inquiryPath = trim($oemInquiry, '/') . '/'; + $encodedPartNumber = urlencode(trim($partNumber)); + return $baseUrl . $inquiryPath . $encodedPartNumber; + } + + /** + * Sorts the results data array based on the specified search keyword and sorting criteria. + * The sorting process involves multiple phases: + * 1. Exact match with the search keyword. + * 2. Prefix match with the search keyword. + * 3. Alphabetical order of the suffix following the keyword. + * 4. Optional sorting by completeness or manufacturer based on the sort criteria. + * + * The sorting criteria (`sort_criteria`) is an environment variable configured in the `.env.local` file: + * PROVIDER_OEMSECRETS_SORT_CRITERIA + * It determines the final sorting phase: + * - 'C': Sort by completeness. + * - 'M': Sort by manufacturer. + * + * @param array $resultsData The array of result objects to be sorted. Each object should have 'name' and 'manufacturer' properties. + * @param string $searchKeyword The search keyword used for sorting the results. + * + * @return void + */ + private function sortResultsData(array &$resultsData, string $searchKeyword): void + { + // If the SORT_CRITERIA is not 'C' or 'M', do not sort + if ($this->sort_criteria !== 'C' && $this->sort_criteria !== 'M') { + return; + } + usort($resultsData, function ($a, $b) use ($searchKeyword) { + $nameA = trim($a->name); + $nameB = trim($b->name); + + // First phase: Sorting by exact match with the keyword + $exactMatchA = strcasecmp($nameA, $searchKeyword) === 0; + $exactMatchB = strcasecmp($nameB, $searchKeyword) === 0; + + if ($exactMatchA && !$exactMatchB) { + return -1; + } elseif (!$exactMatchA && $exactMatchB) { + return 1; + } + + // Second phase: Sorting by prefix (name starting with the keyword) + $startsWithKeywordA = stripos($nameA, $searchKeyword) === 0; + $startsWithKeywordB = stripos($nameB, $searchKeyword) === 0; + + if ($startsWithKeywordA && !$startsWithKeywordB) { + return -1; + } elseif (!$startsWithKeywordA && $startsWithKeywordB) { + return 1; + } + + if ($startsWithKeywordA && $startsWithKeywordB) { + // Alphabetical sorting of suffixes + $suffixA = substr($nameA, strlen($searchKeyword)); + $suffixB = substr($nameB, strlen($searchKeyword)); + $suffixComparison = strcasecmp($suffixA, $suffixB); + + if ($suffixComparison !== 0) { + return $suffixComparison; + } + } + + // Final sorting: by completeness or manufacturer, if necessary + if ($this->sort_criteria === 'C') { + return $this->compareByCompleteness($a, $b); + } elseif ($this->sort_criteria === 'M') { + return strcasecmp($a->manufacturer, $b->manufacturer); + } + + }); + } + + /** + * Compares two objects based on their "completeness" score. + * The completeness score is calculated by the `calculateCompleteness` method, which assigns a numeric score + * based on the amount of information (such as parameters, datasheets, images, etc.) available for each object. + * The comparison is done in descending order, giving priority to the objects with higher completeness. + * + * @param object $a The first object to compare. + * @param object $b The second object to compare. + * + * @return int A negative value if $b is more complete than $a, zero if they are equally complete, + * or a positive value if $a is more complete than $b. + */ + private function compareByCompleteness(object $a, object $b): int + { + // Calculate the completeness score for each object + $completenessA = $this->calculateCompleteness($a); + $completenessB = $this->calculateCompleteness($b); + + // Sort in descending order by completeness (higher score is better) + return $completenessB - $completenessA; + } + + + /** + * Calculates a "completeness" score for a given part object based on the presence and count of various attributes. + * The completeness score is used to prioritize parts that have more detailed information. + * + * The score is calculated as follows: + * - Counts the number of elements in the `parameters`, `datasheets`, `images`, and `vendor_infos` arrays. + * - Adds 1 point for the presence of `category`, `description`, `mpn`, `preview_image_url`, and `footprint`. + * - Adds 1 or 2 points based on the presence or absence of `manufacturing_status` (higher score if `null`). + * + * @param object $part The part object for which the completeness score is calculated. The object is expected + * to have properties like `parameters`, `datasheets`, `images`, `vendor_infos`, `category`, + * `description`, `mpn`, `preview_image_url`, `footprint`, and `manufacturing_status`. + * + * @return int The calculated completeness score, with a higher score indicating more complete information. + */ + private function calculateCompleteness(object $part): int + { + // Counts the number of elements in each field that can have multiple values + $paramsCount = is_array($part->parameters) ? count($part->parameters) : 0; + $datasheetsCount = is_array($part->datasheets) ? count($part->datasheets) : 0; + $imagesCount = is_array($part->images) ? count($part->images) : 0; + $vendorInfosCount = is_array($part->vendor_infos) ? count($part->vendor_infos) : 0; + + // Check for the presence of single fields and assign a score + $categoryScore = !empty($part->category) ? 1 : 0; + $descriptionScore = !empty($part->description) ? 1 : 0; + $mpnScore = !empty($part->mpn) ? 1 : 0; + $previewImageScore = !empty($part->preview_image_url) ? 1 : 0; + $footprintScore = !empty($part->footprint) ? 1 : 0; + + // Weight for manufacturing_status: higher if null + $manufacturingStatusScore = is_null($part->manufacturing_status) ? 2 : 1; + + // Sum the counts and scores to obtain a completeness score + return $paramsCount + + $datasheetsCount + + $imagesCount + + $vendorInfosCount + + $categoryScore + + $descriptionScore + + $mpnScore + + $previewImageScore + + $footprintScore + + $manufacturingStatusScore; + } + + + /** + * Generates a unique provider ID by concatenating the part number and manufacturer name, + * separated by a pipe (`|`). The generated ID is typically used to uniquely identify + * a specific part from a particular manufacturer. + * + * @param string $partNumber The part number of the product. + * @param string $manufacturer The name of the manufacturer. + * + * @return string The generated provider ID, in the format "partNumber|manufacturer". + */ + private function generateProviderId(string $partNumber, string $manufacturer): string + { + return trim($partNumber) . '|' . trim($manufacturer); + } + + /** + * Maps the name of a country to its corresponding ISO 3166-1 alpha-2 code. + * + * @param string|null $countryName The name of the country to map. + * @return string|null The ISO code for the country, or null if not found. + */ + private function mapCountryNameToCode(?string $countryName): ?string + { + return $this->countryNameToCodeMap[$countryName] ?? null; + } + + /** + * Removes the analytics tracking parts from the URLs returned by the API. + * + * @param string|null $url + * @return string|null + */ + private function unwrapURL(?string $url): ?string + { + if ($url === null) { + return null; + } + + //Check if the URL is a one redirected via analytics + if (str_contains($url, 'analytics.oemsecrets.com/main.php')) { + //Extract the URL from the analytics URL + $queryParams = []; + parse_str(parse_url($url, PHP_URL_QUERY), $queryParams); + + //The real URL is stored in the 'event_link' query parameter + if (isset($queryParams['event_link']) && trim($queryParams['event_link']) !== '') { + $url = $queryParams['event_link']; + + //Replace any spaces in the URL by %20 to avoid invalid URLs + return str_replace(' ', '%20', $url); + } + } + + //Otherwise return the URL as it is + return $url; + } + +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/OctopartProvider.php b/src/Services/InfoProviderSystem/Providers/OctopartProvider.php new file mode 100644 index 00000000..e28162ba --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/OctopartProvider.php @@ -0,0 +1,406 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Entity\Parts\ManufacturingStatus; +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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\Services\OAuth\OAuthTokenManager; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\HttpClient\HttpOptions; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +/** + * This class implements the Octopart/Nexar API as an InfoProvider + * + * As the limits for Octopart are quite limited, we use an additional layer of caching here, we get the full parts during a search + * and cache them, so we can use them for the detail view without having to query the API again. + */ +class OctopartProvider implements InfoProviderInterface +{ + private const OAUTH_APP_NAME = 'ip_octopart_oauth'; + + /** + * This defines what fields are returned in the answer from the Octopart API + */ + private const GRAPHQL_PART_SECTION = <<<'GRAPHQL' + { + id + mpn + octopartUrl + manufacturer { + name + } + shortDescription + category { + ancestors { + name + } + name + } + bestImage { + url + } + bestDatasheet { + url + name + } + manufacturerUrl + medianPrice1000 { + price + currency + quantity + } + sellers(authorizedOnly: $authorizedOnly) { + company { + name + } + isAuthorized + offers { + clickUrl + inventoryLevel + moq + sku + packaging + prices { + price + currency + quantity + } + } + }, + specs { + attribute { + name + shortname + group + id + } + displayValue + value + siValue + units + unitsName + unitsSymbol + valueType + } + } + GRAPHQL; + + + public function __construct(private readonly HttpClientInterface $httpClient, + private readonly OAuthTokenManager $authTokenManager, private readonly CacheItemPoolInterface $partInfoCache, + 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) + { + + } + + /** + * Gets the latest OAuth token for the Octopart API, or creates a new one if none is available + * @return string + */ + private function getToken(): string + { + //Check if we already have a token saved for this app, otherwise we have to retrieve one via OAuth + if (!$this->authTokenManager->hasToken(self::OAUTH_APP_NAME)) { + $this->authTokenManager->retrieveClientCredentialsToken(self::OAUTH_APP_NAME); + } + + $tmp = $this->authTokenManager->getAlwaysValidTokenString(self::OAUTH_APP_NAME); + if ($tmp === null) { + throw new \RuntimeException('Could not retrieve OAuth token for Octopart'); + } + + return $tmp; + } + + /** + * Make a GraphQL call to the Octopart API + * @return array + */ + private function makeGraphQLCall(string $query, ?array $variables = null): array + { + if ($variables === []) { + $variables = null; + } + + $options = (new HttpOptions()) + ->setJson(['query' => $query, 'variables' => $variables]) + ->setAuthBearer($this->getToken()) + ; + + $response = $this->httpClient->request( + 'POST', + 'https://api.nexar.com/graphql/', + $options->toArray(), + ); + + return $response->toArray(true); + } + + public function getProviderInfo(): array + { + return [ + 'name' => 'Octopart', + 'description' => 'This provider uses the Nexar/Octopart API to search for parts on Octopart.', + 'url' => 'https://www.octopart.com/', + 'disabled_help' => 'Set the PROVIDER_OCTOPART_CLIENT_ID and PROVIDER_OCTOPART_SECRET env option.' + ]; + } + + public function getProviderKey(): string + { + return 'octopart'; + } + + public function isActive(): bool + { + //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->clientId !== '' && $this->secret !== ''; + } + + private function mapLifeCycleStatus(?string $value): ?ManufacturingStatus + { + return match ($value) { + 'Production', 'New' => ManufacturingStatus::ACTIVE, + 'Obsolete' => ManufacturingStatus::DISCONTINUED, + 'NRND' => ManufacturingStatus::NRFND, + 'EOL' => ManufacturingStatus::EOL, + default => null, + }; + } + + /** + * Saves the given part to the cache. + * Everytime this function is called, the cache is overwritten. + * @param PartDetailDTO $part + * @return void + */ + private function saveToCache(PartDetailDTO $part): void + { + $key = 'octopart_part_'.$part->provider_id; + + $item = $this->partInfoCache->getItem($key); + $item->set($part); + $item->expiresAfter(3600 * 24); //Cache for 1 day + $this->partInfoCache->save($item); + } + + /** + * Retrieves a from the cache, or null if it was not cached yet. + * @param string $id + * @return PartDetailDTO|null + */ + private function getFromCache(string $id): ?PartDetailDTO + { + $key = 'octopart_part_'.$id; + + $item = $this->partInfoCache->getItem($key); + if ($item->isHit()) { + return $item->get(); + } + + return null; + } + + private function partResultToDTO(array $part): PartDetailDTO + { + //Parse the specifications + $parameters = []; + $mass = null; + $package = null; + $pinCount = null; + $mStatus = null; + foreach ($part['specs'] as $spec) { + + //If we encounter the mass spec, we save it for later + if ($spec['attribute']['shortname'] === "weight") { + $mass = (float) $spec['siValue']; + } elseif ($spec['attribute']['shortname'] === "case_package") { + //Package + $package = $spec['value']; + } elseif ($spec['attribute']['shortname'] === "numberofpins") { + //Pin Count + $pinCount = $spec['value']; + } elseif ($spec['attribute']['shortname'] === "lifecyclestatus") { + //LifeCycleStatus + $mStatus = $this->mapLifeCycleStatus($spec['value']); + } + + $parameters[] = new ParameterDTO( + name: $spec['attribute']['name'], + value_text: $spec['valueType'] === 'text' ? $spec['value'] : null, + value_typ: in_array($spec['valueType'], ['float', 'integer'], true) ? (float) $spec['value'] : null, + unit: $spec['valueType'] === 'text' ? null : $spec['units'], + group: $spec['attribute']['group'], + ); + } + + //Parse the offers + $orderinfos = []; + foreach ($part['sellers'] as $seller) { + foreach ($seller['offers'] as $offer) { + $prices = []; + foreach ($offer['prices'] as $price) { + $prices[] = new PriceDTO( + minimum_discount_amount: $price['quantity'], + price: (string) $price['price'], + currency_iso_code: $price['currency'], + ); + } + + $orderinfos[] = new PurchaseInfoDTO( + distributor_name: $seller['company']['name'], + order_number: $offer['sku'], + prices: $prices, + product_url: $offer['clickUrl'], + ); + } + } + + //Generate a footprint name from the package and pin count + $footprint = null; + if ($package !== null) { + $footprint = $package; + if ($pinCount !== null) { //Add pin count if available + $footprint .= '-' . $pinCount; + } + } + + //Built the category full path + $category = null; + if (!empty($part['category']['name'])) { + $category = implode(' -> ', array_map(static fn($c) => $c['name'], $part['category']['ancestors'] ?? [])); + if ($category !== '' && $category !== '0') { + $category .= ' -> '; + } + $category .= $part['category']['name']; + } + + return new PartDetailDTO( + provider_key: $this->getProviderKey(), + provider_id: $part['id'], + name: $part['mpn'], + description: $part['shortDescription'] ?? null, + category: $category , + manufacturer: $part['manufacturer']['name'] ?? null, + mpn: $part['mpn'], + preview_image_url: $part['bestImage']['url'] ?? null, + manufacturing_status: $mStatus, + provider_url: $part['octopartUrl'] ?? null, + footprint: $footprint, + datasheets: $part['bestDatasheet'] !== null ? [new FileDTO($part['bestDatasheet']['url'], $part['bestDatasheet']['name'])]: null, + parameters: $parameters, + vendor_infos: $orderinfos, + mass: $mass, + manufacturer_product_url: $part['manufacturerUrl'] ?? null, + ); + } + + public function searchByKeyword(string $keyword): array + { + $graphQL = sprintf(<<<'GRAPHQL' + query partSearch($keyword: String, $limit: Int, $currency: String!, $country: String!, $authorizedOnly: Boolean!) { + supSearch( + q: $keyword + inStockOnly: false + limit: $limit + currency: $currency + country: $country + ) { + hits + results { + part + %s + } + } + } + GRAPHQL, self::GRAPHQL_PART_SECTION); + + + $result = $this->makeGraphQLCall($graphQL, [ + 'keyword' => $keyword, + 'limit' => $this->search_limit, + 'currency' => $this->currency, + 'country' => $this->country, + 'authorizedOnly' => $this->onlyAuthorizedSellers, + ]); + + $tmp = []; + + foreach ($result['data']['supSearch']['results'] ?? [] as $p) { + $dto = $this->partResultToDTO($p['part']); + $tmp[] = $dto; + //Cache the part, so we can get the details later, without having to make another request + $this->saveToCache($dto); + } + + return $tmp; + } + + public function getDetails(string $id): PartDetailDTO + { + //Check if we have the part cached + $cached = $this->getFromCache($id); + if ($cached !== null) { + return $cached; + } + + //Otherwise we have to make a request + $graphql = sprintf(<<<'GRAPHQL' + query partSearch($ids: [String!]!, $currency: String!, $country: String!, $authorizedOnly: Boolean!) { + supParts(ids: $ids, currency: $currency, country: $country) + %s + } + GRAPHQL, self::GRAPHQL_PART_SECTION); + + $result = $this->makeGraphQLCall($graphql, [ + 'ids' => [$id], + 'currency' => $this->currency, + 'country' => $this->country, + 'authorizedOnly' => $this->onlyAuthorizedSellers, + ]); + + $tmp = $this->partResultToDTO($result['data']['supParts'][0]); + $this->saveToCache($tmp); + return $tmp; + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::FOOTPRINT, + ProviderCapabilities::PICTURE, + ProviderCapabilities::DATASHEET, + ProviderCapabilities::PRICE, + ]; + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/PollinProvider.php b/src/Services/InfoProviderSystem/Providers/PollinProvider.php new file mode 100644 index 00000000..09ab8fd4 --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/PollinProvider.php @@ -0,0 +1,249 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Entity\Parts\ManufacturingStatus; +use App\Entity\Parts\Part; +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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\Services\InfoProviderSystem\DTOs\SearchResultDTO; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DomCrawler\Crawler; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +class PollinProvider implements InfoProviderInterface +{ + + public function __construct(private readonly HttpClientInterface $client, + #[Autowire(env: 'bool:PROVIDER_POLLIN_ENABLED')] + private readonly bool $enabled = true, + ) + { + } + + public function getProviderInfo(): array + { + return [ + 'name' => 'Pollin', + 'description' => 'Webscraping from pollin.de to get part information', + 'url' => 'https://www.pollin.de/', + 'disabled_help' => 'Set PROVIDER_POLLIN_ENABLED env to 1' + ]; + } + + public function getProviderKey(): string + { + return 'pollin'; + } + + public function isActive(): bool + { + return $this->enabled; + } + + public function searchByKeyword(string $keyword): array + { + $response = $this->client->request('GET', 'https://www.pollin.de/search', [ + 'query' => [ + 'search' => $keyword + ] + ]); + + $content = $response->getContent(); + + //If the response has us redirected to the product page, then just return the single item + if ($response->getInfo('redirect_count') > 0) { + return [$this->parseProductPage($content)]; + } + + $dom = new Crawler($content); + + $results = []; + + //Iterate over each div.product-box + $dom->filter('div.product-box')->each(function (Crawler $node) use (&$results) { + $results[] = new SearchResultDTO( + provider_key: $this->getProviderKey(), + provider_id: $node->filter('meta[itemprop="productID"]')->attr('content'), + name: $node->filter('a.product-name')->text(), + description: '', + preview_image_url: $node->filter('img.product-image')->attr('src'), + manufacturing_status: $this->mapAvailability($node->filter('link[itemprop="availability"]')->attr('href')), + provider_url: $node->filter('a.product-name')->attr('href') + ); + }); + + return $results; + } + + private function mapAvailability(string $availabilityURI): ManufacturingStatus + { + return match( $availabilityURI) { + 'http://schema.org/InStock' => ManufacturingStatus::ACTIVE, + 'http://schema.org/OutOfStock' => ManufacturingStatus::DISCONTINUED, + default => ManufacturingStatus::NOT_SET + }; + } + + public function getDetails(string $id): PartDetailDTO + { + //Ensure that $id is numeric + if (!is_numeric($id)) { + throw new \InvalidArgumentException("The id must be numeric!"); + } + + $response = $this->client->request('GET', 'https://www.pollin.de/search', [ + 'query' => [ + 'search' => $id + ] + ]); + + //The response must have us redirected to the product page + if ($response->getInfo('redirect_count') > 0) { + throw new \RuntimeException("Could not resolve the product page for the given id!"); + } + + $content = $response->getContent(); + + return $this->parseProductPage($content); + } + + private function parseProductPage(string $content): PartDetailDTO + { + $dom = new Crawler($content); + + $productPageUrl = $dom->filter('meta[property="product:product_link"]')->attr('content'); + $orderId = trim($dom->filter('span[itemprop="sku"]')->text()); //Text is important here + + //Calculate the mass + $massStr = $dom->filter('meta[itemprop="weight"]')->attr('content'); + //Remove the unit + $massStr = str_replace('kg', '', $massStr); + //Convert to float and convert to grams + $mass = (float) $massStr * 1000; + + //Parse purchase info + $purchaseInfo = new PurchaseInfoDTO('Pollin', $orderId, $this->parsePrices($dom), $productPageUrl); + + return new PartDetailDTO( + provider_key: $this->getProviderKey(), + provider_id: $orderId, + name: trim($dom->filter('meta[property="og:title"]')->attr('content')), + description: $dom->filter('meta[property="og:description"]')->attr('content'), + category: $this->parseCategory($dom), + manufacturer: $dom->filter('meta[property="product:brand"]')->count() > 0 ? $dom->filter('meta[property="product:brand"]')->attr('content') : null, + preview_image_url: $dom->filter('meta[property="og:image"]')->attr('content'), + manufacturing_status: $this->mapAvailability($dom->filter('link[itemprop="availability"]')->attr('href')), + provider_url: $productPageUrl, + notes: $this->parseNotes($dom), + datasheets: $this->parseDatasheets($dom), + parameters: $this->parseParameters($dom), + vendor_infos: [$purchaseInfo], + mass: $mass, + ); + } + + private function parseDatasheets(Crawler $dom): array + { + //Iterate over each a element withing div.pol-product-detail-download-files + $datasheets = []; + $dom->filter('div.pol-product-detail-download-files a')->each(function (Crawler $node) use (&$datasheets) { + $datasheets[] = new FileDTO($node->attr('href'), $node->text()); + }); + + return $datasheets; + } + + private function parseParameters(Crawler $dom): array + { + $parameters = []; + + //Iterate over each tr.properties-row inside table.product-detail-properties-table + $dom->filter('table.product-detail-properties-table tr.properties-row')->each(function (Crawler $node) use (&$parameters) { + $parameters[] = ParameterDTO::parseValueIncludingUnit( + name: rtrim($node->filter('th.properties-label')->text(), ':'), + value: trim($node->filter('td.properties-value')->text()) + ); + }); + + return $parameters; + } + + private function parseCategory(Crawler $dom): string + { + $category = ''; + + //Iterate over each li.breadcrumb-item inside ol.breadcrumb + $dom->filter('ol.breadcrumb li.breadcrumb-item')->each(function (Crawler $node) use (&$category) { + //Skip if it has breadcrumb-item-home class + if (str_contains($node->attr('class'), 'breadcrumb-item-home')) { + return; + } + + + $category .= $node->text() . ' -> '; + }); + + //Remove the last ' -> ' + return substr($category, 0, -4); + } + + private function parseNotes(Crawler $dom): string + { + //Concat product highlights and product description + return $dom->filter('div.product-detail-top-features')->html('') . '

' . $dom->filter('div.product-detail-description-text')->html(''); + } + + private function parsePrices(Crawler $dom): array + { + //TODO: Properly handle multiple prices, for now we just look at the price for one piece + + //We assume the currency is always the same + $currency = $dom->filter('meta[property="product:price:currency"]')->attr('content'); + + //If there is meta[property=highPrice] then use this as the price + if ($dom->filter('meta[itemprop="highPrice"]')->count() > 0) { + $price = $dom->filter('meta[itemprop="highPrice"]')->attr('content'); + } else { + $price = $dom->filter('meta[property="product:price:amount"]')->attr('content'); + } + + return [ + new PriceDTO(1.0, $price, $currency) + ]; + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::PICTURE, + ProviderCapabilities::PRICE, + ProviderCapabilities::DATASHEET + ]; + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/ProviderCapabilities.php b/src/Services/InfoProviderSystem/Providers/ProviderCapabilities.php new file mode 100644 index 00000000..fd67cd2c --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/ProviderCapabilities.php @@ -0,0 +1,67 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +/** + * This enum contains all capabilities (which data it can provide) a provider can have. + */ +enum ProviderCapabilities +{ + /** Basic information about a part, like the name, description, part number, manufacturer etc */ + case BASIC; + + /** Information about the footprint of a part */ + case FOOTPRINT; + + /** Provider can provide a picture for a part */ + case PICTURE; + + /** Provider can provide datasheets for a part */ + case DATASHEET; + + /** Provider can provide prices for a part */ + case PRICE; + + public function getTranslationKey(): string + { + return 'info_providers.capabilities.' . match($this) { + self::BASIC => 'basic', + self::FOOTPRINT => 'footprint', + self::PICTURE => 'picture', + self::DATASHEET => 'datasheet', + self::PRICE => 'price', + }; + } + + public function getFAIconClass(): string + { + return 'fa-solid ' . match($this) { + self::BASIC => 'fa-info-circle', + self::FOOTPRINT => 'fa-microchip', + self::PICTURE => 'fa-image', + self::DATASHEET => 'fa-file-alt', + self::PRICE => 'fa-money-bill-wave', + }; + } +} diff --git a/src/Services/InfoProviderSystem/Providers/ReicheltProvider.php b/src/Services/InfoProviderSystem/Providers/ReicheltProvider.php new file mode 100644 index 00000000..0c31c411 --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/ReicheltProvider.php @@ -0,0 +1,285 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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\Services\InfoProviderSystem\DTOs\SearchResultDTO; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DomCrawler\Crawler; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +class ReicheltProvider implements InfoProviderInterface +{ + + public const DISTRIBUTOR_NAME = "Reichelt"; + + public function __construct(private readonly HttpClientInterface $client, + #[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", + ) + { + } + + public function getProviderInfo(): array + { + return [ + 'name' => 'Reichelt', + 'description' => 'Webscraping from reichelt.com to get part information', + 'url' => 'https://www.reichelt.com/', + 'disabled_help' => 'Set PROVIDER_REICHELT_ENABLED env to 1' + ]; + } + + public function getProviderKey(): string + { + return 'reichelt'; + } + + public function isActive(): bool + { + return $this->enabled; + } + + public function searchByKeyword(string $keyword): array + { + $response = $this->client->request('GET', sprintf($this->getBaseURL() . '/shop/search/%s', $keyword)); + $html = $response->getContent(); + + //Parse the HTML and return the results + $dom = new Crawler($html); + //Iterate over all div.al_gallery_article elements + $results = []; + $dom->filter('div.al_gallery_article')->each(function (Crawler $element) use (&$results) { + + //Extract product id from data-product attribute + $artId = json_decode($element->attr('data-product'), true, 2, JSON_THROW_ON_ERROR)['artid']; + + $productID = $element->filter('meta[itemprop="productID"]')->attr('content'); + $name = $element->filter('meta[itemprop="name"]')->attr('content'); + $sku = $element->filter('meta[itemprop="sku"]')->attr('content'); + + //Try to extract a picture URL: + $pictureURL = $element->filter("div.al_artlogo img")->attr('src'); + + $results[] = new SearchResultDTO( + provider_key: $this->getProviderKey(), + provider_id: $artId, + name: $productID, + description: $name, + category: null, + manufacturer: $sku, + preview_image_url: $pictureURL, + provider_url: $element->filter('a.al_artinfo_link')->attr('href') + ); + }); + + return $results; + } + + public function getDetails(string $id): PartDetailDTO + { + //Check that the ID is a number + if (!is_numeric($id)) { + throw new \InvalidArgumentException("Invalid ID"); + } + + //Use this endpoint to resolve the artID to a product page + $response = $this->client->request('GET', + sprintf( + 'https://www.reichelt.com/?ACTION=514&id=74&article=%s&LANGUAGE=%s&CCOUNTRY=%s', + $id, + strtoupper($this->language), + strtoupper($this->country) + ) + ); + $json = $response->toArray(); + + //Retrieve the product page from the response + $productPage = $this->getBaseURL() . '/shop/product' . $json[0]['article_path']; + + + $response = $this->client->request('GET', $productPage, [ + 'query' => [ + 'CCTYPE' => $this->includeVAT ? 'private' : 'business', + 'currency' => $this->currency, + ], + ]); + $html = $response->getContent(); + $dom = new Crawler($html); + + //Extract the product notes + $notes = $dom->filter('p[itemprop="description"]')->html(); + + //Extract datasheets + $datasheets = []; + $dom->filter('div.articleDatasheet a')->each(function (Crawler $element) use (&$datasheets) { + $datasheets[] = new FileDTO($element->attr('href'), $element->filter('span')->text()); + }); + + //Determine price for one unit + $priceString = $dom->filter('meta[itemprop="price"]')->attr('content'); + $currency = $dom->filter('meta[itemprop="priceCurrency"]')->attr('content', 'EUR'); + + //Create purchase info + $purchaseInfo = new PurchaseInfoDTO( + distributor_name: self::DISTRIBUTOR_NAME, + order_number: $json[0]['article_artnr'], + prices: array_merge( + [new PriceDTO(1.0, $priceString, $currency, $this->includeVAT)] + , $this->parseBatchPrices($dom, $currency)), + product_url: $productPage + ); + + //Create part object + return new PartDetailDTO( + provider_key: $this->getProviderKey(), + provider_id: $id, + name: $json[0]['article_artnr'], + description: $json[0]['article_besch'], + category: $this->parseCategory($dom), + manufacturer: $json[0]['manufacturer_name'], + mpn: $this->parseMPN($dom), + preview_image_url: $json[0]['article_picture'], + provider_url: $productPage, + notes: $notes, + datasheets: $datasheets, + parameters: $this->parseParameters($dom), + vendor_infos: [$purchaseInfo] + ); + + } + + private function parseMPN(Crawler $dom): string + { + //Find the small element directly after meta[itemprop="url"] element + $element = $dom->filter('meta[itemprop="url"] + small'); + //If the text contains GTIN text, take the small element afterwards + if (str_contains($element->text(), 'GTIN')) { + $element = $dom->filter('meta[itemprop="url"] + small + small'); + } + + //The MPN is contained in the span inside the element + return $element->filter('span')->text(); + } + + private function parseBatchPrices(Crawler $dom, string $currency): array + { + //Iterate over each a.inline-block element in div.discountValue + $prices = []; + $dom->filter('div.discountValue a.inline-block')->each(function (Crawler $element) use (&$prices, $currency) { + //The minimum amount is the number in the span.block element + $minAmountText = $element->filter('span.block')->text(); + + //Extract a integer from the text + $matches = []; + if (!preg_match('/\d+/', $minAmountText, $matches)) { + return; + } + + $minAmount = (int) $matches[0]; + + //The price is the text of the p.productPrice element + $priceString = $element->filter('p.productPrice')->text(); + //Replace comma with dot + $priceString = str_replace(',', '.', $priceString); + //Strip any non-numeric characters + $priceString = preg_replace('/[^0-9.]/', '', $priceString); + + $prices[] = new PriceDTO($minAmount, $priceString, $currency, $this->includeVAT); + }); + + return $prices; + } + + + private function parseCategory(Crawler $dom): string + { + // Look for ol.breadcrumb and iterate over the li elements + $category = ''; + $dom->filter('ol.breadcrumb li.triangle-left')->each(function (Crawler $element) use (&$category) { + //Do not include the .breadcrumb-showmore element + if ($element->attr('id') === 'breadcrumb-showmore') { + return; + } + + $category .= $element->text() . ' -> '; + }); + //Remove the trailing ' -> ' + $category = substr($category, 0, -4); + + return $category; + } + + /** + * @param Crawler $dom + * @return ParameterDTO[] + */ + private function parseParameters(Crawler $dom): array + { + $parameters = []; + //Iterate over each ul.articleTechnicalData which contains the specifications of each group + $dom->filter('ul.articleTechnicalData')->each(function (Crawler $groupElement) use (&$parameters) { + $groupName = $groupElement->filter('li.articleTechnicalHeadline')->text(); + + //Iterate over each second li in ul.articleAttribute, which contains the specifications + $groupElement->filter('ul.articleAttribute li:nth-child(2n)')->each(function (Crawler $specElement) use (&$parameters, $groupName) { + $parameters[] = ParameterDTO::parseValueIncludingUnit( + name: $specElement->previousAll()->text(), + value: $specElement->text(), + group: $groupName + ); + }); + }); + + return $parameters; + } + + private function getBaseURL(): string + { + //Without the trailing slash + return 'https://www.reichelt.com/' . strtolower($this->country) . '/' . strtolower($this->language); + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::PICTURE, + ProviderCapabilities::DATASHEET, + ProviderCapabilities::PRICE, + ]; + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/TMEClient.php b/src/Services/InfoProviderSystem/Providers/TMEClient.php new file mode 100644 index 00000000..d4df133e --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/TMEClient.php @@ -0,0 +1,96 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; + +class TMEClient +{ + public const BASE_URI = 'https://api.tme.eu'; + + 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->token; + $parameters['ApiSignature'] = $this->getSignature($action, $parameters, $this->secret); + + return $this->tmeClient->request('POST', $this->getUrlForAction($action), [ + 'body' => $parameters, + ]); + } + + public function isUsable(): bool + { + return $this->token !== '' && $this->secret !== ''; + } + + /** + * Returns true if the client is using a private (account related token) instead of a deprecated anonymous token + * to authenticate with TME. + * @return bool + */ + public function isUsingPrivateToken(): bool + { + //Private tokens are longer than anonymous ones (50 instead of 45 characters) + return strlen($this->token) > 45; + } + + /** + * Generates the signature for the given action and parameters. + * Taken from https://github.com/tme-dev/TME-API/blob/master/PHP/basic/using_curl.php + */ + public function getSignature(string $action, array $parameters, string $appSecret): string + { + $parameters = $this->sortSignatureParams($parameters); + + $queryString = http_build_query($parameters, '', '&', PHP_QUERY_RFC3986); + $signatureBase = strtoupper('POST') . + '&' . rawurlencode($this->getUrlForAction($action)) . '&' . rawurlencode($queryString); + + return base64_encode(hash_hmac('sha1', $signatureBase, $appSecret, true)); + } + + private function getUrlForAction(string $action): string + { + return self::BASE_URI . '/' . $action . '.json'; + } + + private function sortSignatureParams(array $params): array + { + ksort($params); + + foreach ($params as &$value) { + if (is_array($value)) { + $value = $this->sortSignatureParams($value); + } + } + + return $params; + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/TMEProvider.php b/src/Services/InfoProviderSystem/Providers/TMEProvider.php new file mode 100644 index 00000000..32fc0c72 --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/TMEProvider.php @@ -0,0 +1,301 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Entity\Parts\ManufacturingStatus; +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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\Services\InfoProviderSystem\DTOs\SearchResultDTO; + +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 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 = $get_gross_prices; + } + } + + public function getProviderInfo(): array + { + return [ + 'name' => 'TME', + 'description' => 'This provider uses the API of TME (Transfer Multipart).', + 'url' => 'https://tme.eu/', + 'disabled_help' => 'Configure the PROVIDER_TME_KEY and PROVIDER_TME_SECRET environment variables to use this provider.' + ]; + } + + public function getProviderKey(): string + { + return 'tme'; + } + + public function isActive(): bool + { + return $this->tmeClient->isUsable(); + } + + public function searchByKeyword(string $keyword): array + { + $response = $this->tmeClient->makeRequest('Products/Search', [ + 'Country' => $this->country, + 'Language' => $this->language, + 'SearchPlain' => $keyword, + ]); + + $data = $response->toArray()['Data']; + + $result = []; + + foreach($data['ProductList'] as $product) { + $result[] = new SearchResultDTO( + provider_key: $this->getProviderKey(), + provider_id: $product['Symbol'], + name: empty($product['OriginalSymbol']) ? $product['Symbol'] : $product['OriginalSymbol'], + description: $product['Description'], + category: $product['Category'], + manufacturer: $product['Producer'], + mpn: $product['OriginalSymbol'] ?? null, + preview_image_url: $this->normalizeURL($product['Photo']), + manufacturing_status: $this->productStatusArrayToManufacturingStatus($product['ProductStatusList']), + provider_url: $this->normalizeURL($product['ProductInformationPage']), + ); + } + + return $result; + } + + public function getDetails(string $id): PartDetailDTO + { + $response = $this->tmeClient->makeRequest('Products/GetProducts', [ + 'Country' => $this->country, + 'Language' => $this->language, + 'SymbolList' => [$id], + ]); + + $product = $response->toArray()['Data']['ProductList'][0]; + + //Add a explicit https:// to the url if it is missing + $productInfoPage = $this->normalizeURL($product['ProductInformationPage']); + + $files = $this->getFiles($id); + + $footprint = null; + + $parameters = $this->getParameters($id, $footprint); + + return new PartDetailDTO( + provider_key: $this->getProviderKey(), + provider_id: $product['Symbol'], + name: empty($product['OriginalSymbol']) ? $product['Symbol'] : $product['OriginalSymbol'], + description: $product['Description'], + category: $product['Category'], + manufacturer: $product['Producer'], + mpn: $product['OriginalSymbol'] ?? null, + preview_image_url: $this->normalizeURL($product['Photo']), + manufacturing_status: $this->productStatusArrayToManufacturingStatus($product['ProductStatusList']), + provider_url: $productInfoPage, + footprint: $footprint, + datasheets: $files['datasheets'], + images: $files['images'], + parameters: $parameters, + vendor_infos: [$this->getVendorInfo($id, $productInfoPage)], + mass: $product['WeightUnit'] === 'g' ? $product['Weight'] : null, + ); + } + + /** + * Fetches all files for a given product id + * @param string $id + * @return array> An array with the keys 'datasheet' + * @phpstan-return array{datasheets: list, images: list} + */ + public function getFiles(string $id): array + { + $response = $this->tmeClient->makeRequest('Products/GetProductsFiles', [ + 'Country' => $this->country, + 'Language' => $this->language, + 'SymbolList' => [$id], + ]); + + $data = $response->toArray()['Data']; + $files = $data['ProductList'][0]['Files']; + + //Extract datasheets + $documentList = $files['DocumentList']; + $datasheets = []; + foreach($documentList as $document) { + $datasheets[] = new FileDTO( + url: $this->normalizeURL($document['DocumentUrl']), + ); + } + + //Extract images + $imageList = $files['AdditionalPhotoList']; + $images = []; + foreach($imageList as $image) { + $images[] = new FileDTO( + url: $this->normalizeURL($image['HighResolutionPhoto']), + ); + } + + + return [ + 'datasheets' => $datasheets, + 'images' => $images, + ]; + } + + /** + * Fetches the vendor/purchase information for a given product id. + * @param string $id + * @param string|null $productURL + * @return PurchaseInfoDTO + */ + public function getVendorInfo(string $id, ?string $productURL = null): PurchaseInfoDTO + { + $response = $this->tmeClient->makeRequest('Products/GetPricesAndStocks', [ + 'Country' => $this->country, + 'Language' => $this->language, + 'Currency' => $this->currency, + 'GrossPrices' => $this->get_gross_prices, + 'SymbolList' => [$id], + ]); + + $data = $response->toArray()['Data']; + $currency = $data['Currency']; + $include_tax = $data['PriceType'] === 'GROSS'; + + + $product = $response->toArray()['Data']['ProductList'][0]; + $vendor_order_number = $product['Symbol']; + $priceList = $product['PriceList']; + + $prices = []; + foreach ($priceList as $price) { + $prices[] = new PriceDTO( + minimum_discount_amount: $price['Amount'], + price: (string) $price['PriceValue'], + currency_iso_code: $currency, + includes_tax: $include_tax, + ); + } + + return new PurchaseInfoDTO( + distributor_name: self::VENDOR_NAME, + order_number: $vendor_order_number, + prices: $prices, + product_url: $productURL, + ); + } + + /** + * Fetches the parameters of a product + * @param string $id + * @param string|null $footprint_name You can pass a variable by reference, where the name of the footprint will be stored + * @return ParameterDTO[] + */ + public function getParameters(string $id, string|null &$footprint_name = null): array + { + $response = $this->tmeClient->makeRequest('Products/GetParameters', [ + 'Country' => $this->country, + 'Language' => $this->language, + 'SymbolList' => [$id], + ]); + + $data = $response->toArray()['Data']['ProductList'][0]; + + $result = []; + + $footprint_name = null; + + foreach($data['ParameterList'] as $parameter) { + $result[] = ParameterDTO::parseValueIncludingUnit($parameter['ParameterName'], $parameter['ParameterValue']); + + //Check if the parameter is the case/footprint + if ($parameter['ParameterId'] === 35) { + $footprint_name = $parameter['ParameterValue']; + } + } + + return $result; + } + + /** + * Convert the array of product statuses to a single manufacturing status + * @param array $statusArray + * @return ManufacturingStatus + */ + private function productStatusArrayToManufacturingStatus(array $statusArray): ManufacturingStatus + { + if (in_array('AVAILABLE_WHILE_STOCKS_LAST', $statusArray, true)) { + return ManufacturingStatus::EOL; + } + + if (in_array('INVALID', $statusArray, true)) { + return ManufacturingStatus::DISCONTINUED; + } + + //By default we assume that the part is active + return ManufacturingStatus::ACTIVE; + } + + + + private function normalizeURL(string $url): string + { + //If a URL starts with // we assume that it is a relative URL and we add the protocol + if (str_starts_with($url, '//')) { + return 'https:' . $url; + } + + return $url; + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::FOOTPRINT, + ProviderCapabilities::PICTURE, + ProviderCapabilities::DATASHEET, + ProviderCapabilities::PRICE, + ]; + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/TestProvider.php b/src/Services/InfoProviderSystem/Providers/TestProvider.php new file mode 100644 index 00000000..8b78c95a --- /dev/null +++ b/src/Services/InfoProviderSystem/Providers/TestProvider.php @@ -0,0 +1,95 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\InfoProviderSystem\Providers; + +use App\Services\InfoProviderSystem\DTOs\FileDTO; +use App\Services\InfoProviderSystem\DTOs\PartDetailDTO; +use App\Services\InfoProviderSystem\DTOs\SearchResultDTO; +use Symfony\Component\DependencyInjection\Attribute\When; + +/** + * This is a provider, which is used during tests + */ +#[When(env: 'test')] +class TestProvider implements InfoProviderInterface +{ + + public function getProviderInfo(): array + { + return [ + 'name' => 'Test Provider', + 'description' => 'This is a test provider', + //'url' => 'https://example.com', + 'disabled_help' => 'This provider is disabled for testing purposes' + ]; + } + + public function getProviderKey(): string + { + return 'test'; + } + + public function isActive(): bool + { + return true; + } + + public function searchByKeyword(string $keyword): array + { + return [ + new SearchResultDTO(provider_key: $this->getProviderKey(), provider_id: 'element1', name: 'Element 1', description: 'fd'), + new SearchResultDTO(provider_key: $this->getProviderKey(), provider_id: 'element2', name: 'Element 2', description: 'fd'), + new SearchResultDTO(provider_key: $this->getProviderKey(), provider_id: 'element3', name: 'Element 3', description: 'fd'), + ]; + } + + public function getCapabilities(): array + { + return [ + ProviderCapabilities::BASIC, + ProviderCapabilities::FOOTPRINT, + ]; + } + + public function getDetails(string $id): PartDetailDTO + { + return new PartDetailDTO( + provider_key: $this->getProviderKey(), + provider_id: $id, + name: 'Test Element', + description: 'fd', + manufacturer: 'Test Manufacturer', + mpn: '1234', + provider_url: 'https://invalid.invalid', + footprint: 'Footprint', + notes: 'Notes', + datasheets: [ + new FileDTO('https://invalid.invalid/invalid.pdf', 'Datasheet') + ], + images: [ + new FileDTO('https://invalid.invalid/invalid.png', 'Image') + ] + ); + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/BarcodeGenerator.php b/src/Services/LabelSystem/BarcodeGenerator.php deleted file mode 100644 index a192abf2..00000000 --- a/src/Services/LabelSystem/BarcodeGenerator.php +++ /dev/null @@ -1,144 +0,0 @@ -. - */ - -declare(strict_types=1); - -/** - * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). - * - * Copyright (C) 2019 - 2022 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 . - */ - -namespace App\Services\LabelSystem; - -use App\Entity\LabelSystem\LabelOptions; -use App\Services\LabelSystem\Barcodes\BarcodeContentGenerator; -use Com\Tecnick\Barcode\Barcode; -use InvalidArgumentException; -use PhpParser\Node\Stmt\Label; -use Symfony\Component\Mime\MimeTypes; -use Twig\Extra\Html\HtmlExtension; - -final class BarcodeGenerator -{ - private BarcodeContentGenerator $barcodeContentGenerator; - - - public function __construct(BarcodeContentGenerator $barcodeContentGenerator) - { - $this->barcodeContentGenerator = $barcodeContentGenerator; - } - - public function generateHTMLBarcode(LabelOptions $options, object $target): ?string - { - $svg = $this->generateSVG($options, $target); - $base64 = $this->dataUri($svg, 'image/svg+xml'); - return ''. $this->getContent($options, $target) . ''; - } - - /** - * Creates a data URI (RFC 2397). - * Based on the Twig implementaion from HTMLExtension - * - * Length validation is not performed on purpose, validation should - * be done before calling this filter. - * - * @return string The generated data URI - */ - private function dataUri(string $data, string $mime): string - { - $repr = 'data:'; - - $repr .= $mime; - if (0 === strpos($mime, 'text/')) { - $repr .= ','.rawurlencode($data); - } else { - $repr .= ';base64,'.base64_encode($data); - } - - return $repr; - } - - public function generateSVG(LabelOptions $options, object $target): ?string - { - $barcode = new Barcode(); - - switch ($options->getBarcodeType()) { - case 'qr': - $type = 'QRCODE'; - - break; - case 'datamatrix': - $type = 'DATAMATRIX'; - - break; - case 'code39': - $type = 'C39'; - - break; - case 'code93': - $type = 'C93'; - - break; - case 'code128': - $type = 'C128A'; - - break; - case 'none': - return null; - default: - throw new InvalidArgumentException('Unknown label type!'); - } - - $bobj = $barcode->getBarcodeObj($type, $this->getContent($options, $target)); - - return $bobj->getSvgCode(); - } - - public function getContent(LabelOptions $options, object $target): ?string - { - switch ($options->getBarcodeType()) { - case 'qr': - case 'datamatrix': - return $this->barcodeContentGenerator->getURLContent($target); - case 'code39': - case 'code93': - case 'code128': - return $this->barcodeContentGenerator->get1DBarcodeContent($target); - case 'none': - return null; - default: - throw new InvalidArgumentException('Unknown label type!'); - } - } -} diff --git a/src/Services/LabelSystem/BarcodeScanner/BarcodeRedirector.php b/src/Services/LabelSystem/BarcodeScanner/BarcodeRedirector.php new file mode 100644 index 00000000..2de7c035 --- /dev/null +++ b/src/Services/LabelSystem/BarcodeScanner/BarcodeRedirector.php @@ -0,0 +1,166 @@ +. + */ + +declare(strict_types=1); + +/** + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2022 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 . + */ + +namespace App\Services\LabelSystem\BarcodeScanner; + +use App\Entity\LabelSystem\LabelSupportedElement; +use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityNotFoundException; +use InvalidArgumentException; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * @see \App\Tests\Services\LabelSystem\Barcodes\BarcodeRedirectorTest + */ +final class BarcodeRedirector +{ + public function __construct(private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $em) + { + } + + /** + * Determines the URL to which the user should be redirected, when scanning a QR code. + * + * @param BarcodeScanResultInterface $barcodeScan The result of the barcode scan + * @return string the URL to which should be redirected + * + * @throws EntityNotFoundException + */ + public function getRedirectURL(BarcodeScanResultInterface $barcodeScan): string + { + if($barcodeScan instanceof LocalBarcodeScanResult) { + return $this->getURLLocalBarcode($barcodeScan); + } + + if ($barcodeScan instanceof EIGP114BarcodeScanResult) { + return $this->getURLVendorBarcode($barcodeScan); + } + + throw new InvalidArgumentException('Unknown $barcodeScan type: '.get_class($barcodeScan)); + } + + private function getURLLocalBarcode(LocalBarcodeScanResult $barcodeScan): string + { + switch ($barcodeScan->target_type) { + case LabelSupportedElement::PART: + return $this->urlGenerator->generate('app_part_show', ['id' => $barcodeScan->target_id]); + case LabelSupportedElement::PART_LOT: + //Try to determine the part to the given lot + $lot = $this->em->find(PartLot::class, $barcodeScan->target_id); + if (!$lot instanceof PartLot) { + throw new EntityNotFoundException(); + } + + return $this->urlGenerator->generate('app_part_show', ['id' => $lot->getPart()->getID()]); + + case LabelSupportedElement::STORELOCATION: + return $this->urlGenerator->generate('part_list_store_location', ['id' => $barcodeScan->target_id]); + + default: + throw new InvalidArgumentException('Unknown $type: '.$barcodeScan->target_type->name); + } + } + + /** + * Gets the URL to a part from a scan of a Vendor Barcode + */ + private function getURLVendorBarcode(EIGP114BarcodeScanResult $barcodeScan): string + { + $part = $this->getPartFromVendor($barcodeScan); + return $this->urlGenerator->generate('app_part_show', ['id' => $part->getID()]); + } + + /** + * Gets a part from a scan of a Vendor Barcode by filtering for parts + * with the same Info Provider Id or, if that fails, by looking for parts with a + * matching manufacturer product number. Only returns the first matching part. + */ + private function getPartFromVendor(EIGP114BarcodeScanResult $barcodeScan) : Part + { + // first check via the info provider ID (e.g. Vendor ID). This might fail if the part was not added via + // the info provider system or if the part was bought from a different vendor than the data was retrieved + // from. + if($barcodeScan->digikeyPartNumber) { + $qb = $this->em->getRepository(Part::class)->createQueryBuilder('part'); + //Lower() to be case insensitive + $qb->where($qb->expr()->like('LOWER(part.providerReference.provider_id)', 'LOWER(:vendor_id)')); + $qb->setParameter('vendor_id', $barcodeScan->digikeyPartNumber); + $results = $qb->getQuery()->getResult(); + if ($results) { + return $results[0]; + } + } + + if(!$barcodeScan->supplierPartNumber){ + throw new EntityNotFoundException(); + } + + //Fallback to the manufacturer part number. This may return false positives, since it is common for + //multiple manufacturers to use the same part number for their version of a common product + //We assume the user is able to realize when this returns the wrong part + //If the barcode specifies the manufacturer we try to use that as well + $mpnQb = $this->em->getRepository(Part::class)->createQueryBuilder('part'); + $mpnQb->where($mpnQb->expr()->like('LOWER(part.manufacturer_product_number)', 'LOWER(:mpn)')); + $mpnQb->setParameter('mpn', $barcodeScan->supplierPartNumber); + + if($barcodeScan->mouserManufacturer){ + $manufacturerQb = $this->em->getRepository(Manufacturer::class)->createQueryBuilder("manufacturer"); + $manufacturerQb->where($manufacturerQb->expr()->like("LOWER(manufacturer.name)", "LOWER(:manufacturer_name)")); + $manufacturerQb->setParameter("manufacturer_name", $barcodeScan->mouserManufacturer); + $manufacturers = $manufacturerQb->getQuery()->getResult(); + + if($manufacturers) { + $mpnQb->andWhere($mpnQb->expr()->eq("part.manufacturer", ":manufacturer")); + $mpnQb->setParameter("manufacturer", $manufacturers); + } + + } + + $results = $mpnQb->getQuery()->getResult(); + if($results){ + return $results[0]; + } + throw new EntityNotFoundException(); + } +} diff --git a/src/Services/LabelSystem/BarcodeScanner/BarcodeScanHelper.php b/src/Services/LabelSystem/BarcodeScanner/BarcodeScanHelper.php new file mode 100644 index 00000000..e5930b36 --- /dev/null +++ b/src/Services/LabelSystem/BarcodeScanner/BarcodeScanHelper.php @@ -0,0 +1,243 @@ +. + */ + +declare(strict_types=1); + +/** + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2022 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 . + */ + +namespace App\Services\LabelSystem\BarcodeScanner; + +use App\Entity\LabelSystem\LabelSupportedElement; +use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; +use Doctrine\ORM\EntityManagerInterface; +use InvalidArgumentException; + +/** + * @see \App\Tests\Services\LabelSystem\Barcodes\BarcodeScanHelperTest + */ +final class BarcodeScanHelper +{ + private const PREFIX_TYPE_MAP = [ + 'L' => LabelSupportedElement::PART_LOT, + 'P' => LabelSupportedElement::PART, + 'S' => LabelSupportedElement::STORELOCATION, + ]; + + public const QR_TYPE_MAP = [ + 'lot' => LabelSupportedElement::PART_LOT, + 'part' => LabelSupportedElement::PART, + 'location' => LabelSupportedElement::STORELOCATION, + ]; + + public function __construct(private readonly EntityManagerInterface $entityManager) + { + } + + /** + * Parse the given barcode content and return the target type and ID. + * If the barcode could not be parsed, an exception is thrown. + * Using the $type parameter, you can specify how the barcode should be parsed. If set to null, the function + * will try to guess the type. + * @param string $input + * @param BarcodeSourceType|null $type + * @return BarcodeScanResultInterface + */ + public function scanBarcodeContent(string $input, ?BarcodeSourceType $type = null): BarcodeScanResultInterface + { + //Do specific parsing + if ($type === BarcodeSourceType::INTERNAL) { + return $this->parseInternalBarcode($input) ?? throw new InvalidArgumentException('Could not parse barcode'); + } + if ($type === BarcodeSourceType::USER_DEFINED) { + return $this->parseUserDefinedBarcode($input) ?? throw new InvalidArgumentException('Could not parse barcode'); + } + if ($type === BarcodeSourceType::IPN) { + return $this->parseIPNBarcode($input) ?? throw new InvalidArgumentException('Could not parse barcode'); + } + if ($type === BarcodeSourceType::EIGP114) { + return $this->parseEIGP114Barcode($input); + } + + //Null means auto and we try the different formats + $result = $this->parseInternalBarcode($input); + + if ($result !== null) { + return $result; + } + + //Try to parse as User defined barcode + $result = $this->parseUserDefinedBarcode($input); + if ($result !== null) { + return $result; + } + + //If the barcode is formatted as EIGP114, we can parse it directly + if (EIGP114BarcodeScanResult::isFormat06Code($input)) { + return $this->parseEIGP114Barcode($input); + } + + //Try to parse as IPN barcode + $result = $this->parseIPNBarcode($input); + if ($result !== null) { + return $result; + } + + throw new InvalidArgumentException('Unknown barcode'); + } + + private function parseEIGP114Barcode(string $input): EIGP114BarcodeScanResult + { + return EIGP114BarcodeScanResult::parseFormat06Code($input); + } + + private function parseUserDefinedBarcode(string $input): ?LocalBarcodeScanResult + { + $lot_repo = $this->entityManager->getRepository(PartLot::class); + //Find only the first result + $results = $lot_repo->findBy(['user_barcode' => $input], limit: 1); + + if (count($results) === 0) { + return null; + } + //We found a part, so use it to create the result + $lot = $results[0]; + + return new LocalBarcodeScanResult( + target_type: LabelSupportedElement::PART_LOT, + target_id: $lot->getID(), + source_type: BarcodeSourceType::USER_DEFINED + ); + } + + private function parseIPNBarcode(string $input): ?LocalBarcodeScanResult + { + $part_repo = $this->entityManager->getRepository(Part::class); + //Find only the first result + $results = $part_repo->findBy(['ipn' => $input], limit: 1); + + if (count($results) === 0) { + return null; + } + //We found a part, so use it to create the result + $part = $results[0]; + + return new LocalBarcodeScanResult( + target_type: LabelSupportedElement::PART, + target_id: $part->getID(), + source_type: BarcodeSourceType::IPN + ); + } + + /** + * This function tries to interpret the given barcode content as an internal barcode. + * If the barcode could not be parsed at all, null is returned. If the barcode is a valid format, but could + * not be found in the database, an exception is thrown. + * @param string $input + * @return LocalBarcodeScanResult|null + */ + private function parseInternalBarcode(string $input): ?LocalBarcodeScanResult + { + $input = trim($input); + $matches = []; + + //Some scanner output '-' as ß, so replace it (ß is never used, so we can replace it safely) + $input = str_replace('ß', '-', $input); + + //Extract parts from QR code's URL + if (preg_match('#^https?://.*/scan/(\w+)/(\d+)/?$#', $input, $matches)) { + return new LocalBarcodeScanResult( + target_type: self::QR_TYPE_MAP[strtolower($matches[1])], + target_id: (int) $matches[2], + source_type: BarcodeSourceType::INTERNAL + ); + } + + //New Code39 barcode use L0001 format + if (preg_match('#^([A-Z])(\d{4,})$#', $input, $matches)) { + $prefix = $matches[1]; + $id = (int) $matches[2]; + + if (!isset(self::PREFIX_TYPE_MAP[$prefix])) { + throw new InvalidArgumentException('Unknown prefix '.$prefix); + } + + return new LocalBarcodeScanResult( + target_type: self::PREFIX_TYPE_MAP[$prefix], + target_id: $id, + source_type: BarcodeSourceType::INTERNAL + ); + } + + //During development the L-000001 format was used + if (preg_match('#^(\w)-(\d{6,})$#', $input, $matches)) { + $prefix = $matches[1]; + $id = (int) $matches[2]; + + if (!isset(self::PREFIX_TYPE_MAP[$prefix])) { + throw new InvalidArgumentException('Unknown prefix '.$prefix); + } + + return new LocalBarcodeScanResult( + target_type: self::PREFIX_TYPE_MAP[$prefix], + target_id: $id, + source_type: BarcodeSourceType::INTERNAL + ); + } + + //Legacy Part-DB location labels used $L00336 format + if (preg_match('#^\$L(\d{5,})$#', $input, $matches)) { + return new LocalBarcodeScanResult( + target_type: LabelSupportedElement::STORELOCATION, + target_id: (int) $matches[1], + source_type: BarcodeSourceType::INTERNAL + ); + } + + //Legacy Part-DB used EAN8 barcodes for part labels. Format 0000001(2) (note the optional 8th digit => checksum) + if (preg_match('#^(\d{7})\d?$#', $input, $matches)) { + return new LocalBarcodeScanResult( + target_type: LabelSupportedElement::PART, + target_id: (int) $matches[1], + source_type: BarcodeSourceType::INTERNAL + ); + } + + //This function abstain from further parsing + return null; + } +} diff --git a/src/Services/LabelSystem/BarcodeScanner/BarcodeScanResultInterface.php b/src/Services/LabelSystem/BarcodeScanner/BarcodeScanResultInterface.php new file mode 100644 index 00000000..88130351 --- /dev/null +++ b/src/Services/LabelSystem/BarcodeScanner/BarcodeScanResultInterface.php @@ -0,0 +1,36 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\LabelSystem\BarcodeScanner; + +interface BarcodeScanResultInterface +{ + /** + * Returns all data that was decoded from the barcode in a format, that can be shown in a table to the user. + * The return values of this function are not meant to be parsed by code again, but should just give a information + * to the user. + * The keys of the returned array are the first column of the table and the values are the second column. + * @return array + */ + public function getDecodedForInfoMode(): array; +} \ No newline at end of file diff --git a/src/Services/LabelSystem/BarcodeScanner/BarcodeSourceType.php b/src/Services/LabelSystem/BarcodeScanner/BarcodeSourceType.php new file mode 100644 index 00000000..40f707de --- /dev/null +++ b/src/Services/LabelSystem/BarcodeScanner/BarcodeSourceType.php @@ -0,0 +1,45 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\LabelSystem\BarcodeScanner; + +/** + * This enum represents the different types, where a barcode/QR-code can be generated from + */ +enum BarcodeSourceType +{ + /** This Barcode was generated using Part-DB internal recommended barcode generator */ + case INTERNAL; + /** This barcode is containing an internal part number (IPN) */ + case IPN; + + /** + * This barcode is a user defined barcode defined on a part lot + */ + case USER_DEFINED; + + /** + * EIGP114 formatted barcodes like used by digikey, mouser, etc. + */ + case EIGP114; +} \ No newline at end of file diff --git a/src/Services/LabelSystem/BarcodeScanner/EIGP114BarcodeScanResult.php b/src/Services/LabelSystem/BarcodeScanner/EIGP114BarcodeScanResult.php new file mode 100644 index 00000000..0b4f4b56 --- /dev/null +++ b/src/Services/LabelSystem/BarcodeScanner/EIGP114BarcodeScanResult.php @@ -0,0 +1,332 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\LabelSystem\BarcodeScanner; + +/** + * This class represents the content of a EIGP114 barcode. + * Based on PR 811, EIGP 114.2018 (https://www.ecianow.org/assets/docs/GIPC/EIGP-114.2018%20ECIA%20Labeling%20Specification%20for%20Product%20and%20Shipment%20Identification%20in%20the%20Electronics%20Industry%20-%202D%20Barcode.pdf), + * , https://forum.digikey.com/t/digikey-product-labels-decoding-digikey-barcodes/41097 + */ +class EIGP114BarcodeScanResult implements BarcodeScanResultInterface +{ + + /** + * @var string|null Ship date in format YYYYMMDD + */ + public readonly ?string $shipDate; + + /** + * @var string|null Customer assigned part number – Optional based on + * agreements between Distributor and Supplier + */ + public readonly ?string $customerPartNumber; + + /** + * @var string|null Supplier assigned part number + */ + public readonly ?string $supplierPartNumber; + + /** + * @var int|null Quantity of product + */ + public readonly ?int $quantity; + + /** + * @var string|null Customer assigned purchase order number + */ + public readonly ?string $customerPO; + + /** + * @var string|null Line item number from PO. Required on Logistic Label when + * used on back of Packing Slip. See Section 4.9 + */ + public readonly ?string $customerPOLine; + + /** + * 9D - YYWW (Year and Week of Manufacture). ) If no date code is used + * for a particular part, this field should be populated with N/T + * to indicate the product is Not Traceable by this data field. + * @var string|null + */ + public readonly ?string $dateCode; + + /** + * 10D - YYWW (Year and Week of Manufacture). ) If no date code is used + * for a particular part, this field should be populated with N/T + * to indicate the product is Not Traceable by this data field. + * @var string|null + */ + public readonly ?string $alternativeDateCode; + + /** + * Traceability number assigned to a batch or group of items. If + * no lot code is used for a particular part, this field should be + * populated with N/T to indicate the product is Not Traceable + * by this data field. + * @var string|null + */ + public readonly ?string $lotCode; + + /** + * Country where part was manufactured. Two-letter code from + * ISO 3166 country code list + * @var string|null + */ + public readonly ?string $countryOfOrigin; + + /** + * @var string|null Unique alphanumeric number assigned by supplier + * 3S - Package ID for Inner Pack when part of a mixed Logistic + * Carton. Always used in conjunction with a mixed logistic label + * with a 5S data identifier for Package ID. + */ + public readonly ?string $packageId1; + + /** + * @var string|null + * 4S - Package ID for Logistic Carton with like items + */ + public readonly ?string $packageId2; + + /** + * @var string|null + * 5S - Package ID for Logistic Carton with mixed items + */ + public readonly ?string $packageId3; + + /** + * @var string|null Unique alphanumeric number assigned by supplier. + */ + public readonly ?string $packingListNumber; + + /** + * @var string|null Ship date in format YYYYMMDD + */ + public readonly ?string $serialNumber; + + /** + * @var string|null Code for sorting and classifying LEDs. Use when applicable + */ + public readonly ?string $binCode; + + /** + * @var int|null Sequential carton count in format “#/#” or “# of #” + */ + public readonly ?int $packageCount; + + /** + * @var string|null Alphanumeric string assigned by the supplier to distinguish + * from one closely-related design variation to another. Use as + * required or when applicable + */ + public readonly ?string $revisionNumber; + + /** + * @var string|null Digikey Extension: This is not represented in the ECIA spec, but the field being used is found in the ANSI MH10.8.2-2016 spec on which the ECIA spec is based. In the ANSI spec it is called First Level (Supplier Assigned) Part Number. + */ + public readonly ?string $digikeyPartNumber; + + /** + * @var string|null Digikey Extension: This can be shared across multiple invoices and time periods and is generated as an order enters our system from any vector (web, API, phone order, etc.) + */ + public readonly ?string $digikeySalesOrderNumber; + + /** + * @var string|null Digikey extension: This is typically assigned per shipment as items are being released to be picked in the warehouse. A SO can have many Invoice numbers + */ + public readonly ?string $digikeyInvoiceNumber; + + /** + * @var string|null Digikey extension: This is for internal DigiKey purposes and defines the label type. + */ + public readonly ?string $digikeyLabelType; + + /** + * @var string|null You will also see this as the last part of a URL for a product detail page. Ex https://www.digikey.com/en/products/detail/w%C3%BCrth-elektronik/860010672008/5726907 + */ + public readonly ?string $digikeyPartID; + + /** + * @var string|null Digikey Extension: For internal use of Digikey. Probably not needed + */ + public readonly ?string $digikeyNA; + + /** + * @var string|null Digikey Extension: This is a field of varying length used to keep the barcode approximately the same size between labels. It is safe to ignore. + */ + public readonly ?string $digikeyPadding; + + public readonly ?string $mouserPositionInOrder; + + public readonly ?string $mouserManufacturer; + + + + /** + * + * @param array $data The fields of the EIGP114 barcode, where the key is the field name and the value is the field content + */ + public function __construct(public readonly array $data) + { + //IDs per EIGP 114.2018 + $this->shipDate = $data['6D'] ?? null; + $this->customerPartNumber = $data['P'] ?? null; + $this->supplierPartNumber = $data['1P'] ?? null; + $this->quantity = isset($data['Q']) ? (int)$data['Q'] : null; + $this->customerPO = $data['K'] ?? null; + $this->customerPOLine = $data['4K'] ?? null; + $this->dateCode = $data['9D'] ?? null; + $this->alternativeDateCode = $data['10D'] ?? null; + $this->lotCode = $data['1T'] ?? null; + $this->countryOfOrigin = $data['4L'] ?? null; + $this->packageId1 = $data['3S'] ?? null; + $this->packageId2 = $data['4S'] ?? null; + $this->packageId3 = $data['5S'] ?? null; + $this->packingListNumber = $data['11K'] ?? null; + $this->serialNumber = $data['S'] ?? null; + $this->binCode = $data['33P'] ?? null; + $this->packageCount = isset($data['13Q']) ? (int)$data['13Q'] : null; + $this->revisionNumber = $data['2P'] ?? null; + //IDs used by Digikey + $this->digikeyPartNumber = $data['30P'] ?? null; + $this->digikeySalesOrderNumber = $data['1K'] ?? null; + $this->digikeyInvoiceNumber = $data['10K'] ?? null; + $this->digikeyLabelType = $data['11Z'] ?? null; + $this->digikeyPartID = $data['12Z'] ?? null; + $this->digikeyNA = $data['13Z'] ?? null; + $this->digikeyPadding = $data['20Z'] ?? null; + //IDs used by Mouser + $this->mouserPositionInOrder = $data['14K'] ?? null; + $this->mouserManufacturer = $data['1V'] ?? null; + } + + /** + * Tries to guess the vendor of the barcode based on the supplied data field. + * This is experimental and should not be relied upon. + * @return string|null The guessed vendor as smallcase string (e.g. "digikey", "mouser", etc.), or null if the vendor could not be guessed + */ + public function guessBarcodeVendor(): ?string + { + //If the barcode data contains the digikey extensions, we assume it is a digikey barcode + if (isset($this->data['13Z']) || isset($this->data['20Z']) || isset($this->data['12Z']) || isset($this->data['11Z'])) { + return 'digikey'; + } + + //If the barcode data contains the mouser extensions, we assume it is a mouser barcode + if (isset($this->data['14K']) || isset($this->data['1V'])) { + return 'mouser'; + } + + //According to this thread (https://github.com/inventree/InvenTree/issues/853), Newark/element14 codes contains a "3P" field + if (isset($this->data['3P'])) { + return 'element14'; + } + + return null; + } + + /** + * Checks if the given input is a valid format06 formatted data. + * This just perform a simple check for the header, the content might be malformed still. + * @param string $input + * @return bool + */ + public static function isFormat06Code(string $input): bool + { + //Code must begin with [)>06 + if(!str_starts_with($input, "[)>\u{1E}06\u{1D}")){ + return false; + } + + //Digikey does not put a trailer onto the barcode, so we just check for the header + + return true; + } + + /** + * Parses a format06 code a returns a new instance of this class + * @param string $input + * @return self + */ + public static function parseFormat06Code(string $input): self + { + //Ensure that the input is a valid format06 code + if (!self::isFormat06Code($input)) { + throw new \InvalidArgumentException("The given input is not a valid format06 code"); + } + + //Remove the trailer, if present + if (str_ends_with($input, "\u{1E}\u{04}")){ + $input = substr($input, 5, -2); + } + + //Split the input into the different fields (using the separator) + $parts = explode("\u{1D}", $input); + + //The first field is the format identifier, which we do not need + array_shift($parts); + + //Split the fields into key-value pairs + $results = []; + + foreach($parts as $part) { + //^ 0* ([1-9]? \d* [A-Z]) + //Start of the string Leading zeros are discarded Not a zero Any number of digits single uppercase Letter + // 00 1 4 K + + if(!preg_match('/^0*([1-9]?\d*[A-Z])/', $part, $matches)) { + throw new \LogicException("Could not parse field: $part"); + } + //Extract the key + $key = $matches[0]; + //Extract the field value + $fieldValue = substr($part, strlen($matches[0])); + + $results[$key] = $fieldValue; + } + + return new self($results); + } + + public function getDecodedForInfoMode(): array + { + $tmp = [ + 'Barcode type' => 'EIGP114', + 'Guessed vendor from barcode' => $this->guessBarcodeVendor() ?? 'Unknown', + ]; + + //Iterate over all fields of this object and add them to the array if they are not null + foreach((array) $this as $key => $value) { + //Skip data key + if ($key === 'data') { + continue; + } + if($value !== null) { + $tmp[$key] = $value; + } + } + + return $tmp; + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/BarcodeScanner/LocalBarcodeScanResult.php b/src/Services/LabelSystem/BarcodeScanner/LocalBarcodeScanResult.php new file mode 100644 index 00000000..050aff6f --- /dev/null +++ b/src/Services/LabelSystem/BarcodeScanner/LocalBarcodeScanResult.php @@ -0,0 +1,49 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\LabelSystem\BarcodeScanner; + +use App\Entity\LabelSystem\LabelSupportedElement; + +/** + * This class represents the result of a barcode scan of a barcode that uniquely identifies a local entity, + * like an internally generated barcode or a barcode that was added manually to the system by a user + */ +class LocalBarcodeScanResult implements BarcodeScanResultInterface +{ + public function __construct( + public readonly LabelSupportedElement $target_type, + public readonly int $target_id, + public readonly BarcodeSourceType $source_type, + ) { + } + + public function getDecodedForInfoMode(): array + { + return [ + 'Barcode type' => $this->source_type->name, + 'Target type' => $this->target_type->name, + 'Target ID' => $this->target_id, + ]; + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/Barcodes/BarcodeContentGenerator.php b/src/Services/LabelSystem/Barcodes/BarcodeContentGenerator.php index 588e34b4..7ceb30dd 100644 --- a/src/Services/LabelSystem/Barcodes/BarcodeContentGenerator.php +++ b/src/Services/LabelSystem/Barcodes/BarcodeContentGenerator.php @@ -44,29 +44,29 @@ namespace App\Services\LabelSystem\Barcodes; use App\Entity\Base\AbstractDBElement; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use InvalidArgumentException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +/** + * @see \App\Tests\Services\LabelSystem\Barcodes\BarcodeContentGeneratorTest + */ final class BarcodeContentGenerator { public const PREFIX_MAP = [ Part::class => 'P', PartLot::class => 'L', - Storelocation::class => 'S', + StorageLocation::class => 'S', ]; private const URL_MAP = [ Part::class => 'part', PartLot::class => 'lot', - Storelocation::class => 'location', + StorageLocation::class => 'location', ]; - private UrlGeneratorInterface $urlGenerator; - - public function __construct(UrlGeneratorInterface $urlGenerator) + public function __construct(private readonly UrlGeneratorInterface $urlGenerator) { - $this->urlGenerator = $urlGenerator; } /** @@ -76,11 +76,11 @@ final class BarcodeContentGenerator { $type = $this->classToString(self::URL_MAP, $target); - return $this->urlGenerator->generate('scan_qr', [ - 'type' => $type, + return $this->urlGenerator->generate('scan_qr', [ + 'type' => $type, 'id' => $target->getID() ?? 0, '_locale' => null, -], UrlGeneratorInterface::ABSOLUTE_URL); + ], UrlGeneratorInterface::ABSOLUTE_URL); } /** @@ -97,17 +97,17 @@ final class BarcodeContentGenerator private function classToString(array $map, object $target): string { - $class = get_class($target); + $class = $target::class; if (isset($map[$class])) { return $map[$class]; } foreach ($map as $class => $string) { - if (is_a($target, $class)) { + if ($target instanceof $class) { return $string; } } - throw new InvalidArgumentException('Unknown object class '.get_class($target)); + throw new InvalidArgumentException('Unknown object class '.$target::class); } } diff --git a/src/Services/LabelSystem/Barcodes/BarcodeHelper.php b/src/Services/LabelSystem/Barcodes/BarcodeHelper.php new file mode 100644 index 00000000..c9fe64f3 --- /dev/null +++ b/src/Services/LabelSystem/Barcodes/BarcodeHelper.php @@ -0,0 +1,97 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\LabelSystem\Barcodes; + +use App\Entity\LabelSystem\BarcodeType; +use Com\Tecnick\Barcode\Barcode; + +/** + * This function is used to generate barcodes of various types using arbitrary (text) content. + * @see \App\Tests\Services\LabelSystem\Barcodes\BarcodeHelperTest + */ +class BarcodeHelper +{ + + /** + * Generates a barcode with the given content and type and returns it as SVG string. + * @param string $content + * @param BarcodeType $type + * @return string + */ + public function barcodeAsSVG(string $content, BarcodeType $type): string + { + $barcode = new Barcode(); + + $type_str = match ($type) { + BarcodeType::NONE => throw new \InvalidArgumentException('Barcode type must not be NONE! This would make no sense...'), + BarcodeType::QR => 'QRCODE', + BarcodeType::DATAMATRIX => 'DATAMATRIX', + BarcodeType::CODE39 => 'C39', + BarcodeType::CODE93 => 'C93', + BarcodeType::CODE128 => 'C128A', + }; + + return $barcode->getBarcodeObj($type_str, $content)->getSvgCode(); + } + + /** + * Generates a barcode with the given content and type and returns it as HTML image tag. + * @param string $content + * @param BarcodeType $type + * @param string $width Width of the image tag + * @param string|null $alt_text The alt text of the image tag. If null, the content is used. + * @return string + */ + public function barcodeAsHTML(string $content, BarcodeType $type, string $width = '100%', ?string $alt_text = null): string + { + $svg = $this->barcodeAsSVG($content, $type); + $base64 = $this->dataUri($svg, 'image/svg+xml'); + $alt_text ??= $content; + + return ''.$alt_text.''; + } + + /** + * Creates a data URI (RFC 2397). + * Based on the Twig implementation from HTMLExtension + * + * Length validation is not performed on purpose, validation should + * be done before calling this filter. + * + * @return string The generated data URI + */ + private function dataUri(string $data, string $mime): string + { + $repr = 'data:'; + + $repr .= $mime; + if (str_starts_with($mime, 'text/')) { + $repr .= ','.rawurlencode($data); + } else { + $repr .= ';base64,'.base64_encode($data); + } + + return $repr; + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/Barcodes/BarcodeNormalizer.php b/src/Services/LabelSystem/Barcodes/BarcodeNormalizer.php deleted file mode 100644 index 4e6d8cbd..00000000 --- a/src/Services/LabelSystem/Barcodes/BarcodeNormalizer.php +++ /dev/null @@ -1,107 +0,0 @@ -. - */ - -declare(strict_types=1); - -/** - * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). - * - * Copyright (C) 2019 - 2022 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 . - */ - -namespace App\Services\LabelSystem\Barcodes; - -use InvalidArgumentException; - -final class BarcodeNormalizer -{ - private const PREFIX_TYPE_MAP = [ - 'L' => 'lot', - 'P' => 'part', - 'S' => 'location', - ]; - - /** - * Parses barcode content and normalizes it. - * Returns an array in the format ['part', 1]: First entry contains element type, second the ID of the element. - */ - public function normalizeBarcodeContent(string $input): array - { - $input = trim($input); - $matches = []; - - //Some scanner output '-' as ß, so replace it (ß is never used, so we can replace it safely) - $input = str_replace('ß', '-', $input); - - //Extract parts from QR code's URL - if (preg_match('#^https?://.*/scan/(\w+)/(\d+)/?$#', $input, $matches)) { - return [$matches[1], (int) $matches[2]]; - } - - //New Code39 barcode use L0001 format - if (preg_match('#^([A-Z])(\d{4,})$#', $input, $matches)) { - $prefix = $matches[1]; - $id = (int) $matches[2]; - - if (!isset(self::PREFIX_TYPE_MAP[$prefix])) { - throw new InvalidArgumentException('Unknown prefix '.$prefix); - } - - return [self::PREFIX_TYPE_MAP[$prefix], $id]; - } - - //During development the L-000001 format was used - if (preg_match('#^(\w)-(\d{6,})$#', $input, $matches)) { - $prefix = $matches[1]; - $id = (int) $matches[2]; - - if (!isset(self::PREFIX_TYPE_MAP[$prefix])) { - throw new InvalidArgumentException('Unknown prefix '.$prefix); - } - - return [self::PREFIX_TYPE_MAP[$prefix], $id]; - } - - //Legacy Part-DB location labels used $L00336 format - if (preg_match('#^\$L(\d{5,})$#', $input, $matches)) { - return ['location', (int) $matches[1]]; - } - - //Legacy Part-DB used EAN8 barcodes for part labels. Format 0000001(2) (note the optional 8th digit => checksum) - if (preg_match('#^(\d{7})\d?$#', $input, $matches)) { - return ['part', (int) $matches[1]]; - } - - throw new InvalidArgumentException('Unknown barcode format!'); - } -} diff --git a/src/Services/LabelSystem/Barcodes/BarcodeRedirector.php b/src/Services/LabelSystem/Barcodes/BarcodeRedirector.php deleted file mode 100644 index 198cb43b..00000000 --- a/src/Services/LabelSystem/Barcodes/BarcodeRedirector.php +++ /dev/null @@ -1,92 +0,0 @@ -. - */ - -declare(strict_types=1); - -/** - * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). - * - * Copyright (C) 2019 - 2022 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 . - */ - -namespace App\Services\LabelSystem\Barcodes; - -use App\Entity\Parts\PartLot; -use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\EntityNotFoundException; -use InvalidArgumentException; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; - -final class BarcodeRedirector -{ - private UrlGeneratorInterface $urlGenerator; - private EntityManagerInterface $em; - - public function __construct(UrlGeneratorInterface $urlGenerator, EntityManagerInterface $entityManager) - { - $this->urlGenerator = $urlGenerator; - $this->em = $entityManager; - } - - /** - * Determines the URL to which the user should be redirected, when scanning a QR code. - * - * @param string $type The type of the element that was scanned (e.g. 'part', 'lot', etc.) - * @param int $id The ID of the element that was scanned - * - * @return string the URL to which should be redirected - * - * @throws EntityNotFoundException - */ - public function getRedirectURL(string $type, int $id): string - { - switch ($type) { - case 'part': - return $this->urlGenerator->generate('app_part_show', ['id' => $id]); - case 'lot': - //Try to determine the part to the given lot - $lot = $this->em->find(PartLot::class, $id); - if (null === $lot) { - throw new EntityNotFoundException(); - } - - return $this->urlGenerator->generate('app_part_show', ['id' => $lot->getPart()->getID()]); - - case 'location': - return $this->urlGenerator->generate('part_list_store_location', ['id' => $id]); - - default: - throw new InvalidArgumentException('Unknown $type: '.$type); - } - } -} diff --git a/src/Services/LabelSystem/DompdfFactory.php b/src/Services/LabelSystem/DompdfFactory.php new file mode 100644 index 00000000..a2c8c3cd --- /dev/null +++ b/src/Services/LabelSystem/DompdfFactory.php @@ -0,0 +1,54 @@ +. + */ +namespace App\Services\LabelSystem; + +use Dompdf\Dompdf; +use Jbtronics\DompdfFontLoaderBundle\Services\DompdfFactoryInterface; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; + +#[AsDecorator(decorates: DompdfFactoryInterface::class)] +class DompdfFactory implements DompdfFactoryInterface +{ + public function __construct(private readonly string $fontDirectory, private readonly string $tmpDirectory) + { + //Create folder if it does not exist + $this->createDirectoryIfNotExisting($this->fontDirectory); + $this->createDirectoryIfNotExisting($this->tmpDirectory); + } + + private function createDirectoryIfNotExisting(string $path): void + { + if (!is_dir($path) && (!mkdir($concurrentDirectory = $path, 0777, true) && !is_dir($concurrentDirectory))) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); + } + } + + public function create(): Dompdf + { + return new Dompdf([ + 'fontDir' => $this->fontDirectory, + 'fontCache' => $this->fontDirectory, + 'tempDir' => $this->tmpDirectory, + ]); + } +} diff --git a/src/Services/LabelSystem/LabelBarcodeGenerator.php b/src/Services/LabelSystem/LabelBarcodeGenerator.php new file mode 100644 index 00000000..66f74e58 --- /dev/null +++ b/src/Services/LabelSystem/LabelBarcodeGenerator.php @@ -0,0 +1,100 @@ +. + */ + +declare(strict_types=1); + +/** + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2022 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 . + */ + +namespace App\Services\LabelSystem; + +use App\Entity\Base\AbstractDBElement; +use App\Entity\LabelSystem\BarcodeType; +use App\Entity\LabelSystem\LabelOptions; +use App\Services\LabelSystem\Barcodes\BarcodeContentGenerator; +use App\Services\LabelSystem\Barcodes\BarcodeHelper; +use InvalidArgumentException; + +/** + * @see \App\Tests\Services\LabelSystem\LabelBarcodeGeneratorTest + */ +final class LabelBarcodeGenerator +{ + public function __construct(private readonly BarcodeContentGenerator $barcodeContentGenerator, private readonly BarcodeHelper $barcodeHelper) + { + } + + /** + * Generate the barcode for the given label as HTML image tag. + * @param LabelOptions $options + * @param AbstractDBElement $target + * @return string|null + */ + public function generateHTMLBarcode(LabelOptions $options, AbstractDBElement $target): ?string + { + if ($options->getBarcodeType() === BarcodeType::NONE) { + return null; + } + + return $this->barcodeHelper->barcodeAsHTML($this->getContent($options, $target), $options->getBarcodeType()); + } + + /** + * Generate the barcode for the given label as SVG string. + * @param LabelOptions $options + * @param AbstractDBElement $target + * @return string|null + */ + public function generateSVG(LabelOptions $options, AbstractDBElement $target): ?string + { + if ($options->getBarcodeType() === BarcodeType::NONE) { + return null; + } + + return $this->barcodeHelper->barcodeAsSVG($this->getContent($options, $target), $options->getBarcodeType()); + } + + public function getContent(LabelOptions $options, AbstractDBElement $target): ?string + { + $barcode = $options->getBarcodeType(); + return match (true) { + $barcode->is2D() => $this->barcodeContentGenerator->getURLContent($target), + $barcode->is1D() => $this->barcodeContentGenerator->get1DBarcodeContent($target), + $barcode === BarcodeType::NONE => null, + default => throw new InvalidArgumentException('Unknown label type!'), + }; + } +} diff --git a/src/Services/LabelSystem/Barcodes/BarcodeExampleElementsGenerator.php b/src/Services/LabelSystem/LabelExampleElementsGenerator.php similarity index 71% rename from src/Services/LabelSystem/Barcodes/BarcodeExampleElementsGenerator.php rename to src/Services/LabelSystem/LabelExampleElementsGenerator.php index 8a2d020d..d344c929 100644 --- a/src/Services/LabelSystem/Barcodes/BarcodeExampleElementsGenerator.php +++ b/src/Services/LabelSystem/LabelExampleElementsGenerator.php @@ -39,33 +39,31 @@ declare(strict_types=1); * along with this program. If not, see . */ -namespace App\Services\LabelSystem\Barcodes; +namespace App\Services\LabelSystem; use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\LabelSystem\LabelSupportedElement; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\ManufacturingStatus; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; +use App\Entity\UserSystem\User; use DateTime; use InvalidArgumentException; use ReflectionClass; -final class BarcodeExampleElementsGenerator +final class LabelExampleElementsGenerator { - public function getElement(string $type): object + public function getElement(LabelSupportedElement $type): object { - switch ($type) { - case 'part': - return $this->getExamplePart(); - case 'part_lot': - return $this->getExamplePartLot(); - case 'storelocation': - return $this->getStorelocation(); - default: - throw new InvalidArgumentException('Unknown $type.'); - } + return match ($type) { + LabelSupportedElement::PART => $this->getExamplePart(), + LabelSupportedElement::PART_LOT => $this->getExamplePartLot(), + LabelSupportedElement::STORELOCATION => $this->getStorelocation(), + }; } public function getExamplePart(): Part @@ -82,7 +80,7 @@ final class BarcodeExampleElementsGenerator $part->setMass(123.4); $part->setManufacturerProductNumber('CUSTOM MPN'); $part->setTags('Tag1, Tag2, Tag3'); - $part->setManufacturingStatus('active'); + $part->setManufacturingStatus(ManufacturingStatus::ACTIVE); $part->updateTimestamps(); $part->setFavorite(true); @@ -99,21 +97,24 @@ final class BarcodeExampleElementsGenerator $lot->setDescription('Example Lot'); $lot->setComment('Lot comment'); - $lot->setExpirationDate(new DateTime('+1 days')); - $lot->setStorageLocation($this->getStructuralData(Storelocation::class)); + $lot->setExpirationDate(new \DateTimeImmutable('+1 day')); + $lot->setStorageLocation($this->getStructuralData(StorageLocation::class)); $lot->setAmount(123); + $lot->setOwner($this->getUser()); return $lot; } - private function getStorelocation(): Storelocation + private function getStorelocation(): StorageLocation { - $storelocation = new Storelocation(); + $storelocation = new StorageLocation(); $storelocation->setName('Location 1'); $storelocation->setComment('Example comment'); $storelocation->updateTimestamps(); + $storelocation->setOwner($this->getUser()); - $parent = new Storelocation(); + + $parent = new StorageLocation(); $parent->setName('Parent'); $storelocation->setParent($parent); @@ -121,17 +122,35 @@ final class BarcodeExampleElementsGenerator return $storelocation; } + private function getUser(): User + { + $user = new User(); + $user->setName('user'); + $user->setFirstName('John'); + $user->setLastName('Doe'); + + return $user; + } + + /** + * @template T of AbstractStructuralDBElement + * @param string $class + * @phpstan-param class-string $class + * @return AbstractStructuralDBElement + * @phpstan-return T + * @throws \ReflectionException + */ private function getStructuralData(string $class): AbstractStructuralDBElement { if (!is_a($class, AbstractStructuralDBElement::class, true)) { throw new InvalidArgumentException('$class must be an child of AbstractStructuralDBElement'); } - /** @var AbstractStructuralDBElement $parent */ + /** @var T $parent */ $parent = new $class(); $parent->setName('Example'); - /** @var AbstractStructuralDBElement $child */ + /** @var T $child */ $child = new $class(); $child->setName((new ReflectionClass($class))->getShortName()); $child->setParent($parent); diff --git a/src/Services/LabelSystem/LabelGenerator.php b/src/Services/LabelSystem/LabelGenerator.php index 3244875f..bfb8d27b 100644 --- a/src/Services/LabelSystem/LabelGenerator.php +++ b/src/Services/LabelSystem/LabelGenerator.php @@ -42,38 +42,26 @@ declare(strict_types=1); namespace App\Services\LabelSystem; use App\Entity\LabelSystem\LabelOptions; -use App\Entity\Parts\Part; -use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; -use Dompdf\Dompdf; use InvalidArgumentException; +use Jbtronics\DompdfFontLoaderBundle\Services\DompdfFactoryInterface; +/** + * @see \App\Tests\Services\LabelSystem\LabelGeneratorTest + */ final class LabelGenerator { - public const CLASS_SUPPORT_MAPPING = [ - 'part' => Part::class, - 'part_lot' => PartLot::class, - 'storelocation' => Storelocation::class, - ]; - public const MM_TO_POINTS_FACTOR = 2.83465; - private LabelHTMLGenerator $labelHTMLGenerator; - - public function __construct(LabelHTMLGenerator $labelHTMLGenerator) + public function __construct(private readonly LabelHTMLGenerator $labelHTMLGenerator, + private readonly DompdfFactoryInterface $dompdfFactory) { - $this->labelHTMLGenerator = $labelHTMLGenerator; } /** - * @param object|object[] $elements An element or an array of elements for which labels should be generated + * @param object|object[] $elements An element or an array of elements for which labels should be generated */ - public function generateLabel(LabelOptions $options, $elements): string + public function generateLabel(LabelOptions $options, object|array $elements): string { - if (!is_array($elements) && !is_object($elements)) { - throw new InvalidArgumentException('$element must be an object or an array of objects!'); - } - if (!is_array($elements)) { $elements = [$elements]; } @@ -84,12 +72,12 @@ final class LabelGenerator } } - $dompdf = new Dompdf(); + $dompdf = $this->dompdfFactory->create(); $dompdf->setPaper($this->mmToPointsArray($options->getWidth(), $options->getHeight())); $dompdf->loadHtml($this->labelHTMLGenerator->getLabelHTML($options, $elements)); $dompdf->render(); - return $dompdf->output(); + return $dompdf->output() ?? throw new \RuntimeException('Could not generate label!'); } /** @@ -98,15 +86,12 @@ final class LabelGenerator public function supports(LabelOptions $options, object $element): bool { $supported_type = $options->getSupportedElement(); - if (!isset(static::CLASS_SUPPORT_MAPPING[$supported_type])) { - throw new InvalidArgumentException('Supported type name of the Label options not known!'); - } - return is_a($element, static::CLASS_SUPPORT_MAPPING[$supported_type]); + return is_a($element, $supported_type->getEntityClass()); } /** - * Converts width and height given in mm to an size array, that can be used by DOMPDF for page size. + * Converts width and height given in mm to a size array, that can be used by DOMPDF for page size. * * @param float $width The width of the paper * @param float $height The height of the paper diff --git a/src/Services/LabelSystem/LabelHTMLGenerator.php b/src/Services/LabelSystem/LabelHTMLGenerator.php index f526ac9d..42aa1e72 100644 --- a/src/Services/LabelSystem/LabelHTMLGenerator.php +++ b/src/Services/LabelSystem/LabelHTMLGenerator.php @@ -41,61 +41,56 @@ declare(strict_types=1); namespace App\Services\LabelSystem; +use App\Entity\LabelSystem\LabelProcessMode; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Contracts\NamedElementInterface; use App\Entity\LabelSystem\LabelOptions; use App\Exceptions\TwigModeException; use App\Services\ElementTypeNameGenerator; use InvalidArgumentException; -use Symfony\Component\Security\Core\Security; use Twig\Environment; use Twig\Error\Error; final class LabelHTMLGenerator { - private Environment $twig; - private ElementTypeNameGenerator $elementTypeNameGenerator; - private LabelTextReplacer $replacer; - private BarcodeGenerator $barcodeGenerator; - private SandboxedTwigProvider $sandboxedTwigProvider; - private string $partdb_title; - private Security $security; - - public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, LabelTextReplacer $replacer, Environment $twig, - BarcodeGenerator $barcodeGenerator, SandboxedTwigProvider $sandboxedTwigProvider, Security $security, string $partdb_title) + public function __construct( + private readonly ElementTypeNameGenerator $elementTypeNameGenerator, + private readonly LabelTextReplacer $replacer, + private readonly Environment $twig, + private readonly LabelBarcodeGenerator $barcodeGenerator, + private readonly SandboxedTwigFactory $sandboxedTwigProvider, + private readonly Security $security, + private readonly string $partdb_title) { - $this->twig = $twig; - $this->elementTypeNameGenerator = $elementTypeNameGenerator; - $this->replacer = $replacer; - $this->barcodeGenerator = $barcodeGenerator; - $this->sandboxedTwigProvider = $sandboxedTwigProvider; - $this->security = $security; - $this->partdb_title = $partdb_title; } public function getLabelHTML(LabelOptions $options, array $elements): string { - if (empty($elements)) { + if ($elements === []) { throw new InvalidArgumentException('$elements must not be empty'); } $twig_elements = []; - if ('twig' === $options->getLinesMode()) { - $sandboxed_twig = $this->sandboxedTwigProvider->getTwig($options); + if (LabelProcessMode::TWIG === $options->getProcessMode()) { + $sandboxed_twig = $this->sandboxedTwigProvider->createTwig($options); $current_user = $this->security->getUser(); } $page = 1; foreach ($elements as $element) { - if (isset($sandboxed_twig, $current_user) && 'twig' === $options->getLinesMode()) { + if (isset($sandboxed_twig, $current_user) && LabelProcessMode::TWIG === $options->getProcessMode()) { try { $lines = $sandboxed_twig->render( 'lines', [ 'element' => $element, 'page' => $page, + 'last_page' => count($elements), 'user' => $current_user, 'install_title' => $this->partdb_title, + 'paper_width' => $options->getWidth(), + 'paper_height' => $options->getHeight(), ] ); } catch (Error $exception) { diff --git a/src/Services/LabelSystem/LabelProfileDropdownHelper.php b/src/Services/LabelSystem/LabelProfileDropdownHelper.php index 662922f6..773923ab 100644 --- a/src/Services/LabelSystem/LabelProfileDropdownHelper.php +++ b/src/Services/LabelSystem/LabelProfileDropdownHelper.php @@ -42,35 +42,43 @@ declare(strict_types=1); namespace App\Services\LabelSystem; use App\Entity\LabelSystem\LabelProfile; +use App\Entity\LabelSystem\LabelSupportedElement; use App\Repository\LabelProfileRepository; -use App\Services\UserSystem\UserCacheKeyGenerator; +use App\Services\Cache\ElementCacheTagGenerator; +use App\Services\Cache\UserCacheKeyGenerator; use Doctrine\ORM\EntityManagerInterface; use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; final class LabelProfileDropdownHelper { - private TagAwareCacheInterface $cache; - private EntityManagerInterface $entityManager; - private UserCacheKeyGenerator $keyGenerator; - - public function __construct(TagAwareCacheInterface $treeCache, EntityManagerInterface $entityManager, UserCacheKeyGenerator $keyGenerator) - { - $this->cache = $treeCache; - $this->entityManager = $entityManager; - $this->keyGenerator = $keyGenerator; + public function __construct( + private readonly TagAwareCacheInterface $cache, + private readonly EntityManagerInterface $entityManager, + private readonly UserCacheKeyGenerator $keyGenerator, + private readonly ElementCacheTagGenerator $tagGenerator, + ) { } - public function getDropdownProfiles(string $type): array + /** + * Return all label profiles for the given supported element type + * @param LabelSupportedElement|string $type + * @return array + */ + public function getDropdownProfiles(LabelSupportedElement|string $type): array { - $secure_class_name = str_replace('\\', '_', LabelProfile::class); - $key = 'profile_dropdown_'.$this->keyGenerator->generateKey().'_'.$secure_class_name.'_'.$type; + //Useful for the twig templates, where we use the string representation of the enum + if (is_string($type)) { + $type = LabelSupportedElement::from($type); + } - /** @var LabelProfileRepository $repo */ + $secure_class_name = $this->tagGenerator->getElementTypeCacheTag(LabelProfile::class); + $key = 'profile_dropdown_'.$this->keyGenerator->generateKey().'_'.$secure_class_name.'_'.$type->value; + $repo = $this->entityManager->getRepository(LabelProfile::class); return $this->cache->get($key, function (ItemInterface $item) use ($repo, $type, $secure_class_name) { - // Invalidate when groups, a element with the class or the user changes + // Invalidate when groups, an element with the class or the user changes $item->tag(['groups', 'tree_treeview', $this->keyGenerator->generateKey(), $secure_class_name]); return $repo->getDropdownProfiles($type); diff --git a/src/Services/LabelSystem/LabelTextReplacer.php b/src/Services/LabelSystem/LabelTextReplacer.php index 5b94352b..6f0a9ee8 100644 --- a/src/Services/LabelSystem/LabelTextReplacer.php +++ b/src/Services/LabelSystem/LabelTextReplacer.php @@ -46,14 +46,12 @@ use App\Services\LabelSystem\PlaceholderProviders\PlaceholderProviderInterface; /** * This service replaces the Placeholders of the user provided lines with the proper informations. * It uses the PlaceholderProviders provided by PlaceholderProviderInterface classes. + * @see \App\Tests\Services\LabelSystem\LabelTextReplacerTest */ final class LabelTextReplacer { - private iterable $providers; - - public function __construct(iterable $providers) + public function __construct(private readonly iterable $providers) { - $this->providers = $providers; } /** @@ -66,6 +64,17 @@ final class LabelTextReplacer * @return string If the placeholder was valid, the replaced info. Otherwise the passed string. */ public function handlePlaceholder(string $placeholder, object $target): string + { + return $this->handlePlaceholderOrReturnNull($placeholder, $target) ?? $placeholder; + } + + /** + * Similar to handlePlaceholder, but returns null if the placeholder is not known (instead of the original string) + * @param string $placeholder + * @param object $target + * @return string|null + */ + public function handlePlaceholderOrReturnNull(string $placeholder, object $target): ?string { foreach ($this->providers as $provider) { /** @var PlaceholderProviderInterface $provider */ @@ -75,25 +84,24 @@ final class LabelTextReplacer } } - return $placeholder; + return null; } /** - * Replaces all placeholders in the input lines. + * Replaces all placeholders in the input lines. * * @param string $lines The input lines that should be replaced - * @param object $target the object that should be used as source for the informations + * @param object $target the object that should be used as source for the information * - * @return string the Lines with replaced informations + * @return string the Lines with replaced information */ public function replace(string $lines, object $target): string { $patterns = [ - '/(\[\[[A-Z_0-9]+\]\])/' => function ($match) use ($target) { - return $this->handlePlaceholder($match[0], $target); - }, + '/(\[\[[A-Z_0-9]+\]\])/' => fn($match): string => $this->handlePlaceholder($match[0], $target), ]; - return preg_replace_callback_array($patterns, $lines); + return preg_replace_callback_array($patterns, $lines) ?? throw new \RuntimeException('Could not replace placeholders!'); + } } diff --git a/src/Services/LabelSystem/PlaceholderProviders/AbstractDBElementProvider.php b/src/Services/LabelSystem/PlaceholderProviders/AbstractDBElementProvider.php index f765cd0c..081b3e91 100644 --- a/src/Services/LabelSystem/PlaceholderProviders/AbstractDBElementProvider.php +++ b/src/Services/LabelSystem/PlaceholderProviders/AbstractDBElementProvider.php @@ -46,11 +46,8 @@ use App\Services\ElementTypeNameGenerator; final class AbstractDBElementProvider implements PlaceholderProviderInterface { - private ElementTypeNameGenerator $elementTypeNameGenerator; - - public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator) + public function __construct(private readonly ElementTypeNameGenerator $elementTypeNameGenerator) { - $this->elementTypeNameGenerator = $elementTypeNameGenerator; } public function replace(string $placeholder, object $label_target, array $options = []): ?string diff --git a/src/Services/LabelSystem/PlaceholderProviders/BarcodeProvider.php b/src/Services/LabelSystem/PlaceholderProviders/BarcodeProvider.php index 9fbcd293..400fef35 100644 --- a/src/Services/LabelSystem/PlaceholderProviders/BarcodeProvider.php +++ b/src/Services/LabelSystem/PlaceholderProviders/BarcodeProvider.php @@ -1,4 +1,7 @@ . */ - namespace App\Services\LabelSystem\PlaceholderProviders; +use App\Entity\LabelSystem\BarcodeType; use App\Entity\LabelSystem\LabelOptions; -use App\Entity\LabelSystem\LabelProfile; -use App\Services\LabelSystem\BarcodeGenerator; +use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; +use App\Services\LabelSystem\Barcodes\BarcodeHelper; +use App\Services\LabelSystem\LabelBarcodeGenerator; use App\Services\LabelSystem\Barcodes\BarcodeContentGenerator; +use Com\Tecnick\Barcode\Exception; final class BarcodeProvider implements PlaceholderProviderInterface { - private BarcodeGenerator $barcodeGenerator; - private BarcodeContentGenerator $barcodeContentGenerator; - - public function __construct(BarcodeGenerator $barcodeGenerator, BarcodeContentGenerator $barcodeContentGenerator) + public function __construct(private readonly LabelBarcodeGenerator $barcodeGenerator, + private readonly BarcodeContentGenerator $barcodeContentGenerator, + private readonly BarcodeHelper $barcodeHelper) { - $this->barcodeGenerator = $barcodeGenerator; - $this->barcodeContentGenerator = $barcodeContentGenerator; } public function replace(string $placeholder, object $label_target, array $options = []): ?string @@ -41,7 +44,7 @@ final class BarcodeProvider implements PlaceholderProviderInterface if ('[[1D_CONTENT]]' === $placeholder) { try { return $this->barcodeContentGenerator->get1DBarcodeContent($label_target); - } catch (\InvalidArgumentException $e) { + } catch (\InvalidArgumentException) { return 'ERROR!'; } } @@ -49,29 +52,72 @@ final class BarcodeProvider implements PlaceholderProviderInterface if ('[[2D_CONTENT]]' === $placeholder) { try { return $this->barcodeContentGenerator->getURLContent($label_target); - } catch (\InvalidArgumentException $e) { + } catch (\InvalidArgumentException) { return 'ERROR!'; } } if ('[[BARCODE_QR]]' === $placeholder) { $label_options = new LabelOptions(); - $label_options->setBarcodeType('qr'); + $label_options->setBarcodeType(BarcodeType::QR); + return $this->barcodeGenerator->generateHTMLBarcode($label_options, $label_target); + } + + if ('[[BARCODE_DATAMATRIX]]' === $placeholder) { + $label_options = new LabelOptions(); + $label_options->setBarcodeType(BarcodeType::DATAMATRIX); return $this->barcodeGenerator->generateHTMLBarcode($label_options, $label_target); } if ('[[BARCODE_C39]]' === $placeholder) { $label_options = new LabelOptions(); - $label_options->setBarcodeType('code39'); + $label_options->setBarcodeType(BarcodeType::CODE39); + return $this->barcodeGenerator->generateHTMLBarcode($label_options, $label_target); + } + + if ('[[BARCODE_C93]]' === $placeholder) { + $label_options = new LabelOptions(); + $label_options->setBarcodeType(BarcodeType::CODE93); return $this->barcodeGenerator->generateHTMLBarcode($label_options, $label_target); } if ('[[BARCODE_C128]]' === $placeholder) { $label_options = new LabelOptions(); - $label_options->setBarcodeType('code128'); + $label_options->setBarcodeType(BarcodeType::CODE128); return $this->barcodeGenerator->generateHTMLBarcode($label_options, $label_target); } + if (($label_target instanceof Part || $label_target instanceof PartLot) + && str_starts_with($placeholder, '[[IPN_BARCODE_')) { + if ($label_target instanceof PartLot) { + $label_target = $label_target->getPart(); + } + + if ($label_target === null || $label_target->getIPN() === null || $label_target->getIPN() === '') { + //Replace with empty result, if no IPN is set + return ''; + } + + try { + //Add placeholders for the IPN barcode + if ('[[IPN_BARCODE_C39]]' === $placeholder) { + return $this->barcodeHelper->barcodeAsHTML($label_target->getIPN(), BarcodeType::CODE39); + } + if ('[[IPN_BARCODE_C128]]' === $placeholder) { + return $this->barcodeHelper->barcodeAsHTML($label_target->getIPN(), BarcodeType::CODE128); + } + if ('[[IPN_BARCODE_QR]]' === $placeholder) { + return $this->barcodeHelper->barcodeAsHTML($label_target->getIPN(), BarcodeType::QR); + } + } catch (Exception $e) { + //If an error occurs, output it + return 'IPN Barcode ERROR!: '.$e->getMessage(); + } + } + + + + return null; } -} \ No newline at end of file +} diff --git a/src/Services/LabelSystem/PlaceholderProviders/GlobalProviders.php b/src/Services/LabelSystem/PlaceholderProviders/GlobalProviders.php index 1dd7188a..ddd4dbf1 100644 --- a/src/Services/LabelSystem/PlaceholderProviders/GlobalProviders.php +++ b/src/Services/LabelSystem/PlaceholderProviders/GlobalProviders.php @@ -41,28 +41,21 @@ declare(strict_types=1); namespace App\Services\LabelSystem\PlaceholderProviders; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\UserSystem\User; use DateTime; use IntlDateFormatter; use Locale; -use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; /** * Provides Placeholders for infos about global infos like Installation name or datetimes. + * @see \App\Tests\Services\LabelSystem\PlaceholderProviders\GlobalProvidersTest */ final class GlobalProviders implements PlaceholderProviderInterface { - private string $partdb_title; - private Security $security; - private UrlGeneratorInterface $url_generator; - - public function __construct(string $partdb_title, Security $security, UrlGeneratorInterface $url_generator) + public function __construct(private readonly string $partdb_title, private readonly Security $security, private readonly UrlGeneratorInterface $url_generator) { - $this->partdb_title = $partdb_title; - $this->security = $security; - $this->url_generator = $url_generator; } public function replace(string $placeholder, object $label_target, array $options = []): ?string @@ -88,7 +81,7 @@ final class GlobalProviders implements PlaceholderProviderInterface return 'anonymous'; } - $now = new DateTime(); + $now = new \DateTimeImmutable(); if ('[[DATETIME]]' === $placeholder) { $formatter = IntlDateFormatter::create( diff --git a/src/Services/LabelSystem/PlaceholderProviders/NamedElementProvider.php b/src/Services/LabelSystem/PlaceholderProviders/NamedElementProvider.php index fc5fedfe..d8d38120 100644 --- a/src/Services/LabelSystem/PlaceholderProviders/NamedElementProvider.php +++ b/src/Services/LabelSystem/PlaceholderProviders/NamedElementProvider.php @@ -43,6 +43,9 @@ namespace App\Services\LabelSystem\PlaceholderProviders; use App\Entity\Contracts\NamedElementInterface; +/** + * @see \App\Tests\Services\LabelSystem\PlaceholderProviders\NamedElementProviderTest + */ final class NamedElementProvider implements PlaceholderProviderInterface { public function replace(string $placeholder, object $label_target, array $options = []): ?string diff --git a/src/Services/LabelSystem/PlaceholderProviders/PartLotProvider.php b/src/Services/LabelSystem/PlaceholderProviders/PartLotProvider.php index 3f204f32..946b4892 100644 --- a/src/Services/LabelSystem/PlaceholderProviders/PartLotProvider.php +++ b/src/Services/LabelSystem/PlaceholderProviders/PartLotProvider.php @@ -41,21 +41,21 @@ declare(strict_types=1); namespace App\Services\LabelSystem\PlaceholderProviders; +use App\Entity\Parts\StorageLocation; +use App\Entity\UserSystem\User; use App\Entity\Parts\PartLot; use App\Services\Formatters\AmountFormatter; use App\Services\LabelSystem\LabelTextReplacer; use IntlDateFormatter; use Locale; +/** + * @see \App\Tests\Services\LabelSystem\PlaceholderProviders\PartLotProviderTest + */ final class PartLotProvider implements PlaceholderProviderInterface { - private LabelTextReplacer $labelTextReplacer; - private AmountFormatter $amountFormatter; - - public function __construct(LabelTextReplacer $labelTextReplacer, AmountFormatter $amountFormatter) + public function __construct(private readonly LabelTextReplacer $labelTextReplacer, private readonly AmountFormatter $amountFormatter) { - $this->labelTextReplacer = $labelTextReplacer; - $this->amountFormatter = $amountFormatter; } public function replace(string $placeholder, object $label_target, array $options = []): ?string @@ -74,7 +74,7 @@ final class PartLotProvider implements PlaceholderProviderInterface } if ('[[EXPIRATION_DATE]]' === $placeholder) { - if (null === $label_target->getExpirationDate()) { + if (!$label_target->getExpirationDate() instanceof \DateTimeInterface) { return ''; } $formatter = IntlDateFormatter::create( @@ -95,11 +95,19 @@ final class PartLotProvider implements PlaceholderProviderInterface } if ('[[LOCATION]]' === $placeholder) { - return $label_target->getStorageLocation() ? $label_target->getStorageLocation()->getName() : ''; + return $label_target->getStorageLocation() instanceof StorageLocation ? $label_target->getStorageLocation()->getName() : ''; } if ('[[LOCATION_FULL]]' === $placeholder) { - return $label_target->getStorageLocation() ? $label_target->getStorageLocation()->getFullPath() : ''; + return $label_target->getStorageLocation() instanceof StorageLocation ? $label_target->getStorageLocation()->getFullPath() : ''; + } + + if ('[[OWNER]]' === $placeholder) { + return $label_target->getOwner() instanceof User ? $label_target->getOwner()->getFullName() : ''; + } + + if ('[[OWNER_USERNAME]]' === $placeholder) { + return $label_target->getOwner() instanceof User ? $label_target->getOwner()->getUsername() : ''; } return $this->labelTextReplacer->handlePlaceholder($placeholder, $label_target->getPart()); diff --git a/src/Services/LabelSystem/PlaceholderProviders/PartProvider.php b/src/Services/LabelSystem/PlaceholderProviders/PartProvider.php index 48e547f6..0df4d3d7 100644 --- a/src/Services/LabelSystem/PlaceholderProviders/PartProvider.php +++ b/src/Services/LabelSystem/PlaceholderProviders/PartProvider.php @@ -41,20 +41,21 @@ declare(strict_types=1); namespace App\Services\LabelSystem\PlaceholderProviders; +use App\Entity\Parts\Category; +use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\Footprint; use App\Entity\Parts\Part; use App\Services\Formatters\SIFormatter; use Parsedown; use Symfony\Contracts\Translation\TranslatorInterface; +/** + * @see \App\Tests\Services\LabelSystem\PlaceholderProviders\PartProviderTest + */ final class PartProvider implements PlaceholderProviderInterface { - private SIFormatter $siFormatter; - private TranslatorInterface $translator; - - public function __construct(SIFormatter $SIFormatter, TranslatorInterface $translator) + public function __construct(private readonly SIFormatter $siFormatter, private readonly TranslatorInterface $translator) { - $this->siFormatter = $SIFormatter; - $this->translator = $translator; } public function replace(string $placeholder, object $part, array $options = []): ?string @@ -64,27 +65,27 @@ final class PartProvider implements PlaceholderProviderInterface } if ('[[CATEGORY]]' === $placeholder) { - return $part->getCategory() ? $part->getCategory()->getName() : ''; + return $part->getCategory() instanceof Category ? $part->getCategory()->getName() : ''; } if ('[[CATEGORY_FULL]]' === $placeholder) { - return $part->getCategory() ? $part->getCategory()->getFullPath() : ''; + return $part->getCategory() instanceof Category ? $part->getCategory()->getFullPath() : ''; } if ('[[MANUFACTURER]]' === $placeholder) { - return $part->getManufacturer() ? $part->getManufacturer()->getName() : ''; + return $part->getManufacturer() instanceof Manufacturer ? $part->getManufacturer()->getName() : ''; } if ('[[MANUFACTURER_FULL]]' === $placeholder) { - return $part->getManufacturer() ? $part->getManufacturer()->getFullPath() : ''; + return $part->getManufacturer() instanceof Manufacturer ? $part->getManufacturer()->getFullPath() : ''; } if ('[[FOOTPRINT]]' === $placeholder) { - return $part->getFootprint() ? $part->getFootprint()->getName() : ''; + return $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getName() : ''; } if ('[[FOOTPRINT_FULL]]' === $placeholder) { - return $part->getFootprint() ? $part->getFootprint()->getFullPath() : ''; + return $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getFullPath() : ''; } if ('[[MASS]]' === $placeholder) { @@ -95,16 +96,20 @@ final class PartProvider implements PlaceholderProviderInterface return $part->getManufacturerProductNumber(); } + if ('[[IPN]]' === $placeholder) { + return $part->getIpn() ?? ''; + } + if ('[[TAGS]]' === $placeholder) { return $part->getTags(); } if ('[[M_STATUS]]' === $placeholder) { - if ('' === $part->getManufacturingStatus()) { + if (null === $part->getManufacturingStatus()) { return ''; } - return $this->translator->trans('m_status.'.$part->getManufacturingStatus()); + return $this->translator->trans($part->getManufacturingStatus()->toTranslationKey()); } $parsedown = new Parsedown(); @@ -114,7 +119,7 @@ final class PartProvider implements PlaceholderProviderInterface } if ('[[DESCRIPTION_T]]' === $placeholder) { - return strip_tags($parsedown->line($part->getDescription())); + return strip_tags((string) $parsedown->line($part->getDescription())); } if ('[[COMMENT]]' === $placeholder) { @@ -122,7 +127,7 @@ final class PartProvider implements PlaceholderProviderInterface } if ('[[COMMENT_T]]' === $placeholder) { - return strip_tags($parsedown->line($part->getComment())); + return strip_tags((string) $parsedown->line($part->getComment())); } return null; diff --git a/src/Services/LabelSystem/PlaceholderProviders/StorelocationProvider.php b/src/Services/LabelSystem/PlaceholderProviders/StorelocationProvider.php new file mode 100644 index 00000000..4b4d8dcd --- /dev/null +++ b/src/Services/LabelSystem/PlaceholderProviders/StorelocationProvider.php @@ -0,0 +1,44 @@ +. + */ +namespace App\Services\LabelSystem\PlaceholderProviders; + +use App\Entity\UserSystem\User; +use App\Entity\Parts\StorageLocation; + +class StorelocationProvider implements PlaceholderProviderInterface +{ + public function replace(string $placeholder, object $label_target, array $options = []): ?string + { + if ($label_target instanceof StorageLocation) { + if ('[[OWNER]]' === $placeholder) { + return $label_target->getOwner() instanceof User ? $label_target->getOwner()->getFullName() : ''; + } + + if ('[[OWNER_USERNAME]]' === $placeholder) { + return $label_target->getOwner() instanceof User ? $label_target->getOwner()->getUsername() : ''; + } + } + + return null; + } +} diff --git a/src/Services/LabelSystem/PlaceholderProviders/StructuralDBElementProvider.php b/src/Services/LabelSystem/PlaceholderProviders/StructuralDBElementProvider.php index f4aebd8a..f37f5901 100644 --- a/src/Services/LabelSystem/PlaceholderProviders/StructuralDBElementProvider.php +++ b/src/Services/LabelSystem/PlaceholderProviders/StructuralDBElementProvider.php @@ -52,16 +52,16 @@ final class StructuralDBElementProvider implements PlaceholderProviderInterface return $label_target->getComment(); } if ('[[COMMENT_T]]' === $placeholder) { - return strip_tags($label_target->getComment()); + return strip_tags((string) $label_target->getComment()); } if ('[[FULL_PATH]]' === $placeholder) { return $label_target->getFullPath(); } if ('[[PARENT]]' === $placeholder) { - return $label_target->getParent() ? $label_target->getParent()->getName() : ''; + return $label_target->getParent() instanceof AbstractStructuralDBElement ? $label_target->getParent()->getName() : ''; } if ('[[PARENT_FULL_PATH]]' === $placeholder) { - return $label_target->getParent() ? $label_target->getParent()->getFullPath() : ''; + return $label_target->getParent() instanceof AbstractStructuralDBElement ? $label_target->getParent()->getFullPath() : ''; } } diff --git a/src/Services/LabelSystem/PlaceholderProviders/TimestampableElementProvider.php b/src/Services/LabelSystem/PlaceholderProviders/TimestampableElementProvider.php index ef5967b2..b316abf2 100644 --- a/src/Services/LabelSystem/PlaceholderProviders/TimestampableElementProvider.php +++ b/src/Services/LabelSystem/PlaceholderProviders/TimestampableElementProvider.php @@ -42,10 +42,12 @@ declare(strict_types=1); namespace App\Services\LabelSystem\PlaceholderProviders; use App\Entity\Contracts\TimeStampableInterface; -use DateTime; use IntlDateFormatter; use Locale; +/** + * @see \App\Tests\Services\LabelSystem\PlaceholderProviders\TimestampableElementProviderTest + */ final class TimestampableElementProvider implements PlaceholderProviderInterface { public function replace(string $placeholder, object $label_target, array $options = []): ?string @@ -54,11 +56,11 @@ final class TimestampableElementProvider implements PlaceholderProviderInterface $formatter = new IntlDateFormatter(Locale::getDefault(), IntlDateFormatter::SHORT, IntlDateFormatter::SHORT); if ('[[LAST_MODIFIED]]' === $placeholder) { - return $formatter->format($label_target->getLastModified() ?? new DateTime()); + return $formatter->format($label_target->getLastModified() ?? new \DateTimeImmutable()); } if ('[[CREATION_DATE]]' === $placeholder) { - return $formatter->format($label_target->getAddedDate() ?? new DateTime()); + return $formatter->format($label_target->getAddedDate() ?? new \DateTimeImmutable()); } } diff --git a/src/Services/LabelSystem/SandboxedTwigProvider.php b/src/Services/LabelSystem/SandboxedTwigFactory.php similarity index 59% rename from src/Services/LabelSystem/SandboxedTwigProvider.php rename to src/Services/LabelSystem/SandboxedTwigFactory.php index 66488fca..d6ea6968 100644 --- a/src/Services/LabelSystem/SandboxedTwigProvider.php +++ b/src/Services/LabelSystem/SandboxedTwigFactory.php @@ -49,81 +49,125 @@ use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Contracts\NamedElementInterface; use App\Entity\Contracts\TimeStampableInterface; use App\Entity\LabelSystem\LabelOptions; +use App\Entity\LabelSystem\LabelProcessMode; use App\Entity\Parameters\AbstractParameter; +use App\Entity\Parts\InfoProviderReference; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; +use App\Entity\Parts\PartAssociation; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\PriceInformations\Orderdetail; use App\Entity\PriceInformations\Pricedetail; use App\Entity\UserSystem\User; +use App\Twig\BarcodeExtension; +use App\Twig\EntityExtension; use App\Twig\FormatExtension; use App\Twig\Sandbox\InheritanceSecurityPolicy; +use App\Twig\Sandbox\SandboxedLabelExtension; +use App\Twig\TwigCoreExtension; use InvalidArgumentException; use Twig\Environment; use Twig\Extension\SandboxExtension; +use Twig\Extra\Html\HtmlExtension; use Twig\Extra\Intl\IntlExtension; +use Twig\Extra\Markdown\MarkdownExtension; +use Twig\Extra\String\StringExtension; use Twig\Loader\ArrayLoader; use Twig\Sandbox\SecurityPolicyInterface; -final class SandboxedTwigProvider +/** + * This service creates a sandboxed twig environment for the label system. + * @see \App\Tests\Services\LabelSystem\SandboxedTwigFactoryTest + */ +final class SandboxedTwigFactory { private const ALLOWED_TAGS = ['apply', 'autoescape', 'do', 'for', 'if', 'set', 'verbatim', 'with']; private const ALLOWED_FILTERS = ['abs', 'batch', 'capitalize', 'column', 'country_name', - 'currency_name', 'currency_symbol', 'date', 'date_modify', 'default', 'escape', 'filter', 'first', 'format', - 'format_currency', 'format_date', 'format_datetime', 'format_number', 'format_time', 'join', 'keys', - 'language_name', 'last', 'length', 'locale_name', 'lower', 'map', 'merge', 'nl2br', 'raw', 'number_format', - 'reduce', 'replace', 'reverse', 'slice', 'sort', 'spaceless', 'split', 'striptags', 'timezone_name', 'title', - 'trim', 'upper', 'url_encode', - //Part-DB specific filters: - 'moneyFormat', 'siFormat', 'amountFormat', ]; + 'currency_name', 'currency_symbol', 'date', 'date_modify', 'data_uri', 'default', 'escape', 'filter', 'first', 'format', + 'format_currency', 'format_date', 'format_datetime', 'format_number', 'format_time', 'html_to_markdown', 'join', 'keys', + 'language_name', 'last', 'length', 'locale_name', 'lower', 'map', 'markdown_to_html', 'merge', 'nl2br', 'raw', 'number_format', + 'reduce', 'replace', 'reverse', 'round', 'slice', 'slug', 'sort', 'spaceless', 'split', 'striptags', 'timezone_name', 'title', + 'trim', 'u', 'upper', 'url_encode', - private const ALLOWED_FUNCTIONS = ['date', 'html_classes', 'max', 'min', 'random', 'range']; + //Part-DB specific filters: + + //FormatExtension: + 'format_money', 'format_si', 'format_amount', 'format_bytes', + + //SandboxedLabelExtension + 'placeholders', + ]; + + private const ALLOWED_FUNCTIONS = ['country_names', 'country_timezones', 'currency_names', 'cycle', + 'date', 'html_classes', 'language_names', 'locale_names', 'max', 'min', 'random', 'range', 'script_names', + 'template_from_string', 'timezone_names', + + //Part-DB specific extensions: + //EntityExtension: + 'entity_type', 'entity_url', + //BarcodeExtension: + 'barcode_svg', + //SandboxedLabelExtension + 'placeholder', + ]; private const ALLOWED_METHODS = [ NamedElementInterface::class => ['getName'], AbstractDBElement::class => ['getID', '__toString'], TimeStampableInterface::class => ['getLastModified', 'getAddedDate'], AbstractStructuralDBElement::class => ['isChildOf', 'isRoot', 'getParent', 'getComment', 'getLevel', - 'getFullPath', 'getPathArray', 'getChildren', 'isNotSelectable', ], - AbstractCompany::class => ['getAddress', 'getPhoneNumber', 'getFaxNumber', 'getEmailAddress', 'getWebsite'], + 'getFullPath', 'getPathArray', 'getSubelements', 'getChildren', 'isNotSelectable', ], + AbstractCompany::class => ['getAddress', 'getPhoneNumber', 'getFaxNumber', 'getEmailAddress', 'getWebsite', 'getAutoProductUrl'], AttachmentContainingDBElement::class => ['getAttachments', 'getMasterPictureAttachment'], - Attachment::class => ['isPicture', 'is3DModel', 'isExternal', 'isSecure', 'isBuiltIn', 'getExtension', - 'getElement', 'getURL', 'getFilename', 'getAttachmentType', 'getShowInTable', ], + Attachment::class => ['isPicture', 'is3DModel', 'hasExternal', 'hasInternal', 'isSecure', 'isBuiltIn', 'getExtension', + 'getElement', 'getExternalPath', 'getHost', 'getFilename', 'getAttachmentType', 'getShowInTable'], AbstractParameter::class => ['getFormattedValue', 'getGroup', 'getSymbol', 'getValueMin', 'getValueMax', 'getValueTypical', 'getUnit', 'getValueText', ], MeasurementUnit::class => ['getUnit', 'isInteger', 'useSIPrefix'], PartLot::class => ['isExpired', 'getDescription', 'getComment', 'getExpirationDate', 'getStorageLocation', - 'getPart', 'isInstockUnknown', 'getAmount', 'getNeedsRefill', ], - Storelocation::class => ['isFull', 'isOnlySinglePart', 'isLimitToExistingParts', 'getStorageType'], + 'getPart', 'isInstockUnknown', 'getAmount', 'getNeedsRefill', 'getVendorBarcode'], + StorageLocation::class => ['isFull', 'isOnlySinglePart', 'isLimitToExistingParts', 'getStorageType'], Supplier::class => ['getShippingCosts', 'getDefaultCurrency'], - Part::class => ['isNeedsReview', 'getTags', 'getMass', 'getDescription', 'isFavorite', 'getCategory', - 'getFootprint', 'getPartLots', 'getPartUnit', 'useFloatAmount', 'getMinAmount', 'getAmountSum', + Part::class => ['isNeedsReview', 'getTags', 'getMass', 'getIpn', 'getProviderReference', + 'getDescription', 'getComment', 'isFavorite', 'getCategory', 'getFootprint', + 'getPartLots', 'getPartUnit', 'useFloatAmount', 'getMinAmount', 'getAmountSum', 'isNotEnoughInstock', 'isAmountUnknown', 'getExpiredAmountSum', 'getManufacturerProductUrl', 'getCustomProductURL', 'getManufacturingStatus', 'getManufacturer', - 'getManufacturerProductNumber', 'getOrderdetails', 'isObsolete', ], + 'getManufacturerProductNumber', 'getOrderdetails', 'isObsolete', + 'getParameters', 'getGroupedParameters', + 'isProjectBuildPart', 'getBuiltProject', + 'getAssociatedPartsAsOwner', 'getAssociatedPartsAsOther', 'getAssociatedPartsAll', + 'getEdaInfo' + ], Currency::class => ['getIsoCode', 'getInverseExchangeRate', 'getExchangeRate'], Orderdetail::class => ['getPart', 'getSupplier', 'getSupplierPartNr', 'getObsolete', - 'getPricedetails', 'findPriceForQty', ], + 'getPricedetails', 'findPriceForQty', 'isObsolete', 'getSupplierProductUrl'], Pricedetail::class => ['getOrderdetail', 'getPrice', 'getPricePerUnit', 'getPriceRelatedQuantity', - 'getMinDiscountQuantity', 'getCurrency', ], + 'getMinDiscountQuantity', 'getCurrency', 'getCurrencyISOCode'], + InfoProviderReference:: class => ['getProviderKey', 'getProviderId', 'getProviderUrl', 'getLastUpdated', 'isProviderCreated'], + PartAssociation::class => ['getType', 'getComment', 'getOwner', 'getOther', 'getOtherType'], + //Only allow very little information about users... User::class => ['isAnonymousUser', 'getUsername', 'getFullName', 'getFirstName', 'getLastName', 'getDepartment', 'getEmail', ], ]; private const ALLOWED_PROPERTIES = []; - private FormatExtension $appExtension; - - public function __construct(FormatExtension $appExtension) + public function __construct( + private readonly FormatExtension $formatExtension, + private readonly BarcodeExtension $barcodeExtension, + private readonly EntityExtension $entityExtension, + private readonly TwigCoreExtension $twigCoreExtension, + private readonly SandboxedLabelExtension $sandboxedLabelExtension, + ) { - $this->appExtension = $appExtension; } - public function getTwig(LabelOptions $options): Environment + public function createTwig(LabelOptions $options): Environment { - if ('twig' !== $options->getLinesMode()) { + if (LabelProcessMode::TWIG !== $options->getProcessMode()) { throw new InvalidArgumentException('The LabelOptions must explicitly allow twig via lines_mode = "twig"!'); } @@ -138,9 +182,16 @@ final class SandboxedTwigProvider //Add IntlExtension $twig->addExtension(new IntlExtension()); + $twig->addExtension(new MarkdownExtension()); + $twig->addExtension(new StringExtension()); + $twig->addExtension(new HtmlExtension()); //Add Part-DB specific extension - $twig->addExtension($this->appExtension); + $twig->addExtension($this->formatExtension); + $twig->addExtension($this->barcodeExtension); + $twig->addExtension($this->entityExtension); + $twig->addExtension($this->twigCoreExtension); + $twig->addExtension($this->sandboxedLabelExtension); return $twig; } diff --git a/src/Services/LogSystem/EventCommentHelper.php b/src/Services/LogSystem/EventCommentHelper.php index 4afcf04d..45e95b2c 100644 --- a/src/Services/LogSystem/EventCommentHelper.php +++ b/src/Services/LogSystem/EventCommentHelper.php @@ -41,15 +41,17 @@ declare(strict_types=1); namespace App\Services\LogSystem; +/** + * @see \App\Tests\Services\LogSystem\EventCommentHelperTest + */ class EventCommentHelper { protected const MAX_MESSAGE_LENGTH = 255; - protected ?string $message; + protected ?string $message = null; public function __construct() { - $this->message = null; } /** @@ -60,11 +62,7 @@ class EventCommentHelper public function setMessage(?string $message): void { //Restrict the length of the string - if ($message) { - $this->message = mb_strimwidth($message, 0, self::MAX_MESSAGE_LENGTH, '...'); - } else { - $this->message = null; - } + $this->message = $message ? mb_strimwidth($message, 0, self::MAX_MESSAGE_LENGTH, '...') : null; } /** diff --git a/src/Services/LogSystem/EventCommentNeededHelper.php b/src/Services/LogSystem/EventCommentNeededHelper.php new file mode 100644 index 00000000..8440f199 --- /dev/null +++ b/src/Services/LogSystem/EventCommentNeededHelper.php @@ -0,0 +1,58 @@ +. + */ +namespace App\Services\LogSystem; + +/** + * 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 + */ +class EventCommentNeededHelper +{ + 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(string $comment_type): bool + { + //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); + } +} diff --git a/src/Services/LogSystem/EventLogger.php b/src/Services/LogSystem/EventLogger.php index 80ee067e..11147de6 100644 --- a/src/Services/LogSystem/EventLogger.php +++ b/src/Services/LogSystem/EventLogger.php @@ -22,26 +22,24 @@ declare(strict_types=1); namespace App\Services\LogSystem; +use App\Entity\LogSystem\LogLevel; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Security\Core\User\UserInterface; use App\Entity\LogSystem\AbstractLogEntry; use App\Entity\UserSystem\User; +use App\Services\Misc\ConsoleInfoHelper; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\Security\Core\Security; +/** + * @see \App\Tests\Services\LogSystem\EventLoggerTest + */ class EventLogger { - protected int $minimum_log_level; - protected array $blacklist; - protected array $whitelist; - protected EntityManagerInterface $em; - protected Security $security; + protected LogLevel $minimum_log_level; - public function __construct(int $minimum_log_level, array $blacklist, array $whitelist, EntityManagerInterface $em, Security $security) + public function __construct(int $minimum_log_level, protected array $blacklist, protected array $whitelist, protected EntityManagerInterface $em, protected Security $security, protected ConsoleInfoHelper $console_info_helper) { - $this->minimum_log_level = $minimum_log_level; - $this->blacklist = $blacklist; - $this->whitelist = $whitelist; - $this->em = $em; - $this->security = $security; + $this->minimum_log_level = LogLevel::tryFrom($minimum_log_level); } /** @@ -54,19 +52,24 @@ class EventLogger { $user = $this->security->getUser(); //If the user is not specified explicitly, set it to the current user - if ((null === $user || $user instanceof User) && null === $logEntry->getUser()) { - if (null === $user) { + if ((!$user instanceof UserInterface || $user instanceof User) && !$logEntry->getUser() instanceof User) { + if (!$user instanceof User) { $repo = $this->em->getRepository(User::class); $user = $repo->getAnonymousUser(); } //If no anonymous user is available skip the log (needed for data fixtures) - if (null === $user) { + if (!$user instanceof User) { return false; } $logEntry->setUser($user); } + //Set the console user info, if the log entry was created in a console command + if ($this->console_info_helper->isCLI()) { + $logEntry->setCLIUsername($this->console_info_helper->getCLIUser() ?? 'Unknown'); + } + if ($this->shouldBeAdded($logEntry)) { $this->em->persist($logEntry); @@ -79,25 +82,23 @@ class EventLogger /** * Same as log(), but this function can be safely called from within the onFlush() doctrine event, as it * updated the changesets of the unit of work. - * @param AbstractLogEntry $logEntry - * @return bool */ public function logFromOnFlush(AbstractLogEntry $logEntry): bool { if ($this->log($logEntry)) { $uow = $this->em->getUnitOfWork(); //As we call it from onFlush, we have to recompute the changeset here, according to https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/events.html#reference-events-on-flush - $uow->computeChangeSet($this->em->getClassMetadata(get_class($logEntry)), $logEntry); + $uow->computeChangeSet($this->em->getClassMetadata($logEntry::class), $logEntry); return true; } - //If the normal log function does not added the log entry, we just do nothing + //If the normal log function does not get added to the log entry, we just do nothing return false; } /** - * Adds the given log entry to the Log, if the entry fullfills the global configured criterias and flush afterwards. + * Adds the given log entry to the Log, if the entry fulfills the global configured criteria and flush afterward. * * @return bool returns true, if the event was added to log */ @@ -111,32 +112,27 @@ class EventLogger public function shouldBeAdded( AbstractLogEntry $logEntry, - ?int $minimum_log_level = null, + ?LogLevel $minimum_log_level = null, ?array $blacklist = null, ?array $whitelist = null ): bool { //Apply the global settings, if nothing was specified - $minimum_log_level = $minimum_log_level ?? $this->minimum_log_level; - $blacklist = $blacklist ?? $this->blacklist; - $whitelist = $whitelist ?? $this->whitelist; + $minimum_log_level ??= $this->minimum_log_level; + $blacklist ??= $this->blacklist; + $whitelist ??= $this->whitelist; - //Dont add the entry if it does not reach the minimum level - if ($logEntry->getLevel() > $minimum_log_level) { + //Don't add the entry if it does not reach the minimum level + if ($logEntry->getLevel()->lessImportThan($minimum_log_level)) { return false; } - //Check if the event type is black listed - if (!empty($blacklist) && $this->isObjectClassInArray($logEntry, $blacklist)) { + //Check if the event type is blacklisted + if ($blacklist !== [] && $this->isObjectClassInArray($logEntry, $blacklist)) { return false; } - //Check for whitelisting - if (!empty($whitelist) && !$this->isObjectClassInArray($logEntry, $whitelist)) { - return false; - } - - // By default all things should be added - return true; + // By default, all things should be added + return !($whitelist !== [] && !$this->isObjectClassInArray($logEntry, $whitelist)); } /** @@ -148,13 +144,13 @@ class EventLogger protected function isObjectClassInArray(object $object, array $classes): bool { //Check if the class is directly in the classes array - if (in_array(get_class($object), $classes, true)) { + if (in_array($object::class, $classes, true)) { return true; } //Iterate over all classes and check for inheritance foreach ($classes as $class) { - if (is_a($object, $class)) { + if ($object instanceof $class) { return true; } } diff --git a/src/Services/LogSystem/EventUndoHelper.php b/src/Services/LogSystem/EventUndoHelper.php index 3dd65eb1..c57f7724 100644 --- a/src/Services/LogSystem/EventUndoHelper.php +++ b/src/Services/LogSystem/EventUndoHelper.php @@ -42,33 +42,22 @@ declare(strict_types=1); namespace App\Services\LogSystem; use App\Entity\LogSystem\AbstractLogEntry; -use InvalidArgumentException; class EventUndoHelper { - public const MODE_UNDO = 'undo'; - public const MODE_REVERT = 'revert'; - - protected const ALLOWED_MODES = [self::MODE_REVERT, self::MODE_UNDO]; - - protected ?AbstractLogEntry $undone_event; - protected string $mode; + protected ?AbstractLogEntry $undone_event = null; + protected EventUndoMode $mode = EventUndoMode::UNDO; public function __construct() { - $this->undone_event = null; - $this->mode = self::MODE_UNDO; } - public function setMode(string $mode): void + public function setMode(EventUndoMode $mode): void { - if (!in_array($mode, self::ALLOWED_MODES, true)) { - throw new InvalidArgumentException('Invalid mode passed!'); - } $this->mode = $mode; } - public function getMode(): string + public function getMode(): EventUndoMode { return $this->mode; } @@ -91,7 +80,7 @@ class EventUndoHelper } /** - * Clear the currently the set undone event. + * Clear the currently set undone event. */ public function clearUndoneEvent(): void { @@ -99,7 +88,7 @@ class EventUndoHelper } /** - * Check if a event is undone. + * Check if an event is undone. */ public function isUndo(): bool { diff --git a/src/Services/LogSystem/EventUndoMode.php b/src/Services/LogSystem/EventUndoMode.php new file mode 100644 index 00000000..de30dcfd --- /dev/null +++ b/src/Services/LogSystem/EventUndoMode.php @@ -0,0 +1,48 @@ +. + */ +namespace App\Services\LogSystem; + +use InvalidArgumentException; + +enum EventUndoMode: string +{ + case UNDO = 'undo'; + case REVERT = 'revert'; + + public function toExtraInt(): int + { + return match ($this) { + self::UNDO => 1, + self::REVERT => 2, + }; + } + + public static function fromExtraInt(int $int): self + { + return match ($int) { + 1 => self::UNDO, + 2 => self::REVERT, + default => throw new InvalidArgumentException('Invalid int ' . (string) $int . ' for EventUndoMode'), + }; + } +} diff --git a/src/Services/LogSystem/HistoryHelper.php b/src/Services/LogSystem/HistoryHelper.php index e1638f41..3a31f127 100644 --- a/src/Services/LogSystem/HistoryHelper.php +++ b/src/Services/LogSystem/HistoryHelper.php @@ -44,7 +44,6 @@ namespace App\Services\LogSystem; use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractStructuralDBElement; -use App\Entity\Parameters\AbstractParameter; use App\Entity\Parts\Part; use App\Entity\ProjectSystem\Project; @@ -55,10 +54,10 @@ class HistoryHelper } /** - * Returns an array containing all elements that are associated with the argument. - * The returned array contains the given element. + * Returns an array containing all elements that are associated with the argument. + * The returned array contains the given element. * - * @psalm-return array + * @return AbstractDBElement[] */ public function getAssociatedElements(AbstractDBElement $element): array { diff --git a/src/Services/LogSystem/LogDataFormatter.php b/src/Services/LogSystem/LogDataFormatter.php new file mode 100644 index 00000000..af54c60c --- /dev/null +++ b/src/Services/LogSystem/LogDataFormatter.php @@ -0,0 +1,153 @@ +. + */ +namespace App\Services\LogSystem; + +use App\Entity\LogSystem\AbstractLogEntry; +use App\Services\ElementTypeNameGenerator; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +class LogDataFormatter +{ + private const STRING_MAX_LENGTH = 1024; + + public function __construct(private readonly TranslatorInterface $translator, private readonly EntityManagerInterface $entityManager, private readonly ElementTypeNameGenerator $elementTypeNameGenerator) + { + } + + /** + * Formats the given data of a log entry as HTML + */ + public function formatData(mixed $data, AbstractLogEntry $logEntry, string $fieldName): string + { + if (is_string($data)) { + $tmp = '"' . mb_strimwidth(htmlspecialchars($data), 0, self::STRING_MAX_LENGTH) . '"'; + + //Show special characters and line breaks + $tmp = preg_replace('/\n/', '\\n
', $tmp); + $tmp = preg_replace('/\r/', '\\r', $tmp); + + return preg_replace('/\t/', '\\t', $tmp); + } + + if (is_bool($data)) { + return $this->formatBool($data); + } + + if (is_int($data)) { + return (string) $data; + } + + if (is_float($data)) { + return (string) $data; + } + + if (is_null($data)) { + return 'null'; + } + + if (is_array($data)) { + //If the array contains only one element with the key @id, it is a reference to another entity (foreign key) + if (isset($data['@id'])) { + return $this->formatForeignKey($data, $logEntry, $fieldName); + } + + //If the array contains a "date", "timezone_type" and "timezone" key, it is a DateTime object + if (isset($data['date'], $data['timezone_type'], $data['timezone'])) { + return $this->formatDateTime($data); + } + + + return $this->formatJSON($data); + } + + + throw new \RuntimeException('Type of $data not supported (' . gettype($data) . ')'); + } + + private function formatJSON(array $data): string + { + $json = htmlspecialchars(json_encode($data, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT), ENT_QUOTES | ENT_SUBSTITUTE); + + return sprintf( + '
', + $json + ); + } + + private function formatForeignKey(array $data, AbstractLogEntry $logEntry, string $fieldName): string + { + //Extract the id from the @id key + $id = $data['@id']; + + try { + //Retrieve the class type from the logEntry and retrieve the doctrine metadata + $classMetadata = $this->entityManager->getClassMetadata($logEntry->getTargetClass()); + $fkTargetClass = $classMetadata->getAssociationTargetClass($fieldName); + + //Try to retrieve the entity from the database + $entity = $this->entityManager->getRepository($fkTargetClass)->find($id); + + //If the entity was found, return a label for this entity + if ($entity) { + return $this->elementTypeNameGenerator->formatLabelHTMLForEntity($entity, true); + } else { //Otherwise the entity was deleted, so return the id + return $this->elementTypeNameGenerator->formatElementDeletedHTML($fkTargetClass, $id); + } + + + } catch (\InvalidArgumentException|\ReflectionException) { + return 'unknown target class: ' . $id; + } + } + + private function formatDateTime(array $data): string + { + if (!isset($data['date'], $data['timezone_type'], $data['timezone'])) { + return 'unknown DateTime format'; + } + + $date = $data['date']; + $timezoneType = $data['timezone_type']; + $timezone = $data['timezone']; + + if (!is_string($date) || !is_int($timezoneType) || !is_string($timezone)) { + return 'unknown DateTime format'; + } + + try { + $dateTime = new \DateTimeImmutable($date, new \DateTimeZone($timezone)); + } catch (\Exception) { + return 'unknown DateTime format'; + } + + //Format it to the users locale + $formatter = new \IntlDateFormatter(null, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::MEDIUM); + return $formatter->format($dateTime); + } + + private function formatBool(bool $data): string + { + return $data ? $this->translator->trans('true') : $this->translator->trans('false'); + } +} diff --git a/src/Services/LogSystem/LogDiffFormatter.php b/src/Services/LogSystem/LogDiffFormatter.php new file mode 100644 index 00000000..8b165d5a --- /dev/null +++ b/src/Services/LogSystem/LogDiffFormatter.php @@ -0,0 +1,81 @@ +. + */ +namespace App\Services\LogSystem; + +use Jfcherng\Diff\DiffHelper; + +class LogDiffFormatter +{ + /** + * Format the diff between the given data, depending on the type of the data. + * If the diff is not possible, an empty string is returned. + * @param $old_data + * @param $new_data + */ + public function formatDiff($old_data, $new_data): string + { + if (is_string($old_data) && is_string($new_data)) { + return $this->diffString($old_data, $new_data); + } + + if (is_numeric($old_data) && is_numeric($new_data)) { + return $this->diffNumeric($old_data, $new_data); + } + + return ''; + } + + private function diffString(string $old_data, string $new_data): string + { + return DiffHelper::calculate($old_data, $new_data, 'Combined', + [ //Diff options + 'context' => 2, + ], + [ //Render options + 'detailLevel' => 'char', + 'showHeader' => false, + ]); + } + + /** + * @param numeric $old_data + * @param numeric $new_data + */ + private function diffNumeric(int|float|string $old_data, int|float|string $new_data): string + { + if ((!is_numeric($old_data)) || (!is_numeric($new_data))) { + throw new \InvalidArgumentException('The given data is not numeric.'); + } + + $difference = $new_data - $old_data; + + //Positive difference + if ($difference > 0) { + return sprintf('+%s', $difference); + } elseif ($difference < 0) { + return sprintf('%s', $difference); + } else { + return sprintf('%s', $difference); + } + } +} diff --git a/src/Services/LogSystem/LogEntryExtraFormatter.php b/src/Services/LogSystem/LogEntryExtraFormatter.php index bfdaf379..ae2a5eba 100644 --- a/src/Services/LogSystem/LogEntryExtraFormatter.php +++ b/src/Services/LogSystem/LogEntryExtraFormatter.php @@ -33,6 +33,7 @@ use App\Entity\LogSystem\ElementEditedLogEntry; use App\Entity\LogSystem\ExceptionLogEntry; use App\Entity\LogSystem\LegacyInstockChangedLogEntry; use App\Entity\LogSystem\PartStockChangedLogEntry; +use App\Entity\LogSystem\PartStockChangeType; use App\Entity\LogSystem\SecurityEventLogEntry; use App\Entity\LogSystem\UserLoginLogEntry; use App\Entity\LogSystem\UserLogoutLogEntry; @@ -48,17 +49,13 @@ class LogEntryExtraFormatter { protected const CONSOLE_SEARCH = ['', '', '', '', '']; protected const CONSOLE_REPLACE = ['→', '', '', '', '']; - protected TranslatorInterface $translator; - protected ElementTypeNameGenerator $elementTypeNameGenerator; - public function __construct(TranslatorInterface $translator, ElementTypeNameGenerator $elementTypeNameGenerator) + public function __construct(protected TranslatorInterface $translator, protected ElementTypeNameGenerator $elementTypeNameGenerator) { - $this->translator = $translator; - $this->elementTypeNameGenerator = $elementTypeNameGenerator; } /** - * Return an user viewable representation of the extra data in a log entry, styled for console output. + * Return a user viewable representation of the extra data in a log entry, styled for console output. */ public function formatConsole(AbstractLogEntry $logEntry): string { @@ -72,7 +69,7 @@ class LogEntryExtraFormatter $str .= ''.$this->translator->trans($key).': '; } $str .= $value; - if (!empty($str)) { + if ($str !== '') { $tmp[] = $str; } } @@ -81,7 +78,7 @@ class LogEntryExtraFormatter } /** - * Return a HTML formatted string containing a user viewable form of the Extra data. + * Return an HTML formatted string containing a user viewable form of the Extra data. */ public function format(AbstractLogEntry $context): string { @@ -95,7 +92,7 @@ class LogEntryExtraFormatter $str .= ''.$this->translator->trans($key).': '; } $str .= $value; - if (!empty($str)) { + if ($str !== '') { $tmp[] = $str; } } @@ -130,15 +127,15 @@ class LogEntryExtraFormatter } if (($context instanceof LogWithEventUndoInterface) && $context->isUndoEvent()) { - if ('undo' === $context->getUndoMode()) { - $array['log.undo_mode.undo'] = (string) $context->getUndoEventID(); - } elseif ('revert' === $context->getUndoMode()) { - $array['log.undo_mode.revert'] = (string) $context->getUndoEventID(); + if (EventUndoMode::UNDO === $context->getUndoMode()) { + $array['log.undo_mode.undo'] = '#' . $context->getUndoEventID(); + } elseif (EventUndoMode::REVERT === $context->getUndoMode()) { + $array['log.undo_mode.revert'] = '#' . $context->getUndoEventID(); } } if ($context instanceof LogWithCommentInterface && $context->hasComment()) { - $array[] = htmlspecialchars($context->getComment()); + $array[] = htmlspecialchars((string) $context->getComment()); } if ($context instanceof ElementCreatedLogEntry && $context->hasCreationInstockValue()) { @@ -163,7 +160,7 @@ class LogEntryExtraFormatter '%s %s (%s)', $context->getOldInstock(), $context->getNewInstock(), - (!$context->isWithdrawal() ? '+' : '-').$context->getDifference(true) + ($context->isWithdrawal() ? '-' : '+').$context->getDifference(true) ); $array['log.instock_changed.comment'] = htmlspecialchars($context->getComment()); } @@ -188,14 +185,18 @@ class LogEntryExtraFormatter $context->getNewStock(), ($context->getNewStock() > $context->getOldStock() ? '+' : '-'). $context->getChangeAmount(), ); - if (!empty($context->getComment())) { + if ($context->getComment() !== '') { $array['log.part_stock_changed.comment'] = htmlspecialchars($context->getComment()); } - if ($context->getInstockChangeType() === PartStockChangedLogEntry::TYPE_MOVE) { + if ($context->getInstockChangeType() === PartStockChangeType::MOVE) { $array['log.part_stock_changed.move_target'] = - $this->elementTypeNameGenerator->getLocalizedTypeLabel(PartLot::class) + htmlspecialchars($this->elementTypeNameGenerator->getLocalizedTypeLabel(PartLot::class)) .' ' . $context->getMoveToTargetID(); } + if ($context->getActionTimestamp() !== null) { + $formatter = new \IntlDateFormatter($this->translator->getLocale(), \IntlDateFormatter::SHORT, \IntlDateFormatter::SHORT); + $array['log.part_stock_changed.timestamp'] = $formatter->format($context->getActionTimestamp()); + } } return $array; diff --git a/src/Services/LogSystem/LogLevelHelper.php b/src/Services/LogSystem/LogLevelHelper.php new file mode 100644 index 00000000..67e87392 --- /dev/null +++ b/src/Services/LogSystem/LogLevelHelper.php @@ -0,0 +1,64 @@ +. + */ +namespace App\Services\LogSystem; + +use Psr\Log\LogLevel; + +class LogLevelHelper +{ + /** + * Returns the FontAwesome icon class for the given log level. + * This returns just the specific icon class (so 'fa-info' for example). + * @param string $logLevel The string representation of the log level (one of the LogLevel::* constants) + */ + public function logLevelToIconClass(string $logLevel): string + { + return match ($logLevel) { + LogLevel::DEBUG => 'fa-bug', + LogLevel::INFO => 'fa-info', + LogLevel::NOTICE => 'fa-flag', + LogLevel::WARNING => 'fa-exclamation-circle', + LogLevel::ERROR => 'fa-exclamation-triangle', + LogLevel::CRITICAL => 'fa-bolt', + LogLevel::ALERT => 'fa-radiation', + LogLevel::EMERGENCY => 'fa-skull-crossbones', + default => 'fa-question-circle', + }; + } + + /** + * Returns the Bootstrap table color class for the given log level. + * @param string $logLevel The string representation of the log level (one of the LogLevel::* constants) + * @return string The table color class (one of the 'table-*' classes) + */ + public function logLevelToTableColorClass(string $logLevel): string + { + + return match ($logLevel) { + LogLevel::EMERGENCY, LogLevel::ALERT, LogLevel::CRITICAL, LogLevel::ERROR => 'table-danger', + LogLevel::WARNING => 'table-warning', + LogLevel::NOTICE => 'table-info', + default => '', + }; + } +} diff --git a/src/Services/LogSystem/LogTargetHelper.php b/src/Services/LogSystem/LogTargetHelper.php new file mode 100644 index 00000000..5dd649f1 --- /dev/null +++ b/src/Services/LogSystem/LogTargetHelper.php @@ -0,0 +1,79 @@ +. + */ +namespace App\Services\LogSystem; + +use App\Entity\Base\AbstractDBElement; +use App\Entity\LogSystem\AbstractLogEntry; +use App\Entity\LogSystem\UserNotAllowedLogEntry; +use App\Repository\LogEntryRepository; +use App\Services\ElementTypeNameGenerator; +use App\Services\EntityURLGenerator; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\Translation\TranslatorInterface; + +class LogTargetHelper +{ + protected LogEntryRepository $entryRepository; + + public function __construct(protected EntityManagerInterface $em, protected EntityURLGenerator $entityURLGenerator, + protected ElementTypeNameGenerator $elementTypeNameGenerator, protected TranslatorInterface $translator) + { + $this->entryRepository = $em->getRepository(AbstractLogEntry::class); + } + + private function configureOptions(OptionsResolver $resolver): self + { + $resolver->setDefault('show_associated', true); + $resolver->setDefault('showAccessDeniedPath', true); + + return $this; + } + + public function formatTarget(AbstractLogEntry $context, array $options = []): string + { + $optionsResolver = new OptionsResolver(); + $this->configureOptions($optionsResolver); + $options = $optionsResolver->resolve($options); + + if ($context instanceof UserNotAllowedLogEntry && $options['showAccessDeniedPath']) { + return htmlspecialchars($context->getPath()); + } + + /** @var AbstractLogEntry $context */ + $target = $this->entryRepository->getTargetElement($context); + + //If the target is null and the context has a target, that means that the target was deleted. Show it that way. + if (!$target instanceof AbstractDBElement) { + if ($context->hasTarget()) { + return $this->elementTypeNameGenerator->formatElementDeletedHTML($context->getTargetClass(), + $context->getTargetID()); + } + //If no target is set, we can't do anything + return ''; + } + + //Otherwise we can return a label for the target + return $this->elementTypeNameGenerator->formatLabelHTMLForEntity($target, $options['show_associated']); + } +} diff --git a/src/Services/LogSystem/TimeTravel.php b/src/Services/LogSystem/TimeTravel.php index 9933d235..68d962bb 100644 --- a/src/Services/LogSystem/TimeTravel.php +++ b/src/Services/LogSystem/TimeTravel.php @@ -34,23 +34,21 @@ use App\Repository\LogEntryRepository; use Brick\Math\BigDecimal; use DateTime; use Doctrine\Common\Collections\Collection; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Mapping\MappingException; -use DoctrineExtensions\Query\Mysql\Date; use Exception; use InvalidArgumentException; use ReflectionClass; +use Symfony\Component\PropertyAccess\PropertyAccessor; class TimeTravel { - protected EntityManagerInterface $em; protected LogEntryRepository $repo; - public function __construct(EntityManagerInterface $em) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $em; $this->repo = $em->getRepository(AbstractLogEntry::class); } @@ -58,8 +56,11 @@ class TimeTravel /** * Undeletes the element with the given ID. * + * @template T of AbstractDBElement * @param string $class The class name of the element that should be undeleted + * @phpstan-param class-string $class * @param int $id the ID of the element that should be undeleted + * @phpstan-return T */ public function undeleteEntity(string $class, int $id): AbstractDBElement { @@ -83,7 +84,7 @@ class TimeTravel * * @throws Exception */ - public function revertEntityToTimestamp(AbstractDBElement $element, DateTime $timestamp, array $reverted_elements = []): void + public function revertEntityToTimestamp(AbstractDBElement $element, \DateTimeInterface $timestamp, array $reverted_elements = []): void { if (!$element instanceof TimeStampableInterface) { throw new InvalidArgumentException('$element must have a Timestamp!'); @@ -128,7 +129,7 @@ class TimeTravel } // Revert any of the associated elements - $metadata = $this->em->getClassMetadata(get_class($element)); + $metadata = $this->em->getClassMetadata($element::class); $associations = $metadata->getAssociationMappings(); foreach ($associations as $field => $mapping) { if ( @@ -138,27 +139,27 @@ class TimeTravel continue; } - //Revert many to one association (one element in property) + //Revert many-to-one association (one element in property) if ( - ClassMetadataInfo::MANY_TO_ONE === $mapping['type'] - || ClassMetadataInfo::ONE_TO_ONE === $mapping['type'] + ClassMetadata::MANY_TO_ONE === $mapping['type'] + || ClassMetadata::ONE_TO_ONE === $mapping['type'] ) { $target_element = $this->getField($element, $field); if (null !== $target_element && $element->getLastModified() > $timestamp) { $this->revertEntityToTimestamp($target_element, $timestamp, $reverted_elements); } } elseif ( //Revert *_TO_MANY associations (collection properties) - (ClassMetadataInfo::MANY_TO_MANY === $mapping['type'] - || ClassMetadataInfo::ONE_TO_MANY === $mapping['type']) - && false === $mapping['isOwningSide'] + (ClassMetadata::MANY_TO_MANY === $mapping['type'] + || ClassMetadata::ONE_TO_MANY === $mapping['type']) + && !$mapping['isOwningSide'] ) { $target_elements = $this->getField($element, $field); - if (null === $target_elements || count($target_elements) > 10) { + if (null === $target_elements || (is_countable($target_elements) ? count($target_elements) : 0) > 10) { continue; } foreach ($target_elements as $target_element) { if (null !== $target_element && $element->getLastModified() >= $timestamp) { - //Remove the element from collection, if it did not existed at $timestamp + //Remove the element from collection, if it did not exist at $timestamp if (!$this->repo->getElementExistedAtTimestamp( $target_element, $timestamp @@ -175,17 +176,26 @@ class TimeTravel /** * This function decodes the array which is created during the json_encode of a datetime object and returns a DateTime object. * @param array $input - * @return DateTime + * @return \DateTimeInterface * @throws Exception */ - private function dateTimeDecode(?array $input): ?\DateTime + private function dateTimeDecode(?array $input, string $doctrineType): ?\DateTimeInterface { //Allow null values if ($input === null) { return null; } - return new \DateTime($input['date'], new \DateTimeZone($input['timezone'])); + //Mutable types + if (in_array($doctrineType, [Types::DATETIME_MUTABLE, Types::DATE_MUTABLE], true)) { + return new \DateTime($input['date'], new \DateTimeZone($input['timezone'])); + } + //Immutable types + if (in_array($doctrineType, [Types::DATETIME_IMMUTABLE, Types::DATE_IMMUTABLE], true)) { + return new \DateTimeImmutable($input['date'], new \DateTimeZone($input['timezone'])); + } + + throw new InvalidArgumentException('The given doctrine type is not a datetime type!'); } /** @@ -196,24 +206,34 @@ class TimeTravel public function applyEntry(AbstractDBElement $element, TimeTravelInterface $logEntry): void { //Skip if this does not provide any info... - if (!$logEntry->hasOldDataInformations()) { + if (!$logEntry->hasOldDataInformation()) { return; } if (!$element instanceof TimeStampableInterface) { return; } - $metadata = $this->em->getClassMetadata(get_class($element)); + $metadata = $this->em->getClassMetadata($element::class); $old_data = $logEntry->getOldData(); foreach ($old_data as $field => $data) { - if ($metadata->hasField($field)) { + + //We use the fieldMappings property directly instead of the hasField method, as we do not want to match the embedded field itself + //The sub fields are handled in the setField method + if (isset($metadata->fieldMappings[$field])) { //We need to convert the string to a BigDecimal first - if (!$data instanceof BigDecimal && ('big_decimal' === $metadata->getFieldMapping($field)['type'])) { + if (!$data instanceof BigDecimal && ('big_decimal' === $metadata->getFieldMapping($field)->type)) { $data = BigDecimal::of($data); } - if (!$data instanceof DateTime && ('datetime' === $metadata->getFieldMapping($field)['type'])) { - $data = $this->dateTimeDecode($data); + if (!$data instanceof \DateTimeInterface + && (in_array($metadata->getFieldMapping($field)->type, + [ + Types::DATETIME_IMMUTABLE, + Types::DATETIME_IMMUTABLE, + Types::DATE_MUTABLE, + Types::DATETIME_IMMUTABLE + ], true))) { + $data = $this->dateTimeDecode($data, $metadata->getFieldMapping($field)->type); } $this->setField($element, $field, $data); @@ -223,7 +243,7 @@ class TimeTravel $target_class = $mapping['targetEntity']; //Try to extract the old ID: if (is_array($data) && isset($data['@id'])) { - $entity = $this->em->getPartialReference($target_class, $data['@id']); + $entity = $this->em->getReference($target_class, $data['@id']); $this->setField($element, $field, $entity); } } @@ -232,24 +252,45 @@ class TimeTravel $this->setField($element, 'lastModified', $logEntry->getTimestamp()); } - protected function getField(AbstractDBElement $element, string $field) + protected function getField(AbstractDBElement $element, string $field): mixed { - $reflection = new ReflectionClass(get_class($element)); + $reflection = new ReflectionClass($element::class); $property = $reflection->getProperty($field); - $property->setAccessible(true); return $property->getValue($element); } /** - * @param DateTime|int|null $new_value + * @param int|null|object $new_value */ - protected function setField(AbstractDBElement $element, string $field, $new_value): void + protected function setField(AbstractDBElement $element, string $field, mixed $new_value): void { - $reflection = new ReflectionClass(get_class($element)); - $property = $reflection->getProperty($field); - $property->setAccessible(true); + //If the field name contains a dot, it is a embeddedable object and we need to split the field name + if (str_contains($field, '.')) { + [$embedded, $embedded_field] = explode('.', $field); - $property->setValue($element, $new_value); + $elementClass = new ReflectionClass($element::class); + $property = $elementClass->getProperty($embedded); + $embeddedClass = $property->getValue($element); + + $embeddedReflection = new ReflectionClass($embeddedClass::class); + $property = $embeddedReflection->getProperty($embedded_field); + $target_element = $embeddedClass; + } else { + $reflection = new ReflectionClass($element::class); + $property = $reflection->getProperty($field); + $target_element = $element; + } + + //Check if the property is an BackedEnum, then convert the int or float value to an enum instance + if ((is_string($new_value) || is_int($new_value)) + && $property->getType() instanceof \ReflectionNamedType + && is_a($property->getType()->getName(), \BackedEnum::class, true)) { + /** @phpstan-var class-string<\BackedEnum> $enum_class */ + $enum_class = $property->getType()->getName(); + $new_value = $enum_class::from($new_value); + } + + $property->setValue($target_element, $new_value); } } diff --git a/src/Services/Misc/ConsoleInfoHelper.php b/src/Services/Misc/ConsoleInfoHelper.php new file mode 100644 index 00000000..98de5e07 --- /dev/null +++ b/src/Services/Misc/ConsoleInfoHelper.php @@ -0,0 +1,56 @@ +. + */ +namespace App\Services\Misc; + +class ConsoleInfoHelper +{ + /** + * Returns true if the current script is executed in a CLI environment. + * @return bool true if the current script is executed in a CLI environment, false otherwise + */ + public function isCLI(): bool + { + return \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true); + } + + /** + * Returns the username of the user who started the current script if possible. + * @return string|null the username of the user who started the current script if possible, null otherwise + * @noinspection PhpUndefinedFunctionInspection + */ + public function getCLIUser(): ?string + { + if (!$this->isCLI()) { + return null; + } + + //Try to use the posix extension if available (Linux) + if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { + $user = posix_getpwuid(posix_geteuid()); + return $user['name']; + } + + //Otherwise we can't determine the username + return $_SERVER['USERNAME'] ?? $_SERVER['USER'] ?? null; + } +} diff --git a/src/Services/Misc/FAIconGenerator.php b/src/Services/Misc/FAIconGenerator.php index b1687f2f..2ea727af 100644 --- a/src/Services/Misc/FAIconGenerator.php +++ b/src/Services/Misc/FAIconGenerator.php @@ -24,21 +24,24 @@ namespace App\Services\Misc; use App\Entity\Attachments\Attachment; use function in_array; -use InvalidArgumentException; +/** + * @see \App\Tests\Services\Misc\FAIconGeneratorTest + */ class FAIconGenerator { protected const EXT_MAPPING = [ - 'fa-file-pdf' => ['pdf'], + 'fa-file-pdf' => ['pdf', 'ps', 'eps'], 'fa-file-image' => Attachment::PICTURE_EXTS, - 'fa-file-alt' => ['txt', 'md', 'rtf', 'log', 'rst', 'tex'], - 'fa-file-csv' => ['csv'], - 'fa-file-word' => ['doc', 'docx', 'odt'], - 'fa-file-archive' => ['zip', 'rar', 'bz2', 'tar', '7z', 'gz'], - 'fa-file-audio' => ['mp3', 'wav', 'aac', 'm4a', 'wma'], + 'fa-file-lines' => ['txt', 'md', 'log', 'rst', 'tex'], + 'fa-file-csv' => ['csv', 'tsv'], + 'fa-file-word' => ['doc', 'docx', 'odt', 'rtf'], + 'fa-file-zipper' => ['zip', 'rar', 'bz2', 'tar', '7z', 'gz', 'tgz', 'xz', 'txz', 'tbz'], + 'fa-file-audio' => ['mp3', 'wav', 'aac', 'm4a', 'wma', 'ogg', 'flac', 'alac'], 'fa-file-powerpoint' => ['ppt', 'pptx', 'odp', 'pps', 'key'], - 'fa-file-excel' => ['xls', 'xlr', 'xlsx', 'ods'], - 'fa-file-code' => ['php', 'xml', 'html', 'js', 'ts', 'htm', 'c', 'cpp'], + 'fa-file-excel' => ['xls', 'xlr', 'xlsx', 'ods', 'numbers'], + 'fa-file-code' => ['php', 'xml', 'html', 'js', 'ts', 'htm', 'c', 'cpp', 'json', 'py', 'css', 'yml', 'yaml', + 'sql', 'sh', 'bat', 'exe', 'dll', 'lib', 'so', 'a', 'o', 'h', 'hpp', 'java', 'class', 'jar', 'rb', 'rbw', 'rake', 'gem',], 'fa-file-video' => ['webm', 'avi', 'mp4', 'mkv', 'wmv'], ]; @@ -52,9 +55,6 @@ class FAIconGenerator */ public function fileExtensionToFAType(string $extension): string { - if ('' === $extension) { - throw new InvalidArgumentException('You must specify an extension!'); - } //Normalize file extension $extension = strtolower($extension); foreach (self::EXT_MAPPING as $fa => $exts) { diff --git a/src/Services/Misc/RangeParser.php b/src/Services/Misc/RangeParser.php index ab6e9aba..f1a5db5b 100644 --- a/src/Services/Misc/RangeParser.php +++ b/src/Services/Misc/RangeParser.php @@ -45,6 +45,7 @@ use InvalidArgumentException; /** * This Parser allows to parse number ranges like 1-3, 4, 5. + * @see \App\Tests\Services\Misc\RangeParserTest */ class RangeParser { @@ -70,7 +71,7 @@ class RangeParser $ranges[] = $this->generateMinMaxRange($matches[1], $matches[2]); } elseif (is_numeric($number)) { $ranges[] = [(int) $number]; - } elseif (empty($number)) { //Allow empty tokens + } elseif ($number === '') { //Allow empty tokens continue; } else { throw new InvalidArgumentException('Invalid range encoutered: '.$number); @@ -94,7 +95,7 @@ class RangeParser $this->parse($range_str); return true; - } catch (InvalidArgumentException $exception) { + } catch (InvalidArgumentException) { return false; } } diff --git a/src/Services/OAuth/OAuthTokenManager.php b/src/Services/OAuth/OAuthTokenManager.php new file mode 100644 index 00000000..9c22503b --- /dev/null +++ b/src/Services/OAuth/OAuthTokenManager.php @@ -0,0 +1,162 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\OAuth; + +use App\Entity\OAuthToken; +use Doctrine\ORM\EntityManagerInterface; +use KnpU\OAuth2ClientBundle\Client\ClientRegistry; +use League\OAuth2\Client\Token\AccessTokenInterface; + +final class OAuthTokenManager +{ + public function __construct(private readonly ClientRegistry $clientRegistry, private readonly EntityManagerInterface $entityManager) + { + + } + + /** + * Saves the given token to the database, so it can be retrieved later + * @param string $app_name + * @param AccessTokenInterface $token + * @return OAuthToken The saved token as database entity + */ + public function saveToken(string $app_name, AccessTokenInterface $token): OAuthToken + { + //Check if we already have a token for this app + $tokenEntity = $this->entityManager->getRepository(OAuthToken::class)->findOneBy(['name' => $app_name]); + + //If the token was already existing, we just replace it with the new one + if ($tokenEntity !== null) { + $tokenEntity->replaceWithNewToken($token); + + $this->entityManager->flush(); + + //We are done + return $tokenEntity; + } + + //If the token was not existing, we create a new one + $tokenEntity = OAuthToken::fromAccessToken($token, $app_name); + $this->entityManager->persist($tokenEntity); + + $this->entityManager->flush(); + + return $tokenEntity; + } + + /** + * Returns the token for the given app name + * @param string $app_name + * @return OAuthToken|null + */ + public function getToken(string $app_name): ?OAuthToken + { + return $this->entityManager->getRepository(OAuthToken::class)->findOneBy(['name' => $app_name]); + } + + /** + * Checks if a token for the given app name is existing + * @param string $app_name + * @return bool + */ + public function hasToken(string $app_name): bool + { + return $this->getToken($app_name) !== null; + } + + /** + * This function refreshes the token for the given app name. The new token is saved to the database + * The app_name must be registered in the knpu_oauth2_client.yaml + * @param string $app_name + * @return OAuthToken + * @throws \Exception + */ + public function refreshToken(string $app_name): OAuthToken + { + $token = $this->getToken($app_name); + + if ($token === null) { + throw new \RuntimeException('No token was saved yet for '.$app_name); + } + + $client = $this->clientRegistry->getClient($app_name); + + //Check if the token is refreshable or if it is an client credentials token + if ($token->isClientCredentialsGrant()) { + $new_token = $client->getOAuth2Provider()->getAccessToken('client_credentials'); + } else { + //Otherwise we can use the refresh token to get a new access token + $new_token = $client->refreshAccessToken($token->getRefreshToken()); + } + + //Persist the token + $token->replaceWithNewToken($new_token); + $this->entityManager->flush(); + + return $token; + } + + /** + * This function returns the token of the given app name + * @param string $app_name + * @return string|null + */ + public function getAlwaysValidTokenString(string $app_name): ?string + { + //Get the token for the application + $token = $this->getToken($app_name); + + //If the token is not existing, we return null + if ($token === null) { + return null; + } + + //If the token is still valid, we return it + if (!$token->hasExpired()) { + return $token->getToken(); + } + + //If the token is expired, we refresh it + $this->refreshToken($app_name); + + //And return the new token + return $token->getToken(); + } + + /** + * Retrieves an access token for the given app name using the client credentials grant (so no user flow is needed) + * The app_name must be registered in the knpu_oauth2_client.yaml + * The token is saved to the database, and afterward can be used as usual + * @param string $app_name + * @return OAuthToken + */ + public function retrieveClientCredentialsToken(string $app_name): OAuthToken + { + $client = $this->clientRegistry->getClient($app_name); + $access_token = $client->getOAuth2Provider()->getAccessToken('client_credentials'); + + + return $this->saveToken($app_name, $access_token); + } +} \ No newline at end of file diff --git a/src/Services/Parameters/ParameterExtractor.php b/src/Services/Parameters/ParameterExtractor.php index de5ecb51..a133b282 100644 --- a/src/Services/Parameters/ParameterExtractor.php +++ b/src/Services/Parameters/ParameterExtractor.php @@ -47,6 +47,9 @@ use InvalidArgumentException; use function preg_match; +/** + * @see \App\Tests\Services\Parameters\ParameterExtractorTest + */ class ParameterExtractor { protected const ALLOWED_PARAM_SEPARATORS = [', ', "\n"]; @@ -74,7 +77,7 @@ class ParameterExtractor $split = $this->splitString($input); foreach ($split as $param_string) { $tmp = $this->stringToParam($param_string, $class); - if (null !== $tmp) { + if ($tmp instanceof AbstractParameter) { $parameters[] = $tmp; } } @@ -85,16 +88,16 @@ class ParameterExtractor protected function stringToParam(string $input, string $class): ?AbstractParameter { $input = trim($input); - $regex = '/^(.*) *(?:=|:) *(.+)/u'; + $regex = '/^(.*) *(?:=|:)(?!\/) *(.+)/u'; $matches = []; preg_match($regex, $input, $matches); - if (!empty($matches)) { + if ($matches !== []) { [, $name, $value] = $matches; $value = trim($value); - //Dont allow empty names or values (these are a sign of an invalid extracted string) - if (empty($name) || empty($value)) { + //Don't allow empty names or values (these are a sign of an invalid extracted string) + if ($name === '' || $value === '') { return null; } diff --git a/src/Services/Parts/PartLotWithdrawAddHelper.php b/src/Services/Parts/PartLotWithdrawAddHelper.php index 80403dd4..34ec4c1d 100644 --- a/src/Services/Parts/PartLotWithdrawAddHelper.php +++ b/src/Services/Parts/PartLotWithdrawAddHelper.php @@ -1,29 +1,28 @@ eventLogger = $eventLogger; - $this->eventCommentHelper = $eventCommentHelper; } /** * Checks whether the given part can - * @param PartLot $partLot - * @return bool */ public function canAdd(PartLot $partLot): bool { @@ -33,16 +32,11 @@ final class PartLotWithdrawAddHelper } //So far all other restrictions are defined at the storelocation level - if($partLot->getStorageLocation() === null) { + if(!$partLot->getStorageLocation() instanceof StorageLocation) { return true; } - //We can not add parts if the storage location of the lot is marked as full - if($partLot->getStorageLocation()->isFull()) { - return false; - } - - return true; + return !$partLot->getStorageLocation()->isFull(); } public function canWithdraw(PartLot $partLot): bool @@ -51,13 +45,8 @@ final class PartLotWithdrawAddHelper if ($partLot->isInstockUnknown()) { return false; } - //Part must contain more than 0 parts - if ($partLot->getAmount() <= 0) { - return false; - } - - return true; + return $partLot->getAmount() > 0; } /** @@ -66,9 +55,10 @@ final class PartLotWithdrawAddHelper * @param PartLot $partLot The partLot from which the instock should be taken (which value should be decreased) * @param float $amount The amount of parts that should be taken from the part lot * @param string|null $comment The optional comment describing the reason for the withdrawal - * @return PartLot The modified part lot + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. + * @param bool $delete_lot_if_empty If true, the part lot will be deleted if the amount is 0 after the withdrawal. */ - public function withdraw(PartLot $partLot, float $amount, ?string $comment = null): PartLot + public function withdraw(PartLot $partLot, float $amount, ?string $comment = null, ?\DateTimeInterface $action_timestamp = null, bool $delete_lot_if_empty = false): void { //Ensure that amount is positive if ($amount <= 0) { @@ -96,15 +86,17 @@ final class PartLotWithdrawAddHelper $oldAmount = $partLot->getAmount(); $partLot->setAmount($oldAmount - $amount); - $event = PartStockChangedLogEntry::withdraw($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment); + $event = PartStockChangedLogEntry::withdraw($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment, $action_timestamp); $this->eventLogger->log($event); //Apply the comment also to global events, so it gets associated with the elementChanged log entry - if (!$this->eventCommentHelper->isMessageSet() && !empty($comment)) { + if (!$this->eventCommentHelper->isMessageSet() && ($comment !== null && $comment !== '')) { $this->eventCommentHelper->setMessage($comment); } - return $partLot; + if ($delete_lot_if_empty && $partLot->getAmount() === 0.0) { + $this->entityManager->remove($partLot); + } } /** @@ -113,9 +105,10 @@ final class PartLotWithdrawAddHelper * @param PartLot $partLot The partLot from which the instock should be taken (which value should be decreased) * @param float $amount The amount of parts that should be taken from the part lot * @param string|null $comment The optional comment describing the reason for the withdrawal + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. * @return PartLot The modified part lot */ - public function add(PartLot $partLot, float $amount, ?string $comment = null): PartLot + public function add(PartLot $partLot, float $amount, ?string $comment = null, ?\DateTimeInterface $action_timestamp = null): PartLot { if ($amount <= 0) { throw new \InvalidArgumentException('Amount must be positive'); @@ -136,11 +129,11 @@ final class PartLotWithdrawAddHelper $oldAmount = $partLot->getAmount(); $partLot->setAmount($oldAmount + $amount); - $event = PartStockChangedLogEntry::add($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment); + $event = PartStockChangedLogEntry::add($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment, $action_timestamp); $this->eventLogger->log($event); //Apply the comment also to global events, so it gets associated with the elementChanged log entry - if (!$this->eventCommentHelper->isMessageSet() && !empty($comment)) { + if (!$this->eventCommentHelper->isMessageSet() && ($comment !== null && $comment !== '')) { $this->eventCommentHelper->setMessage($comment); } @@ -154,9 +147,10 @@ final class PartLotWithdrawAddHelper * @param PartLot $target The part lot to which the parts should be added * @param float $amount The amount of parts that should be moved * @param string|null $comment A comment describing the reason for the move - * @return void + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. + * @param bool $delete_lot_if_empty If true, the part lot will be deleted if the amount is 0 after the withdrawal. */ - public function move(PartLot $origin, PartLot $target, float $amount, ?string $comment = null): void + public function move(PartLot $origin, PartLot $target, float $amount, ?string $comment = null, ?\DateTimeInterface $action_timestamp = null, bool $delete_lot_if_empty = false): void { if ($amount <= 0) { throw new \InvalidArgumentException('Amount must be positive'); @@ -191,12 +185,16 @@ final class PartLotWithdrawAddHelper //And add it to the target $target->setAmount($target->getAmount() + $amount); - $event = PartStockChangedLogEntry::move($origin, $oldOriginAmount, $origin->getAmount(), $part->getAmountSum() , $comment, $target); + $event = PartStockChangedLogEntry::move($origin, $oldOriginAmount, $origin->getAmount(), $part->getAmountSum() , $comment, $target, $action_timestamp); $this->eventLogger->log($event); //Apply the comment also to global events, so it gets associated with the elementChanged log entry - if (!$this->eventCommentHelper->isMessageSet() && !empty($comment)) { + if (!$this->eventCommentHelper->isMessageSet() && ($comment !== null && $comment !== '')) { $this->eventCommentHelper->setMessage($comment); } + + if ($delete_lot_if_empty && $origin->getAmount() === 0.0) { + $this->entityManager->remove($origin); + } } -} \ No newline at end of file +} diff --git a/src/Services/Parts/PartsTableActionHandler.php b/src/Services/Parts/PartsTableActionHandler.php index 8b695141..616df229 100644 --- a/src/Services/Parts/PartsTableActionHandler.php +++ b/src/Services/Parts/PartsTableActionHandler.php @@ -1,4 +1,7 @@ . */ - namespace App\Services\Parts; +use App\Entity\Parts\StorageLocation; +use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Repository\DBElementRepository; use App\Repository\PartRepository; use Doctrine\ORM\EntityManagerInterface; use InvalidArgumentException; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; -use Symfony\Component\Security\Core\Security; +use Symfony\Contracts\Translation\TranslatableInterface; + +use function Symfony\Component\Translation\t; final class PartsTableActionHandler { - private EntityManagerInterface $entityManager; - private Security $security; - private UrlGeneratorInterface $urlGenerator; - - public function __construct(EntityManagerInterface $entityManager, Security $security, UrlGeneratorInterface $urlGenerator) + public function __construct(private readonly EntityManagerInterface $entityManager, private readonly Security $security, private readonly UrlGeneratorInterface $urlGenerator) { - $this->entityManager = $entityManager; - $this->security = $security; - $this->urlGenerator = $urlGenerator; } /** @@ -59,7 +57,6 @@ final class PartsTableActionHandler { $id_array = explode(',', $ids); - /** @var PartRepository $repo */ $repo = $this->entityManager->getRepository(Part::class); return $repo->getElementsFromIDArray($id_array); @@ -68,8 +65,9 @@ final class PartsTableActionHandler /** * @param Part[] $selected_parts * @return RedirectResponse|null Returns a redirect response if the user should be redirected to another page, otherwise null + * //@param-out list|array $errors */ - public function handleAction(string $action, array $selected_parts, ?int $target_id, ?string $redirect_url = null): ?RedirectResponse + public function handleAction(string $action, array $selected_parts, ?int $target_id, ?string $redirect_url = null, array &$errors = []): ?RedirectResponse { if ($action === 'add_to_project') { return new RedirectResponse( @@ -86,10 +84,8 @@ final class PartsTableActionHandler if ($action === 'generate_label') { $targets = implode(',', array_map(static fn (Part $part) => $part->getID(), $selected_parts)); } else { //For lots we have to extract the part lots - $targets = implode(',', array_map(static function (Part $part) { - //We concat the lot IDs of every part with a comma (which are later concated with a comma too per part) - return implode(',', array_map(static fn (PartLot $lot) => $lot->getID(), $part->getPartLots()->toArray())); - }, $selected_parts)); + $targets = implode(',', array_map(static fn(Part $part): string => //We concat the lot IDs of every part with a comma (which are later concated with a comma too per part) +implode(',', array_map(static fn (PartLot $lot) => $lot->getID(), $part->getPartLots()->toArray())), $selected_parts)); } return new RedirectResponse( @@ -102,6 +98,27 @@ final class PartsTableActionHandler ); } + //When action starts with "export_" we have to redirect to the export controller + $matches = []; + if (preg_match('/^export_(json|yaml|xml|csv)$/', $action, $matches)) { + $ids = implode(',', array_map(static fn (Part $part) => $part->getID(), $selected_parts)); + $level = match ($target_id) { + 2 => 'extended', + 3 => 'full', + default => 'simple', + }; + + + return new RedirectResponse( + $this->urlGenerator->generate('parts_export', [ + 'format' => $matches[1], + 'level' => $level, + 'ids' => $ids, + '_redirect' => $redirect_url + ]) + ); + } + //Iterate over the parts and apply the action to it: foreach ($selected_parts as $part) { @@ -149,6 +166,29 @@ final class PartsTableActionHandler $this->denyAccessUnlessGranted('@measurement_units.read'); $part->setPartUnit(null === $target_id ? null : $this->entityManager->find(MeasurementUnit::class, $target_id)); break; + case 'change_location': + $this->denyAccessUnlessGranted('@storelocations.read'); + //Retrieve the first part lot and set the location for it + $part_lots = $part->getPartLots(); + if ($part_lots->count() > 0) { + if ($part_lots->count() > 1) { + $errors[] = [ + 'part' => $part, + 'message' => t('parts.table.action_handler.error.part_lots_multiple'), + ]; + break; + } + + $part_lot = $part_lots->first(); + $part_lot->setStorageLocation(null === $target_id ? null : $this->entityManager->find(StorageLocation::class, $target_id)); + } else { //Create a new part lot if there are none + $part_lot = new PartLot(); + $part_lot->setPart($part); + $part_lot->setInstockUnknown(true); //We do not know how many parts are in stock, so we set it to true + $part_lot->setStorageLocation(null === $target_id ? null : $this->entityManager->find(StorageLocation::class, $target_id)); + $this->entityManager->persist($part_lot); + } + break; default: throw new InvalidArgumentException('The given action is unknown! ('.$action.')'); @@ -164,7 +204,7 @@ final class PartsTableActionHandler * * @throws AccessDeniedException */ - private function denyAccessUnlessGranted($attributes, $subject = null, string $message = 'Access Denied.'): void + private function denyAccessUnlessGranted(mixed $attributes, mixed $subject = null, string $message = 'Access Denied.'): void { if (!$this->security->isGranted($attributes, $subject)) { $exception = new AccessDeniedException($message); diff --git a/src/Services/Parts/PricedetailHelper.php b/src/Services/Parts/PricedetailHelper.php index 464034a6..092cc278 100644 --- a/src/Services/Parts/PricedetailHelper.php +++ b/src/Services/Parts/PricedetailHelper.php @@ -32,14 +32,15 @@ use Locale; use function count; +/** + * @see \App\Tests\Services\Parts\PricedetailHelperTest + */ class PricedetailHelper { - protected string $base_currency; protected string $locale; - public function __construct(string $base_currency) + public function __construct(protected string $base_currency) { - $this->base_currency = $base_currency; $this->locale = Locale::getDefault(); } @@ -56,7 +57,7 @@ class PricedetailHelper foreach ($orderdetails as $orderdetail) { $pricedetails = $orderdetail->getPricedetails(); //The orderdetail must have pricedetails, otherwise this will not work! - if (0 === count($pricedetails)) { + if (0 === (is_countable($pricedetails) ? count($pricedetails) : 0)) { continue; } @@ -67,9 +68,7 @@ class PricedetailHelper } else { // We have to sort the pricedetails manually $array = $pricedetails->map( - static function (Pricedetail $pricedetail) { - return $pricedetail->getMinDiscountQuantity(); - } + static fn(Pricedetail $pricedetail) => $pricedetail->getMinDiscountQuantity() )->toArray(); sort($array); $max_amount = end($array); @@ -103,7 +102,7 @@ class PricedetailHelper foreach ($orderdetails as $orderdetail) { $pricedetails = $orderdetail->getPricedetails(); //The orderdetail must have pricedetails, otherwise this will not work! - if (0 === count($pricedetails)) { + if (0 === (is_countable($pricedetails) ? count($pricedetails) : 0)) { continue; } @@ -153,14 +152,14 @@ class PricedetailHelper foreach ($orderdetails as $orderdetail) { $pricedetail = $orderdetail->findPriceForQty($amount); - //When we dont have informations about this amount, ignore it - if (null === $pricedetail) { + //When we don't have information about this amount, ignore it + if (!$pricedetail instanceof Pricedetail) { continue; } $converted = $this->convertMoneyToCurrency($pricedetail->getPricePerUnit(), $pricedetail->getCurrency(), $currency); - //Ignore price informations that can not be converted to base currency. - if (null !== $converted) { + //Ignore price information that can not be converted to base currency. + if ($converted instanceof BigDecimal) { $avg = $avg->plus($converted); ++$count; } @@ -170,7 +169,7 @@ class PricedetailHelper return null; } - return $avg->dividedBy($count)->toScale(Pricedetail::PRICE_PRECISION, RoundingMode::HALF_UP); + return $avg->dividedBy($count, Pricedetail::PRICE_PRECISION, RoundingMode::HALF_UP); } /** @@ -193,9 +192,9 @@ class PricedetailHelper $val_base = $value; //Convert value to base currency - if (null !== $originCurrency) { + if ($originCurrency instanceof Currency) { //Without an exchange rate we can not calculate the exchange rate - if (null === $originCurrency->getExchangeRate() || $originCurrency->getExchangeRate()->isZero()) { + if (!$originCurrency->getExchangeRate() instanceof BigDecimal || $originCurrency->getExchangeRate()->isZero()) { return null; } @@ -204,9 +203,9 @@ class PricedetailHelper $val_target = $val_base; //Convert value in base currency to target currency - if (null !== $targetCurrency) { + if ($targetCurrency instanceof Currency) { //Without an exchange rate we can not calculate the exchange rate - if (null === $targetCurrency->getExchangeRate()) { + if (!$targetCurrency->getExchangeRate() instanceof BigDecimal) { return null; } diff --git a/src/Services/ProjectSystem/ProjectBuildHelper.php b/src/Services/ProjectSystem/ProjectBuildHelper.php index 8eee0772..269c7e4c 100644 --- a/src/Services/ProjectSystem/ProjectBuildHelper.php +++ b/src/Services/ProjectSystem/ProjectBuildHelper.php @@ -1,4 +1,7 @@ . */ - namespace App\Services\ProjectSystem; +use App\Entity\Parts\Part; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; use App\Helpers\Projects\ProjectBuildRequest; use App\Services\Parts\PartLotWithdrawAddHelper; +/** + * @see \App\Tests\Services\ProjectSystem\ProjectBuildHelperTest + */ class ProjectBuildHelper { - private PartLotWithdrawAddHelper $withdraw_add_helper; - - public function __construct(PartLotWithdrawAddHelper $withdraw_add_helper) + public function __construct(private readonly PartLotWithdrawAddHelper $withdraw_add_helper) { - $this->withdraw_add_helper = $withdraw_add_helper; } /** * Returns the maximum buildable amount of the given BOM entry based on the stock of the used parts. * This function only works for BOM entries that are associated with a part. - * @param ProjectBOMEntry $projectBOMEntry - * @return int */ public function getMaximumBuildableCountForBOMEntry(ProjectBOMEntry $projectBOMEntry): int { $part = $projectBOMEntry->getPart(); - if ($part === null) { + if (!$part instanceof Part) { throw new \InvalidArgumentException('This function cannot determine the maximum buildable count for a BOM entry without a part!'); } @@ -59,13 +60,11 @@ class ProjectBuildHelper /** * Returns the maximum buildable amount of the given project, based on the stock of the used parts in the BOM. - * @param Project $project - * @return int */ public function getMaximumBuildableCount(Project $project): int { $maximum_buildable_count = PHP_INT_MAX; - foreach ($project->getBOMEntries() as $bom_entry) { + foreach ($project->getBomEntries() as $bom_entry) { //Skip BOM entries without a part (as we can not determine that) if (!$bom_entry->isPartBomEntry()) { continue; @@ -79,11 +78,9 @@ class ProjectBuildHelper } /** - * Checks if the given project can be build with the current stock. + * Checks if the given project can be built with the current stock. * This means that the maximum buildable count is greater or equal than the requested $number_of_projects - * @param Project $project - * @parm int $number_of_builds - * @return bool + * @param int $number_of_builds */ public function isProjectBuildable(Project $project, int $number_of_builds = 1): bool { @@ -91,11 +88,8 @@ class ProjectBuildHelper } /** - * Check if the given BOM entry can be build with the current stock. + * Check if the given BOM entry can be built with the current stock. * This means that the maximum buildable count is greater or equal than the requested $number_of_projects - * @param ProjectBOMEntry $bom_entry - * @param int $number_of_builds - * @return bool */ public function isBOMEntryBuildable(ProjectBOMEntry $bom_entry, int $number_of_builds = 1): bool { @@ -120,7 +114,7 @@ class ProjectBuildHelper $part = $bomEntry->getPart(); //Skip BOM entries without a part (as we can not determine that) - if ($part === null) { + if (!$part instanceof Part) { continue; } @@ -137,9 +131,7 @@ class ProjectBuildHelper /** * Withdraw the parts from the stock using the given ProjectBuildRequest and create the build parts entries, if needed. * The ProjectBuildRequest has to be validated before!! - * You have to flush changes to DB afterwards - * @param ProjectBuildRequest $buildRequest - * @return void + * You have to flush changes to DB afterward */ public function doBuild(ProjectBuildRequest $buildRequest): void { @@ -159,4 +151,4 @@ class ProjectBuildHelper $this->withdraw_add_helper->add($buildRequest->getBuildsPartLot(), $buildRequest->getNumberOfBuilds(), $message); } } -} \ No newline at end of file +} diff --git a/src/Services/ProjectSystem/ProjectBuildPartHelper.php b/src/Services/ProjectSystem/ProjectBuildPartHelper.php index 136e2ff7..218f456e 100644 --- a/src/Services/ProjectSystem/ProjectBuildPartHelper.php +++ b/src/Services/ProjectSystem/ProjectBuildPartHelper.php @@ -1,17 +1,20 @@ . + */ + +declare(strict_types=1); + + +namespace App\Services\System; + +/** + * Helper service to retrieve the banner of this Part-DB installation + */ +class BannerHelper +{ + public function __construct(private readonly string $project_dir, private readonly string $partdb_banner) + { + + } + + /** + * Retrieves the banner from either the env variable or the banner.md file. + * @return string + */ + public function getBanner(): string + { + $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; + } +} \ No newline at end of file diff --git a/src/Services/System/UpdateAvailableManager.php b/src/Services/System/UpdateAvailableManager.php new file mode 100644 index 00000000..31cb3266 --- /dev/null +++ b/src/Services/System/UpdateAvailableManager.php @@ -0,0 +1,143 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\System; + +use Psr\Log\LoggerInterface; +use Shivas\VersioningBundle\Service\VersionManagerInterface; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Cache\ItemInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Version\Version; + +/** + * This class checks if a new version of Part-DB is available. + */ +class UpdateAvailableManager +{ + + private const API_URL = 'https://api.github.com/repos/Part-DB/Part-DB-server/releases/latest'; + private const CACHE_KEY = 'uam_latest_version'; + private const CACHE_TTL = 60 * 60 * 24 * 2; // 2 day + + public function __construct(private readonly HttpClientInterface $httpClient, + private readonly CacheInterface $updateCache, private readonly VersionManagerInterface $versionManager, + private readonly bool $check_for_updates, private readonly LoggerInterface $logger, + #[Autowire(param: 'kernel.debug')] private readonly bool $is_dev_mode) + { + + } + + /** + * Gets the latest version of Part-DB as string (e.g. "1.2.3"). + * This value is cached for 2 days. + * @return string + */ + public function getLatestVersionString(): string + { + return $this->getLatestVersionInfo()['version']; + } + + /** + * Gets the latest version of Part-DB as Version object. + */ + public function getLatestVersion(): Version + { + return Version::fromString($this->getLatestVersionString()); + } + + /** + * Gets the URL to the latest version of Part-DB on GitHub. + * @return string + */ + public function getLatestVersionUrl(): string + { + return $this->getLatestVersionInfo()['url']; + } + + /** + * Checks if a new version of Part-DB is available. This value is cached for 2 days. + * @return bool + */ + public function isUpdateAvailable(): bool + { + //If we don't want to check for updates, we can return false + if (!$this->check_for_updates) { + return false; + } + + $latestVersion = $this->getLatestVersion(); + $currentVersion = $this->versionManager->getVersion(); + + return $latestVersion->isGreaterThan($currentVersion); + } + + /** + * Get the latest version info. The value is cached for 2 days. + * @return array + * @phpstan-return array{version: string, url: string} + */ + private function getLatestVersionInfo(): array + { + //If we don't want to check for updates, we can return dummy data + if (!$this->check_for_updates) { + return [ + 'version' => '0.0.1', + 'url' => 'update-checking-disabled' + ]; + } + + return $this->updateCache->get(self::CACHE_KEY, function (ItemInterface $item) { + $item->expiresAfter(self::CACHE_TTL); + try { + $response = $this->httpClient->request('GET', self::API_URL); + $result = $response->toArray(); + $tag_name = $result['tag_name']; + + // Remove the leading 'v' from the tag name + $version = substr($tag_name, 1); + + return [ + 'version' => $version, + 'url' => $result['html_url'], + ]; + } catch (\Exception $e) { + //When we are in dev mode, throw the exception, otherwise just silently log it + if ($this->is_dev_mode) { + throw $e; + } + + //In the case of an error, try it again after half of the cache time + $item->expiresAfter(self::CACHE_TTL / 2); + + $this->logger->error('Checking for updates failed: ' . $e->getMessage()); + + return [ + 'version' => '0.0.1', + 'url' => 'update-checking-error' + ]; + } + }); + } +} \ No newline at end of file diff --git a/src/Services/Tools/ExchangeRateUpdater.php b/src/Services/Tools/ExchangeRateUpdater.php index 241e2539..eac6de16 100644 --- a/src/Services/Tools/ExchangeRateUpdater.php +++ b/src/Services/Tools/ExchangeRateUpdater.php @@ -1,4 +1,7 @@ . */ - namespace App\Services\Tools; use App\Entity\PriceInformations\Currency; @@ -27,13 +29,8 @@ use Swap\Swap; class ExchangeRateUpdater { - private string $base_currency; - private Swap $swap; - - public function __construct(string $base_currency, Swap $swap) + public function __construct(private readonly string $base_currency, private readonly Swap $swap) { - $this->base_currency = $base_currency; - $this->swap = $swap; } /** diff --git a/src/Services/Tools/StatisticsHelper.php b/src/Services/Tools/StatisticsHelper.php index 60ed568d..00bb05c9 100644 --- a/src/Services/Tools/StatisticsHelper.php +++ b/src/Services/Tools/StatisticsHelper.php @@ -49,7 +49,7 @@ use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Repository\AttachmentRepository; @@ -62,13 +62,11 @@ use InvalidArgumentException; class StatisticsHelper { - protected EntityManagerInterface $em; protected PartRepository $part_repo; protected AttachmentRepository $attachment_repo; - public function __construct(EntityManagerInterface $em) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $em; $this->part_repo = $this->em->getRepository(Part::class); $this->attachment_repo = $this->em->getRepository(Attachment::class); } @@ -93,7 +91,7 @@ class StatisticsHelper } /** - * Returns the number of all parts which have price informations. + * Returns the number of all parts which have price information. * * @throws NoResultException * @throws NonUniqueResultException @@ -115,7 +113,7 @@ class StatisticsHelper 'footprint' => Footprint::class, 'manufacturer' => Manufacturer::class, 'measurement_unit' => MeasurementUnit::class, - 'storelocation' => Storelocation::class, + 'storelocation' => StorageLocation::class, 'supplier' => Supplier::class, 'currency' => Currency::class, ]; @@ -124,7 +122,6 @@ class StatisticsHelper throw new InvalidArgumentException('No count for the given type available!'); } - /** @var EntityRepository $repo */ $repo = $this->em->getRepository($arr[$type]); return $repo->count([]); @@ -147,7 +144,7 @@ class StatisticsHelper } /** - * Gets the count of all external (only containing an URL) attachments. + * Gets the count of all external (only containing a URL) attachments. * * @throws NoResultException * @throws NonUniqueResultException @@ -158,7 +155,7 @@ class StatisticsHelper } /** - * Gets the count of all attachments where the user uploaded an file. + * Gets the count of all attachments where the user uploaded a file. * * @throws NoResultException * @throws NonUniqueResultException diff --git a/src/Services/Tools/TagFinder.php b/src/Services/Tools/TagFinder.php index aa0d02bd..80c89e0f 100644 --- a/src/Services/Tools/TagFinder.php +++ b/src/Services/Tools/TagFinder.php @@ -34,11 +34,8 @@ use function array_slice; */ class TagFinder { - protected EntityManagerInterface $em; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $entityManager; } /** @@ -59,7 +56,7 @@ class TagFinder $options = $resolver->resolve($options); - //If the keyword is too short we will get to much results, which takes too much time... + //If the keyword is too short we will get too much results, which takes too much time... if (mb_strlen($keyword) < $options['min_keyword_length']) { return []; } @@ -69,7 +66,7 @@ class TagFinder $qb->select('p.tags') ->from(Part::class, 'p') - ->where('p.tags LIKE ?1') + ->where('ILIKE(p.tags, ?1) = TRUE') ->setMaxResults($options['query_limit']) //->orderBy('RAND()') ->setParameter(1, '%'.$keyword.'%'); @@ -78,7 +75,7 @@ class TagFinder //Iterate over each possible tags (which are comma separated) and extract tags which match our keyword foreach ($possible_tags as $tags) { - $tags = explode(',', $tags['tags']); + $tags = explode(',', (string) $tags['tags']); $results = array_merge($results, preg_grep($keyword_regex, $tags)); } diff --git a/src/Services/TranslationExtractor/PermissionExtractor.php b/src/Services/TranslationExtractor/PermissionExtractor.php index 4994e054..e17cba7a 100644 --- a/src/Services/TranslationExtractor/PermissionExtractor.php +++ b/src/Services/TranslationExtractor/PermissionExtractor.php @@ -32,7 +32,7 @@ use Symfony\Component\Translation\MessageCatalogue; */ final class PermissionExtractor implements ExtractorInterface { - private array $permission_structure; + private readonly array $permission_structure; private bool $finished = false; public function __construct(PermissionManager $resolver) @@ -81,7 +81,7 @@ final class PermissionExtractor implements ExtractorInterface } /** - * Sets the prefix that should be used for new found messages. + * Sets the prefix that should be used for new-found messages. * * @param string $prefix The prefix */ diff --git a/src/Services/Trees/NodesListBuilder.php b/src/Services/Trees/NodesListBuilder.php index ff8240e0..e65fa37e 100644 --- a/src/Services/Trees/NodesListBuilder.php +++ b/src/Services/Trees/NodesListBuilder.php @@ -22,64 +22,105 @@ declare(strict_types=1); namespace App\Services\Trees; +use App\Entity\Base\AbstractDBElement; +use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractStructuralDBElement; +use App\Repository\AttachmentContainingDBElementRepository; +use App\Repository\DBElementRepository; +use App\Repository\NamedDBElementRepository; use App\Repository\StructuralDBElementRepository; -use App\Services\UserSystem\UserCacheKeyGenerator; +use App\Services\Cache\ElementCacheTagGenerator; +use App\Services\Cache\UserCacheKeyGenerator; use Doctrine\ORM\EntityManagerInterface; use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; /** * This service gives you a flat list containing all structured entities in the order of the structure. + * @see \App\Tests\Services\Trees\NodesListBuilderTest */ class NodesListBuilder { - protected EntityManagerInterface $em; - protected TagAwareCacheInterface $cache; - protected UserCacheKeyGenerator $keyGenerator; - - public function __construct(EntityManagerInterface $em, TagAwareCacheInterface $treeCache, UserCacheKeyGenerator $keyGenerator) - { - $this->em = $em; - $this->keyGenerator = $keyGenerator; - $this->cache = $treeCache; + public function __construct( + protected EntityManagerInterface $em, + protected TagAwareCacheInterface $cache, + protected UserCacheKeyGenerator $keyGenerator, + protected ElementCacheTagGenerator $tagGenerator, + ) { } /** - * Gets a flattened hierachical tree. Useful for generating option lists. + * Gets a flattened hierarchical tree. Useful for generating option lists. * In difference to the Repository Function, the results here are cached. * - * @param string $class_name the class name of the entity you want to retrieve - * @param AbstractStructuralDBElement|null $parent This entity will be used as root element. Set to null, to use global root + * @template T of AbstractNamedDBElement * - * @return AbstractStructuralDBElement[] a flattened list containing the tree elements + * @param string $class_name the class name of the entity you want to retrieve + * @phpstan-param class-string $class_name + * @param AbstractStructuralDBElement|null $parent This entity will be used as root element. Set to null, to use global root + * + * @return AbstractDBElement[] a flattened list containing the tree elements + * @phpstan-return list */ public function typeToNodesList(string $class_name, ?AbstractStructuralDBElement $parent = null): array { - $parent_id = null !== $parent ? $parent->getID() : '0'; + /** + * We can not cache the entities directly, because loading them from cache will break the doctrine proxies. + */ + //Retrieve the IDs of the elements + $ids = $this->getFlattenedIDs($class_name, $parent); + + //Retrieve the elements from the IDs, the order is the same as in the $ids array + /** @var NamedDBElementRepository $repo */ + $repo = $this->em->getRepository($class_name); + + if ($repo instanceof AttachmentContainingDBElementRepository) { + return $repo->getElementsAndPreviewAttachmentByIDs($ids); + } + + return $repo->findByIDInMatchingOrder($ids); + } + + /** + * This functions returns the (cached) list of the IDs of the elements for the flattened tree. + * @template T of AbstractNamedDBElement + * @param string $class_name + * @phpstan-param class-string $class_name + * @param AbstractStructuralDBElement|null $parent + * @return int[] + */ + private function getFlattenedIDs(string $class_name, ?AbstractStructuralDBElement $parent = null): array + { + $parent_id = $parent instanceof AbstractStructuralDBElement ? $parent->getID() : '0'; // Backslashes are not allowed in cache keys - $secure_class_name = str_replace('\\', '_', $class_name); + $secure_class_name = $this->tagGenerator->getElementTypeCacheTag($class_name); $key = 'list_'.$this->keyGenerator->generateKey().'_'.$secure_class_name.$parent_id; return $this->cache->get($key, function (ItemInterface $item) use ($class_name, $parent, $secure_class_name) { - // Invalidate when groups, a element with the class or the user changes + // Invalidate when groups, an element with the class or the user changes $item->tag(['groups', 'tree_list', $this->keyGenerator->generateKey(), $secure_class_name]); - /** @var StructuralDBElementRepository $repo */ + + /** @var NamedDBElementRepository $repo */ $repo = $this->em->getRepository($class_name); - return $repo->toNodesList($parent); + + return array_map(static fn(AbstractDBElement $element) => $element->getID(), + //@phpstan-ignore-next-line For some reason phpstan does not understand that $repo is a StructuralDBElementRepository + $repo->getFlatList($parent)); }); } /** - * Returns a flattened list of all (recursive) children elements of the given AbstractStructuralDBElement. - * The value is cached for performance reasons. + * Returns a flattened list of all (recursive) children elements of the given AbstractStructuralDBElement. + * The value is cached for performance reasons. * * @template T of AbstractStructuralDBElement - * @param T $element - * @return T[] + * @param T $element + * @return AbstractStructuralDBElement[] + * + * @phpstan-return list */ public function getChildrenFlatList(AbstractStructuralDBElement $element): array { - return $this->typeToNodesList(get_class($element), $element); + return $this->typeToNodesList($element::class, $element); } } diff --git a/src/Services/Trees/SidebarTreeUpdater.php b/src/Services/Trees/SidebarTreeUpdater.php index 13c3fb6c..c0f93b1f 100644 --- a/src/Services/Trees/SidebarTreeUpdater.php +++ b/src/Services/Trees/SidebarTreeUpdater.php @@ -1,4 +1,7 @@ . */ - namespace App\Services\Trees; -use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; final class SidebarTreeUpdater { private const CACHE_KEY = 'sidebar_tree_updated'; - private const TTL = 60 * 60 * 24; // 24 hours + private const TTL = 60 * 60 * 24; - private CacheInterface $cache; - - public function __construct(TagAwareCacheInterface $treeCache) + public function __construct( + // 24 hours + private readonly TagAwareCacheInterface $cache + ) { - $this->cache = $treeCache; } /** * Returns the time when the sidebar tree was updated the last time. * The frontend uses this information to reload the sidebar tree. - * @return \DateTimeInterface */ public function getLastTreeUpdate(): \DateTimeInterface { @@ -49,7 +49,7 @@ final class SidebarTreeUpdater //This tag and therfore this whole cache gets cleared by TreeCacheInvalidationListener when a structural element is changed $item->tag('sidebar_tree_update'); - return new \DateTime(); + return new \DateTimeImmutable(); }); } -} \ No newline at end of file +} diff --git a/src/Services/Trees/StructuralElementRecursionHelper.php b/src/Services/Trees/StructuralElementRecursionHelper.php index 4038798f..bc46d7f7 100644 --- a/src/Services/Trees/StructuralElementRecursionHelper.php +++ b/src/Services/Trees/StructuralElementRecursionHelper.php @@ -27,15 +27,12 @@ use Doctrine\ORM\EntityManagerInterface; class StructuralElementRecursionHelper { - protected EntityManagerInterface $em; - - public function __construct(EntityManagerInterface $em) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $em; } /** - * Executes an function (callable) recursivly for $element and every of its children. + * Executes a function (callable) recursivly for $element and every of its children. * * @param AbstractStructuralDBElement $element The element on which the func should be executed * @param callable $func The function which should be executed for each element. diff --git a/src/Services/Trees/ToolsTreeBuilder.php b/src/Services/Trees/ToolsTreeBuilder.php index 38018c6e..18571306 100644 --- a/src/Services/Trees/ToolsTreeBuilder.php +++ b/src/Services/Trees/ToolsTreeBuilder.php @@ -23,55 +23,38 @@ declare(strict_types=1); namespace App\Services\Trees; use App\Entity\Attachments\AttachmentType; -use App\Entity\Attachments\PartAttachment; -use App\Entity\ProjectSystem\Project; use App\Entity\LabelSystem\LabelProfile; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; +use App\Entity\ProjectSystem\Project; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use App\Helpers\Trees\TreeViewNode; -use App\Services\UserSystem\UserCacheKeyGenerator; +use App\Services\Cache\UserCacheKeyGenerator; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** * This Service generates the tree structure for the tools. - * Whenever you change something here, you has to clear the cache, because the results are cached for performance reasons. + * Whenever you change something here, you have to clear the cache, because the results are cached for performance reasons. */ class ToolsTreeBuilder { - protected TranslatorInterface $translator; - protected UrlGeneratorInterface $urlGenerator; - protected UserCacheKeyGenerator $keyGenerator; - protected TagAwareCacheInterface $cache; - protected Security $security; - - public function __construct(TranslatorInterface $translator, UrlGeneratorInterface $urlGenerator, - TagAwareCacheInterface $treeCache, UserCacheKeyGenerator $keyGenerator, - Security $security) + public function __construct(protected TranslatorInterface $translator, protected UrlGeneratorInterface $urlGenerator, protected TagAwareCacheInterface $cache, protected UserCacheKeyGenerator $keyGenerator, protected Security $security) { - $this->translator = $translator; - $this->urlGenerator = $urlGenerator; - - $this->cache = $treeCache; - - $this->keyGenerator = $keyGenerator; - - $this->security = $security; } /** - * Generates the tree for the tools menu. + * Generates the tree for the tools' menu. * The result is cached. * * @return TreeViewNode[] the array containing all Nodes for the tools menu @@ -85,20 +68,20 @@ class ToolsTreeBuilder $item->tag(['tree_tools', 'groups', $this->keyGenerator->generateKey()]); $tree = []; - if (!empty($this->getToolsNode())) { + if ($this->getToolsNode() !== []) { $tree[] = (new TreeViewNode($this->translator->trans('tree.tools.tools'), null, $this->getToolsNode())) ->setIcon('fa-fw fa-treeview fa-solid fa-toolbox'); } - if (!empty($this->getEditNodes())) { + if ($this->getEditNodes() !== []) { $tree[] = (new TreeViewNode($this->translator->trans('tree.tools.edit'), null, $this->getEditNodes())) ->setIcon('fa-fw fa-treeview fa-solid fa-pen-to-square'); } - if (!empty($this->getShowNodes())) { + if ($this->getShowNodes() !== []) { $tree[] = (new TreeViewNode($this->translator->trans('tree.tools.show'), null, $this->getShowNodes())) ->setIcon('fa-fw fa-treeview fa-solid fa-eye'); } - if (!empty($this->getSystemNodes())) { + if ($this->getSystemNodes() !== []) { $tree[] = (new TreeViewNode($this->translator->trans('tree.tools.system'), null, $this->getSystemNodes())) ->setIcon('fa-fw fa-treeview fa-solid fa-server'); } @@ -143,6 +126,19 @@ class ToolsTreeBuilder $this->urlGenerator->generate('tools_ic_logos') ))->setIcon('fa-treeview fa-fw fa-solid fa-flag'); } + if ($this->security->isGranted('@parts.import')) { + $nodes[] = (new TreeViewNode( + $this->translator->trans('parts.import.title'), + $this->urlGenerator->generate('parts_import') + ))->setIcon('fa-treeview fa-fw fa-solid fa-file-import'); + } + + if ($this->security->isGranted('@info_providers.create_parts')) { + $nodes[] = (new TreeViewNode( + $this->translator->trans('info_providers.search.title'), + $this->urlGenerator->generate('info_providers_search') + ))->setIcon('fa-treeview fa-fw fa-solid fa-cloud-arrow-down'); + } return $nodes; } @@ -186,7 +182,7 @@ class ToolsTreeBuilder $this->urlGenerator->generate('manufacturer_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-industry'); } - if ($this->security->isGranted('read', new Storelocation())) { + if ($this->security->isGranted('read', new StorageLocation())) { $nodes[] = (new TreeViewNode( $this->translator->trans('tree.tools.edit.storelocation'), $this->urlGenerator->generate('store_location_new') diff --git a/src/Services/Trees/TreeViewGenerator.php b/src/Services/Trees/TreeViewGenerator.php index bc66ba47..23d6a406 100644 --- a/src/Services/Trees/TreeViewGenerator.php +++ b/src/Services/Trees/TreeViewGenerator.php @@ -25,65 +25,100 @@ namespace App\Services\Trees; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractStructuralDBElement; -use App\Entity\ProjectSystem\Project; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; +use App\Entity\ProjectSystem\Project; use App\Helpers\Trees\TreeViewNode; use App\Helpers\Trees\TreeViewNodeIterator; -use App\Helpers\Trees\TreeViewNodeState; +use App\Repository\NamedDBElementRepository; use App\Repository\StructuralDBElementRepository; +use App\Services\Cache\ElementCacheTagGenerator; +use App\Services\Cache\UserCacheKeyGenerator; use App\Services\EntityURLGenerator; -use App\Services\Formatters\MarkdownParser; -use App\Services\UserSystem\UserCacheKeyGenerator; use Doctrine\ORM\EntityManagerInterface; use InvalidArgumentException; use RecursiveIteratorIterator; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; use Symfony\Contracts\Translation\TranslatorInterface; use function count; +/** + * @see \App\Tests\Services\Trees\TreeViewGeneratorTest + */ class TreeViewGenerator { - protected $urlGenerator; - protected $em; - protected $cache; - protected $keyGenerator; - protected $translator; + public function __construct( + protected EntityURLGenerator $urlGenerator, + protected EntityManagerInterface $em, + protected TagAwareCacheInterface $cache, + protected ElementCacheTagGenerator $tagGenerator, + protected UserCacheKeyGenerator $keyGenerator, + protected TranslatorInterface $translator, + private readonly UrlGeneratorInterface $router, + protected bool $rootNodeExpandedByDefault, + protected bool $rootNodeEnabled, - protected $rootNodeExpandedByDefault; - protected $rootNodeEnabled; + ) { + } - public function __construct(EntityURLGenerator $URLGenerator, EntityManagerInterface $em, - TagAwareCacheInterface $treeCache, UserCacheKeyGenerator $keyGenerator, TranslatorInterface $translator, bool $rootNodeExpandedByDefault, bool $rootNodeEnabled) + /** + * Gets a TreeView list for the entities of the given class. + * The result is cached, if the full tree should be shown and no element should be selected. + * + * @param string $class The class for which the treeView should be generated + * @param AbstractStructuralDBElement|null $parent The root nodes in the tree should have this element as parent (use null, if you want to get all entities) + * @param string $mode The link type that will be generated for the hyperlink section of each node (see EntityURLGenerator for possible values). + * Set to empty string, to disable href field. + * @param AbstractDBElement|null $selectedElement The element that should be selected. If set to null, no element will be selected. + * + * @return TreeViewNode[] an array of TreeViewNode[] elements of the root elements + */ + public function getTreeView( + string $class, + ?AbstractStructuralDBElement $parent = null, + string $mode = 'list_parts', + ?AbstractDBElement $selectedElement = null + ): array { - $this->urlGenerator = $URLGenerator; - $this->em = $em; - $this->cache = $treeCache; - $this->keyGenerator = $keyGenerator; - $this->translator = $translator; + //If we just want a part of a tree, don't cache it or select a specific element, don't cache it + if ($parent instanceof AbstractStructuralDBElement || $selectedElement instanceof AbstractDBElement) { + return $this->getTreeViewUncached($class, $parent, $mode, $selectedElement); + } - $this->rootNodeExpandedByDefault = $rootNodeExpandedByDefault; - $this->rootNodeEnabled = $rootNodeEnabled; + $secure_class_name = $this->tagGenerator->getElementTypeCacheTag($class); + $key = 'sidebar_treeview_'.$this->keyGenerator->generateKey().'_'.$secure_class_name; + $key .= $mode; + + return $this->cache->get($key, function (ItemInterface $item) use ($class, $parent, $mode, $selectedElement, $secure_class_name) { + // Invalidate when groups, an element with the class or the user changes + $item->tag(['groups', 'tree_treeview', $this->keyGenerator->generateKey(), $secure_class_name]); + return $this->getTreeViewUncached($class, $parent, $mode, $selectedElement); + }); } /** * Gets a TreeView list for the entities of the given class. * - * @param string $class The class for which the treeView should be generated - * @param AbstractStructuralDBElement|null $parent The root nodes in the tree should have this element as parent (use null, if you want to get all entities) - * @param string $mode The link type that will be generated for the hyperlink section of each node (see EntityURLGenerator for possible values). + * @param string $class The class for which the treeView should be generated + * @param AbstractStructuralDBElement|null $parent The root nodes in the tree should have this element as parent (use null, if you want to get all entities) + * @param string $mode The link type that will be generated for the hyperlink section of each node (see EntityURLGenerator for possible values). * Set to empty string, to disable href field. - * @param AbstractDBElement|null $selectedElement The element that should be selected. If set to null, no element will be selected. + * @param AbstractDBElement|null $selectedElement The element that should be selected. If set to null, no element will be selected. * * @return TreeViewNode[] an array of TreeViewNode[] elements of the root elements */ - public function getTreeView(string $class, ?AbstractStructuralDBElement $parent = null, string $mode = 'list_parts', ?AbstractDBElement $selectedElement = null): array - { + private function getTreeViewUncached( + string $class, + ?AbstractStructuralDBElement $parent = null, + string $mode = 'list_parts', + ?AbstractDBElement $selectedElement = null + ): array { $head = []; $href_type = $mode; @@ -91,10 +126,11 @@ class TreeViewGenerator //When we use the newEdit type, add the New Element node. if ('newEdit' === $mode) { //Generate the url for the new node - $href = $this->urlGenerator->createURL(new $class()); + //DO NOT try to create an object from the class, as this might be an proxy, which can not be easily initialized, so just pass the class_name directly + $href = $this->urlGenerator->createURL($class); $new_node = new TreeViewNode($this->translator->trans('entity.tree.new'), $href); //When the id of the selected element is null, then we have a new element, and we need to select "new" node - if (null === $selectedElement || null === $selectedElement->getID()) { + if (!$selectedElement instanceof AbstractDBElement || null === $selectedElement->getID()) { $new_node->setSelected(true); } $head[] = $new_node; @@ -118,27 +154,30 @@ class TreeViewGenerator $recursiveIterator = new RecursiveIteratorIterator($treeIterator, RecursiveIteratorIterator::SELF_FIRST); foreach ($recursiveIterator as $item) { /** @var TreeViewNode $item */ - if (null !== $selectedElement && $item->getId() === $selectedElement->getID()) { + if ($selectedElement instanceof AbstractDBElement && $item->getId() === $selectedElement->getID()) { $item->setSelected(true); } - if (!empty($item->getNodes())) { - $item->addTag((string) count($item->getNodes())); + if ($item->getNodes() !== null && $item->getNodes() !== []) { + $item->addTag((string)count($item->getNodes())); } - if (!empty($href_type) && null !== $item->getId()) { - $entity = $this->em->getPartialReference($class, $item->getId()); + if ($href_type !== '' && null !== $item->getId()) { + $entity = $this->em->find($class, $item->getId()); $item->setHref($this->urlGenerator->getURL($entity, $href_type)); } //Translate text if text starts with $$ - if (0 === strpos($item->getText(), '$$')) { + if (str_starts_with($item->getText(), '$$')) { $item->setText($this->translator->trans(substr($item->getText(), 2))); } } if (($mode === 'list_parts_root' || $mode === 'devices') && $this->rootNodeEnabled) { - $root_node = new TreeViewNode($this->entityClassToRootNodeString($class), null, $generic); + //We show the root node as a link to the list of all parts + $show_all_parts_url = $this->router->generate('parts_show_all'); + + $root_node = new TreeViewNode($this->entityClassToRootNodeString($class), $show_all_parts_url, $generic); $root_node->setExpanded($this->rootNodeExpandedByDefault); $root_node->setIcon($this->entityClassToRootNodeIcon($class)); @@ -150,43 +189,29 @@ class TreeViewGenerator protected function entityClassToRootNodeString(string $class): string { - switch ($class) { - case Category::class: - return $this->translator->trans('category.labelp'); - case Storelocation::class: - return $this->translator->trans('storelocation.labelp'); - case Footprint::class: - return $this->translator->trans('footprint.labelp'); - case Manufacturer::class: - return $this->translator->trans('manufacturer.labelp'); - case Supplier::class: - return $this->translator->trans('supplier.labelp'); - case Project::class: - return $this->translator->trans('project.labelp'); - default: - return $this->translator->trans('tree.root_node.text'); - } + return match ($class) { + Category::class => $this->translator->trans('category.labelp'), + StorageLocation::class => $this->translator->trans('storelocation.labelp'), + Footprint::class => $this->translator->trans('footprint.labelp'), + Manufacturer::class => $this->translator->trans('manufacturer.labelp'), + Supplier::class => $this->translator->trans('supplier.labelp'), + Project::class => $this->translator->trans('project.labelp'), + default => $this->translator->trans('tree.root_node.text'), + }; } protected function entityClassToRootNodeIcon(string $class): ?string { $icon = "fa-fw fa-treeview fa-solid "; - switch ($class) { - case Category::class: - return $icon . 'fa-tags'; - case Storelocation::class: - return $icon . 'fa-cube'; - case Footprint::class: - return $icon . 'fa-microchip'; - case Manufacturer::class: - return $icon . 'fa-industry'; - case Supplier::class: - return $icon . 'fa-truck'; - case Project::class: - return $icon . 'fa-archive'; - default: - return null; - } + return match ($class) { + Category::class => $icon.'fa-tags', + StorageLocation::class => $icon.'fa-cube', + Footprint::class => $icon.'fa-microchip', + Manufacturer::class => $icon.'fa-industry', + Supplier::class => $icon.'fa-truck', + Project::class => $icon.'fa-archive', + default => null, + }; } /** @@ -194,8 +219,9 @@ class TreeViewGenerator * Gets a tree of TreeViewNode elements. The root elements has $parent as parent. * The treeview is generic, that means the href are null and ID values are set. * - * @param string $class The class for which the tree should be generated - * @param AbstractStructuralDBElement|null $parent the parent the root elements should have + * @param string $class The class for which the tree should be generated + * @phpstan-param class-string $class + * @param AbstractStructuralDBElement|null $parent the parent the root elements should have * * @return TreeViewNode[] */ @@ -204,26 +230,25 @@ class TreeViewGenerator if (!is_a($class, AbstractNamedDBElement::class, true)) { throw new InvalidArgumentException('$class must be a class string that implements StructuralDBElement or NamedDBElement!'); } - if (null !== $parent && !is_a($parent, $class)) { + if ($parent instanceof AbstractStructuralDBElement && !$parent instanceof $class) { throw new InvalidArgumentException('$parent must be of the type $class!'); } - /** @var StructuralDBElementRepository $repo */ + /** @var NamedDBElementRepository $repo */ $repo = $this->em->getRepository($class); - //If we just want a part of a tree, dont cache it - if (null !== $parent) { - return $repo->getGenericNodeTree($parent); + //If we just want a part of a tree, don't cache it + if ($parent instanceof AbstractStructuralDBElement) { + return $repo->getGenericNodeTree($parent); //@phpstan-ignore-line PHPstan does not seem to recognize, that we have a StructuralDBElementRepository here, which have 1 argument } - $secure_class_name = str_replace('\\', '_', $class); + $secure_class_name = $this->tagGenerator->getElementTypeCacheTag($class); $key = 'treeview_'.$this->keyGenerator->generateKey().'_'.$secure_class_name; return $this->cache->get($key, function (ItemInterface $item) use ($repo, $parent, $secure_class_name) { - // Invalidate when groups, a element with the class or the user changes + // Invalidate when groups, an element with the class or the user changes $item->tag(['groups', 'tree_treeview', $this->keyGenerator->generateKey(), $secure_class_name]); - - return $repo->getGenericNodeTree($parent); + return $repo->getGenericNodeTree($parent); //@phpstan-ignore-line }); } } diff --git a/src/Services/UserSystem/PasswordResetManager.php b/src/Services/UserSystem/PasswordResetManager.php index 7b8a5be3..1a78cb60 100644 --- a/src/Services/UserSystem/PasswordResetManager.php +++ b/src/Services/UserSystem/PasswordResetManager.php @@ -35,21 +35,13 @@ use Symfony\Contracts\Translation\TranslatorInterface; class PasswordResetManager { - protected MailerInterface $mailer; - protected EntityManagerInterface $em; protected PasswordHasherInterface $passwordEncoder; - protected TranslatorInterface $translator; - protected UserPasswordHasherInterface $userPasswordEncoder; - public function __construct(MailerInterface $mailer, EntityManagerInterface $em, - TranslatorInterface $translator, UserPasswordHasherInterface $userPasswordEncoder, + public function __construct(protected MailerInterface $mailer, protected EntityManagerInterface $em, + protected TranslatorInterface $translator, protected UserPasswordHasherInterface $userPasswordEncoder, PasswordHasherFactoryInterface $encoderFactory) { - $this->em = $em; - $this->mailer = $mailer; $this->passwordEncoder = $encoderFactory->getPasswordHasher(User::class); - $this->translator = $translator; - $this->userPasswordEncoder = $userPasswordEncoder; } public function request(string $name_or_email): void @@ -59,7 +51,7 @@ class PasswordResetManager //Try to find a user by the given string $user = $repo->findByEmailOrName($name_or_email); //Do nothing if no user was found - if (null === $user) { + if (!$user instanceof User) { return; } @@ -67,11 +59,10 @@ class PasswordResetManager $user->setPwResetToken($this->passwordEncoder->hash($unencrypted_token)); //Determine the expiration datetime of - $expiration_date = new DateTime(); - $expiration_date->add(date_interval_create_from_date_string('1 day')); + $expiration_date = new \DateTimeImmutable("+1 day"); $user->setPwResetExpires($expiration_date); - if (!empty($user->getEmail())) { + if ($user->getEmail() !== null && $user->getEmail() !== '') { $address = new Address($user->getEmail(), $user->getFullName()); $mail = new TemplatedEmail(); $mail->to($address); @@ -105,16 +96,15 @@ class PasswordResetManager { //Try to find the user $repo = $this->em->getRepository(User::class); - /** @var User|null $user */ - $user = $repo->findOneBy(['name' => $username]); + $user = $repo->findByUsername($username); //If no user matching the name, show an error message - if (null === $user) { + if (!$user instanceof User) { return false; } //Check if token is expired yet - if ($user->getPwResetExpires() < new DateTime()) { + if ($user->getPwResetExpires() < new \DateTimeImmutable()) { return false; } @@ -128,7 +118,7 @@ class PasswordResetManager //Remove token $user->setPwResetToken(null); - $user->setPwResetExpires(new DateTime()); + $user->setPwResetExpires(new \DateTimeImmutable()); //Save to DB $this->em->flush(); diff --git a/src/Services/UserSystem/PermissionManager.php b/src/Services/UserSystem/PermissionManager.php index 717c0bac..663a7b67 100644 --- a/src/Services/UserSystem/PermissionManager.php +++ b/src/Services/UserSystem/PermissionManager.php @@ -36,23 +36,21 @@ use Symfony\Component\Yaml\Yaml; * This class manages the permissions of users and groups. * Permissions are defined in the config/permissions.yaml file, and are parsed and resolved by this class using the * user and hierachical group PermissionData information. + * @see \App\Tests\Services\UserSystem\PermissionManagerTest */ class PermissionManager { - protected $permission_structure; - - protected bool $is_debug; + protected array $permission_structure; protected string $cache_file; /** * PermissionResolver constructor. */ - public function __construct(bool $kernel_debug, string $kernel_cache_dir) + public function __construct(protected readonly bool $kernel_debug_enabled, string $kernel_cache_dir) { $cache_dir = $kernel_cache_dir; //Here the cached structure will be saved. $this->cache_file = $cache_dir.'/permissions.php.cache'; - $this->is_debug = $kernel_debug; $this->permission_structure = $this->generatePermissionStructure(); } @@ -113,8 +111,8 @@ class PermissionManager /** @var Group $parent */ $parent = $user->getGroup(); - while (null !== $parent) { //The top group, has parent == null - //Check if our current element gives a info about disallow/allow + while ($parent instanceof Group) { //The top group, has parent == null + //Check if our current element gives an info about disallow/allow $allowed = $this->dontInherit($parent, $permission, $operation); if (null !== $allowed) { return $allowed; @@ -123,7 +121,41 @@ class PermissionManager $parent = $parent->getParent(); } - return null; //The inherited value is never resolved. Should be treat as false, in Voters. + return null; //The inherited value is never resolved. Should be treated as false, in Voters. + } + + /** + * Same as inherit(), but it checks if the access token has the required role. + * @param User $user the user for which the operation should be checked + * @param array $roles The roles associated with the authentication token + * @param string $permission the name of the permission for which should be checked + * @param string $operation the name of the operation for which should be checked + * + * @return bool|null true, if the user is allowed to do the operation (ALLOW), false if not (DISALLOW), and null, + * if the value is set to inherit + */ + public function inheritWithAPILevel(User $user, array $roles, string $permission, string $operation): ?bool + { + //Check that the permission/operation combination is valid + if (! $this->isValidOperation($permission, $operation)) { + throw new InvalidArgumentException('The permission/operation combination "'.$permission.'/'.$operation.'" is not valid!'); + } + + //Get what API level we require for the permission/operation + $level_role = $this->permission_structure['perms'][$permission]['operations'][$operation]['apiTokenRole']; + + //When no role was set (or it is null), then the operation is blocked for API access + if (null === $level_role) { + return false; + } + + //Otherwise check if the token has the required role, if not, then the operation is blocked for API access + if (!in_array($level_role, $roles, true)) { + return false; + } + + //If we have the required role, then we can check the permission + return $this->inherit($user, $permission, $operation); } /** @@ -150,7 +182,7 @@ class PermissionManager /** * Lists the names of all operations that is supported for the given permission. * - * If the Permission is not existing at all, a exception is thrown. + * If the Permission is not existing at all, an exception is thrown. * * This function is useful for the support() function of the voters. * @@ -196,14 +228,15 @@ class PermissionManager /** * This functions sets all operations mentioned in the alsoSet value of a permission, so that the structure is always valid. - * @param HasPermissionsInterface $user - * @return void + * This function should be called after every setPermission() call. + * @return bool true if values were changed/corrected, false if not */ - public function ensureCorrectSetOperations(HasPermissionsInterface $user): void + public function ensureCorrectSetOperations(HasPermissionsInterface $user): bool { //If we have changed anything on the permission structure due to the alsoSet value, this becomes true, so we //redo the whole process, to ensure that all alsoSet values are set recursively. - $anything_changed = false; + + $return_value = false; do { $anything_changed = false; //Reset the variable for the next iteration @@ -216,31 +249,26 @@ class PermissionManager //Set every op listed in also Set foreach ($op['alsoSet'] as $set_also) { //If the alsoSet value contains a dot then we set the operation of another permission - if (false !== strpos($set_also, '.')) { - [$set_perm, $set_op] = explode('.', $set_also); - } else { - //Else we set the operation of the same permission - [$set_perm, $set_op] = [$perm_key, $set_also]; - } + [$set_perm, $set_op] = str_contains((string) $set_also, '.') ? explode('.', (string) $set_also) : [$perm_key, $set_also]; //Check if we change the value of the permission if ($this->dontInherit($user, $set_perm, $set_op) !== true) { $this->setPermission($user, $set_perm, $set_op, true); //Mark the change, so we redo the whole process $anything_changed = true; + $return_value = true; } } } } } } while($anything_changed); + + return $return_value; } /** * Sets all possible operations of all possible permissions of the given entity to the given value. - * @param HasPermissionsInterface $perm_holder - * @param bool|null $new_value - * @return void */ public function setAllPermissions(HasPermissionsInterface $perm_holder, ?bool $new_value): void { @@ -254,11 +282,6 @@ class PermissionManager /** * Sets all operations of the given permissions to the given value. * Please note that you have to call ensureCorrectSetOperations() after this function, to ensure that all alsoSet values are set. - * - * @param HasPermissionsInterface $perm_holder - * @param string $permission - * @param bool|null $new_value - * @return void */ public function setAllOperationsOfPermission(HasPermissionsInterface $perm_holder, string $permission, ?bool $new_value): void { @@ -271,9 +294,47 @@ class PermissionManager } } + /** + * This function sets all operations of the given permission to the given value, except the ones listed in the except array. + */ + public function setAllOperationsOfPermissionExcept(HasPermissionsInterface $perm_holder, string $permission, ?bool $new_value, array $except): void + { + if (!$this->isValidPermission($permission)) { + throw new InvalidArgumentException(sprintf('A permission with that name is not existing! Got %s.', $permission)); + } + + foreach ($this->permission_structure['perms'][$permission]['operations'] as $op_key => $op) { + if (in_array($op_key, $except, true)) { + continue; + } + $this->setPermission($perm_holder, $permission, $op_key, $new_value); + } + } + + /** + * This function checks if the given user has any permission set to allow, either directly or inherited. + * @param User $user + * @return bool + */ + public function hasAnyPermissionSetToAllowInherited(User $user): bool + { + //Iterate over all permissions + foreach ($this->permission_structure['perms'] as $perm_key => $permission) { + //Iterate over all operations of the permission + foreach ($permission['operations'] as $op_key => $op) { + //Check if the user has the permission set to allow + if ($this->inherit($user, $perm_key, $op_key) === true) { + return true; + } + } + } + + return false; + } + protected function generatePermissionStructure() { - $cache = new ConfigCache($this->cache_file, $this->is_debug); + $cache = new ConfigCache($this->cache_file, $this->kernel_debug_enabled); //Check if the cache is fresh, else regenerate it. if (!$cache->isFresh()) { diff --git a/src/Services/UserSystem/PermissionPresetsHelper.php b/src/Services/UserSystem/PermissionPresetsHelper.php index 732e29f5..eeb80f61 100644 --- a/src/Services/UserSystem/PermissionPresetsHelper.php +++ b/src/Services/UserSystem/PermissionPresetsHelper.php @@ -1,4 +1,7 @@ . */ - namespace App\Services\UserSystem; use App\Entity\UserSystem\PermissionData; @@ -25,18 +27,15 @@ use App\Security\Interfaces\HasPermissionsInterface; class PermissionPresetsHelper { - public const PRESET_ALL_INHERIT = 'all_inherit'; - public const PRESET_ALL_FORBID = 'all_forbid'; - public const PRESET_ALL_ALLOW = 'all_allow'; - public const PRESET_READ_ONLY = 'read_only'; - public const PRESET_EDITOR = 'editor'; - public const PRESET_ADMIN = 'admin'; + final public const PRESET_ALL_INHERIT = 'all_inherit'; + final public const PRESET_ALL_FORBID = 'all_forbid'; + final public const PRESET_ALL_ALLOW = 'all_allow'; + final public const PRESET_READ_ONLY = 'read_only'; + final public const PRESET_EDITOR = 'editor'; + final public const PRESET_ADMIN = 'admin'; - private PermissionManager $permissionResolver; - - public function __construct(PermissionManager $permissionResolver) + public function __construct(private readonly PermissionManager $permissionResolver) { - $this->permissionResolver = $permissionResolver; } /** @@ -44,11 +43,10 @@ class PermissionPresetsHelper * The permission data will be reset during the process and then the preset will be applied. * * @param string $preset_name The name of the preset to use - * @return HasPermissionsInterface */ public function applyPreset(HasPermissionsInterface $perm_holder, string $preset_name): HasPermissionsInterface { - //We need to reset the permission data first (afterwards all values are inherit) + //We need to reset the permission data first (afterward all values are inherit) $perm_holder->getPermissions()->resetPermissions(); switch($preset_name) { @@ -93,6 +91,25 @@ class PermissionPresetsHelper //Allow access to system log and server infos $this->permissionResolver->setPermission($perm_holder, 'system', 'show_logs', PermissionData::ALLOW); $this->permissionResolver->setPermission($perm_holder, 'system', 'server_infos', PermissionData::ALLOW); + + //Allow import for all datastructures + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'parts', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'parts_stock', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'categories', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'storelocations', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'footprints', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'manufacturers', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'attachment_types', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'currencies', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'measurement_units', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'suppliers', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'projects', PermissionData::ALLOW); + + //Allow to manage Oauth tokens + $this->permissionResolver->setPermission($perm_holder, 'system', 'manage_oauth_tokens', PermissionData::ALLOW); + //Allow to show updates + $this->permissionResolver->setPermission($perm_holder, 'system', 'show_updates', PermissionData::ALLOW); + } private function editor(HasPermissionsInterface $permHolder): HasPermissionsInterface @@ -101,17 +118,18 @@ class PermissionPresetsHelper $this->readOnly($permHolder); //Set datastructures - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'parts', PermissionData::ALLOW); + //By default import is restricted to administrators, as it allows to fill up the database very fast + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'parts', PermissionData::ALLOW, ['import']); $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'parts_stock', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'categories', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'storelocations', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'footprints', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'manufacturers', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'attachment_types', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'currencies', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'measurement_units', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'suppliers', PermissionData::ALLOW); - $this->permissionResolver->setAllOperationsOfPermission($permHolder, 'projects', PermissionData::ALLOW); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'categories', PermissionData::ALLOW, ['import']); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'storelocations', PermissionData::ALLOW, ['import']); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'footprints', PermissionData::ALLOW, ['import']); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'manufacturers', PermissionData::ALLOW, ['import']); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'attachment_types', PermissionData::ALLOW, ['import']); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'currencies', PermissionData::ALLOW, ['import']); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'measurement_units', PermissionData::ALLOW, ['import']); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'suppliers', PermissionData::ALLOW, ['import']); + $this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'projects', PermissionData::ALLOW, ['import']); //Attachments permissions $this->permissionResolver->setPermission($permHolder, 'attachments', 'show_private', PermissionData::ALLOW); @@ -126,6 +144,9 @@ class PermissionPresetsHelper //Various other permissions $this->permissionResolver->setPermission($permHolder, 'tools', 'lastActivity', PermissionData::ALLOW); + //Allow to create parts from information providers + $this->permissionResolver->setPermission($permHolder, 'info_providers', 'create_parts', PermissionData::ALLOW); + return $permHolder; } @@ -159,15 +180,15 @@ class PermissionPresetsHelper return $perm_holder; } - private function AllForbid(HasPermissionsInterface $perm_holder): HasPermissionsInterface + private function allForbid(HasPermissionsInterface $perm_holder): HasPermissionsInterface { $this->permissionResolver->setAllPermissions($perm_holder, PermissionData::DISALLOW); return $perm_holder; } - private function AllAllow(HasPermissionsInterface $perm_holder): HasPermissionsInterface + private function allAllow(HasPermissionsInterface $perm_holder): HasPermissionsInterface { $this->permissionResolver->setAllPermissions($perm_holder, PermissionData::ALLOW); return $perm_holder; } -} \ No newline at end of file +} diff --git a/src/Services/UserSystem/PermissionSchemaUpdater.php b/src/Services/UserSystem/PermissionSchemaUpdater.php index e8ebc6d0..104800dc 100644 --- a/src/Services/UserSystem/PermissionSchemaUpdater.php +++ b/src/Services/UserSystem/PermissionSchemaUpdater.php @@ -1,4 +1,7 @@ . */ - namespace App\Services\UserSystem; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\PermissionData; use App\Entity\UserSystem\User; +use App\Helpers\TrinaryLogicHelper; use App\Security\Interfaces\HasPermissionsInterface; +/** + * @see \App\Tests\Services\UserSystem\PermissionSchemaUpdaterTest + */ class PermissionSchemaUpdater { /** * Check if the given user/group needs an update of its permission schema. - * @param HasPermissionsInterface $holder * @return bool True if the permission schema needs an update, false otherwise. */ public function isSchemaUpdateNeeded(HasPermissionsInterface $holder): bool { $perm_data = $holder->getPermissions(); - if ($perm_data->getSchemaVersion() < PermissionData::CURRENT_SCHEMA_VERSION) { - return true; - } - - return false; + return $perm_data->getSchemaVersion() < PermissionData::CURRENT_SCHEMA_VERSION; } /** * Upgrades the permission schema of the given user/group to the chosen version. * Please note that this function does not flush the changes to DB! - * @param HasPermissionsInterface $holder - * @param int $target_version * @return bool True, if an upgrade was done, false if it was not needed. */ public function upgradeSchema(HasPermissionsInterface $holder, int $target_version = PermissionData::CURRENT_SCHEMA_VERSION): bool { + $e = null; if ($target_version > PermissionData::CURRENT_SCHEMA_VERSION) { throw new \InvalidArgumentException('The target version is higher than the maximum possible schema version!'); } @@ -66,11 +66,9 @@ class PermissionSchemaUpdater $reflectionClass = new \ReflectionClass(self::class); try { $method = $reflectionClass->getMethod('upgradeSchemaToVersion'.($n + 1)); - //Set the method accessible, so we can call it (needed for PHP < 8.1) - $method->setAccessible(true); $method->invoke($this, $holder); } catch (\ReflectionException $e) { - throw new \RuntimeException('Could not find update method for schema version '.($n + 1)); + throw new \RuntimeException('Could not find update method for schema version '.($n + 1), $e->getCode(), $e); } //Bump the schema version @@ -84,8 +82,6 @@ class PermissionSchemaUpdater /** * Upgrades the permission schema of the given group and all of its parent groups to the chosen version. * Please note that this function does not flush the changes to DB! - * @param Group $group - * @param int $target_version * @return bool True if an upgrade was done, false if it was not needed. */ public function groupUpgradeSchemaRecursively(Group $group, int $target_version = PermissionData::CURRENT_SCHEMA_VERSION): bool @@ -105,21 +101,19 @@ class PermissionSchemaUpdater /** * Upgrades the permissions schema of the given users and its parent (including parent groups) to the chosen version. * Please note that this function does not flush the changes to DB! - * @param User $user - * @param int $target_version * @return bool True if an upgrade was done, false if it was not needed. */ public function userUpgradeSchemaRecursively(User $user, int $target_version = PermissionData::CURRENT_SCHEMA_VERSION): bool { $updated = $this->upgradeSchema($user, $target_version); - if ($user->getGroup()) { + if ($user->getGroup() instanceof Group) { $updated = $this->groupUpgradeSchemaRecursively($user->getGroup(), $target_version) || $updated; } return $updated; } - private function upgradeSchemaToVersion1(HasPermissionsInterface $holder): void + private function upgradeSchemaToVersion1(HasPermissionsInterface $holder): void //@phpstan-ignore-line This is called via reflection { //Use the part edit permission to set the preset value for the new part stock permission if ( @@ -136,7 +130,7 @@ class PermissionSchemaUpdater } } - private function upgradeSchemaToVersion2(HasPermissionsInterface $holder): void + private function upgradeSchemaToVersion2(HasPermissionsInterface $holder): void //@phpstan-ignore-line This is called via reflection { //If the projects permissions are not defined yet, rename devices permission to projects (just copy its data over) if (!$holder->getPermissions()->isAnyOperationOfPermissionSet('projects')) { @@ -145,4 +139,22 @@ class PermissionSchemaUpdater $holder->getPermissions()->removePermission('devices'); } } -} \ No newline at end of file + + private function upgradeSchemaToVersion3(HasPermissionsInterface $holder): void //@phpstan-ignore-line This is called via reflection + { + $permissions = $holder->getPermissions(); + + //If the system.show_updates permission is not defined yet, set it to true, if the user can view server info, server logs or edit users or groups + if (!$permissions->isPermissionSet('system', 'show_updates')) { + + $new_value = TrinaryLogicHelper::or( + $permissions->getPermissionValue('system', 'server_infos'), + $permissions->getPermissionValue('system', 'show_logs'), + $permissions->getPermissionValue('users', 'edit'), + $permissions->getPermissionValue('groups', 'edit') + ); + + $permissions->setPermissionValue('system', 'show_updates', $new_value); + } + } +} diff --git a/src/Services/UserSystem/TFA/BackupCodeGenerator.php b/src/Services/UserSystem/TFA/BackupCodeGenerator.php index bc47cab8..a13b9804 100644 --- a/src/Services/UserSystem/TFA/BackupCodeGenerator.php +++ b/src/Services/UserSystem/TFA/BackupCodeGenerator.php @@ -26,12 +26,12 @@ use Exception; use RuntimeException; /** - * This class generates random backup codes for two factor authentication. + * This class generates random backup codes for two-factor authentication. + * @see \App\Tests\Services\UserSystem\TFA\BackupCodeGeneratorTest */ class BackupCodeGenerator { protected int $code_length; - protected int $code_count; /** * BackupCodeGenerator constructor. @@ -39,7 +39,7 @@ class BackupCodeGenerator * @param int $code_length how many characters a single code should have * @param int $code_count how many codes are generated for a whole backup set */ - public function __construct(int $code_length, int $code_count) + public function __construct(int $code_length, protected int $code_count) { if ($code_length > 32) { throw new RuntimeException('Backup code can have maximum 32 digits!'); @@ -47,8 +47,6 @@ class BackupCodeGenerator if ($code_length < 6) { throw new RuntimeException('Code must have at least 6 digits to ensure security!'); } - - $this->code_count = $code_count; $this->code_length = $code_length; } diff --git a/src/Services/UserSystem/TFA/BackupCodeManager.php b/src/Services/UserSystem/TFA/BackupCodeManager.php index 9a422aa3..07484618 100644 --- a/src/Services/UserSystem/TFA/BackupCodeManager.php +++ b/src/Services/UserSystem/TFA/BackupCodeManager.php @@ -25,30 +25,28 @@ namespace App\Services\UserSystem\TFA; use App\Entity\UserSystem\User; /** - * This services offers methods to manage backup codes for two factor authentication. + * This services offers methods to manage backup codes for two-factor authentication. + * @see \App\Tests\Services\UserSystem\TFA\BackupCodeManagerTest */ class BackupCodeManager { - protected BackupCodeGenerator $backupCodeGenerator; - - public function __construct(BackupCodeGenerator $backupCodeGenerator) + public function __construct(protected BackupCodeGenerator $backupCodeGenerator) { - $this->backupCodeGenerator = $backupCodeGenerator; } /** * Enable backup codes for the given user, by generating a set of backup codes. - * If the backup codes were already enabled before, they a. + * If the backup codes were already enabled before, nothing happens. */ public function enableBackupCodes(User $user): void { - if (empty($user->getBackupCodes())) { + if ($user->getBackupCodes() === []) { $this->regenerateBackupCodes($user); } } /** - * Disable (remove) the backup codes when no other 2 factor authentication methods are enabled. + * Disable (remove) the backup codes when no other two-factor authentication methods are enabled. */ public function disableBackupCodesIfUnused(User $user): void { diff --git a/src/Services/UserSystem/TFA/DecoratedGoogleAuthenticator.php b/src/Services/UserSystem/TFA/DecoratedGoogleAuthenticator.php new file mode 100644 index 00000000..05e5ed4c --- /dev/null +++ b/src/Services/UserSystem/TFA/DecoratedGoogleAuthenticator.php @@ -0,0 +1,72 @@ +. + */ +namespace App\Services\UserSystem\TFA; + +use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; +use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated; +use Symfony\Component\HttpFoundation\RequestStack; + +#[AsDecorator(GoogleAuthenticatorInterface::class)] +class DecoratedGoogleAuthenticator implements GoogleAuthenticatorInterface +{ + + public function __construct( + #[AutowireDecorated] + private readonly GoogleAuthenticatorInterface $inner, + private readonly RequestStack $requestStack) + { + + } + + public function checkCode(TwoFactorInterface $user, string $code): bool + { + return $this->inner->checkCode($user, $code); + } + + public function getQRContent(TwoFactorInterface $user): string + { + $qr_content = $this->inner->getQRContent($user); + + //Replace $$DOMAIN$$ with the current domain + $request = $this->requestStack->getCurrentRequest(); + + //If no request is available, just put "Part-DB" as domain + $domain = "Part-DB"; + + if ($request !== null) { + $domain = $request->getHttpHost(); + } + + //Domain must be url encoded + $domain = urlencode($domain); + + return str_replace(urlencode('$$DOMAIN$$'), $domain, $qr_content); + } + + public function generateSecret(): string + { + return $this->inner->generateSecret(); + } +} diff --git a/src/Services/UserSystem/UserAvatarHelper.php b/src/Services/UserSystem/UserAvatarHelper.php index 95b94dca..a694fa77 100644 --- a/src/Services/UserSystem/UserAvatarHelper.php +++ b/src/Services/UserSystem/UserAvatarHelper.php @@ -1,4 +1,7 @@ use_gravatar = $use_gravatar; - $this->packages = $packages; - $this->attachmentURLGenerator = $attachmentURLGenerator; - $this->filterService = $filterService; - $this->entityManager = $entityManager; - $this->submitHandler = $attachmentSubmitHandler; + public function __construct( + private readonly bool $use_gravatar, + private readonly Packages $packages, + private readonly AttachmentURLGenerator $attachmentURLGenerator, + private readonly EntityManagerInterface $entityManager, + private readonly AttachmentSubmitHandler $submitHandler + ) { } /** - * Returns the URL to the profile picture of the given user (in big size) - * @param User $user + * Returns the URL to the profile picture of the given user (in big size) + * * @return string */ public function getAvatarURL(User $user): string { //Check if the user has a master attachment defined (meaning he has explicitly defined a profile picture) - if ($user->getMasterPictureAttachment() !== null) { - return $this->attachmentURLGenerator->getThumbnailURL($user->getMasterPictureAttachment(), 'thumbnail_md'); + if ($user->getMasterPictureAttachment() instanceof Attachment) { + return $this->attachmentURLGenerator->getThumbnailURL($user->getMasterPictureAttachment(), 'thumbnail_md') + ?? $this->packages->getUrl(self::IMG_DEFAULT_AVATAR_PATH); } //If not check if gravatar is enabled (then use gravatar URL) @@ -70,14 +70,15 @@ class UserAvatarHelper } //Fallback to the default avatar picture - return $this->packages->getUrl('/img/default_avatar.png'); + return $this->packages->getUrl(self::IMG_DEFAULT_AVATAR_PATH); } public function getAvatarSmURL(User $user): string { //Check if the user has a master attachment defined (meaning he has explicitly defined a profile picture) - if ($user->getMasterPictureAttachment() !== null) { - return $this->attachmentURLGenerator->getThumbnailURL($user->getMasterPictureAttachment(), 'thumbnail_xs'); + if ($user->getMasterPictureAttachment() instanceof Attachment) { + return $this->attachmentURLGenerator->getThumbnailURL($user->getMasterPictureAttachment(), 'thumbnail_xs') + ?? $this->packages->getUrl(self::IMG_DEFAULT_AVATAR_PATH); } //If not check if gravatar is enabled (then use gravatar URL) @@ -85,20 +86,16 @@ class UserAvatarHelper return $this->getGravatar($user, 50); //50px wide picture } - try { - //Otherwise we can serve the relative path via Asset component - return $this->filterService->getUrlOfFilteredImage('/img/default_avatar.png', 'thumbnail_xs'); - } catch (\Imagine\Exception\RuntimeException $e) { - //If the filter fails, we can not serve the thumbnail and fall back to the original image and log an warning - return $this->packages->getUrl('/img/default_avatar.png'); - } + //Otherwise serve the default image (its an SVG, so we dont need to thumbnail it) + return $this->packages->getUrl(self::IMG_DEFAULT_AVATAR_PATH); } public function getAvatarMdURL(User $user): string { //Check if the user has a master attachment defined (meaning he has explicitly defined a profile picture) - if ($user->getMasterPictureAttachment() !== null) { - return $this->attachmentURLGenerator->getThumbnailURL($user->getMasterPictureAttachment(), 'thumbnail_sm'); + if ($user->getMasterPictureAttachment() instanceof Attachment) { + return $this->attachmentURLGenerator->getThumbnailURL($user->getMasterPictureAttachment(), 'thumbnail_sm') + ?? $this->packages->getUrl(self::IMG_DEFAULT_AVATAR_PATH); } //If not check if gravatar is enabled (then use gravatar URL) @@ -106,20 +103,15 @@ class UserAvatarHelper return $this->getGravatar($user, 150); } - try { - //Otherwise we can serve the relative path via Asset component - return $this->filterService->getUrlOfFilteredImage('/img/default_avatar.png', 'thumbnail_xs'); - } catch (\Imagine\Exception\RuntimeException $e) { - //If the filter fails, we can not serve the thumbnail and fall back to the original image and log an warning - return $this->packages->getUrl('/img/default_avatar.png'); - } + //Otherwise serve the default image (its an SVG, so we dont need to thumbnail it) + return $this->packages->getUrl(self::IMG_DEFAULT_AVATAR_PATH); } /** * Get either a Gravatar URL or complete image tag for a specified email address. * - * @param User $user The user for which the gravator should be generated + * @param User $user The user for which the gravator should be generated * @param int $s Size in pixels, defaults to 80px [ 1 - 2048 ] * @param string $d Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ] * @param string $r Maximum rating (inclusive) [ g | pg | r | x ] @@ -130,28 +122,24 @@ class UserAvatarHelper private function getGravatar(User $user, int $s = 80, string $d = 'identicon', string $r = 'g'): string { $email = $user->getEmail(); - if (empty($email)) { + if ($email === null || $email === '') { $email = 'Part-DB'; } $url = 'https://www.gravatar.com/avatar/'; $url .= md5(strtolower(trim($email))); - $url .= "?s=${s}&d=${d}&r=${r}"; - return $url; + return $url."?s=$s&d=$d&r=$r"; } /** * Handles the upload of the user avatar. - * @param User $user - * @param UploadedFile $file - * @return Attachment */ public function handleAvatarUpload(User $user, UploadedFile $file): Attachment { //Determine which attachment to user //If the user already has a master attachment, we use this one - if ($user->getMasterPictureAttachment()) { + if ($user->getMasterPictureAttachment() instanceof Attachment) { $attachment = $user->getMasterPictureAttachment(); } else { //Otherwise we have to create one $attachment = new UserAttachment(); @@ -169,15 +157,14 @@ class UserAvatarHelper } $attachment->setAttachmentType($attachment_type); - //$user->setMasterPictureAttachment($attachment); } //Handle the upload - $this->submitHandler->handleFormSubmit($attachment, $file); + $this->submitHandler->handleUpload($attachment, new AttachmentUpload(file: $file)); //Set attachment as master picture $user->setMasterPictureAttachment($attachment); return $attachment; } -} \ No newline at end of file +} diff --git a/src/Services/UserSystem/VoterHelper.php b/src/Services/UserSystem/VoterHelper.php new file mode 100644 index 00000000..644351f4 --- /dev/null +++ b/src/Services/UserSystem/VoterHelper.php @@ -0,0 +1,127 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Services\UserSystem; + +use App\Entity\UserSystem\User; +use App\Repository\UserRepository; +use App\Security\ApiTokenAuthenticatedToken; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * @see \App\Tests\Services\UserSystem\VoterHelperTest + */ +final class VoterHelper +{ + private readonly UserRepository $userRepository; + + public function __construct(private readonly PermissionManager $permissionManager, private readonly EntityManagerInterface $entityManager) + { + $this->userRepository = $this->entityManager->getRepository(User::class); + } + + /** + * Checks if the operation on the given permission is granted for the given token. + * Similar to isGrantedTrinary, but returns false if the permission is not granted. + * @param TokenInterface $token The token to check + * @param string $permission The permission to check + * @param string $operation The operation to check + * @return bool + */ + public function isGranted(TokenInterface $token, string $permission, string $operation): bool + { + return $this->isGrantedTrinary($token, $permission, $operation) ?? false; + } + + /** + * Checks if the operation on the given permission is granted for the given token. + * The result is returned in trinary value, where null means inherted from the parent. + * @param TokenInterface $token The token to check + * @param string $permission The permission to check + * @param string $operation The operation to check + * @return bool|null The result of the check. Null means inherted from the parent. + */ + public function isGrantedTrinary(TokenInterface $token, string $permission, string $operation): ?bool + { + $user = $token->getUser(); + + if ($user instanceof User) { + //A disallowed user is not allowed to do anything... + if ($user->isDisabled()) { + return false; + } + } else { + //Try to resolve the user from the token + $user = $this->resolveUser($token); + } + + //If the token is a APITokenAuthenticated + if ($token instanceof ApiTokenAuthenticatedToken) { + //Use the special API token checker + return $this->permissionManager->inheritWithAPILevel($user, $token->getRoleNames(), $permission, $operation); + } + + //Otherwise use the normal permission checker + return $this->permissionManager->inherit($user, $permission, $operation); + } + + /** + * Resolves the user from the given token. If the token is anonymous, the anonymous user is returned. + * @return User + */ + public function resolveUser(TokenInterface $token): User + { + $user = $token->getUser(); + //If the user is a User entity, just return it + if ($user instanceof User) { + return $user; + } + + //If the user is null, return the anonymous user + if ($user === null) { + $user = $this->userRepository->getAnonymousUser(); + if (!$user instanceof User) { + throw new \RuntimeException('The anonymous user could not be resolved.'); + } + return $user; + } + + //Otherwise throw an exception + throw new \RuntimeException('The user could not be resolved.'); + } + + /** + * Checks if the permission operation combination with the given names is existing. + * Just a proxy to the permission manager. + * + * @param string $permission the name of the permission which should be checked + * @param string $operation the name of the operation which should be checked + * + * @return bool true if the given permission operation combination is existing + */ + public function isValidOperation(string $permission, string $operation): bool + { + return $this->permissionManager->isValidOperation($permission, $operation); + } +} \ No newline at end of file diff --git a/src/State/CurrentApiTokenProvider.php b/src/State/CurrentApiTokenProvider.php new file mode 100644 index 00000000..f989d504 --- /dev/null +++ b/src/State/CurrentApiTokenProvider.php @@ -0,0 +1,49 @@ +. + */ + +declare(strict_types=1); + + +namespace App\State; + +use ApiPlatform\Metadata\Operation; +use ApiPlatform\State\ProviderInterface; +use App\Security\ApiTokenAuthenticatedToken; +use Symfony\Bundle\SecurityBundle\Security; + + +class CurrentApiTokenProvider implements ProviderInterface +{ + + public function __construct(private readonly Security $security) + { + + } + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $securityToken = $this->security->getToken(); + if (!$securityToken instanceof ApiTokenAuthenticatedToken) { + return null; + } + + return $securityToken->getApiToken(); + } +} \ No newline at end of file diff --git a/src/State/PartDBInfoProvider.php b/src/State/PartDBInfoProvider.php new file mode 100644 index 00000000..c6760ede --- /dev/null +++ b/src/State/PartDBInfoProvider.php @@ -0,0 +1,44 @@ +versionManager->getVersion()->toString(), + git_branch: $this->gitVersionInfo->getGitBranchName(), + git_commit: $this->gitVersionInfo->getGitCommitHash(), + title: $this->partdb_title, + banner: $this->bannerHelper->getBanner(), + default_uri: $this->default_uri, + global_timezone: $this->global_timezone, + base_currency: $this->base_currency, + global_locale: $this->global_locale, + ); + } +} diff --git a/src/Translation/Fixes/SegmentAwareXliffFileDumper.php b/src/Translation/Fixes/SegmentAwareXliffFileDumper.php new file mode 100644 index 00000000..4b44ef01 --- /dev/null +++ b/src/Translation/Fixes/SegmentAwareXliffFileDumper.php @@ -0,0 +1,242 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Translation\Fixes; + +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Symfony\Component\Translation\Dumper\FileDumper; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * Backport of the XliffFile dumper from Symfony 7.2, which supports segment attributes and notes, this keeps the + * metadata when editing the translations from inside Symfony. + */ +#[AsDecorator("translation.dumper.xliff")] +class SegmentAwareXliffFileDumper extends FileDumper +{ + + public function __construct( + private string $extension = 'xlf', + ) { + } + + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string + { + $xliffVersion = '1.2'; + if (\array_key_exists('xliff_version', $options)) { + $xliffVersion = $options['xliff_version']; + } + + if (\array_key_exists('default_locale', $options)) { + $defaultLocale = $options['default_locale']; + } else { + $defaultLocale = \Locale::getDefault(); + } + + if ('1.2' === $xliffVersion) { + return $this->dumpXliff1($defaultLocale, $messages, $domain, $options); + } + if ('2.0' === $xliffVersion) { + return $this->dumpXliff2($defaultLocale, $messages, $domain); + } + + throw new InvalidArgumentException(\sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion)); + } + + protected function getExtension(): string + { + return $this->extension; + } + + private function dumpXliff1(string $defaultLocale, MessageCatalogue $messages, ?string $domain, array $options = []): string + { + $toolInfo = ['tool-id' => 'symfony', 'tool-name' => 'Symfony']; + if (\array_key_exists('tool_info', $options)) { + $toolInfo = array_merge($toolInfo, $options['tool_info']); + } + + $dom = new \DOMDocument('1.0', 'utf-8'); + $dom->formatOutput = true; + + $xliff = $dom->appendChild($dom->createElement('xliff')); + $xliff->setAttribute('version', '1.2'); + $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2'); + + $xliffFile = $xliff->appendChild($dom->createElement('file')); + $xliffFile->setAttribute('source-language', str_replace('_', '-', $defaultLocale)); + $xliffFile->setAttribute('target-language', str_replace('_', '-', $messages->getLocale())); + $xliffFile->setAttribute('datatype', 'plaintext'); + $xliffFile->setAttribute('original', 'file.ext'); + + $xliffHead = $xliffFile->appendChild($dom->createElement('header')); + $xliffTool = $xliffHead->appendChild($dom->createElement('tool')); + foreach ($toolInfo as $id => $value) { + $xliffTool->setAttribute($id, $value); + } + + if ($catalogueMetadata = $messages->getCatalogueMetadata('', $domain) ?? []) { + $xliffPropGroup = $xliffHead->appendChild($dom->createElement('prop-group')); + foreach ($catalogueMetadata as $key => $value) { + $xliffProp = $xliffPropGroup->appendChild($dom->createElement('prop')); + $xliffProp->setAttribute('prop-type', $key); + $xliffProp->appendChild($dom->createTextNode($value)); + } + } + + $xliffBody = $xliffFile->appendChild($dom->createElement('body')); + foreach ($messages->all($domain) as $source => $target) { + $translation = $dom->createElement('trans-unit'); + + $translation->setAttribute('id', strtr(substr(base64_encode(hash('xxh128', $source, true)), 0, 7), '/+', '._')); + $translation->setAttribute('resname', $source); + + $s = $translation->appendChild($dom->createElement('source')); + $s->appendChild($dom->createTextNode($source)); + + // Does the target contain characters requiring a CDATA section? + $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target); + + $targetElement = $dom->createElement('target'); + $metadata = $messages->getMetadata($source, $domain); + if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) { + foreach ($metadata['target-attributes'] as $name => $value) { + $targetElement->setAttribute($name, $value); + } + } + $t = $translation->appendChild($targetElement); + $t->appendChild($text); + + if ($this->hasMetadataArrayInfo('notes', $metadata)) { + foreach ($metadata['notes'] as $note) { + if (!isset($note['content'])) { + continue; + } + + $n = $translation->appendChild($dom->createElement('note')); + $n->appendChild($dom->createTextNode($note['content'])); + + if (isset($note['priority'])) { + $n->setAttribute('priority', $note['priority']); + } + + if (isset($note['from'])) { + $n->setAttribute('from', $note['from']); + } + } + } + + $xliffBody->appendChild($translation); + } + + return $dom->saveXML(); + } + + private function dumpXliff2(string $defaultLocale, MessageCatalogue $messages, ?string $domain): string + { + $dom = new \DOMDocument('1.0', 'utf-8'); + $dom->formatOutput = true; + + $xliff = $dom->appendChild($dom->createElement('xliff')); + $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0'); + $xliff->setAttribute('version', '2.0'); + $xliff->setAttribute('srcLang', str_replace('_', '-', $defaultLocale)); + $xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale())); + + $xliffFile = $xliff->appendChild($dom->createElement('file')); + if (str_ends_with($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX)) { + $xliffFile->setAttribute('id', substr($domain, 0, -\strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX)).'.'.$messages->getLocale()); + } else { + $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale()); + } + + if ($catalogueMetadata = $messages->getCatalogueMetadata('', $domain) ?? []) { + $xliff->setAttribute('xmlns:m', 'urn:oasis:names:tc:xliff:metadata:2.0'); + $xliffMetadata = $xliffFile->appendChild($dom->createElement('m:metadata')); + foreach ($catalogueMetadata as $key => $value) { + $xliffMeta = $xliffMetadata->appendChild($dom->createElement('prop')); + $xliffMeta->setAttribute('type', $key); + $xliffMeta->appendChild($dom->createTextNode($value)); + } + } + + foreach ($messages->all($domain) as $source => $target) { + $translation = $dom->createElement('unit'); + $translation->setAttribute('id', strtr(substr(base64_encode(hash('xxh128', $source, true)), 0, 7), '/+', '._')); + + if (\strlen($source) <= 80) { + $translation->setAttribute('name', $source); + } + + $metadata = $messages->getMetadata($source, $domain); + + // Add notes section + if ($this->hasMetadataArrayInfo('notes', $metadata)) { + $notesElement = $dom->createElement('notes'); + foreach ($metadata['notes'] as $note) { + $n = $dom->createElement('note'); + $n->appendChild($dom->createTextNode($note['content'] ?? '')); + unset($note['content']); + + foreach ($note as $name => $value) { + $n->setAttribute($name, $value); + } + $notesElement->appendChild($n); + } + $translation->appendChild($notesElement); + } + + $segment = $translation->appendChild($dom->createElement('segment')); + + if ($this->hasMetadataArrayInfo('segment-attributes', $metadata)) { + foreach ($metadata['segment-attributes'] as $name => $value) { + $segment->setAttribute($name, $value); + } + } + + $s = $segment->appendChild($dom->createElement('source')); + $s->appendChild($dom->createTextNode($source)); + + // Does the target contain characters requiring a CDATA section? + $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target); + + $targetElement = $dom->createElement('target'); + if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) { + foreach ($metadata['target-attributes'] as $name => $value) { + $targetElement->setAttribute($name, $value); + } + } + $t = $segment->appendChild($targetElement); + $t->appendChild($text); + + $xliffFile->appendChild($translation); + } + + return $dom->saveXML(); + } + + private function hasMetadataArrayInfo(string $key, ?array $metadata = null): bool + { + return is_iterable($metadata[$key] ?? null); + } +} \ No newline at end of file diff --git a/src/Translation/Fixes/SegmentAwareXliffFileLoader.php b/src/Translation/Fixes/SegmentAwareXliffFileLoader.php new file mode 100644 index 00000000..12455e87 --- /dev/null +++ b/src/Translation/Fixes/SegmentAwareXliffFileLoader.php @@ -0,0 +1,262 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Translation\Fixes; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Util\Exception\InvalidXmlException; +use Symfony\Component\Config\Util\Exception\XmlParsingException; +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\Exception\RuntimeException; +use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Util\XliffUtils; + +/** + * Backport of the XliffFile dumper from Symfony 7.2, which supports segment attributes and notes, this keeps the + * metadata when editing the translations from inside Symfony. + */ +#[AsDecorator("translation.loader.xliff")] +class SegmentAwareXliffFileLoader implements LoaderInterface +{ + public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + if (!class_exists(XmlUtils::class)) { + throw new RuntimeException('Loading translations from the Xliff format requires the Symfony Config component.'); + } + + if (!$this->isXmlString($resource)) { + if (!stream_is_local($resource)) { + throw new InvalidResourceException(\sprintf('This is not a local file "%s".', $resource)); + } + + if (!file_exists($resource)) { + throw new NotFoundResourceException(\sprintf('File "%s" not found.', $resource)); + } + + if (!is_file($resource)) { + throw new InvalidResourceException(\sprintf('This is neither a file nor an XLIFF string "%s".', $resource)); + } + } + + try { + if ($this->isXmlString($resource)) { + $dom = XmlUtils::parse($resource); + } else { + $dom = XmlUtils::loadFile($resource); + } + } catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) { + throw new InvalidResourceException(\sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e); + } + + if ($errors = XliffUtils::validateSchema($dom)) { + throw new InvalidResourceException(\sprintf('Invalid resource provided: "%s"; Errors: ', $resource).XliffUtils::getErrorsAsString($errors)); + } + + $catalogue = new MessageCatalogue($locale); + $this->extract($dom, $catalogue, $domain); + + if (is_file($resource) && class_exists(FileResource::class)) { + $catalogue->addResource(new FileResource($resource)); + } + + return $catalogue; + } + + private function extract(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void + { + $xliffVersion = XliffUtils::getVersionNumber($dom); + + if ('1.2' === $xliffVersion) { + $this->extractXliff1($dom, $catalogue, $domain); + } + + if ('2.0' === $xliffVersion) { + $this->extractXliff2($dom, $catalogue, $domain); + } + } + + /** + * Extract messages and metadata from DOMDocument into a MessageCatalogue. + */ + private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void + { + $xml = simplexml_import_dom($dom); + $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; + + $namespace = 'urn:oasis:names:tc:xliff:document:1.2'; + $xml->registerXPathNamespace('xliff', $namespace); + + foreach ($xml->xpath('//xliff:file') as $file) { + $fileAttributes = $file->attributes(); + + $file->registerXPathNamespace('xliff', $namespace); + + foreach ($file->xpath('.//xliff:prop') as $prop) { + $catalogue->setCatalogueMetadata($prop->attributes()['prop-type'], (string) $prop, $domain); + } + + foreach ($file->xpath('.//xliff:trans-unit') as $translation) { + $attributes = $translation->attributes(); + + if (!(isset($attributes['resname']) || isset($translation->source))) { + continue; + } + + $source = (string) (isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source); + + if (isset($translation->target) + && 'needs-translation' === (string) $translation->target->attributes()['state'] + && \in_array((string) $translation->target, [$source, (string) $translation->source], true) + ) { + continue; + } + + // If the xlf file has another encoding specified, try to convert it because + // simple_xml will always return utf-8 encoded values + $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding); + + $catalogue->set($source, $target, $domain); + + $metadata = [ + 'source' => (string) $translation->source, + 'file' => [ + 'original' => (string) $fileAttributes['original'], + ], + ]; + if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) { + $metadata['notes'] = $notes; + } + + if (isset($translation->target) && $translation->target->attributes()) { + $metadata['target-attributes'] = []; + foreach ($translation->target->attributes() as $key => $value) { + $metadata['target-attributes'][$key] = (string) $value; + } + } + + if (isset($attributes['id'])) { + $metadata['id'] = (string) $attributes['id']; + } + + $catalogue->setMetadata($source, $metadata, $domain); + } + } + } + + private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void + { + $xml = simplexml_import_dom($dom); + $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; + + $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0'); + + foreach ($xml->xpath('//xliff:unit') as $unit) { + foreach ($unit->segment as $segment) { + $attributes = $unit->attributes(); + $source = $attributes['name'] ?? $segment->source; + + // If the xlf file has another encoding specified, try to convert it because + // simple_xml will always return utf-8 encoded values + $target = $this->utf8ToCharset((string) ($segment->target ?? $segment->source), $encoding); + + $catalogue->set((string) $source, $target, $domain); + + $metadata = []; + if ($segment->attributes()) { + $metadata['segment-attributes'] = []; + foreach ($segment->attributes() as $key => $value) { + $metadata['segment-attributes'][$key] = (string) $value; + } + } + + if (isset($segment->target) && $segment->target->attributes()) { + $metadata['target-attributes'] = []; + foreach ($segment->target->attributes() as $key => $value) { + $metadata['target-attributes'][$key] = (string) $value; + } + } + + if (isset($unit->notes)) { + $metadata['notes'] = []; + foreach ($unit->notes->note as $noteNode) { + $note = []; + foreach ($noteNode->attributes() as $key => $value) { + $note[$key] = (string) $value; + } + $note['content'] = (string) $noteNode; + $metadata['notes'][] = $note; + } + } + + $catalogue->setMetadata((string) $source, $metadata, $domain); + } + } + } + + /** + * Convert a UTF8 string to the specified encoding. + */ + private function utf8ToCharset(string $content, ?string $encoding = null): string + { + if ('UTF-8' !== $encoding && $encoding) { + return mb_convert_encoding($content, $encoding, 'UTF-8'); + } + + return $content; + } + + private function parseNotesMetadata(?\SimpleXMLElement $noteElement = null, ?string $encoding = null): array + { + $notes = []; + + if (null === $noteElement) { + return $notes; + } + + /** @var \SimpleXMLElement $xmlNote */ + foreach ($noteElement as $xmlNote) { + $noteAttributes = $xmlNote->attributes(); + $note = ['content' => $this->utf8ToCharset((string) $xmlNote, $encoding)]; + if (isset($noteAttributes['priority'])) { + $note['priority'] = (int) $noteAttributes['priority']; + } + + if (isset($noteAttributes['from'])) { + $note['from'] = (string) $noteAttributes['from']; + } + + $notes[] = $note; + } + + return $notes; + } + + private function isXmlString(string $resource): bool + { + return str_starts_with($resource, '. */ - namespace App\Twig; +use App\Entity\Attachments\Attachment; use App\Services\Attachments\AttachmentURLGenerator; use App\Services\Misc\FAIconGenerator; use Twig\Extension\AbstractExtension; @@ -27,22 +30,17 @@ use Twig\TwigFunction; final class AttachmentExtension extends AbstractExtension { - protected AttachmentURLGenerator $attachmentURLGenerator; - protected FAIconGenerator $FAIconGenerator; - - public function __construct(AttachmentURLGenerator $attachmentURLGenerator, FAIconGenerator $FAIconGenerator) + public function __construct(protected AttachmentURLGenerator $attachmentURLGenerator, protected FAIconGenerator $FAIconGenerator) { - $this->attachmentURLGenerator = $attachmentURLGenerator; - $this->FAIconGenerator = $FAIconGenerator; } public function getFunctions(): array { return [ /* Returns the URL to a thumbnail of the given attachment */ - new TwigFunction('attachment_thumbnail', [$this->attachmentURLGenerator, 'getThumbnailURL']), - /* Returns the font awesome icon class which is representing the given file extension */ - new TwigFunction('ext_to_fa_icon', [$this->FAIconGenerator, 'fileExtensionToFAType']), + new TwigFunction('attachment_thumbnail', fn(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string => $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name)), + /* Returns the font awesome icon class which is representing the given file extension (We allow null here for attachments without extension) */ + new TwigFunction('ext_to_fa_icon', fn(?string $extension): string => $this->FAIconGenerator->fileExtensionToFAType($extension ?? '')), ]; } -} \ No newline at end of file +} diff --git a/src/Twig/BarcodeExtension.php b/src/Twig/BarcodeExtension.php index 051995f6..ae1973e3 100644 --- a/src/Twig/BarcodeExtension.php +++ b/src/Twig/BarcodeExtension.php @@ -1,4 +1,7 @@ . */ - namespace App\Twig; use Com\Tecnick\Barcode\Barcode; use Twig\Extension\AbstractExtension; -use Twig\TwigFilter; use Twig\TwigFunction; final class BarcodeExtension extends AbstractExtension @@ -31,7 +32,7 @@ final class BarcodeExtension extends AbstractExtension { return [ /* Generates a barcode with the given Type and Data and returns it as an SVG represenation */ - new TwigFunction('barcode_svg', [$this, 'barcodeSVG']), + new TwigFunction('barcode_svg', fn(string $content, string $type = 'QRCODE'): string => $this->barcodeSVG($content, $type)), ]; } diff --git a/src/Twig/EntityExtension.php b/src/Twig/EntityExtension.php index 6d477d88..762ebb09 100644 --- a/src/Twig/EntityExtension.php +++ b/src/Twig/EntityExtension.php @@ -1,4 +1,7 @@ . */ - namespace App\Twig; use App\Entity\Attachments\Attachment; @@ -29,11 +31,12 @@ use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; +use App\Exceptions\EntityNotSupportedException; use App\Services\ElementTypeNameGenerator; use App\Services\EntityURLGenerator; use App\Services\Trees\TreeViewGenerator; @@ -41,26 +44,20 @@ use Twig\Extension\AbstractExtension; use Twig\TwigFunction; use Twig\TwigTest; +/** + * @see \App\Tests\Twig\EntityExtensionTest + */ final class EntityExtension extends AbstractExtension { - protected EntityURLGenerator $entityURLGenerator; - protected TreeViewGenerator $treeBuilder; - private ElementTypeNameGenerator $nameGenerator; - - public function __construct(EntityURLGenerator $entityURLGenerator, TreeViewGenerator $treeBuilder, ElementTypeNameGenerator $elementTypeNameGenerator) + public function __construct(protected EntityURLGenerator $entityURLGenerator, protected TreeViewGenerator $treeBuilder, private readonly ElementTypeNameGenerator $nameGenerator) { - $this->entityURLGenerator = $entityURLGenerator; - $this->treeBuilder = $treeBuilder; - $this->nameGenerator = $elementTypeNameGenerator; } public function getTests(): array { return [ /* Checks if the given variable is an entitity (instance of AbstractDBElement) */ - new TwigTest('entity', static function ($var) { - return $var instanceof AbstractDBElement; - }), + new TwigTest('entity', static fn($var) => $var instanceof AbstractDBElement), ]; } @@ -68,20 +65,31 @@ final class EntityExtension extends AbstractExtension { return [ /* Returns a string representation of the given entity */ - new TwigFunction('entity_type', [$this, 'getEntityType']), + new TwigFunction('entity_type', fn(object $entity): ?string => $this->getEntityType($entity)), /* Returns the URL to the given entity */ - new TwigFunction('entity_url', [$this, 'generateEntityURL']), + new TwigFunction('entity_url', fn(AbstractDBElement $entity, string $method = 'info'): string => $this->generateEntityURL($entity, $method)), + /* Returns the URL to the given entity in timetravel mode */ + new TwigFunction('timetravel_url', fn(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string => $this->timeTravelURL($element, $dateTime)), /* Generates a JSON array of the given tree */ - new TwigFunction('tree_data', [$this, 'treeData']), + new TwigFunction('tree_data', fn(AbstractDBElement $element, string $type = 'newEdit'): string => $this->treeData($element, $type)), /* Gets a human readable label for the type of the given entity */ - new TwigFunction('entity_type_label', [$this->nameGenerator, 'getLocalizedTypeLabel']), + new TwigFunction('entity_type_label', fn(object|string $entity): string => $this->nameGenerator->getLocalizedTypeLabel($entity)), ]; } + public function timeTravelURL(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string + { + try { + return $this->entityURLGenerator->timeTravelURL($element, $dateTime); + } catch (EntityNotSupportedException) { + return null; + } + } + public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string { - $tree = $this->treeBuilder->getTreeView(get_class($element), null, $type, $element); + $tree = $this->treeBuilder->getTreeView($element::class, null, $type, $element); return json_encode($tree, JSON_THROW_ON_ERROR); } @@ -96,7 +104,7 @@ final class EntityExtension extends AbstractExtension $map = [ Part::class => 'part', Footprint::class => 'footprint', - Storelocation::class => 'storelocation', + StorageLocation::class => 'storelocation', Manufacturer::class => 'manufacturer', Category::class => 'category', Project::class => 'device', @@ -115,6 +123,6 @@ final class EntityExtension extends AbstractExtension } } - return false; + return null; } -} \ No newline at end of file +} diff --git a/src/Twig/FormatExtension.php b/src/Twig/FormatExtension.php index 87c906b1..76628ccd 100644 --- a/src/Twig/FormatExtension.php +++ b/src/Twig/FormatExtension.php @@ -22,72 +22,38 @@ declare(strict_types=1); namespace App\Twig; -use App\Entity\Attachments\Attachment; -use App\Entity\Base\AbstractDBElement; -use App\Entity\ProjectSystem\Project; -use App\Entity\LabelSystem\LabelProfile; -use App\Entity\Parts\Category; -use App\Entity\Parts\Footprint; -use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; -use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; -use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; -use App\Entity\UserSystem\Group; -use App\Entity\UserSystem\User; use App\Services\Formatters\AmountFormatter; -use App\Services\Attachments\AttachmentURLGenerator; -use App\Services\EntityURLGenerator; -use App\Services\Misc\FAIconGenerator; use App\Services\Formatters\MarkdownParser; use App\Services\Formatters\MoneyFormatter; use App\Services\Formatters\SIFormatter; -use App\Services\Trees\TreeViewGenerator; use Brick\Math\BigDecimal; -use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; -use Symfony\Component\Serializer\SerializerInterface; -use Symfony\Contracts\Translation\TranslatorInterface; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; -use Twig\TwigFunction; -use Twig\TwigTest; - -use function get_class; final class FormatExtension extends AbstractExtension { - protected MarkdownParser $markdownParser; - protected MoneyFormatter $moneyFormatter; - protected SIFormatter $siformatter; - protected AmountFormatter $amountFormatter; - - - public function __construct(MarkdownParser $markdownParser, MoneyFormatter $moneyFormatter, - SIFormatter $SIFormatter, AmountFormatter $amountFormatter) + public function __construct(protected MarkdownParser $markdownParser, protected MoneyFormatter $moneyFormatter, protected SIFormatter $siformatter, protected AmountFormatter $amountFormatter) { - $this->markdownParser = $markdownParser; - $this->moneyFormatter = $moneyFormatter; - $this->siformatter = $SIFormatter; - $this->amountFormatter = $amountFormatter; } public function getFilters(): array { return [ /* Mark the given text as markdown, which will be rendered in the browser */ - new TwigFilter('format_markdown', [$this->markdownParser, 'markForRendering'], [ + new TwigFilter('format_markdown', fn(string $markdown, bool $inline_mode = false): string => $this->markdownParser->markForRendering($markdown, $inline_mode), [ 'pre_escape' => 'html', 'is_safe' => ['html'], ]), /* Format the given amount as money, using a given currency */ - new TwigFilter('format_money', [$this, 'formatCurrency']), + new TwigFilter('format_money', fn($amount, ?Currency $currency = null, int $decimals = 5): string => $this->formatCurrency($amount, $currency, $decimals)), /* Format the given number using SI prefixes and the given unit (string) */ - new TwigFilter('format_si', [$this, 'siFormat']), + new TwigFilter('format_si', fn($value, $unit, $decimals = 2, bool $show_all_digits = false): string => $this->siFormat($value, $unit, $decimals, $show_all_digits)), /** Format the given amount using the given MeasurementUnit */ - new TwigFilter('format_amount', [$this, 'amountFormat']), - /** Format the given number of bytes as human readable number */ - new TwigFilter('format_bytes', [$this, 'formatBytes']), + new TwigFilter('format_amount', fn($value, ?MeasurementUnit $unit, array $options = []): string => $this->amountFormat($value, $unit, $options)), + /** Format the given number of bytes as human-readable number */ + new TwigFilter('format_bytes', fn(int $bytes, int $precision = 2): string => $this->formatBytes($bytes, $precision)), ]; } @@ -112,13 +78,12 @@ final class FormatExtension extends AbstractExtension /** * @param $bytes - * @param int $precision - * @return string */ public function formatBytes(int $bytes, int $precision = 2): string { $size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB']; $factor = floor((strlen((string) $bytes) - 1) / 3); - return sprintf("%.{$precision}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor]; + //We use the real (10 based) SI prefix here + return sprintf("%.{$precision}f", $bytes / (1000 ** $factor)) . ' ' . @$size[$factor]; } } diff --git a/src/Twig/InfoProviderExtension.php b/src/Twig/InfoProviderExtension.php new file mode 100644 index 00000000..a963b778 --- /dev/null +++ b/src/Twig/InfoProviderExtension.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Twig; + +use App\Services\InfoProviderSystem\ProviderRegistry; +use App\Services\InfoProviderSystem\Providers\InfoProviderInterface; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; + +class InfoProviderExtension extends AbstractExtension +{ + public function __construct( + private readonly ProviderRegistry $providerRegistry + ) {} + + public function getFunctions(): array + { + return [ + new TwigFunction('info_provider', $this->getInfoProvider(...)), + new TwigFunction('info_provider_label', $this->getInfoProviderName(...)) + ]; + } + + /** + * Gets the info provider with the given key. Returns null, if the provider does not exist. + * @param string $key + * @return InfoProviderInterface|null + */ + private function getInfoProvider(string $key): ?InfoProviderInterface + { + try { + return $this->providerRegistry->getProviderByKey($key); + } catch (\InvalidArgumentException) { + return null; + } + } + + /** + * Gets the label of the info provider with the given key. Returns null, if the provider does not exist. + * @param string $key + * @return string|null + */ + private function getInfoProviderName(string $key): ?string + { + try { + return $this->providerRegistry->getProviderByKey($key)->getProviderInfo()['name']; + } catch (\InvalidArgumentException) { + return null; + } + } +} \ No newline at end of file diff --git a/src/Twig/LogExtension.php b/src/Twig/LogExtension.php new file mode 100644 index 00000000..34dad988 --- /dev/null +++ b/src/Twig/LogExtension.php @@ -0,0 +1,45 @@ +. + */ +namespace App\Twig; + +use App\Entity\LogSystem\AbstractLogEntry; +use App\Services\LogSystem\LogDataFormatter; +use App\Services\LogSystem\LogDiffFormatter; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; + +final class LogExtension extends AbstractExtension +{ + + public function __construct(private readonly LogDataFormatter $logDataFormatter, private readonly LogDiffFormatter $logDiffFormatter) + { + } + + public function getFunctions(): array + { + return [ + new TwigFunction('format_log_data', fn($data, AbstractLogEntry $logEntry, string $fieldName): string => $this->logDataFormatter->formatData($data, $logEntry, $fieldName), ['is_safe' => ['html']]), + new TwigFunction('format_log_diff', fn($old_data, $new_data): string => $this->logDiffFormatter->formatDiff($old_data, $new_data), ['is_safe' => ['html']]), + ]; + } +} diff --git a/src/Twig/MiscExtension.php b/src/Twig/MiscExtension.php new file mode 100644 index 00000000..93762d35 --- /dev/null +++ b/src/Twig/MiscExtension.php @@ -0,0 +1,60 @@ +. + */ +namespace App\Twig; + +use Symfony\Component\HttpFoundation\Request; +use Twig\TwigFunction; +use App\Services\LogSystem\EventCommentNeededHelper; +use Twig\Extension\AbstractExtension; + +final class MiscExtension extends AbstractExtension +{ + public function __construct(private readonly EventCommentNeededHelper $eventCommentNeededHelper) + { + } + + public function getFunctions(): array + { + return [ + new TwigFunction('event_comment_needed', + fn(string $operation_type) => $this->eventCommentNeededHelper->isCommentNeeded($operation_type) + ), + + new TwigFunction('uri_without_host', $this->uri_without_host(...)) + ]; + } + + /** + * Similar to the getUri function of the request, but does not contain protocol and host. + * @param Request $request + * @return string + */ + public function uri_without_host(Request $request): string + { + if (null !== $qs = $request->getQueryString()) { + $qs = '?'.$qs; + } + + return $request->getBaseUrl().$request->getPathInfo().$qs; + } +} diff --git a/src/Twig/Sandbox/InheritanceSecurityPolicy.php b/src/Twig/Sandbox/InheritanceSecurityPolicy.php index 052366c0..93e874e9 100644 --- a/src/Twig/Sandbox/InheritanceSecurityPolicy.php +++ b/src/Twig/Sandbox/InheritanceSecurityPolicy.php @@ -22,7 +22,6 @@ use Twig\Sandbox\SecurityNotAllowedTagError; use Twig\Sandbox\SecurityPolicyInterface; use Twig\Template; -use function get_class; use function in_array; use function is_array; @@ -35,19 +34,11 @@ use function is_array; */ final class InheritanceSecurityPolicy implements SecurityPolicyInterface { - private array $allowedTags; - private array $allowedFilters; private array $allowedMethods; - private array $allowedProperties; - private array $allowedFunctions; - public function __construct(array $allowedTags = [], array $allowedFilters = [], array $allowedMethods = [], array $allowedProperties = [], array $allowedFunctions = []) + public function __construct(private array $allowedTags = [], private array $allowedFilters = [], array $allowedMethods = [], private array $allowedProperties = [], private array $allowedFunctions = []) { - $this->allowedTags = $allowedTags; - $this->allowedFilters = $allowedFilters; $this->setAllowedMethods($allowedMethods); - $this->allowedProperties = $allowedProperties; - $this->allowedFunctions = $allowedFunctions; } public function setAllowedTags(array $tags): void @@ -65,7 +56,7 @@ final class InheritanceSecurityPolicy implements SecurityPolicyInterface $this->allowedMethods = []; foreach ($methods as $class => $m) { $this->allowedMethods[$class] = array_map( - static function ($value) { return strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); }, is_array($m) ? $m : [$m]); + static fn($value): string => strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), is_array($m) ? $m : [$m]); } } @@ -112,7 +103,7 @@ final class InheritanceSecurityPolicy implements SecurityPolicyInterface if ($obj instanceof $class) { $allowed = in_array($method, $methods, true); - //CHANGED: Only break if we the method is allowed, otherwise try it on the other methods + //CHANGED: Only break if the method is allowed, otherwise try it on the other methods if ($allowed) { break; } @@ -120,7 +111,7 @@ final class InheritanceSecurityPolicy implements SecurityPolicyInterface } if (!$allowed) { - $class = get_class($obj); + $class = $obj::class; throw new SecurityNotAllowedMethodError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, $class), $class, $method); } @@ -133,7 +124,7 @@ final class InheritanceSecurityPolicy implements SecurityPolicyInterface if ($obj instanceof $class) { $allowed = in_array($property, is_array($properties) ? $properties : [$properties], true); - //CHANGED: Only break if we the method is allowed, otherwise try it on the other methods + //CHANGED: Only break if the method is allowed, otherwise try it on the other methods if ($allowed) { break; } @@ -141,7 +132,7 @@ final class InheritanceSecurityPolicy implements SecurityPolicyInterface } if (!$allowed) { - $class = get_class($obj); + $class = $obj::class; throw new SecurityNotAllowedPropertyError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, $class), $class, $property); } diff --git a/src/Twig/Sandbox/SandboxedLabelExtension.php b/src/Twig/Sandbox/SandboxedLabelExtension.php new file mode 100644 index 00000000..59fb0af0 --- /dev/null +++ b/src/Twig/Sandbox/SandboxedLabelExtension.php @@ -0,0 +1,51 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Twig\Sandbox; + +use App\Services\LabelSystem\LabelTextReplacer; +use Twig\Extension\AbstractExtension; +use Twig\TwigFilter; +use Twig\TwigFunction; + +class SandboxedLabelExtension extends AbstractExtension +{ + public function __construct(private readonly LabelTextReplacer $labelTextReplacer) + { + + } + + public function getFunctions(): array + { + return [ + new TwigFunction('placeholder', fn(string $text, object $label_target) => $this->labelTextReplacer->handlePlaceholderOrReturnNull($text, $label_target)), + ]; + } + + public function getFilters(): array + { + return [ + new TwigFilter('placeholders', fn(string $text, object $label_target) => $this->labelTextReplacer->replace($text, $label_target)), + ]; + } +} \ No newline at end of file diff --git a/src/Twig/TwigCoreExtension.php b/src/Twig/TwigCoreExtension.php index aecbdd17..352e09d3 100644 --- a/src/Twig/TwigCoreExtension.php +++ b/src/Twig/TwigCoreExtension.php @@ -1,4 +1,7 @@ . */ - namespace App\Twig; -use App\Entity\Base\AbstractDBElement; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; +use Twig\TwigFunction; use Twig\TwigTest; /** * The functionalities here extend the Twig with some core functions, which are independently of Part-DB. + * @see \App\Tests\Twig\TwigCoreExtensionTest */ final class TwigCoreExtension extends AbstractExtension { - protected ObjectNormalizer $objectNormalizer; - - public function __construct(ObjectNormalizer $objectNormalizer) + public function __construct(protected ObjectNormalizer $objectNormalizer) { - $this->objectNormalizer = $objectNormalizer; + } + + public function getFunctions(): array + { + return [ + /* Returns the enum cases as values */ + new TwigFunction('enum_cases', $this->getEnumCases(...)), + ]; } public function getTests(): array @@ -44,30 +52,37 @@ final class TwigCoreExtension extends AbstractExtension /* * Checks if a given variable is an instance of a given class. E.g. ` x is instanceof('App\Entity\Parts\Part')` */ - new TwigTest('instanceof', static function ($var, $instance) { - return $var instanceof $instance; - }), + new TwigTest('instanceof', static fn($var, $instance) => $var instanceof $instance), /* Checks if a given variable is an object. E.g. `x is object` */ - new TwigTest('object', static function ($var) { - return is_object($var); - }), + new TwigTest('object', static fn($var): bool => is_object($var)), + new TwigTest('enum', fn($var) => $var instanceof \UnitEnum), ]; } - public function getFilters() + /** + * @param string $enum_class + * @phpstan-param class-string $enum_class + */ + public function getEnumCases(string $enum_class): array + { + if (!enum_exists($enum_class)) { + throw new \InvalidArgumentException(sprintf('The given class "%s" is not an enum!', $enum_class)); + } + + /** @noinspection PhpUndefinedMethodInspection */ + return ($enum_class)::cases(); + } + + public function getFilters(): array { return [ /* Converts the given object to an array representation of the public/accessible properties */ - new TwigFilter('to_array', [$this, 'toArray']), + new TwigFilter('to_array', fn($object) => $this->toArray($object)), ]; } - public function toArray($object) + public function toArray(object|array $object): array { - if(! is_object($object) && ! is_array($object)) { - throw new \InvalidArgumentException('The given variable is not an object or array!'); - } - //If it is already an array, we can just return it if(is_array($object)) { return $object; @@ -75,4 +90,4 @@ final class TwigCoreExtension extends AbstractExtension return $this->objectNormalizer->normalize($object, null); } -} \ No newline at end of file +} diff --git a/src/Twig/UserExtension.php b/src/Twig/UserExtension.php index 869bea84..5045257a 100644 --- a/src/Twig/UserExtension.php +++ b/src/Twig/UserExtension.php @@ -41,18 +41,29 @@ declare(strict_types=1); namespace App\Twig; +use App\Entity\Base\AbstractDBElement; +use App\Entity\UserSystem\User; use App\Entity\LogSystem\AbstractLogEntry; use App\Repository\LogEntryRepository; use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; +/** + * @see \App\Tests\Twig\UserExtensionTest + */ final class UserExtension extends AbstractExtension { - private LogEntryRepository $repo; + private readonly LogEntryRepository $repo; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, + private readonly Security $security, + private readonly UrlGeneratorInterface $urlGenerator) { $this->repo = $em->getRepository(AbstractLogEntry::class); } @@ -60,7 +71,7 @@ final class UserExtension extends AbstractExtension public function getFilters(): array { return [ - new TwigFilter('remove_locale_from_path', [$this, 'removeLocaleFromPath']), + new TwigFilter('remove_locale_from_path', fn(string $path): string => $this->removeLocaleFromPath($path)), ]; } @@ -68,19 +79,55 @@ final class UserExtension extends AbstractExtension { return [ /* Returns the user which has edited the given entity the last time. */ - new TwigFunction('last_editing_user', [$this->repo, 'getLastEditingUser']), + new TwigFunction('last_editing_user', fn(AbstractDBElement $element): ?User => $this->repo->getLastEditingUser($element)), /* Returns the user which has created the given entity. */ - new TwigFunction('creating_user', [$this->repo, 'getCreatingUser']), + new TwigFunction('creating_user', fn(AbstractDBElement $element): ?User => $this->repo->getCreatingUser($element)), + new TwigFunction('impersonator_user', $this->getImpersonatorUser(...)), + new TwigFunction('impersonation_active', $this->isImpersonationActive(...)), + new TwigFunction('impersonation_path', $this->getImpersonationPath(...)), ]; } /** - * This function/filter generates an path. + * This function returns the user which has impersonated the current user. + * If the current user is not impersonated, null is returned. + * @return User|null + */ + public function getImpersonatorUser(): ?User + { + $token = $this->security->getToken(); + if ($token instanceof SwitchUserToken) { + $tmp = $token->getOriginalToken()->getUser(); + + if ($tmp instanceof User) { + return $tmp; + } + } + + return null; + } + + public function isImpersonationActive(): bool + { + return $this->security->isGranted('IS_IMPERSONATOR'); + } + + public function getImpersonationPath(User $user, string $route_name = 'homepage'): string + { + if (! $this->security->isGranted('CAN_SWITCH_USER', $user)) { + throw new AccessDeniedException('You are not allowed to impersonate this user!'); + } + + return $this->urlGenerator->generate($route_name, ['_switch_user' => $user->getUsername()]); + } + + /** + * This function/filter generates a path. */ public function removeLocaleFromPath(string $path): string { //Ensure the path has the correct format - if (!preg_match('/^\/\w{2}\//', $path)) { + if (!preg_match('/^\/\w{2}(?:_\w{2})?\//', $path)) { throw new \InvalidArgumentException('The given path is not a localized path!'); } diff --git a/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThanValidator.php b/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThanValidator.php index 62231ea8..76acb25a 100644 --- a/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThanValidator.php +++ b/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThanValidator.php @@ -1,4 +1,7 @@ . */ - namespace App\Validator\Constraints\BigDecimal; use Brick\Math\BigDecimal; diff --git a/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThenOrEqualValidator.php b/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThenOrEqualValidator.php index 3e4c8444..b6df06d0 100644 --- a/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThenOrEqualValidator.php +++ b/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThenOrEqualValidator.php @@ -1,4 +1,7 @@ . */ - namespace App\Validator\Constraints\BigDecimal; use Brick\Math\BigDecimal; diff --git a/src/Validator/Constraints/BigDecimal/BigDecimalPositive.php b/src/Validator/Constraints/BigDecimal/BigDecimalPositive.php index bb4e61eb..0e7e755f 100644 --- a/src/Validator/Constraints/BigDecimal/BigDecimalPositive.php +++ b/src/Validator/Constraints/BigDecimal/BigDecimalPositive.php @@ -1,4 +1,7 @@ . */ - namespace App\Validator\Constraints\BigDecimal; -use Symfony\Component\Validator\Constraints\GreaterThan; +use Symfony\Component\Validator\Constraints\Positive; -/** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * - * @author Jan Schädlich - */ -class BigDecimalPositive extends GreaterThan +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class BigDecimalPositive extends Positive { - use BigNumberConstraintTrait; - - public $message = 'This value should be positive.'; - - public function __construct($options = null) - { - parent::__construct($this->configureNumberConstraintOptions($options)); - } - public function validatedBy(): string { return BigDecimalGreaterThanValidator::class; diff --git a/src/Validator/Constraints/BigDecimal/BigDecimalPositiveOrZero.php b/src/Validator/Constraints/BigDecimal/BigDecimalPositiveOrZero.php index 1e6558a2..408cd582 100644 --- a/src/Validator/Constraints/BigDecimal/BigDecimalPositiveOrZero.php +++ b/src/Validator/Constraints/BigDecimal/BigDecimalPositiveOrZero.php @@ -1,4 +1,7 @@ . */ - namespace App\Validator\Constraints\BigDecimal; -use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; +use Symfony\Component\Validator\Constraints\PositiveOrZero; -/** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * - * @author Jan Schädlich - */ -class BigDecimalPositiveOrZero extends GreaterThanOrEqual +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class BigDecimalPositiveOrZero extends PositiveOrZero { - use BigNumberConstraintTrait; - - public $message = 'This value should be either positive or zero.'; - - public function __construct($options = null) - { - parent::__construct($this->configureNumberConstraintOptions($options)); - } - public function validatedBy(): string { return BigDecimalGreaterThenOrEqualValidator::class; diff --git a/src/Validator/Constraints/BigDecimal/BigNumberConstraintTrait.php b/src/Validator/Constraints/BigDecimal/BigNumberConstraintTrait.php deleted file mode 100644 index a9858730..00000000 --- a/src/Validator/Constraints/BigDecimal/BigNumberConstraintTrait.php +++ /dev/null @@ -1,49 +0,0 @@ -. - */ - -namespace App\Validator\Constraints\BigDecimal; - -use Symfony\Component\Validator\Exception\ConstraintDefinitionException; - -use function is_array; - -trait BigNumberConstraintTrait -{ - private function configureNumberConstraintOptions($options): array - { - if (null === $options) { - $options = []; - } elseif (!is_array($options)) { - $options = [$this->getDefaultOption() => $options]; - } - - if (isset($options['propertyPath'])) { - throw new ConstraintDefinitionException(sprintf('The "propertyPath" option of the "%s" constraint cannot be set.', static::class)); - } - - if (isset($options['value'])) { - throw new ConstraintDefinitionException(sprintf('The "value" option of the "%s" constraint cannot be set.', static::class)); - } - - $options['value'] = 0; - - return $options; - } -} \ No newline at end of file diff --git a/src/Validator/Constraints/Misc/ValidRange.php b/src/Validator/Constraints/Misc/ValidRange.php index eb14cf0d..680eb04d 100644 --- a/src/Validator/Constraints/Misc/ValidRange.php +++ b/src/Validator/Constraints/Misc/ValidRange.php @@ -43,10 +43,8 @@ namespace App\Validator\Constraints\Misc; use Symfony\Component\Validator\Constraint; -/** - * @Annotation - */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] class ValidRange extends Constraint { - public $message = 'validator.invalid_range'; + public string $message = 'validator.invalid_range'; } diff --git a/src/Validator/Constraints/Misc/ValidRangeValidator.php b/src/Validator/Constraints/Misc/ValidRangeValidator.php index 8385cc92..8bc5af0c 100644 --- a/src/Validator/Constraints/Misc/ValidRangeValidator.php +++ b/src/Validator/Constraints/Misc/ValidRangeValidator.php @@ -49,11 +49,8 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; class ValidRangeValidator extends ConstraintValidator { - protected RangeParser $rangeParser; - - public function __construct(RangeParser $rangeParser) + public function __construct(protected RangeParser $rangeParser) { - $this->rangeParser = $rangeParser; } public function validate($value, Constraint $constraint): void diff --git a/src/Validator/Constraints/NoLockout.php b/src/Validator/Constraints/NoLockout.php index 1a515e4d..30eb770f 100644 --- a/src/Validator/Constraints/NoLockout.php +++ b/src/Validator/Constraints/NoLockout.php @@ -26,10 +26,14 @@ use Symfony\Component\Validator\Constraint; /** * This constraint restricts a user in that way that it can not lock itself out of the user system. - * - * @Annotation */ +#[\Attribute(\Attribute::TARGET_CLASS)] class NoLockout extends Constraint { - public $message = 'validator.noLockout'; + public string $message = 'validator.noLockout'; + + public function getTargets(): string|array + { + return [self::CLASS_CONSTRAINT]; + } } diff --git a/src/Validator/Constraints/NoLockoutValidator.php b/src/Validator/Constraints/NoLockoutValidator.php index 4622d7fe..f3998188 100644 --- a/src/Validator/Constraints/NoLockoutValidator.php +++ b/src/Validator/Constraints/NoLockoutValidator.php @@ -22,28 +22,20 @@ declare(strict_types=1); namespace App\Validator\Constraints; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Security\Core\User\UserInterface; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use App\Services\UserSystem\PermissionManager; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Form\Exception\UnexpectedTypeException; -use Symfony\Component\Security\Core\Security; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; class NoLockoutValidator extends ConstraintValidator { - protected PermissionManager $resolver; - protected array $perm_structure; - protected Security $security; - protected EntityManagerInterface $entityManager; - - public function __construct(PermissionManager $resolver, Security $security, EntityManagerInterface $entityManager) + public function __construct(protected PermissionManager $resolver, protected Security $security, protected EntityManagerInterface $entityManager) { - $this->resolver = $resolver; - $this->perm_structure = $resolver->getPermissionStructure(); - $this->security = $security; - $this->entityManager = $entityManager; } /** @@ -52,7 +44,7 @@ class NoLockoutValidator extends ConstraintValidator * @param mixed $value The value that should be validated * @param Constraint $constraint The constraint for the validation */ - public function validate($value, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof NoLockout) { throw new UnexpectedTypeException($constraint, NoLockout::class); @@ -64,18 +56,20 @@ class NoLockoutValidator extends ConstraintValidator if ($perm_holder instanceof User || $perm_holder instanceof Group) { $user = $this->security->getUser(); - if (null === $user) { + if (!$user instanceof UserInterface) { $user = $this->entityManager->getRepository(User::class)->getAnonymousUser(); } - //Check if we the change_permission permission has changed from allow to disallow - if (($user instanceof User) && false === ($this->resolver->inherit( + //Check if the change_permission permission has changed from allow to disallow + if (($user instanceof User) && !($this->resolver->inherit( $user, 'users', 'edit_permissions' ) ?? false)) { $this->context->addViolation($constraint->message); } + } else { + throw new \LogicException('The NoLockout constraint can only be used on User or Group objects.'); } } } diff --git a/src/Validator/Constraints/NoneOfItsChildren.php b/src/Validator/Constraints/NoneOfItsChildren.php index e3bed2f8..8f1e059a 100644 --- a/src/Validator/Constraints/NoneOfItsChildren.php +++ b/src/Validator/Constraints/NoneOfItsChildren.php @@ -25,19 +25,18 @@ namespace App\Validator\Constraints; use Symfony\Component\Validator\Constraint; /** - * Constraints the parent property on StructuralDBElement objects in the way, that neither the object self or any + * Constraints the parent property on StructuralDBElement objects in the way, that neither the object self nor any * of its children can be assigned. - * - * @Annotation */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] class NoneOfItsChildren extends Constraint { /** - * @var string The message used if it is tried to assign a object as its own parent + * @var string The message used if it is tried to assign an object as its own parent */ - public $self_message = 'validator.noneofitschild.self'; + public string $self_message = 'validator.noneofitschild.self'; /** * @var string The message used if it is tried to use one of the children for as parent */ - public $children_message = 'validator.noneofitschild.children'; + public string $children_message = 'validator.noneofitschild.children'; } diff --git a/src/Validator/Constraints/NoneOfItsChildrenValidator.php b/src/Validator/Constraints/NoneOfItsChildrenValidator.php index 7bc3fd5a..2be5f16b 100644 --- a/src/Validator/Constraints/NoneOfItsChildrenValidator.php +++ b/src/Validator/Constraints/NoneOfItsChildrenValidator.php @@ -30,6 +30,7 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; /** * The validator for the NoneOfItsChildren annotation. + * @see \App\Tests\Validator\Constraints\NoneOfItsChildrenValidatorTest */ class NoneOfItsChildrenValidator extends ConstraintValidator { @@ -39,7 +40,7 @@ class NoneOfItsChildrenValidator extends ConstraintValidator * @param mixed $value The value that should be validated * @param Constraint $constraint The constraint for the validation */ - public function validate($value, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof NoneOfItsChildren) { throw new UnexpectedTypeException($constraint, NoneOfItsChildren::class); @@ -63,7 +64,7 @@ class NoneOfItsChildrenValidator extends ConstraintValidator // Check if the targeted parent is the object itself: $entity_id = $entity->getID(); - if (null !== $entity_id && $entity_id === $value->getID()) { + if ($entity === $value || (null !== $entity_id && $entity_id === $value->getID())) { //Set the entity to a valid state $entity->setParent(null); $this->context->buildViolation($constraint->self_message)->addViolation(); diff --git a/src/Validator/Constraints/ProjectSystem/ValidProjectBuildRequest.php b/src/Validator/Constraints/ProjectSystem/ValidProjectBuildRequest.php index b0c99947..1e9ac834 100644 --- a/src/Validator/Constraints/ProjectSystem/ValidProjectBuildRequest.php +++ b/src/Validator/Constraints/ProjectSystem/ValidProjectBuildRequest.php @@ -1,4 +1,7 @@ . */ - namespace App\Validator\Constraints\ProjectSystem; use Symfony\Component\Validator\Constraint; /** * This constraint checks that the given ProjectBuildRequest is valid. - * - * @Annotation */ +#[\Attribute(\Attribute::TARGET_CLASS)] class ValidProjectBuildRequest extends Constraint { public function getTargets(): string { return self::CLASS_CONSTRAINT; } -} \ No newline at end of file +} diff --git a/src/Validator/Constraints/ProjectSystem/ValidProjectBuildRequestValidator.php b/src/Validator/Constraints/ProjectSystem/ValidProjectBuildRequestValidator.php index 03a9b81f..2d59e648 100644 --- a/src/Validator/Constraints/ProjectSystem/ValidProjectBuildRequestValidator.php +++ b/src/Validator/Constraints/ProjectSystem/ValidProjectBuildRequestValidator.php @@ -1,4 +1,7 @@ . */ - namespace App\Validator\Constraints\ProjectSystem; use App\Entity\Parts\PartLot; @@ -36,7 +38,7 @@ class ValidProjectBuildRequestValidator extends ConstraintValidator ->setParameter('{{ lot }}', $partLot->getName()); } - public function validate($value, Constraint $constraint) + public function validate($value, Constraint $constraint): void { if (!$constraint instanceof ValidProjectBuildRequest) { throw new UnexpectedTypeException($constraint, ValidProjectBuildRequest::class); @@ -67,16 +69,16 @@ class ValidProjectBuildRequestValidator extends ConstraintValidator ->addViolation(); } - if ($withdraw_sum > $needed_amount) { + if ($withdraw_sum > $needed_amount && $value->isDontCheckQuantity() === false) { $this->buildViolationForLot($lot, 'validator.project_build.lot_bigger_than_needed') ->addViolation(); } - if ($withdraw_sum < $needed_amount) { + if ($withdraw_sum < $needed_amount && $value->isDontCheckQuantity() === false) { $this->buildViolationForLot($lot, 'validator.project_build.lot_smaller_than_needed') ->addViolation(); } } } } -} \ No newline at end of file +} diff --git a/src/Validator/Constraints/Selectable.php b/src/Validator/Constraints/Selectable.php index 20a51aad..c26e47fa 100644 --- a/src/Validator/Constraints/Selectable.php +++ b/src/Validator/Constraints/Selectable.php @@ -27,10 +27,9 @@ use Symfony\Component\Validator\Constraint; /** * If a property is marked with this constraint, the choosen value (of type StructuralDBElement) * must NOT be marked as not selectable. - * - * @Annotation */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] class Selectable extends Constraint { - public $message = 'validator.isSelectable'; + public string $message = 'validator.isSelectable'; } diff --git a/src/Validator/Constraints/SelectableValidator.php b/src/Validator/Constraints/SelectableValidator.php index 8b93865d..013a3964 100644 --- a/src/Validator/Constraints/SelectableValidator.php +++ b/src/Validator/Constraints/SelectableValidator.php @@ -30,6 +30,7 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; /** * The validator for the Selectable constraint. + * @see \App\Tests\Validator\Constraints\SelectableValidatorTest */ class SelectableValidator extends ConstraintValidator { @@ -39,7 +40,7 @@ class SelectableValidator extends ConstraintValidator * @param mixed $value The value that should be validated * @param Constraint $constraint The constraint for the validation */ - public function validate($value, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Selectable) { throw new UnexpectedTypeException($constraint, Selectable::class); @@ -53,7 +54,7 @@ class SelectableValidator extends ConstraintValidator //Check type of value. Validating only works for StructuralDBElements if (!$value instanceof AbstractStructuralDBElement) { - throw new UnexpectedValueException($value, 'StructuralDBElement'); + throw new UnexpectedValueException($value, AbstractStructuralDBElement::class); } //Check if the value is not selectable -> show error message then. diff --git a/src/Validator/Constraints/UniqueObjectCollection.php b/src/Validator/Constraints/UniqueObjectCollection.php new file mode 100644 index 00000000..6548494e --- /dev/null +++ b/src/Validator/Constraints/UniqueObjectCollection.php @@ -0,0 +1,64 @@ +. + */ +namespace App\Validator\Constraints; + +use InvalidArgumentException; +use Symfony\Component\Validator\Constraint; + +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class UniqueObjectCollection extends Constraint +{ + public const IS_NOT_UNIQUE = '7911c98d-b845-4da0-94b7-a8dac36bc55a'; + + public array|string $fields = []; + + protected const ERROR_NAMES = [ + self::IS_NOT_UNIQUE => 'IS_NOT_UNIQUE', + ]; + + public string $message = 'This value is already used.'; + public $normalizer; + + /** + * @param array|string $fields the combination of fields that must contain unique values or a set of options + */ + public function __construct( + ?array $options = null, + ?string $message = null, + ?callable $normalizer = null, + ?array $groups = null, + mixed $payload = null, + array|string|null $fields = null, + public bool $allowNull = true, + ) { + parent::__construct($options, $groups, $payload); + + $this->message = $message ?? $this->message; + $this->normalizer = $normalizer ?? $this->normalizer; + $this->fields = $fields ?? $this->fields; + + if (null !== $this->normalizer && !\is_callable($this->normalizer)) { + throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + } + } +} diff --git a/src/Validator/Constraints/UniqueObjectCollectionValidator.php b/src/Validator/Constraints/UniqueObjectCollectionValidator.php new file mode 100644 index 00000000..b80889a4 --- /dev/null +++ b/src/Validator/Constraints/UniqueObjectCollectionValidator.php @@ -0,0 +1,114 @@ +. + */ +namespace App\Validator\Constraints; + +use App\Validator\UniqueValidatableInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\UnexpectedValueException; + +/** + * @see \App\Tests\Validator\Constraints\UniqueObjectCollectionValidatorTest + */ +class UniqueObjectCollectionValidator extends ConstraintValidator +{ + + public function validate(mixed $value, Constraint $constraint): void + { + if (!$constraint instanceof UniqueObjectCollection) { + throw new UnexpectedTypeException($constraint, UniqueObjectCollection::class); + } + + $fields = (array) $constraint->fields; + + if (null === $value) { + return; + } + + if (!\is_array($value) && !$value instanceof \IteratorAggregate) { + throw new UnexpectedValueException($value, 'array|IteratorAggregate'); + } + + $collectionElements = []; + $normalizer = $this->getNormalizer($constraint); + foreach ($value as $key => $object) { + + if (!$object instanceof UniqueValidatableInterface) { + throw new UnexpectedValueException($object, UniqueValidatableInterface::class); + } + + //Convert the object to an array using the helper function + $element = $object->getComparableFields(); + + if ($fields && !$element = $this->reduceElementKeys($fields, $element, $constraint)) { + continue; + } + + $element = $normalizer($element); + + if (\in_array($element, $collectionElements, true)) { + + $violation = $this->context->buildViolation($constraint->message); + + //Use the first supplied field as the target field, or the first defined field name of the element if none is supplied + $target_field = $constraint->fields[0] ?? array_keys($element)[0]; + + $violation->atPath('[' . $key . ']' . '.' . $target_field); + + $violation->setParameter('{{ object }}', $this->formatValue($object, ConstraintValidator::OBJECT_TO_STRING)) + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->addViolation(); + + return; + } + $collectionElements[] = $element; + } + } + + private function getNormalizer(UniqueObjectCollection $unique): callable + { + return $unique->normalizer ?? static fn($value) => $value; + } + + private function reduceElementKeys(array $fields, array $element, UniqueObjectCollection $constraint): array + { + $output = []; + foreach ($fields as $field) { + if (!\is_string($field)) { + throw new UnexpectedTypeException($field, 'string'); + } + if (\array_key_exists($field, $element)) { + //Ignore null values if specified + if ($element[$field] === null && $constraint->allowNull) { + continue; + } + + $output[$field] = $element[$field]; + } + } + + return $output; + } + +} diff --git a/src/Validator/Constraints/UrlOrBuiltin.php b/src/Validator/Constraints/UrlOrBuiltin.php index d2c5715f..ceec5d07 100644 --- a/src/Validator/Constraints/UrlOrBuiltin.php +++ b/src/Validator/Constraints/UrlOrBuiltin.php @@ -26,14 +26,13 @@ use App\Entity\Attachments\Attachment; use Symfony\Component\Validator\Constraints\Url; /** - * Constraints the field that way that the content is either a url or a path to a builtin ressource (like %FOOTPRINTS%). - * - * @Annotation + * Constraints the field that way that the content is either an url or a path to a builtin ressource (like %FOOTPRINTS%). */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] class UrlOrBuiltin extends Url { /** * @var array A list of the placeholders that are treated as builtin */ - public $allowed_placeholders = Attachment::BUILTIN_PLACEHOLDER; + public array $allowed_placeholders = Attachment::BUILTIN_PLACEHOLDER; } diff --git a/src/Validator/Constraints/UrlOrBuiltinValidator.php b/src/Validator/Constraints/UrlOrBuiltinValidator.php index b8ad3b6a..71407a6a 100644 --- a/src/Validator/Constraints/UrlOrBuiltinValidator.php +++ b/src/Validator/Constraints/UrlOrBuiltinValidator.php @@ -34,6 +34,7 @@ use function is_object; * The validator for UrlOrBuiltin. * It checks if the value is either a builtin ressource or a valid url. * In both cases it is not checked, if the ressource is really existing. + * @see \App\Tests\Validator\Constraints\UrlOrBuiltinValidatorTest */ class UrlOrBuiltinValidator extends UrlValidator { @@ -57,7 +58,7 @@ class UrlOrBuiltinValidator extends UrlValidator //After the %PLACEHOLDER% comes a slash, so we can check if we have a placholder via explode $tmp = explode('/', $value); //Builtins must have a %PLACEHOLDER% construction - if (in_array($tmp[0], $constraint->allowed_placeholders, false)) { + if (in_array($tmp[0], $constraint->allowed_placeholders, true)) { return; } diff --git a/src/Validator/Constraints/ValidFileFilter.php b/src/Validator/Constraints/ValidFileFilter.php index 8a7b70d0..d962c0ea 100644 --- a/src/Validator/Constraints/ValidFileFilter.php +++ b/src/Validator/Constraints/ValidFileFilter.php @@ -24,9 +24,7 @@ namespace App\Validator\Constraints; use Symfony\Component\Validator\Constraint; -/** - * @Annotation - */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] class ValidFileFilter extends Constraint { } diff --git a/src/Validator/Constraints/ValidFileFilterValidator.php b/src/Validator/Constraints/ValidFileFilterValidator.php index ccce30ce..2a90a010 100644 --- a/src/Validator/Constraints/ValidFileFilterValidator.php +++ b/src/Validator/Constraints/ValidFileFilterValidator.php @@ -32,11 +32,8 @@ use function is_string; class ValidFileFilterValidator extends ConstraintValidator { - protected FileTypeFilterTools $filterTools; - - public function __construct(FileTypeFilterTools $filterTools) + public function __construct(protected FileTypeFilterTools $filterTools) { - $this->filterTools = $filterTools; } /** @@ -45,7 +42,7 @@ class ValidFileFilterValidator extends ConstraintValidator * @param mixed $value The value that should be validated * @param Constraint $constraint The constraint for the validation */ - public function validate($value, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof ValidFileFilter) { throw new UnexpectedTypeException($constraint, ValidFileFilter::class); diff --git a/src/Validator/Constraints/ValidGoogleAuthCode.php b/src/Validator/Constraints/ValidGoogleAuthCode.php index 0956b961..180d346e 100644 --- a/src/Validator/Constraints/ValidGoogleAuthCode.php +++ b/src/Validator/Constraints/ValidGoogleAuthCode.php @@ -22,8 +22,20 @@ declare(strict_types=1); namespace App\Validator\Constraints; +use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; use Symfony\Component\Validator\Constraint; class ValidGoogleAuthCode extends Constraint { + /** + * @param TwoFactorInterface|null $user The user to use for the validation process, if null, the current user is used + */ + public function __construct( + ?array $options = null, + ?array $groups = null, + mixed $payload = null, + public ?TwoFactorInterface $user = null) + { + parent::__construct($options, $groups, $payload); + } } diff --git a/src/Validator/Constraints/ValidGoogleAuthCodeValidator.php b/src/Validator/Constraints/ValidGoogleAuthCodeValidator.php index bb66f395..25afe57b 100644 --- a/src/Validator/Constraints/ValidGoogleAuthCodeValidator.php +++ b/src/Validator/Constraints/ValidGoogleAuthCodeValidator.php @@ -22,10 +22,9 @@ declare(strict_types=1); namespace App\Validator\Constraints; -use App\Entity\UserSystem\User; -use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticator; +use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; -use Symfony\Component\Form\FormInterface; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -34,13 +33,13 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; use function is_string; use function strlen; +/** + * @see \App\Tests\Validator\Constraints\ValidGoogleAuthCodeValidatorTest + */ class ValidGoogleAuthCodeValidator extends ConstraintValidator { - protected GoogleAuthenticatorInterface $googleAuthenticator; - - public function __construct(GoogleAuthenticatorInterface $googleAuthenticator) + public function __construct(private readonly GoogleAuthenticatorInterface $googleAuthenticator, private readonly Security $security) { - $this->googleAuthenticator = $googleAuthenticator; } public function validate($value, Constraint $constraint): void @@ -59,23 +58,24 @@ class ValidGoogleAuthCodeValidator extends ConstraintValidator if (!ctype_digit($value)) { $this->context->addViolation('validator.google_code.only_digits_allowed'); + return; } //Number must have 6 digits if (6 !== strlen($value)) { $this->context->addViolation('validator.google_code.wrong_digit_count'); + return; } - //Try to retrieve the user we want to check - if ($this->context->getObject() instanceof FormInterface && - $this->context->getObject()->getParent() instanceof FormInterface - && $this->context->getObject()->getParent()->getData() instanceof User) { - $user = $this->context->getObject()->getParent()->getData(); + //Use the current user to check the code + $user = $constraint->user ?? $this->security->getUser(); + if (!$user instanceof TwoFactorInterface) { + throw new UnexpectedValueException($user, TwoFactorInterface::class); + } - //Check if the given code is valid - if (!$this->googleAuthenticator->checkCode($user, $value)) { - $this->context->addViolation('validator.google_code.wrong_code'); - } + //Check if the given code is valid + if (!$this->googleAuthenticator->checkCode($user, $value)) { + $this->context->addViolation('validator.google_code.wrong_code'); } } } diff --git a/src/Validator/Constraints/ValidPartLot.php b/src/Validator/Constraints/ValidPartLot.php index 3b9658ac..a82c6a10 100644 --- a/src/Validator/Constraints/ValidPartLot.php +++ b/src/Validator/Constraints/ValidPartLot.php @@ -27,9 +27,8 @@ use Symfony\Component\Validator\Constraint; /** * A constraint "dummy" to validate the PartLot. * We need to access services in our Validator, so we can not use a simple callback on PartLot. - * - * @Annotation */ +#[\Attribute(\Attribute::TARGET_CLASS)] class ValidPartLot extends Constraint { public function getTargets(): string diff --git a/src/Validator/Constraints/ValidPartLotValidator.php b/src/Validator/Constraints/ValidPartLotValidator.php index d77ecd0e..316fedea 100644 --- a/src/Validator/Constraints/ValidPartLotValidator.php +++ b/src/Validator/Constraints/ValidPartLotValidator.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace App\Validator\Constraints; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Form\Exception\UnexpectedTypeException; @@ -32,11 +32,8 @@ use Symfony\Component\Validator\ConstraintValidator; class ValidPartLotValidator extends ConstraintValidator { - protected EntityManagerInterface $em; - - public function __construct(EntityManagerInterface $em) + public function __construct(protected EntityManagerInterface $em) { - $this->em = $em; } /** @@ -45,7 +42,7 @@ class ValidPartLotValidator extends ConstraintValidator * @param mixed $value The value that should be validated * @param Constraint $constraint The constraint for the validation */ - public function validate($value, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof ValidPartLot) { throw new UnexpectedTypeException($constraint, ValidPartLot::class); @@ -56,8 +53,8 @@ class ValidPartLotValidator extends ConstraintValidator } //We can only validate the values if we know the storelocation - if ($value->getStorageLocation()) { - $repo = $this->em->getRepository(Storelocation::class); + if ($value->getStorageLocation() instanceof StorageLocation) { + $repo = $this->em->getRepository(StorageLocation::class); //We can only determine associated parts, if the part have an ID //When the storage location is new (no ID), we can just assume there are no other parts if (null !== $value->getID() && $value->getStorageLocation()->getID()) { diff --git a/src/Validator/Constraints/ValidPermission.php b/src/Validator/Constraints/ValidPermission.php index 58a4ad2c..4d05f6cf 100644 --- a/src/Validator/Constraints/ValidPermission.php +++ b/src/Validator/Constraints/ValidPermission.php @@ -28,8 +28,8 @@ use Symfony\Component\Validator\Constraint; * A PermissionEmbed object with this annotation will be checked with ValidPermissionValidator. * That means the alsoSet values of the permission operations are set. * - * @Annotation */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] class ValidPermission extends Constraint { } diff --git a/src/Validator/Constraints/ValidPermissionValidator.php b/src/Validator/Constraints/ValidPermissionValidator.php index 9b31048f..afb7721b 100644 --- a/src/Validator/Constraints/ValidPermissionValidator.php +++ b/src/Validator/Constraints/ValidPermissionValidator.php @@ -22,20 +22,22 @@ declare(strict_types=1); namespace App\Validator\Constraints; +use App\Controller\GroupController; +use App\Controller\UserController; use App\Security\Interfaces\HasPermissionsInterface; use App\Services\UserSystem\PermissionManager; use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; +use function Symfony\Component\Translation\t; + class ValidPermissionValidator extends ConstraintValidator { - protected PermissionManager $resolver; - protected array $perm_structure; - - public function __construct(PermissionManager $resolver) + public function __construct(protected PermissionManager $resolver, protected RequestStack $requestStack) { - $this->resolver = $resolver; } /** @@ -44,7 +46,7 @@ class ValidPermissionValidator extends ConstraintValidator * @param mixed $value The value that should be validated * @param Constraint $constraint The constraint for the validation */ - public function validate($value, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof ValidPermission) { throw new UnexpectedTypeException($constraint, ValidPermission::class); @@ -53,6 +55,26 @@ class ValidPermissionValidator extends ConstraintValidator /** @var HasPermissionsInterface $perm_holder */ $perm_holder = $this->context->getObject(); - $this->resolver->ensureCorrectSetOperations($perm_holder); + $changed = $this->resolver->ensureCorrectSetOperations($perm_holder); + + //Sending a flash message if the permissions were fixed (only if called from UserController or GroupController) + //This is pretty hacky and bad design but I dont see a better way without a complete rewrite of how permissions are validated + //on the admin pages + if ($changed) { + //Check if this was called in context of UserController + $request = $this->requestStack->getMainRequest(); + if ($request === null) { + return; + } + //Determine the controller class (the part before the ::) + $controller_class = explode('::', (string) $request->attributes->get('_controller'))[0]; + + if (in_array($controller_class, [UserController::class, GroupController::class], true)) { + /** @var Session $session */ + $session = $this->requestStack->getSession(); + $flashBag = $session->getFlashBag(); + $flashBag->add('warning', t('user.edit.flash.permissions_fixed')); + } + } } } diff --git a/src/Validator/Constraints/ValidTheme.php b/src/Validator/Constraints/ValidTheme.php index 70a32a20..92a19f5a 100644 --- a/src/Validator/Constraints/ValidTheme.php +++ b/src/Validator/Constraints/ValidTheme.php @@ -1,4 +1,7 @@ . */ - namespace App\Validator\Constraints; use Symfony\Component\Validator\Constraint; /** * A constraint to validate the theme setting of the user. - * @Annotation */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] class ValidTheme extends Constraint { public string $message = 'validator.selected_theme_is_invalid'; -} \ No newline at end of file +} diff --git a/src/Validator/Constraints/ValidThemeValidator.php b/src/Validator/Constraints/ValidThemeValidator.php index ec437b87..713be9a5 100644 --- a/src/Validator/Constraints/ValidThemeValidator.php +++ b/src/Validator/Constraints/ValidThemeValidator.php @@ -1,4 +1,7 @@ . */ - namespace App\Validator\Constraints; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; +/** + * @see \App\Tests\Validator\Constraints\ValidThemeValidatorTest + */ class ValidThemeValidator extends ConstraintValidator { - private array $available_themes; - - public function __construct(array $available_themes) + public function __construct(private readonly array $available_themes) { - $this->available_themes = $available_themes; } - public function validate($value, Constraint $constraint) + public function validate($value, Constraint $constraint): void { if (!$constraint instanceof ValidTheme) { throw new UnexpectedTypeException($constraint, ValidTheme::class); @@ -51,4 +53,4 @@ class ValidThemeValidator extends ConstraintValidator ->addViolation(); } } -} \ No newline at end of file +} diff --git a/src/Validator/Constraints/Year2038BugWorkaround.php b/src/Validator/Constraints/Year2038BugWorkaround.php new file mode 100644 index 00000000..04a07908 --- /dev/null +++ b/src/Validator/Constraints/Year2038BugWorkaround.php @@ -0,0 +1,41 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * Datetime interfaces properties with this constraint are limited to the year 2038 on 32-bit systems, to prevent a + * Year 2038 bug during rendering. + * + * Current PHP versions can not format dates after 2038 on 32-bit systems and throw an exception. + * (See https://github.com/Part-DB/Part-DB-server/discussions/548). + * + * This constraint does not fix that problem, but can prevent users from entering such invalid dates. + */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] +class Year2038BugWorkaround extends Constraint +{ + public string $message = 'validator.year_2038_bug_on_32bit'; +} \ No newline at end of file diff --git a/src/Validator/Constraints/Year2038BugWorkaroundValidator.php b/src/Validator/Constraints/Year2038BugWorkaroundValidator.php new file mode 100644 index 00000000..747721f9 --- /dev/null +++ b/src/Validator/Constraints/Year2038BugWorkaroundValidator.php @@ -0,0 +1,74 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Validator\Constraints; + +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +class Year2038BugWorkaroundValidator extends ConstraintValidator +{ + + public function __construct( + #[Autowire(env: "DISABLE_YEAR2038_BUG_CHECK")] + private readonly bool $disable_validation = false + ) + { + } + + public function isActivated(): bool + { + //If we are on a 32 bit system and the validation is not disabled, we should activate the validation + return !$this->disable_validation && PHP_INT_SIZE === 4; + } + + public function validate(mixed $value, Constraint $constraint): void + { + if (!$this->isActivated()) { + return; + } + + //If the value is null, we don't need to validate it + if ($value === null) { + return; + } + + //Ensure that we check the correct constraint + if (!$constraint instanceof Year2038BugWorkaround) { + throw new \InvalidArgumentException('This validator can only validate Year2038Bug constraints'); + } + + //We can only validate DateTime objects + if (!$value instanceof \DateTimeInterface) { + throw new UnexpectedTypeException($value, \DateTimeInterface::class); + } + + //If we reach here the validation is active and we should forbid any date after 2038. + if ($value->diff(new \DateTime('2038-01-19 03:14:06'))->invert === 1) { + $this->context->buildViolation($constraint->message) + ->addViolation(); + } + } +} \ No newline at end of file diff --git a/src/Validator/UniqueValidatableInterface.php b/src/Validator/UniqueValidatableInterface.php new file mode 100644 index 00000000..3d954490 --- /dev/null +++ b/src/Validator/UniqueValidatableInterface.php @@ -0,0 +1,34 @@ +. + */ +namespace App\Validator; + +interface UniqueValidatableInterface +{ + /** + * This method should return an array of fields that should be used to compare the objects for the UniqueObjectCollection constraint. + * All instances of the same class should return the same fields. + * The value must be a comparable value (e.g. string, int, float, bool, null). + * @return array An array of the form ['field1' => 'value1', 'field2' => 'value2', ...] + */ + public function getComparableFields(): array; +} diff --git a/symfony.lock b/symfony.lock index d453ff2e..c7471b73 100644 --- a/symfony.lock +++ b/symfony.lock @@ -1,9 +1,17 @@ { - "amphp/amp": { - "version": "v2.2.1" - }, - "amphp/byte-stream": { - "version": "v1.6.1" + "api-platform/core": { + "version": "3.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.2", + "ref": "696d44adc3c0d4f5d25a2f1c4f3700dd8a5c6db9" + }, + "files": [ + "config/packages/api_platform.yaml", + "config/routes/api_platform.yaml", + "src/ApiResource/.gitignore" + ] }, "beberlei/assert": { "version": "v3.2.6" @@ -20,40 +28,16 @@ "composer/package-versions-deprecated": { "version": "1.11.99.4" }, - "composer/pcre": { - "version": "1.0.0" - }, - "composer/semver": { - "version": "1.5.0" - }, - "composer/xdebug-handler": { - "version": "1.3.3" - }, "dama/doctrine-test-bundle": { - "version": "4.0", + "version": "8.0", "recipe": { "repo": "github.com/symfony/recipes-contrib", - "branch": "master", - "version": "4.0", - "ref": "56eaa387b5e48ebcc7c95a893b47dfa1ad51449c" + "branch": "main", + "version": "7.2", + "ref": "896306d79d4ee143af9eadf9b09fd34a8c391b70" }, "files": [ - "./config/packages/test/dama_doctrine_test_bundle.yaml" - ] - }, - "dnoegel/php-xdg-base-dir": { - "version": "v0.1.1" - }, - "doctrine/annotations": { - "version": "1.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "1.0", - "ref": "a2759dd6123694c8d901d0ec80006e044c2e6457" - }, - "files": [ - "./config/routes/annotations.yaml" + "./config/packages/dama_doctrine_test_bundle.yaml" ] }, "doctrine/cache": { @@ -62,9 +46,6 @@ "doctrine/collections": { "version": "v1.5.0" }, - "doctrine/common": { - "version": "v2.10.0" - }, "doctrine/data-fixtures": { "version": "v1.3.2" }, @@ -75,17 +56,17 @@ "version": "v0.5.3" }, "doctrine/doctrine-bundle": { - "version": "2.8", + "version": "2.11", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", - "version": "2.4", - "ref": "013b823e7fee65890b23e40f31e6667a1ac519ac" + "version": "2.10", + "ref": "c170ded8fc587d6bd670550c43dafcf093762245" }, "files": [ - "config/packages/doctrine.yaml", - "src/Entity/.gitignore", - "src/Repository/.gitignore" + "./config/packages/doctrine.yaml", + "./src/Entity/.gitignore", + "./src/Repository/.gitignore" ] }, "doctrine/doctrine-fixtures-bundle": { @@ -149,12 +130,6 @@ "erusev/parsedown": { "version": "1.7.4" }, - "felixfbecker/advanced-json-rpc": { - "version": "v3.0.4" - }, - "felixfbecker/language-server-protocol": { - "version": "v1.4.0" - }, "florianv/exchanger": { "version": "1.4.1" }, @@ -162,25 +137,37 @@ "version": "3.5.0" }, "florianv/swap-bundle": { - "version": "5.0.0" - }, - "friendsofphp/proxy-manager-lts": { - "version": "v1.0.5" + "version": "5.0.x-dev" }, "gregwar/captcha": { "version": "v1.1.7" }, "gregwar/captcha-bundle": { - "version": "v2.0.6" + "version": "v2.2.0" }, "imagine/imagine": { "version": "1.2.2" }, "jbtronics/2fa-webauthn": { - "version": "dev-master" + "version": "v2.2.1" }, - "laminas/laminas-code": { - "version": "3.4.1" + "jbtronics/dompdf-font-loader-bundle": { + "version": "v1.1.1" + }, + "jbtronics/translation-editor-bundle": { + "version": "v1.0" + }, + "knpuniversity/oauth2-client-bundle": { + "version": "2.15", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "1.20", + "ref": "1ff300d8c030f55c99219cc55050b97a695af3f6" + }, + "files": [ + "./config/packages/knpu_oauth2_client.yaml" + ] }, "league/html-to-markdown": { "version": "4.8.2" @@ -204,6 +191,21 @@ "monolog/monolog": { "version": "1.24.0" }, + "nbgrp/onelogin-saml-bundle": { + "version": "v1.4.0" + }, + "nelmio/cors-bundle": { + "version": "2.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.5", + "ref": "6bea22e6c564fba3a1391615cada1437d0bde39c" + }, + "files": [ + "./config/packages/nelmio_cors.yaml" + ] + }, "nelmio/security-bundle": { "version": "2.4", "recipe": { @@ -216,18 +218,12 @@ "./config/packages/nelmio_security.yaml" ] }, - "netresearch/jsonmapper": { - "version": "v1.6.0" - }, "nikic/php-parser": { "version": "v4.2.1" }, "nikolaposa/version": { "version": "2.2.2" }, - "nyholm/nsa": { - "version": "1.1.0" - }, "nyholm/psr7": { "version": "1.0", "recipe": { @@ -259,7 +255,16 @@ "version": "v0.3.3" }, "php-http/discovery": { - "version": "1.7.0" + "version": "1.18", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.18", + "ref": "f45b5dd173a27873ab19f5e3180b2f661c21de02" + }, + "files": [ + "./config/packages/http_discovery.yaml" + ] }, "php-http/httplug": { "version": "v2.0.0" @@ -270,30 +275,6 @@ "php-http/promise": { "version": "v1.0.0" }, - "php-translation/common": { - "version": "1.0.0" - }, - "php-translation/extractor": { - "version": "1.7.1" - }, - "php-translation/symfony-bundle": { - "version": "0.12", - "recipe": { - "repo": "github.com/symfony/recipes-contrib", - "branch": "master", - "version": "0.10", - "ref": "f3ca4e4da63897d177e58da78626c20648c0e102" - }, - "files": [ - "config/packages/dev/php_translation.yaml", - "config/packages/php_translation.yaml", - "config/routes/dev/php_translation.yaml", - "config/routes/php_translation.yaml" - ] - }, - "php-translation/symfony-storage": { - "version": "1.0.1" - }, "phpdocumentor/reflection-common": { "version": "1.0.1" }, @@ -307,7 +288,16 @@ "version": "1.0.3" }, "phpstan/phpstan": { - "version": "0.12.8" + "version": "1.10", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "1.0", + "ref": "5e490cc197fb6bb1ae22e5abbc531ddc633b6767" + }, + "files": [ + "phpstan.dist.neon" + ] }, "phpstan/phpstan-doctrine": { "version": "0.12.9" @@ -315,8 +305,19 @@ "phpstan/phpstan-symfony": { "version": "0.12.4" }, - "psalm/plugin-symfony": { - "version": "v1.2.1" + "phpunit/phpunit": { + "version": "9.6", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "9.6", + "ref": "7364a21d87e658eb363c5020c072ecfdc12e2326" + }, + "files": [ + "./.env.test", + "./phpunit.xml.dist", + "./tests/bootstrap.php" + ] }, "psr/cache": { "version": "1.0.1" @@ -361,38 +362,23 @@ "version": "8.3.0" }, "scheb/2fa-bundle": { - "version": "5.13", + "version": "6.8", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", - "version": "5.0", - "ref": "0a83961ef50ff91812b229a6f0caf28431d94aec" + "version": "6.0", + "ref": "1e6f68089146853a790b5da9946fc5974f6fcd49" }, "files": [ - "./config/packages/scheb_2fa.yaml", - "./config/routes/scheb_2fa.yaml" + "config/packages/scheb_2fa.yaml", + "config/routes/scheb_2fa.yaml" ] }, "sebastian/diff": { "version": "3.0.2" }, - "sensio/framework-extra-bundle": { - "version": "5.2", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "5.2", - "ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b" - }, - "files": [ - "./config/packages/sensio_framework_extra.yaml" - ] - }, "shivas/versioning-bundle": { - "version": "3.1.3" - }, - "spomky-labs/cbor-bundle": { - "version": "v2.0.3" + "version": "4.0.3" }, "symfony/apache-pack": { "version": "1.0", @@ -400,10 +386,10 @@ "repo": "github.com/symfony/recipes-contrib", "branch": "main", "version": "1.0", - "ref": "efb318193e48384eb5c5aadff15396ed698f8ffc" + "ref": "0f18b4decdf5695d692c1d0dfd65516a07a6adf1" }, "files": [ - "public/.htaccess" + "./public/.htaccess" ] }, "symfony/asset": { @@ -422,15 +408,15 @@ "version": "v4.2.3" }, "symfony/console": { - "version": "5.3", + "version": "6.4", "recipe": { "repo": "github.com/symfony/recipes", - "branch": "master", + "branch": "main", "version": "5.3", - "ref": "da0c8be8157600ad34f10ff0c9cc91232522e047" + "ref": "1781ff40d8a17d87cf53f8d4cf0c8346ed2bb461" }, "files": [ - "./bin/console" + "bin/console" ] }, "symfony/css-selector": { @@ -482,27 +468,28 @@ "version": "v4.2.3" }, "symfony/flex": { - "version": "1.19", + "version": "2.4", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", - "version": "1.0", - "ref": "146251ae39e06a95be0fe3d13c807bcf3938b172" + "version": "2.4", + "ref": "52e9754527a15e2b79d9a610f98185a1fe46622a" }, "files": [ - ".env" + ".env", + ".env.dev" ] }, "symfony/form": { "version": "v4.2.3" }, "symfony/framework-bundle": { - "version": "5.4", + "version": "6.4", "recipe": { "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "5.4", - "ref": "3cd216a4d007b78d8554d44a5b1c0a446dab24fb" + "branch": "main", + "version": "6.4", + "ref": "a91c965766ad3ff2ae15981801643330eb42b6a5" }, "files": [ "config/packages/cache.yaml", @@ -530,28 +517,16 @@ "symfony/intl": { "version": "v4.2.3" }, - "symfony/lock": { - "version": "5.4", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "5.2", - "ref": "8e937ff2b4735d110af1770f242c1107fdab4c8e" - }, - "files": [ - "./config/packages/lock.yaml" - ] - }, "symfony/mailer": { - "version": "5.4", + "version": "6.4", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", "version": "4.3", - "ref": "2bf89438209656b85b9a49238c4467bff1b1f939" + "ref": "df66ee1f226c46f01e85c29c2f7acce0596ba35a" }, "files": [ - "config/packages/mailer.yaml" + "./config/packages/mailer.yaml" ] }, "symfony/maker-bundle": { @@ -570,12 +545,12 @@ "version": "v4.4.2" }, "symfony/monolog-bundle": { - "version": "3.7", + "version": "3.10", "recipe": { "repo": "github.com/symfony/recipes", - "branch": "master", + "branch": "main", "version": "3.7", - "ref": "213676c4ec929f046dfde5ea8e97625b81bc0578" + "ref": "aff23899c4440dd995907613c1dd709b6f59503f" }, "files": [ "./config/packages/monolog.yaml" @@ -588,18 +563,18 @@ "version": "v5.3.8" }, "symfony/phpunit-bridge": { - "version": "5.4", + "version": "6.4", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", - "version": "5.3", - "ref": "819d3d2ffa4590eba0b8f4f3e5e89415ee4e45c3" + "version": "6.3", + "ref": "a411a0480041243d97382cac7984f7dce7813c08" }, "files": [ - ".env.test", - "bin/phpunit", - "phpunit.xml.dist", - "tests/bootstrap.php" + "./.env.test", + "./bin/phpunit", + "./phpunit.xml.dist", + "./tests/bootstrap.php" ] }, "symfony/polyfill-ctype": { @@ -620,18 +595,9 @@ "symfony/polyfill-mbstring": { "version": "v1.10.0" }, - "symfony/polyfill-php72": { - "version": "v1.10.0" - }, - "symfony/polyfill-php73": { - "version": "v1.11.0" - }, "symfony/polyfill-php80": { "version": "v1.17.0" }, - "symfony/polyfill-php81": { - "version": "v1.23.0" - }, "symfony/process": { "version": "v4.2.3" }, @@ -641,16 +607,13 @@ "symfony/property-info": { "version": "v4.2.3" }, - "symfony/proxy-manager-bridge": { - "version": "v5.2.1" - }, "symfony/routing": { - "version": "5.4", + "version": "6.2", "recipe": { "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "5.3", - "ref": "85de1d8ae45b284c3c84b668171d2615049e698f" + "branch": "main", + "version": "6.2", + "ref": "e0a11b4ccb8c9e70b574ff5ad3dfdcd41dec5aa6" }, "files": [ "config/packages/routing.yaml", @@ -661,15 +624,16 @@ "version": "v5.3.4" }, "symfony/security-bundle": { - "version": "5.4", + "version": "6.4", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", - "version": "5.3", - "ref": "98f1f2b0d635908c2b40f3675da2d23b1a069d30" + "version": "6.4", + "ref": "2ae08430db28c8eb4476605894296c82a642028f" }, "files": [ - "config/packages/security.yaml" + "config/packages/security.yaml", + "config/routes/security.yaml" ] }, "symfony/security-core": { @@ -678,9 +642,6 @@ "symfony/security-csrf": { "version": "v4.2.3" }, - "symfony/security-guard": { - "version": "v4.2.3" - }, "symfony/security-http": { "version": "v4.2.3" }, @@ -690,6 +651,20 @@ "symfony/service-contracts": { "version": "v1.1.5" }, + "symfony/stimulus-bundle": { + "version": "2.16", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "2.13", + "ref": "6acd9ff4f7fd5626d2962109bd4ebab351d43c43" + }, + "files": [ + "./assets/bootstrap.js", + "./assets/controllers.json", + "./assets/controllers/hello_controller.js" + ] + }, "symfony/stopwatch": { "version": "v4.2.3" }, @@ -697,12 +672,12 @@ "version": "v5.1.0" }, "symfony/translation": { - "version": "5.3", + "version": "6.4", "recipe": { "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "5.3", - "ref": "da64f5a2b6d96f5dc24914517c0350a5f91dee43" + "branch": "main", + "version": "6.3", + "ref": "e28e27f53663cc34f0be2837aba18e3a1bef8e7b" }, "files": [ "./config/packages/translation.yaml", @@ -716,20 +691,47 @@ "version": "v4.2.3" }, "symfony/twig-bundle": { - "version": "5.4", + "version": "6.4", "recipe": { "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "5.4", - "ref": "bb2178c57eee79e6be0b297aa96fc0c0def81387" + "branch": "main", + "version": "6.4", + "ref": "cab5fd2a13a45c266d45a7d9337e28dee6272877" }, "files": [ - "config/packages/twig.yaml", - "templates/base.html.twig" + "./config/packages/twig.yaml", + "./templates/base.html.twig" + ] + }, + "symfony/uid": { + "version": "6.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.2", + "ref": "d294ad4add3e15d7eb1bae0221588ca89b38e558" + }, + "files": [ + "./config/packages/uid.yaml" + ] + }, + "symfony/ux-translator": { + "version": "2.9", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "2.9", + "ref": "bc396565cc4cab95692dd6df810553dc22e352e1" + }, + "files": [ + "./assets/translator.js", + "./config/packages/ux_translator.yaml", + "./var/translations/configuration.js", + "./var/translations/index.js" ] }, "symfony/ux-turbo": { - "version": "v2.0.1" + "version": "v2.16.0" }, "symfony/validator": { "version": "5.4", @@ -753,12 +755,12 @@ "version": "v4.2.3" }, "symfony/web-profiler-bundle": { - "version": "5.4", + "version": "6.3", "recipe": { "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "5.3", - "ref": "24bbc3d84ef2f427f82104f766014e799eefcc3e" + "branch": "main", + "version": "6.1", + "ref": "e42b3f0177df239add25373083a564e5ead4e13a" }, "files": [ "config/packages/web_profiler.yaml", @@ -766,18 +768,15 @@ ] }, "symfony/webpack-encore-bundle": { - "version": "1.16", + "version": "2.1", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", - "version": "1.10", - "ref": "f8fc53f1942f76679e9ee3c25fd44865355707b5" + "version": "2.0", + "ref": "082d754b3bd54b3fc669f278f1eea955cfd23cf5" }, "files": [ "assets/app.js", - "assets/bootstrap.js", - "assets/controllers.json", - "assets/controllers/hello_controller.js", "assets/styles/app.css", "config/packages/webpack_encore.yaml", "package.json", @@ -803,7 +802,7 @@ "version": "v3.0.0" }, "twig/extra-bundle": { - "version": "v3.0.0" + "version": "v3.8.0" }, "twig/html-extra": { "version": "v3.0.3" @@ -823,17 +822,18 @@ "ua-parser/uap-php": { "version": "v3.9.8" }, - "vimeo/psalm": { - "version": "3.5.1" - }, "web-auth/webauthn-symfony-bundle": { - "version": "3.3", + "version": "4.7", "recipe": { "repo": "github.com/symfony/recipes-contrib", "branch": "main", "version": "3.0", - "ref": "9926090a80c2cceeffe96e6c3312b397ea55d4a7" - } + "ref": "a5dff33bd46575bea263af94069650af7742dcb6" + }, + "files": [ + "config/packages/webauthn.yaml", + "config/routes/webauthn_routes.yaml" + ] }, "webmozart/assert": { "version": "1.4.0" diff --git a/templates/_navbar.html.twig b/templates/_navbar.html.twig index 14e40151..cd1f641f 100644 --- a/templates/_navbar.html.twig +++ b/templates/_navbar.html.twig @@ -1,12 +1,20 @@ {% import "helper.twig" as helper %} +{% import "components/search.macro.html.twig" as search %} -
{% for attachment in form %} - {{ form_widget(attachment) }} + {{ form_widget(attachment) }} {% endfor %}
- +
+ + + +
{% endmacro %} -{% macro attachment_icon(attachment, attachment_helper, class = "fa-fw fas fa-3x", link = true) %} - {% set disabled = attachment.secure and not is_granted("show_secure", attachment) %} +{% macro attachment_icon(attachment, attachment_helper, class = "fa-fw fas fa-2x hoverpic", link = true) %} + {# @var App\Entity\Attachments\Attachment attachment #} + {% set disabled = attachment.secure and not is_granted("show_private", attachment) %} {% if not attachment_helper or attachment_helper.fileExisting(attachment) %} {% if link and not disabled %} {% endif %} {% if attachment.picture %} - + {% else %} {% endif %} diff --git a/templates/components/collection_type.macro.html.twig b/templates/components/collection_type.macro.html.twig index 5fcc8053..fde2b961 100644 --- a/templates/components/collection_type.macro.html.twig +++ b/templates/components/collection_type.macro.html.twig @@ -4,11 +4,13 @@ 'deleteMessage': deleteMessage|trans, 'prototype': form_widget(form.vars.prototype)|e('html_attr'), 'rowsToDelete': rowsToDelete, + 'fieldPlaceholder': form.vars.prototype_name }) }} {% else %} {# If add_element is disabled/forbidden, prototype is not available #} {{ stimulus_controller('elements/collection_type', { 'deleteMessage': deleteMessage|trans, - 'rowsToDelete': rowsToDelete + 'rowsToDelete': rowsToDelete, + 'fieldPlaceholder': form.vars.prototype_name }) }} {% endif %} {% endmacro %} @@ -27,4 +29,13 @@ {% macro delete_btn() %} {{ stimulus_action('elements/collection_type', 'deleteElement') }} +{% endmacro %} + +{% macro new_element_indicator(value) %} + {% if value.id is not defined or value.id is null %} + + New alerts + + {% endif %} {% endmacro %} \ No newline at end of file diff --git a/templates/components/datatables.macro.html.twig b/templates/components/datatables.macro.html.twig index 8c4dd82f..5ce0f23f 100644 --- a/templates/components/datatables.macro.html.twig +++ b/templates/components/datatables.macro.html.twig @@ -1,5 +1,5 @@ {% macro datatable(datatable, controller = 'elements/datatables/datatables', state_save_tag = null) %} -
+
@@ -19,12 +19,13 @@ {% macro partsDatatableWithForm(datatable, state_save_tag = 'parts') %} - + @@ -32,6 +33,8 @@ {# #}
+ {% trans with {'%count%': ''} %}part_list.action.part_count{% endtrans %} @@ -47,10 +50,11 @@ - - - - + + + + + @@ -60,14 +64,22 @@ - + + + + + + + + + - +
diff --git a/templates/components/history_log_macros.html.twig b/templates/components/history_log_macros.html.twig index f3481305..68df2015 100644 --- a/templates/components/history_log_macros.html.twig +++ b/templates/components/history_log_macros.html.twig @@ -17,7 +17,7 @@ {{ stimulus_controller('elements/delete_btn') }} {{ stimulus_action('elements/delete_btn', "submit", "submit") }} data-delete-title="{% trans %}log.undo.confirm_title{% endtrans %}" data-delete-message="{% trans %}log.undo.confirm_message{% endtrans %}"> - + {{ datatables.logDataTable(datatable, tag) }} diff --git a/templates/components/new_version.macro.html.twig b/templates/components/new_version.macro.html.twig new file mode 100644 index 00000000..f8bc1e2e --- /dev/null +++ b/templates/components/new_version.macro.html.twig @@ -0,0 +1,9 @@ +{% macro new_version_alert(is_available, new_version, new_version_url) %} + {% if is_available %} +
+ {% endif %} +{% endmacro %} \ No newline at end of file diff --git a/templates/components/search.macro.html.twig b/templates/components/search.macro.html.twig new file mode 100644 index 00000000..e62af2b1 --- /dev/null +++ b/templates/components/search.macro.html.twig @@ -0,0 +1,105 @@ +{% macro settings_drodown(show_label_instead_icon = true) %} + + +{% endmacro %} + +{# Render a complete usable search form including the form tags. mode can be "standalone" or "navbar" #} +{% macro search_form(mode = "standalone") %} + {% set is_navbar = (mode == "navbar") %} + +
+ + {# Show the options left in navbar #} + {% if is_navbar %} + {{ _self.settings_drodown(is_navbar) }} + {% endif %} + +
+ + +
+ + {# And right in the standalone mode #} + {% if not is_navbar %} + {{ _self.settings_drodown(is_navbar) }} + {% endif %} +
+{% endmacro %} \ No newline at end of file diff --git a/templates/components/tree_macros.html.twig b/templates/components/tree_macros.html.twig index b85fcc88..12bef78f 100644 --- a/templates/components/tree_macros.html.twig +++ b/templates/components/tree_macros.html.twig @@ -28,13 +28,13 @@ {% macro treeview_sidebar(id, default_mode) %}
- - +
diff --git a/templates/form/collection_types_layout.html.twig b/templates/form/collection_types_layout.html.twig index 53311061..96b71bf0 100644 --- a/templates/form/collection_types_layout.html.twig +++ b/templates/form/collection_types_layout.html.twig @@ -38,20 +38,21 @@ - {{ form_errors(form.quantity) }} {{ form_widget(form.quantity) }} + {{ form_errors(form.quantity) }} - {{ form_errors(form.part) }} {{ form_widget(form.part) }} + {{ form_errors(form.part) }} - {{ form_errors(form.name) }} {{ form_widget(form.name) }} + {{ form_errors(form.name) }} - {{ form_errors(form) }} diff --git a/templates/form/extended_bootstrap_layout.html.twig b/templates/form/extended_bootstrap_layout.html.twig index 2a599672..811f57ac 100644 --- a/templates/form/extended_bootstrap_layout.html.twig +++ b/templates/form/extended_bootstrap_layout.html.twig @@ -122,4 +122,21 @@ {% block part_select_widget %} {{ form_widget(form.autocomplete) }} +{% endblock %} + +{% block password_widget %} + {# If password_estimator setting is not set render it like normal #} + {% if password_estimator %} +
+
+ + + + {{- parent() -}} +
+ +
+ {% else %} + {{- parent() -}} + {% endif %} {% endblock %} \ No newline at end of file diff --git a/templates/form/filter_types_layout.html.twig b/templates/form/filter_types_layout.html.twig index 70927d57..cae9e3ea 100644 --- a/templates/form/filter_types_layout.html.twig +++ b/templates/form/filter_types_layout.html.twig @@ -44,13 +44,17 @@ {{ block('text_constraint_widget') }} {% endblock %} +{% block enum_constraint_widget %} + {{ block('text_constraint_widget') }} +{% endblock %} + {% block parameter_constraint_widget %} {% import 'components/collection_type.macro.html.twig' as collection %} {{ form_widget(form.name, {"attr": {"data-pages--parameters-autocomplete-target": "name"}}) }} {{ form_widget(form.symbol, {"attr": {"data-pages--parameters-autocomplete-target": "symbol", "data-pages--latex-preview-target": "input"}}) }} {{ form_widget(form.value) }} - {{ form_widget(form.unit, {"attr": {"data-pages--parameters-autocomplete-target": "unit", "data-pages--latex-preview-target": "input"}}) }} + {{ form_widget(form.unit, {"attr": {"data-pages--parameters-autocomplete-target": "unit", "data-pages--latex-preview-target": "input"}}) }} {{ form_widget(form.value_text) }}
+ {% if show_dependency_notice %} + {% trans %}permission.legend.dependency_note{% endtrans %} + {% endif %} +
+ + {% if is_granted("@labels.read_profiles") %} + + {% endif %} + +
+
+
+ {{ form_widget(form.save_profile_name) }} + {{ form_widget(form.save_profile) }} +
+ {{ form_errors(form.save_profile_name) }} +
+
+
diff --git a/templates/label_system/labels/base_label.html.twig b/templates/label_system/labels/base_label.html.twig index da6a3e5c..494b99e4 100644 --- a/templates/label_system/labels/base_label.html.twig +++ b/templates/label_system/labels/base_label.html.twig @@ -8,17 +8,18 @@ {% for element in elements %}
- {% if options.barcodeType == 'none' %} + {% if options.barcodeType.none %} {% include "label_system/labels/label_page_none.html.twig" %} - {% elseif options.barcodeType in ['qr', 'datamatrix'] %} + {% elseif options.barcodeType.is2D() %} {% include "label_system/labels/label_page_qr.html.twig" %} - {% elseif options.barcodeType in ['code39', 'code93', 'code128'] %} + {% elseif options.barcodeType.is1D() %} {% include "label_system/labels/label_page_1d.html.twig" %} {% endif %}
diff --git a/templates/label_system/labels/label_style.css.twig b/templates/label_system/labels/label_style.css.twig index c2434a77..729e8cda 100644 --- a/templates/label_system/labels/label_style.css.twig +++ b/templates/label_system/labels/label_style.css.twig @@ -22,7 +22,7 @@ } body { - font-family: "DejaVu Sans Mono"; + font-family: "DejaVu Sans Mono", "unifont", monospace; font-size: 12px; line-height: 1.0; } @@ -37,6 +37,7 @@ hr { .qr { max-width: 80%; + max-height: 100%; } .qr-container a { diff --git a/templates/label_system/scanner/scanner.html.twig b/templates/label_system/scanner/scanner.html.twig index 39f4e140..1f978a9b 100644 --- a/templates/label_system/scanner/scanner.html.twig +++ b/templates/label_system/scanner/scanner.html.twig @@ -23,4 +23,22 @@ {{ form_end(form) }} + + {% if infoModeData %} +
+

{% trans %}label_scanner.decoded_info.title{% endtrans %}

+ + + + {% for key, value in infoModeData %} + + + + + {% endfor %} + +
{{ key }}{{ value }}
+ + {% endif %} + {% endblock %} diff --git a/templates/log_system/details/_extra_collection_element_deleted.html.twig b/templates/log_system/details/_extra_collection_element_deleted.html.twig new file mode 100644 index 00000000..221fae95 --- /dev/null +++ b/templates/log_system/details/_extra_collection_element_deleted.html.twig @@ -0,0 +1,15 @@ +{# @var entry \App\Entity\LogSystem\CollectionElementDeleted #} + +{% import "log_system/details/helper.macro.html.twig" as log_helper %} + +

+ {% trans %}log.collection_deleted.deleted{% endtrans %}: + {{ entity_type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }} + {% if entry.oldName is not empty %} + ({{ entry.oldName }}) + {% endif %} +

+

+ {% trans %}log.collection_deleted.on_collection{% endtrans %}: + {{ log_helper.translate_field(entry.collectionName) }} +

\ No newline at end of file diff --git a/templates/log_system/details/_extra_database_updated.html.twig b/templates/log_system/details/_extra_database_updated.html.twig new file mode 100644 index 00000000..7ee42c4f --- /dev/null +++ b/templates/log_system/details/_extra_database_updated.html.twig @@ -0,0 +1,23 @@ +{# @var entry \App\Entity\LogSystem\DatabaseUpdatedLogEntry #} + +{% if entry.successful %} +
+ + {% trans %}log.database_updated.success{% endtrans %} +
+{% else %} +
+ + {% trans %}log.database_updated.failed{% endtrans %} +
+{% endif %} + + + + {{ entry.oldVersion }} + + + + + {{ entry.newVersion }} + \ No newline at end of file diff --git a/templates/log_system/details/_extra_element_created.html.twig b/templates/log_system/details/_extra_element_created.html.twig new file mode 100644 index 00000000..8f7ce457 --- /dev/null +++ b/templates/log_system/details/_extra_element_created.html.twig @@ -0,0 +1,11 @@ +{# @var entry \App\Entity\LogSystem\ElementCreatedLogEntry #} + +{% import "log_system/details/helper.macro.html.twig" as log_helper %} + +{{ log_helper.comment_field(entry) }} +{% if entry.creationInstockValue %} +

+ {% trans %}log.element_created.original_instock{% endtrans %}: + {{ entry.creationInstockValue }} +

+{% endif %} \ No newline at end of file diff --git a/templates/log_system/details/_extra_element_deleted.html.twig b/templates/log_system/details/_extra_element_deleted.html.twig new file mode 100644 index 00000000..ceb9e9e2 --- /dev/null +++ b/templates/log_system/details/_extra_element_deleted.html.twig @@ -0,0 +1,8 @@ +{# @var entry \App\Entity\LogSystem\ElementDeletedLogEntry #} + +{% import "log_system/details/helper.macro.html.twig" as log_helper %} + + + +{{ log_helper.comment_field(entry) }} +{{ log_helper.data_change_table(entry) }} \ No newline at end of file diff --git a/templates/log_system/details/_extra_element_edited.html.twig b/templates/log_system/details/_extra_element_edited.html.twig new file mode 100644 index 00000000..33bd7901 --- /dev/null +++ b/templates/log_system/details/_extra_element_edited.html.twig @@ -0,0 +1,7 @@ +{# @var entry \App\Entity\LogSystem\ElementDeletedLogEntry #} + +{% import "log_system/details/helper.macro.html.twig" as log_helper %} + +{{ log_helper.comment_field(entry) }} + +{{ log_helper.data_change_table(entry) }} diff --git a/templates/log_system/details/_extra_security_event.html.twig b/templates/log_system/details/_extra_security_event.html.twig new file mode 100644 index 00000000..fa023db6 --- /dev/null +++ b/templates/log_system/details/_extra_security_event.html.twig @@ -0,0 +1,9 @@ +{# @var entry \App\Entity\LogSystem\UserLoginLogEntry #} + +IP:   + + + {{ entry.iPAddress }} + + +

{% trans %}log.user_login.ip_anonymize_hint{% endtrans %}

\ No newline at end of file diff --git a/templates/log_system/details/_extra_user_login.html.twig b/templates/log_system/details/_extra_user_login.html.twig new file mode 100644 index 00000000..4c7e8680 --- /dev/null +++ b/templates/log_system/details/_extra_user_login.html.twig @@ -0,0 +1,9 @@ +{# @var entry \App\Entity\LogSystem\UserLoginLogEntry #} + +{% trans %}log.user_login.login_from_ip{% endtrans %}:   + + + {{ entry.iPAddress }} + + +

{% trans %}log.user_login.ip_anonymize_hint{% endtrans %}

\ No newline at end of file diff --git a/templates/log_system/details/_extra_user_not_allowed.html.twig b/templates/log_system/details/_extra_user_not_allowed.html.twig new file mode 100644 index 00000000..43ab1500 --- /dev/null +++ b/templates/log_system/details/_extra_user_not_allowed.html.twig @@ -0,0 +1,4 @@ +{# @var entry \App\Entity\LogSystem\UserNotAllowedLogEntry #} + +{% trans %}log.user_not_allowed.unauthorized_access_attempt_to{% endtrans %}:  {{ entry.path }} +

{% trans %}log.user_not_allowed.hint{% endtrans %}

\ No newline at end of file diff --git a/templates/log_system/details/helper.macro.html.twig b/templates/log_system/details/helper.macro.html.twig new file mode 100644 index 00000000..e8957b66 --- /dev/null +++ b/templates/log_system/details/helper.macro.html.twig @@ -0,0 +1,149 @@ +{% macro undo_buttons(entry, target_element) %} + {# @var entry \App\Entity\LogSystem\ElementEditedLogEntry|\App\Entity\LogSystem\ElementDeletedLogEntry entry #} + {% set disabled = not is_granted('revert_element', entry.targetClass) %} + + {% if entry is instanceof('App\\Entity\\LogSystem\\CollectionElementDeleted') + or (entry is instanceof('App\\Entity\\LogSystem\\ElementDeletedLogEntry') and entry.hasOldDataInformation) %} + + {% set icon = 'fa-trash-restore' %} + {% set title = 'log.undo.undelete'|trans %} + {% set title_short = 'log.undo.undelete.short'|trans %} + + {% elseif entry is instanceof('App\\Entity\\LogSystem\\ElementCreatedLogEntry') + or (entry is instanceof('App\\Entity\\LogSystem\\ElementEditedLogEntry') and entry.hasOldDataInformation) %} + + {% set icon = 'fa-undo' %} + {% set title = 'log.undo.undo'|trans %} + {% set title_short = 'log.undo.undo.short'|trans %} + {% endif %} + +
+ + + +
+ + + + {# View button #} + {% if target_element and ((attribute(entry, 'oldDataInformation') is defined and entry.oldDataInformation) + or entry is instanceof('App\\Entity\\LogSystem\\CollectionElementDeleted')) + %} + + {% set url = timetravel_url(target_element, entry.timestamp) %} + + {% if url %} + + {% trans %}log.view_version{% endtrans %} + + {% endif %} + {% endif %} +
+
+{% endmacro %} + + +{% macro comment_field(entry) %} + {# @var entry \App\Entity\Contracts\LogWithComment #} +

+ {% trans %}edit.log_comment{% endtrans %}: + {% if entry.comment %} + {{ entry.comment }} + {% else %} + {% trans %}log.no_comment{% endtrans %} + {% endif %} +

+{% endmacro %} + +{% macro translate_field(field) %} + {% set trans_key = 'log.element_edited.changed_fields.'~field %} + {# If the translation key is not found, the translation key is returned, and we dont show the translation #} + {% if trans_key|trans != trans_key %} + {{ ('log.element_edited.changed_fields.'~field) | trans }} + ({{ field }}) + {% else %} + {{ field }} + {% endif %} +{% endmacro %} + +{% macro data_change_table(entry) %} + {# @var entry \App\Entity\LogSystem\ElementEditedLogEntry|\App\Entity\LogSystem\ElementDeletedLogEntry entry #} + + {% set fields, old_data, new_data = {}, {}, {} %} + + {# For log entries where only the changed fields are saved, this is the last executed assignment #} + {% if attribute(entry, 'changedFieldInfo') is defined and entry.changedFieldsInfo %} + {% set fields = entry.changedFields %} + {% endif %} + + {# For log entries, where we know the old data, this is the last exectuted assignment #} + {% if attribute(entry, 'oldDataInformation') is defined and entry.oldDataInformation %} + {# We have to use the keys of oldData here, as changedFields might not be available #} + {% set fields = entry.oldData | keys %} + {% set old_data = entry.oldData %} + {% endif %} + + {# For log entries, where we have new data, we define it #} + {% if attribute(entry, 'newDataInformation') is defined and entry.newDataInformation %} + {# We have to use the keys of oldData here, as changedFields might not be available #} + {% set fields = entry.newData | keys %} + {% set new_data = entry.newData %} + {% endif %} + + {% if fields is not empty %} + + + + + {% if old_data is not empty %} + + {% endif %} + {% if new_data is not empty %} + + {% endif %} + {% if new_data is not empty and old_data is not empty %} {# Diff column #} + + {% endif %} + + + + {% for field in fields %} + + + {% if old_data is not empty %} + + {% endif %} + {% if new_data is not empty %} + + {% endif %} + + {% if new_data is not empty and old_data is not empty %} + + {% endif %} + + {% endfor %} + +
{% trans %}log.element_changed.field{% endtrans %}{% trans %}log.element_changed.data_before{% endtrans %}{% trans %}log.element_changed.data_after{% endtrans %}{% trans %}log.element_changed.diff{% endtrans %}
+ {{ _self.translate_field(field) }} + + {% if old_data[field] is defined %} + {{ format_log_data(old_data[field], entry, field) }} + {% endif %} + + {% if new_data[field] is defined %} + {{ format_log_data(new_data[field], entry, field) }} + {% endif %} + + {% if new_data[field] is defined and old_data[field] is defined %} + {{ format_log_diff(old_data[field], new_data[field]) }} + {% endif %} +
+ {% endif %} +{% endmacro %} \ No newline at end of file diff --git a/templates/log_system/details/log_details.html.twig b/templates/log_system/details/log_details.html.twig new file mode 100644 index 00000000..aff127f4 --- /dev/null +++ b/templates/log_system/details/log_details.html.twig @@ -0,0 +1,117 @@ +{% extends "main_card.html.twig" %} + +{% import "helper.twig" as helper %} +{% import "log_system/details/helper.macro.html.twig" as log_helper %} + +{% block title %} + {% trans %}log.details.title{% endtrans %}: + {{ ('log.type.' ~ log_entry.type) | trans }} ({{ log_entry.timestamp | format_datetime('short') }}) +{% endblock %} + +{% block card_title %} + + {% trans %}log.details.title{% endtrans %}: + {{ ('log.type.' ~ log_entry.type) | trans }} ({{ log_entry.timestamp | format_datetime('short') }}) + ID: {{ log_entry.iD }} +{% endblock %} + +{% block card_body %} + + + + + + + + + + + + + + + + + + + + +
{% trans %}log.timestamp{% endtrans %}{{ log_entry.timestamp | format_datetime('full') }}
{% trans %}log.type{% endtrans %} + {{ ('log.type.' ~ log_entry.type) | trans }} + {% if log_entry.type == 'part_stock_changed' %} + ({{ ('log.part_stock_changed.' ~ log_entry.instockChangeType.value)|trans }}) + {% endif %} + + {% if log_entry is instanceof('App\\Entity\\Contracts\\LogWithEventUndoInterface') and log_entry.undoEvent %} + ({{ ('log.undo_mode.' ~ log_entry.undoMode.value)|trans }}: #{{ log_entry.UndoEventID }}) + {% endif %} +
{% trans %}log.level{% endtrans %} + + {{ ('log.level.'~ log_entry.levelString)|trans }} +
{% trans %}log.user{% endtrans %} + + {% if log_entry.cLIEntry %} + + {{ log_entry.cLIUsername }} ({% trans %}log.cli_user{% endtrans %}) + {% else %} + {% if log_entry.user %} + {{ helper.user_icon_link(log_entry.user) }} (@{{ log_entry.user.username }}) + {% else %} + @{{ log_entry.username }} ({% trans %}log.target_deleted{% endtrans %}) + {% endif %} + {% endif %} +
{% trans %}log.target{% endtrans %}{{ target_html|raw }}
+ +
+ +
+
+ {% if log_entry is instanceof('App\\Entity\\LogSystem\\CollectionElementDeleted') + or log_entry is instanceof('App\\Entity\\LogSystem\\ElementDeletedLogEntry') + or log_entry is instanceof('App\\Entity\\LogSystem\\ElementCreatedLogEntry') + or log_entry is instanceof('App\\Entity\\LogSystem\\ElementEditedLogEntry') + %} + {{ log_helper.undo_buttons(log_entry, target_element) }} + {% endif %} +
+ +
+
+ + + + + +
+
+
+ + {# This assignment is to improve autocomplete on the subpages, as PHPstorm ignores typehints for log_entry #} + {% set entry = log_entry %} + {% if log_entry is instanceof('App\\Entity\\LogSystem\\DatabaseUpdatedLogEntry') %} + {% include "log_system/details/_extra_database_updated.html.twig" %} + {% elseif log_entry is instanceof('App\\Entity\\LogSystem\\ElementCreatedLogEntry') %} + {% include "log_system/details/_extra_element_created.html.twig" %} + {% elseif log_entry is instanceof('App\\Entity\\LogSystem\\ElementEditedLogEntry') %} + {% include "log_system/details/_extra_element_edited.html.twig" %} + {% elseif log_entry is instanceof('App\\Entity\\LogSystem\\ElementDeletedLogEntry') %} + {% include "log_system/details/_extra_element_deleted.html.twig" %} + {% elseif log_entry is instanceof('App\\Entity\\LogSystem\\UserLoginLogEntry') + or log_entry is instanceof('App\\Entity\\LogSystem\\UserLogoutLogEntry') %} + {% include "log_system/details/_extra_user_login.html.twig" %} + {% elseif log_entry is instanceof('App\\Entity\\LogSystem\\UserNotAllowedLogEntry') %} + {% include "log_system/details/_extra_user_not_allowed.html.twig" %} + {% elseif log_entry is instanceof('App\\Entity\\LogSystem\\SecurityEventLogEntry') %} + {% include "log_system/details/_extra_security_event.html.twig" %} + {% elseif log_entry is instanceof('App\\Entity\\LogSystem\\CollectionElementDeleted') %} + {% include "log_system/details/_extra_collection_element_deleted.html.twig" %} + {% else %} + {{ extra_html | raw }} + {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/templates/mail/pw_reset.html.twig b/templates/mail/pw_reset.html.twig index 4a2794da..18a867d6 100644 --- a/templates/mail/pw_reset.html.twig +++ b/templates/mail/pw_reset.html.twig @@ -3,7 +3,7 @@ {% block content %} -

{% trans with {'%name%': user.fullName} %}email.hi %name%{% endtrans %},

+

{% trans with {'%name%': user.fullName|escape } %}email.hi %name%{% endtrans %},

{% trans %}email.pw_reset.message{% endtrans %}
diff --git a/templates/main_card.html.twig b/templates/main_card.html.twig index 8bcd42ce..9eb80a02 100644 --- a/templates/main_card.html.twig +++ b/templates/main_card.html.twig @@ -1,6 +1,8 @@ {% extends "base.html.twig" %} {% block content %} + {% block before_card %}{% endblock %} +
{% block card_header %}
@@ -14,5 +16,7 @@ {% endblock %}
+ {% block after_card %}{% endblock %} + {% block additional_content %}{% endblock %} {% endblock %} \ No newline at end of file diff --git a/templates/parts/edit/_associated_parts.html.twig b/templates/parts/edit/_associated_parts.html.twig new file mode 100644 index 00000000..9ed0fd88 --- /dev/null +++ b/templates/parts/edit/_associated_parts.html.twig @@ -0,0 +1,18 @@ +{% form_theme form with ['parts/edit/edit_form_styles.html.twig'] %} +{% import 'components/collection_type.macro.html.twig' as collection %} + +
+ + + {% for assoc in form.associated_parts_as_owner %} + {{ form_widget(assoc) }} + {% endfor %} + +
+ + +
\ No newline at end of file diff --git a/templates/parts/edit/_eda.html.twig b/templates/parts/edit/_eda.html.twig new file mode 100644 index 00000000..4df675c4 --- /dev/null +++ b/templates/parts/edit/_eda.html.twig @@ -0,0 +1,24 @@ +{{ form_row(form.eda_info.reference_prefix) }} +{{ form_row(form.eda_info.value) }} + +
+
+ {{ form_row(form.eda_info.visibility) }} +
+
+ +
+
+ {{ form_widget(form.eda_info.exclude_from_bom) }} + {{ form_widget(form.eda_info.exclude_from_board) }} + {{ form_widget(form.eda_info.exclude_from_sim) }} +
+
+ +
+
+
{% trans %}eda_info.kicad_section.title{% endtrans %}:
+
+
+{{ form_row(form.eda_info.kicad_symbol) }} +{{ form_row(form.eda_info.kicad_footprint) }} \ No newline at end of file diff --git a/templates/parts/edit/_orderdetails.html.twig b/templates/parts/edit/_orderdetails.html.twig index 95f0dfef..f8d766d2 100644 --- a/templates/parts/edit/_orderdetails.html.twig +++ b/templates/parts/edit/_orderdetails.html.twig @@ -1,5 +1,5 @@ {# Leave this template at bootstrap 4 for now, as it otherwise destroys our layout #} -{% form_theme form.orderdetails with ['parts/edit/edit_form_styles.html.twig', "bootstrap_4_layout.html.twig"] %} +{% form_theme form.orderdetails with ['parts/edit/edit_form_styles.html.twig', "bootstrap_5_layout.html.twig"] %} {% import 'components/collection_type.macro.html.twig' as collection %}
diff --git a/templates/parts/edit/edit_form_styles.html.twig b/templates/parts/edit/edit_form_styles.html.twig index cb3f7d24..c2a89b6a 100644 --- a/templates/parts/edit/edit_form_styles.html.twig +++ b/templates/parts/edit/edit_form_styles.html.twig @@ -14,9 +14,10 @@ {{ form_widget(form.price_related_quantity, {'attr': {'class': 'form-control-sm'}}) }} {{ form_errors(form.price_related_quantity) }} - {{ form_errors(form) }} @@ -57,8 +58,9 @@
- {{ form_errors(form) }} @@ -73,12 +75,14 @@ {{ form_widget(form.value_min) }}{{ form_errors(form.value_min) }} {{ form_widget(form.value_typical) }}{{ form_errors(form.value_typical) }} {{ form_widget(form.value_max) }}{{ form_errors(form.value_max) }} - {{ form_widget(form.unit, {"attr": {"data-pages--parameters-autocomplete-target": "unit", "data-pages--latex-preview-target": "input"}}) }}{{ form_errors(form.unit) }} + {{ form_widget(form.unit, {"attr": {"data-pages--parameters-autocomplete-target": "unit", "data-pages--latex-preview-target": "input"}}) }}{{ form_errors(form.unit) }} {{ form_widget(form.value_text) }}{{ form_errors(form.value_text) }} {{ form_widget(form.group) }}{{ form_errors(form.group) }} - {{ form_errors(form) }} @@ -89,12 +93,29 @@ {% import 'components/collection_type.macro.html.twig' as collection %} - {{ form_widget(form) }} + {{ form_row(form.description) }} + {{ form_row(form.storage_location) }} + {{ form_row(form.amount) }} + {{ form_row(form.instock_unknown) }} + {{ form_row(form.needs_refill) }} + {{ form_row(form.expiration_date) }} + + {% set id = 'collapse_' ~ random() %} + + +
+ {{ form_row(form.comment) }} + {{ form_row(form.owner) }} + {{ form_row(form.user_barcode) }} +
- {{ form_errors(form) }} @@ -106,44 +127,57 @@ - {{ form_widget(form) }} + {{ form_row(form.name) }} + {{ form_row(form.attachment_type) }} + {{ form_row(form.secureFile) }} + {{ form_row(form.showInTable) }} + {{ form_row(form.url) }} + {{ form_row(form.downloadURL) }} + +
+ {{ form_label(form.file) }} +
+ {{ form_widget(form.file) }} + {{ form_errors(form.file) }} + {% trans %}attachment.max_file_size{% endtrans %}: {{ max_upload_size | format_bytes }} +
+
+ - {% set attach = form.vars.value %} + {# @var \App\Entity\Attachments\Attachment attach #} {% if attach is not null %} - {% if attachment_manager.fileExisting(attach) %} - {% if not attach.external %} -

-
+ {% if not attach.hasInternal() and attach.external %} +
- {{ attach.filename }} + {% trans %}attachment.external_only{% endtrans %} -
- +
+ {% elseif attachment_manager.isInternalFileExisting(attach) %} +
+
+ {{ attach.filename|u.truncate(25, ' ...') }} +
+
+
{{ attachment_manager.humanFileSize(attach) }} - -
- {% else %} -

-
- - {% trans %}attachment.external{% endtrans %} - -
- {% endif %} +
+
{% if attach.secure %} -
+
{% trans %}attachment.secure{% endtrans %} -
+
{% endif %} {% if attach.secure and not is_granted('show_private', attach) %} @@ -153,19 +187,43 @@ {% trans %}attachment.preview.alt{% endtrans %} {% else %} - {% trans %}attachment.view{% endtrans %} + {% trans %}attachment.view_local{% endtrans %} {% endif %} {% else %} -

-
+
{% trans %}attachment.file_not_found{% endtrans %} -
+ + {% endif %} + {% if attach.external %} + {% endif %} {% endif %} +{% endblock %} + +{% block part_association_widget %} + {% import 'components/collection_type.macro.html.twig' as collection %} + + +
+ {{ form_widget(form) }} +
+ + + + {{ form_errors(form) }} + + {% endblock %} \ No newline at end of file diff --git a/templates/parts/edit/edit_part_info.html.twig b/templates/parts/edit/edit_part_info.html.twig index 0fecd432..20cddbd7 100644 --- a/templates/parts/edit/edit_part_info.html.twig +++ b/templates/parts/edit/edit_part_info.html.twig @@ -1,12 +1,12 @@ {% extends "main_card.html.twig" %} {% block title %} - {% trans with {'%name%': part.name} %}part.edit.title{% endtrans %} + {% trans with {'%name%': part.name|escape } %}part.edit.title{% endtrans %} {% endblock %} {% block card_title %} - {% trans with {'%name%': part.name} %}part.edit.card_title{% endtrans %} + {% trans with {'%name%': part.name|escape } %}part.edit.card_title{% endtrans %} {{ part.name }} diff --git a/templates/parts/edit/merge_parts.html.twig b/templates/parts/edit/merge_parts.html.twig new file mode 100644 index 00000000..a91b6423 --- /dev/null +++ b/templates/parts/edit/merge_parts.html.twig @@ -0,0 +1,27 @@ +{% extends "parts/edit/edit_part_info.html.twig" %} + +{# @var merge_other \App\Entity\Parts\Part #} + +{% block card_border %}border-info{% endblock %} +{% block card_type %}bg-info text-bg-info{% endblock %} + +{% block title %} + {% trans %}part.merge.title{% endtrans %} {{ merge_other.name }} {% trans %}part.merge.title.into{% endtrans %} {{ merge_old_name }} +{% endblock %} + +{% block card_title %} + + {% trans %}part.merge.title{% endtrans %} + {{ merge_other.name }} (ID: {{ merge_other.iD }}) + {% trans %}part.merge.title.into{% endtrans %} + {{ merge_old_name }} (ID: {{ part.id }}) +{% endblock %} + +{% block card_content %} +
+ {{ parent() }} +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/parts/edit/new_part.html.twig b/templates/parts/edit/new_part.html.twig index 278c57c3..2c11c7a0 100644 --- a/templates/parts/edit/new_part.html.twig +++ b/templates/parts/edit/new_part.html.twig @@ -3,6 +3,10 @@ {% block card_border %}border-success{% endblock %} {% block card_type %}bg-success text-white{% endblock %} +{% block title %} + {% trans %}part.new.card_title{% endtrans %} +{% endblock %} + {% block card_title %} {% trans %}part.new.card_title{% endtrans %} diff --git a/templates/parts/edit/update_from_ip.html.twig b/templates/parts/edit/update_from_ip.html.twig new file mode 100644 index 00000000..fb1dfad3 --- /dev/null +++ b/templates/parts/edit/update_from_ip.html.twig @@ -0,0 +1,16 @@ +{% extends "parts/edit/edit_part_info.html.twig" %} + +{# @var merge_other \App\Entity\Parts\Part #} + +{% block card_border %}border-info{% endblock %} +{% block card_type %}bg-info text-bg-info{% endblock %} + +{% block title %} + {% trans %}info_providers.update_part.title{% endtrans %}: {{ merge_old_name }} +{% endblock %} + +{% block card_title %} + + {% trans %}info_providers.update_part.title{% endtrans %}: + {{ merge_old_name }} +{% endblock %} \ No newline at end of file diff --git a/templates/parts/import/parts_import.html.twig b/templates/parts/import/parts_import.html.twig new file mode 100644 index 00000000..794b6886 --- /dev/null +++ b/templates/parts/import/parts_import.html.twig @@ -0,0 +1,48 @@ +{% extends "main_card.html.twig" %} + +{% block title %}{% trans %}parts.import.title{% endtrans %}{% endblock %} + +{% block card_title %} + {% trans %}parts.import.title{% endtrans %} +{% endblock %} + +{% block before_card %} + {% if import_errors %} +
+

{% trans %}parts.import.errors.title{% endtrans %}

+
    + {% for name, error in import_errors %} +
  • + {{ name }}: + {% for violation in error.violations %} + {{ violation.propertyPath }}: {{ violation.message|trans(violation.parameters, 'validators') }}
    + {% endfor %} +
  • + {% endfor %} +
+
+ {% endif %} +{% endblock %} + +{% block card_content %} +

+ {% trans %}parts.import.help{% endtrans %}
+ {% trans with {'%link%': 'https://docs.part-db.de/usage/import_export.html'} %}parts.import.help_documentation{% endtrans %} +

+ + {{ form(import_form) }} + + {% if imported_entities %} +
+

{% trans %}parts.import.errors.imported_entities{% endtrans %} ({{ imported_entities | length }}):

+
    + {% for entity in imported_entities %} + {# @var \App\Entity\Parts\Part entity #} + {% if entity.id %} +
  • {{ entity.name }} (ID: {{ entity.iD }})
  • + {% else %} +
  • {{ entity.name }}
  • + {% endif %} + {% endfor %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/parts/info/_associations.html.twig b/templates/parts/info/_associations.html.twig new file mode 100644 index 00000000..7325a5c7 --- /dev/null +++ b/templates/parts/info/_associations.html.twig @@ -0,0 +1,40 @@ +{% import "helper.twig" as helper %} + +{% macro assoc_row(assoc) %} + {# @var assoc \App\Entity\Parts\PartAssociation #} + + {{ helper.part_icon_link(assoc.owner) }} + {{ assoc.typeTranslationKey | trans }} + {{ helper.part_icon_link(assoc.other) }} + {{ assoc.comment }} + +{% endmacro %} + +{% macro assoc_table(assocs, caption) %} +
    + + + + + + + + + + + + {% for assoc in assocs %} + {{ _self.assoc_row(assoc) }} + {% endfor %} + +
    {{ caption | trans }}:
    {% trans %}part_association.table.from{% endtrans %}{% trans %}part_association.table.type{% endtrans %}{% trans %}part_association.table.to{% endtrans %}{% trans %}part_association.edit.comment{% endtrans %}
    +
    +{% endmacro %} + +{% if part.associatedPartsAsOwner is not empty %} + {{ _self.assoc_table(part.associatedPartsAsOwner, 'part_association.table.from_this_part') }} +{% endif %} + +{% if part.associatedPartsAsOther is not empty %} + {{ _self.assoc_table(part.associatedPartsAsOther, 'part_association.table.to_this_part') }} +{% endif %} \ No newline at end of file diff --git a/templates/parts/info/_attachments_info.html.twig b/templates/parts/info/_attachments_info.html.twig index 4b1ba28a..4f7c0455 100644 --- a/templates/parts/info/_attachments_info.html.twig +++ b/templates/parts/info/_attachments_info.html.twig @@ -24,18 +24,16 @@ {{ attachment.name }} {{ attachment.attachmentType.fullPath }} - {% if attachment.external %} - {{ attachment.host }} - {% else %} + {% if attachment.hasInternal() %} {{ attachment.filename }} {% endif %} - {% if attachment.external %} + {% if not attachment.hasInternal() %} - {% trans %}attachment.external{% endtrans %} + {% trans %}attachment.external_only{% endtrans %} - {% elseif attachment_manager.fileExisting(attachment) %} + {% elseif attachment_manager.internalFileExisting(attachment) %} {{ attachment_manager.humanFileSize(attachment) }} @@ -58,14 +56,19 @@
    - + + + + - + diff --git a/templates/parts/info/_extended_infos.html.twig b/templates/parts/info/_extended_infos.html.twig index e0bb01d7..4ed60a09 100644 --- a/templates/parts/info/_extended_infos.html.twig +++ b/templates/parts/info/_extended_infos.html.twig @@ -62,5 +62,48 @@ {% endif %} + + + {% trans %}part.info_provider_reference{% endtrans %} + + {% if part.providerReference.providerCreated %} + {% if part.providerReference.providerUrl %} + + {% endif %} + {{ info_provider_label(part.providerReference.providerKey)|default(part.providerReference.providerKey) }}: {{ part.providerReference.providerId }} + ({{ part.providerReference.lastUpdated | format_datetime() }}) + {% if part.providerReference.providerUrl %} + + {% endif %} + + {# Show last updated date #} + + {% else %} + {{ helper.boolean_badge(part.providerReference.providerCreated) }} + {% endif %} + + + + + + {% trans %}part.edit.tab.eda{% endtrans %} + + {% trans %}eda_info.reference_prefix{% endtrans %}: {{ part.edaInfo.referencePrefix ?? part.category.edaInfo.referencePrefix ?? "" }} +
    + {% trans %}eda_info.value{% endtrans %}: {{ part.edaInfo.value }} +
    + {% trans %}eda_info.visibility{% endtrans %}: {{ helper.boolean_badge( part.edaInfo.visibility ?? part.category.edaInfo.visibility) }} +
    + {% trans %}eda_info.exclude_from_bom{% endtrans %}: {{ helper.boolean_badge( part.edaInfo.excludeFromBom ?? part.category.edaInfo.excludeFromBom ?? false) }} +
    + {% trans %}eda_info.exclude_from_board{% endtrans %}: {{ helper.boolean_badge( part.edaInfo.excludeFromBoard ?? part.category.edaInfo.excludeFromBoard ?? false) }} +
    + {% trans %}eda_info.exclude_from_sim{% endtrans %}: {{ helper.boolean_badge( part.edaInfo.excludeFromSim ?? part.category.edaInfo.excludeFromSim ?? false) }} +
    + {% trans %}eda_info.kicad_symbol{% endtrans %}: {{ part.edaInfo.kicadSymbol ?? part.category.edaInfo.kicadSymbol ?? "" }} +
    + {% trans %}eda_info.kicad_footprint{% endtrans %}: {{ part.edaInfo.kicadFootprint ?? part.footprint.edaInfo.kicadFootprint ?? "" }} + + \ No newline at end of file diff --git a/templates/parts/info/_main_infos.html.twig b/templates/parts/info/_main_infos.html.twig index fdbe3ab0..bced5624 100644 --- a/templates/parts/info/_main_infos.html.twig +++ b/templates/parts/info/_main_infos.html.twig @@ -1,74 +1,131 @@ {% import "helper.twig" as helper %} -
    -
    - {% include "parts/info/_picture.html.twig" %} -
    -
    -
    - {% if part.manufacturer %} - {% if part.manufacturer.id is not null %} - {{ part.manufacturer.name}} +{% if part.manufacturer or part.manufacturerProductUrl or part.manufacturerProductNumber %} +
    + {% if part.manufacturer %} + {% if part.manufacturer.id is not null %} + {{ part.manufacturer.name}} + {% else %} + {{ part.manufacturer.name }} + {% endif %} + {% endif %} + {% if part.manufacturerProductUrl %} + + + {% if part.manufacturerProductNumber is not empty %} + {{ part.manufacturerProductNumber }} {% else %} - {{ part.manufacturer.name }} + {{ part.name }} + {% endif %} + + + {% elseif part.manufacturerProductNumber %} + {{ part.manufacturerProductNumber }} + {% endif %} +
    +{% endif %} + +

    + {{ part.name }} + {# You need edit permission to use the edit button #} + {% if timeTravel is not null %} + + {% elseif is_granted('edit', part) %} + + {% endif %} +

    + +{# Slighlty highlight every text in this block over normal text (similar to h5) #} +
    +
    +
    + {% trans %}description.label{% endtrans %} +
    +
    + {{ part.description|format_markdown(true) }} +
    +
    + +
    +
    + {% trans %}category.label{% endtrans %} + +
    +
    + {{ helper.structural_entity_link(part.category) }} +
    +
    + +
    +
    + {% trans %}part.part_lots.label{% endtrans %} + +
    +
    + + {% if not part.amountUnknown %} + {# For known instock we can just show the label as normal #} + {{ part.amountSum | format_amount(part.partUnit) }} + {% else %} + {% if part.amountSum == 0.0 %} + ? + {% else %} + ≥{{ part.amountSum | format_amount(part.partUnit) }} {% endif %} {% endif %} - {% if part.manufacturerProductUrl %} - - {{ part.manufacturerProductNumber }} - - {% else %} - {{ part.manufacturerProductNumber }} + {% if part.expiredAmountSum > 0 %} + (+{{ part.expiredAmountSum }}) {% endif %} - -

    {{ part.name }} - {# You need edit permission to use the edit button #} - {% if timeTravel is not null %} - - {% elseif is_granted('edit', part) %} - - {% endif %} -

    -
    {{ part.description|format_markdown(true) }}
    -
    - - {{ helper.structural_entity_link(part.category) }} -
    -
    - - {{ part.amountSum | format_amount(part.partUnit) }} - / - {{ part.minAmount | format_amount(part.partUnit) }} - -
    -
    - - {{ helper.structural_entity_link(part.footprint) }} -
    + / + {{ part.minAmount | format_amount(part.partUnit) }} +
    + {% if part.notEnoughInstock %} +  {% trans %}part.info.amount.less_than_desired{% endtrans %} + {% endif %} +
    +
    - {% set min_order_amount = pricedetail_helper.minOrderAmount(part) %} - {% set max_order_amount = pricedetail_helper.maxDiscountAmount(part) %} - {% set max_order_price = pricedetail_helper.calculateAvgPrice(part, max_order_amount, app.user.currency ?? null) %} - {% if max_order_price is not null %} -
    +
    +
    + {% trans %}footprint.label{% endtrans %} + +
    +
    + {{ helper.structural_entity_link(part.footprint) }} +
    +
    + + {% set min_order_amount = pricedetail_helper.minOrderAmount(part) %} + {% set max_order_amount = pricedetail_helper.maxDiscountAmount(part) %} + {% set max_order_price = pricedetail_helper.calculateAvgPrice(part, max_order_amount, app.user.currency ?? null) %} + {% set min_order_price = pricedetail_helper.calculateAvgPrice(part, min_order_amount, app.user.currency ?? null ) %} + {% if max_order_price is not null %} +
    +
    +
    +
    {{ max_order_price | format_money(app.user.currency ?? null) }} - {% if min_order_amount < max_order_amount %} + {% if min_order_price is not null and min_order_amount < max_order_amount %} - - {{pricedetail_helper.calculateAvgPrice(part, min_order_amount, app.user.currency ?? null ) | format_money(app.user.currency ?? null) }} + {% if max_order_price is not null %}{{ min_order_price | format_money(app.user.currency ?? null) }}{% else %}???{% endif %} {% endif %} - -
    - {% endif %} - {# - {% if part.comment != "" %} -
    - -
    - {{ part.comment|nl2br }} -
    -
    - {% endif %} #} -
    -
    \ No newline at end of file + + +
    + {% endif %} + + {# {% if part.comment != "" %} +
    +
    + {% trans %}comment.label{% endtrans %} + +
    +
    + >{{ part.comment|nl2br }} +
    +
    + {% endif %} #} + + diff --git a/templates/parts/info/_merge_modal.html.twig b/templates/parts/info/_merge_modal.html.twig new file mode 100644 index 00000000..ea0d1b13 --- /dev/null +++ b/templates/parts/info/_merge_modal.html.twig @@ -0,0 +1,65 @@ +{# Merge modal #} + +{% if is_granted('edit', part) %} +
    + +{% endif %} + + + diff --git a/templates/parts/info/_part_lots.html.twig b/templates/parts/info/_part_lots.html.twig index e6d8637f..b0dcb455 100644 --- a/templates/parts/info/_part_lots.html.twig +++ b/templates/parts/info/_part_lots.html.twig @@ -41,21 +41,26 @@
    + {% if lot.owner %} + + {{ helper.user_icon_link(lot.owner) }} +
    + {% endif %} {% if lot.expirationDate %} - - {{ lot.expirationDate | format_date() }} + + {{ lot.expirationDate | format_date() }}
    {% endif %} {% if lot.expired %}
    - + {% trans %}part_lots.is_expired{% endtrans %} {% endif %} {% if lot.needsRefill %}
    - + {% trans %}part_lots.need_refill{% endtrans %} diff --git a/templates/parts/info/_picture.html.twig b/templates/parts/info/_picture.html.twig index 29ea936e..b532f9b9 100644 --- a/templates/parts/info/_picture.html.twig +++ b/templates/parts/info/_picture.html.twig @@ -12,10 +12,10 @@ {# @var pic App\Entity\Attachments\Attachment #}
    +{% endif %} + +{# Info provider badge #} +{% if part.providerReference.providerCreated %} + {% endif %} \ No newline at end of file diff --git a/templates/parts/info/_tools.html.twig b/templates/parts/info/_tools.html.twig index 6fee4aee..455d51b7 100644 --- a/templates/parts/info/_tools.html.twig +++ b/templates/parts/info/_tools.html.twig @@ -7,7 +7,7 @@ {% endif %} - +{# Create new button #} {% if is_granted('create', part) %}
    @@ -27,9 +27,22 @@
    {% endif %} +{# Merge modal #} +{% include "parts/info/_merge_modal.html.twig" %} + +{# Update part from info provider button #} +{% if is_granted('edit', part) and is_granted('@info_providers.create_parts') %} +
    + + + {% trans %}part.update_part_from_info_provider.btn{% endtrans %} + +{% endif %} + +
    @@ -44,7 +57,9 @@ @@ -54,7 +69,7 @@ {{ dropdown.profile_dropdown('part', part.id) }} + href="{{ path('project_add_parts_no_id', {"parts": part.id, "_redirect": uri_without_host(app.request)}) }}"> {% trans %}part.info.add_part_to_project{% endtrans %} \ No newline at end of file diff --git a/templates/parts/info/_withdraw_modal.html.twig b/templates/parts/info/_withdraw_modal.html.twig index 79ae2ea2..e0491d9c 100644 --- a/templates/parts/info/_withdraw_modal.html.twig +++ b/templates/parts/info/_withdraw_modal.html.twig @@ -15,7 +15,7 @@ - +
    - +
    {% trans %}part.info.withdraw_modal.comment.hint{% endtrans %}
    + +
    + +
    + {# The timestamp must be between a year ago and 1 hour in the future #} + +
    {% trans %}part.info.withdraw_modal.timestamp.hint{% endtrans %}
    +
    +
    + +
    +
    + {# The timestamp must be between a year ago and 1 hour in the future #} +
    + + +
    +
    +
{{ form_start(filterForm, {"attr": {"data-controller": "helpers--form-cleanup", "data-action": "helpers--form-cleanup#submit"}}) }} @@ -62,9 +67,11 @@ {{ form_row(filterForm.storelocation) }} {{ form_row(filterForm.minAmount) }} {{ form_row(filterForm.amountSum) }} + {{ form_row(filterForm.lessThanDesired) }} {{ form_row(filterForm.lotCount) }} {{ form_row(filterForm.lotExpirationDate) }} {{ form_row(filterForm.lotDescription) }} + {{ form_row(filterForm.lotOwner) }} {{ form_row(filterForm.lotNeedsRefill) }} {{ form_row(filterForm.lotUnknownAmount) }} @@ -111,6 +118,14 @@ + {% if filterForm.project is defined %} +
+ {{ form_row(filterForm.project) }} + {{ form_row(filterForm.bomQuantity) }} + {{ form_row(filterForm.bomName) }} + {{ form_row(filterForm.bomComment) }} +
+ {% endif %} diff --git a/templates/parts/lists/search_list.html.twig b/templates/parts/lists/search_list.html.twig index e4ac30ee..49093c4c 100644 --- a/templates/parts/lists/search_list.html.twig +++ b/templates/parts/lists/search_list.html.twig @@ -16,7 +16,7 @@
-

{% trans with {"%keyword%": keyword} %}parts_list.search.searching_for{% endtrans %}

+

{% trans with {"%keyword%": keyword|escape} %}parts_list.search.searching_for{% endtrans %}

{% trans %}parts_list.search_options.caption{% endtrans %}: @@ -50,7 +50,7 @@
- +
diff --git a/templates/projects/build/_form.html.twig b/templates/projects/build/_form.html.twig index 4a02dd4d..a8f772e9 100644 --- a/templates/projects/build/_form.html.twig +++ b/templates/projects/build/_form.html.twig @@ -74,6 +74,9 @@ {{ form_row(form.comment) }} +
+{{ form_row(form.dontCheckQuantity) }} +
{{ form_row(form.addBuildsToBuildsPart) }} {% if form.buildsPartLot is defined %} diff --git a/templates/projects/import_bom.html.twig b/templates/projects/import_bom.html.twig new file mode 100644 index 00000000..0e7f1787 --- /dev/null +++ b/templates/projects/import_bom.html.twig @@ -0,0 +1,31 @@ +{% extends "main_card.html.twig" %} + +{% block title %}{% trans %}project.import_bom{% endtrans %}{% endblock %} + +{% block before_card %} + {% if errors %} +
+

{% trans %}parts.import.errors.title{% endtrans %}

+
    + {% for violation in errors %} +
  • + {{ violation.propertyPath }}: + {{ violation.message|trans(violation.parameters, 'validators') }} +
  • + {% endfor %} +
+
+ {% endif %} +{% endblock %} + + +{% block card_title %} + + {% trans %}project.import_bom{% endtrans %}{% if project %}: {{ project.name }}{% endif %} +{% endblock %} + +{% block card_content %} + + {{ form(form) }} + +{% endblock %} \ No newline at end of file diff --git a/templates/projects/info/_bom.html.twig b/templates/projects/info/_bom.html.twig index 42ffd015..2a9cfd00 100644 --- a/templates/projects/info/_bom.html.twig +++ b/templates/projects/info/_bom.html.twig @@ -1,11 +1,22 @@ {% import "components/datatables.macro.html.twig" as datatables %} -
+ -{{ datatables.datatable(datatable, 'elements/datatables/datatables', 'projects') }} - - - - {% trans %}project.info.bom_add_parts{% endtrans %} - \ No newline at end of file +{{ datatables.datatable(datatable, 'elements/datatables/datatables', 'projects') }} \ No newline at end of file diff --git a/templates/projects/info/_builds.html.twig b/templates/projects/info/_builds.html.twig index 5418a614..fc49b50d 100644 --- a/templates/projects/info/_builds.html.twig +++ b/templates/projects/info/_builds.html.twig @@ -28,7 +28,7 @@
- +
diff --git a/templates/projects/info/_info.html.twig b/templates/projects/info/_info.html.twig index c2d36d67..b95be253 100644 --- a/templates/projects/info/_info.html.twig +++ b/templates/projects/info/_info.html.twig @@ -6,10 +6,10 @@
{% if project.masterPictureAttachment %} - + {% else %} - Part main image + Part main image {% endif %}
diff --git a/templates/security/2fa_base_form.html.twig b/templates/security/2fa_base_form.html.twig index 8190e7a1..847048e4 100644 --- a/templates/security/2fa_base_form.html.twig +++ b/templates/security/2fa_base_form.html.twig @@ -7,7 +7,7 @@ {% block content %} {% if authenticationError %} {% endif %} diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig index 44d98397..aab566d2 100644 --- a/templates/security/login.html.twig +++ b/templates/security/login.html.twig @@ -27,6 +27,15 @@ + {% if saml_enabled %} +
+ {% trans %}login.sso_saml_login{% endtrans %} + +

{% trans %}login.local_login_hint{% endtrans %}

+
+ + {% endif %} +
diff --git a/templates/tools/ic_logos/ic_logos.html.twig b/templates/tools/ic_logos/ic_logos.html.twig index 60c14882..6a8eaa18 100644 --- a/templates/tools/ic_logos/ic_logos.html.twig +++ b/templates/tools/ic_logos/ic_logos.html.twig @@ -8,15 +8,14 @@ {% extends "main_card.html.twig" %} {% block card_content %} + + -
- +
diff --git a/templates/tools/server_infos/_db.html.twig b/templates/tools/server_infos/_db.html.twig index 0c0e60a2..828783fe 100644 --- a/templates/tools/server_infos/_db.html.twig +++ b/templates/tools/server_infos/_db.html.twig @@ -21,5 +21,13 @@ + + + + + + + +
A
Acer Integrated Circuit Designs
Database User {{ db_user }}
Natural sort method{{ db_natsort_method }}
Slow natural sort allowed{{ helper.boolean_badge(db_natsort_slow_allowed) }}
\ No newline at end of file diff --git a/templates/tools/server_infos/_partdb.html.twig b/templates/tools/server_infos/_partdb.html.twig index 5e5f6e3e..ca2b82a8 100644 --- a/templates/tools/server_infos/_partdb.html.twig +++ b/templates/tools/server_infos/_partdb.html.twig @@ -2,13 +2,16 @@ + + + - + @@ -39,21 +42,34 @@ - - + + + + + - + + + + + + + + + + + @@ -62,5 +78,9 @@ + + + +
General
Part-DB Version {{ shivas_app_version }} {% if git_branch is not empty or git_commit is not empty %}({{ git_branch ?? '' }}/{{ git_commit ?? '' }}){% endif %}
Symfony environment{{ enviroment }} (Debug: {{ helper.boolean_badge(is_debug) }}){{ environment }} (Debug: {{ helper.boolean_badge(is_debug) }})
Part-DB Instance name{{ helper.boolean_badge(demo_mode) }}
GPDR Compliance Mode{{ helper.boolean_badge(gpdr_compliance) }}GDPR Compliance Mode{{ helper.boolean_badge(gdpr_compliance) }}
Users
Use GravatarUse Gravatar for Avatar {{ helper.boolean_badge(use_gravatar) }}
Password Reset via Email enabled {{ helper.boolean_badge(email_password_reset) }}
Single Sign-On via SAML enabled{{ helper.boolean_badge(saml_enabled) }}
Email
Configured E-Mail sender {{ email_sender }} ({{ email_sender_name }})
Attachments
Allow server-side download of attachments {{ helper.boolean_badge(allow_attachments_downloads) }}Detailed error pages enabled {{ helper.boolean_badge(detailed_error_pages) }} (Admin Contact email: {{ error_page_admin_email }})
Maximum file size for attachments{{ configured_max_file_size }} (Effective: {{ effective_max_file_size | format_bytes }})
\ No newline at end of file diff --git a/templates/tools/server_infos/_php.html.twig b/templates/tools/server_infos/_php.html.twig index ae485639..ea5a0b4c 100644 --- a/templates/tools/server_infos/_php.html.twig +++ b/templates/tools/server_infos/_php.html.twig @@ -9,6 +9,12 @@ Server Operating System {{ php_uname }} + + PHP Bit Size + {{ php_bit_size }}-bit + {% if php_bit_size < 64 %}(32-bit PHP is affected by Year 2038 problem, and can not work with dates after 2038!){% endif %} + + Opcache enabled {{ helper.boolean_badge(php_opcache_enabled) }} @@ -25,5 +31,19 @@ Server time {{ "now" | format_datetime("long", "long") }} + + Symfony Kernel Runtime + {{ kernel_runtime }} + + + Symfony Kernel Runtime Environment + {{ kernel_runtime_environment }} + + + Symfony Kernel Runtime mode + {% for key, value in kernel_runtime_mode %} + {{ key }}: {{ value }}
+ {% endfor %} + \ No newline at end of file diff --git a/templates/tools/server_infos/server_infos.html.twig b/templates/tools/server_infos/server_infos.html.twig index fd3d2759..e4e02d32 100644 --- a/templates/tools/server_infos/server_infos.html.twig +++ b/templates/tools/server_infos/server_infos.html.twig @@ -1,4 +1,5 @@ {% extends "main_card.html.twig" %} +{% import "components/new_version.macro.html.twig" as nv %} {% block title %}{% trans %}tools.server_infos.title{% endtrans %}{% endblock %} @@ -6,6 +7,12 @@ {% trans %}tools.server_infos.title{% endtrans %} {% endblock %} +{% block before_card %} + {% if is_granted('@system.show_updates') %} + {{ nv.new_version_alert(new_version_available, new_version, new_version_url) }} + {% endif %} +{% endblock %} + {% block card_content %}
{{ form_row(settings_form.language) }} @@ -52,6 +54,15 @@ {% block content %} {{ parent() }} + {% if user.samlUser %} + + {% endif %} + {% include "users/_2fa_settings.html.twig" %}
@@ -65,4 +76,8 @@ {{ form_end(pw_form) }}
+ + {% if is_granted("@api.access_api") %} + {% include "users/_api_tokens.html.twig" %} + {% endif %} {% endblock %} diff --git a/tests/API/APIDocsAvailabilityTest.php b/tests/API/APIDocsAvailabilityTest.php new file mode 100644 index 00000000..d2ed7fa4 --- /dev/null +++ b/tests/API/APIDocsAvailabilityTest.php @@ -0,0 +1,66 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API; + +use App\Entity\UserSystem\User; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class APIDocsAvailabilityTest extends WebTestCase +{ + /** + * @dataProvider urlProvider + */ + public function testDocAvailabilityForLoggedInUser(string $url): void + { + self::ensureKernelShutdown(); + $client = static::createClient(); + $user = static::getContainer()->get(EntityManagerInterface::class) + ->getRepository(User::class)->findOneBy(['name' => 'admin']); + $client->loginUser($user); + + $client->request('GET',$url); + self::assertResponseIsSuccessful(); + } + + public function testDocForbidden(): void + { + self::ensureKernelShutdown(); + $client = static::createClient(); + $user = static::getContainer()->get(EntityManagerInterface::class) + ->getRepository(User::class)->findOneBy(['name' => 'noread']); + $client->loginUser($user); + + $client->request('GET','/api/docs.json'); + self::assertResponseStatusCodeSame(403); + } + + public static function urlProvider(): \Iterator + { + yield ['/api']; + yield ['/api/docs.html']; + yield ['/api/docs.json']; + yield ['/api/docs.jsonld']; + } +} \ No newline at end of file diff --git a/tests/API/APITokenAuthenticationTest.php b/tests/API/APITokenAuthenticationTest.php new file mode 100644 index 00000000..a78b0594 --- /dev/null +++ b/tests/API/APITokenAuthenticationTest.php @@ -0,0 +1,121 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API; + +use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; +use App\DataFixtures\APITokenFixtures; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use ApiPlatform\Symfony\Bundle\Test\Client; +class APITokenAuthenticationTest extends ApiTestCase +{ + public function testUnauthenticated(): void + { + self::ensureKernelShutdown(); + $client = static::createClient(); + $client->request('GET', '/api/parts'); + $this->assertResponseStatusCodeSame(401); + } + + public function testExpiredToken(): void + { + self::ensureKernelShutdown(); + $client = $this->createClientWithCredentials(APITokenFixtures::TOKEN_EXPIRED); + $client->request('GET', '/api/parts'); + $this->assertResponseStatusCodeSame(401); + } + + public function testReadOnlyToken(): void + { + self::ensureKernelShutdown(); + $client = $this->createClientWithCredentials(APITokenFixtures::TOKEN_READONLY); + + //Read should be possible + $client->request('GET', '/api/parts'); + $this->assertResponseIsSuccessful(); + + //Trying to list all users and create a new footprint should fail + $client->request('GET', '/api/users'); + $this->assertResponseStatusCodeSame(403); + + $client->request('POST', '/api/footprints', ['json' => ['name' => 'post test']]); + $this->assertResponseStatusCodeSame(403); + } + + public function testEditToken(): void + { + self::ensureKernelShutdown(); + $client = $this->createClientWithCredentials(APITokenFixtures::TOKEN_EDIT); + + //Read should be possible + $client->request('GET', '/api/parts'); + $this->assertResponseIsSuccessful(); + + //Trying to list all users + $client->request('GET', '/api/users'); + $this->assertResponseStatusCodeSame(403); + + $client->request('POST', '/api/footprints', ['json' => ['name' => 'post test']]); + $this->assertResponseIsSuccessful(); + } + + public function testAdminToken(): void + { + self::ensureKernelShutdown(); + $client = $this->createClientWithCredentials(APITokenFixtures::TOKEN_ADMIN ); + + //Read should be possible + $client->request('GET', '/api/parts'); + $this->assertResponseIsSuccessful(); + + //Trying to list all users + $client->request('GET', '/api/users'); + $this->assertResponseIsSuccessful(); + + $client->request('POST', '/api/footprints', ['json' => ['name' => 'post test']]); + $this->assertResponseIsSuccessful(); + } + + public function testWithAuthorizationToken(): void + { + //For the KICAD API it should also work with Authorization: Token header instead of Bearer + self::ensureKernelShutdown(); + $client = static::createClient([], ['headers' => ['authorization' => 'Token '.APITokenFixtures::TOKEN_ADMIN]]);; + + //Read should be possible + $client->request('GET', '/api/parts'); + $this->assertResponseIsSuccessful(); + + //Trying to list all users + $client->request('GET', '/api/users'); + $this->assertResponseIsSuccessful(); + + $client->request('POST', '/api/footprints', ['json' => ['name' => 'post test']]); + $this->assertResponseIsSuccessful(); + } + + protected function createClientWithCredentials(string $token): Client + { + return static::createClient([], ['headers' => ['authorization' => 'Bearer '.$token]]); + } +} \ No newline at end of file diff --git a/tests/API/AuthenticatedApiTestCase.php b/tests/API/AuthenticatedApiTestCase.php new file mode 100644 index 00000000..b1bde64b --- /dev/null +++ b/tests/API/AuthenticatedApiTestCase.php @@ -0,0 +1,41 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API; + +use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; +use ApiPlatform\Symfony\Bundle\Test\Client; +use App\DataFixtures\APITokenFixtures; + +abstract class AuthenticatedApiTestCase extends ApiTestCase +{ + /** + * Creates an API client with authentication. + * @param string $token + * @return Client + */ + protected static function createAuthenticatedClient(string $token = APITokenFixtures::TOKEN_ADMIN): Client + { + return static::createClient(defaultOptions: ['headers' => ['authorization' => 'Token '.$token]]); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/ApiTokenEnpointTest.php b/tests/API/Endpoints/ApiTokenEnpointTest.php new file mode 100644 index 00000000..99340182 --- /dev/null +++ b/tests/API/Endpoints/ApiTokenEnpointTest.php @@ -0,0 +1,40 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\AuthenticatedApiTestCase; + +class ApiTokenEnpointTest extends AuthenticatedApiTestCase +{ + public function testGetCurrentToken(): void + { + $response = self::createAuthenticatedClient()->request('GET', '/api/tokens/current'); + $this->assertResponseIsSuccessful(); + + $this->assertJsonContains([ + 'name' => 'admin', + 'level' => 3, + ]); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/AttachmentTypeEndpointTest.php b/tests/API/Endpoints/AttachmentTypeEndpointTest.php new file mode 100644 index 00000000..f90f3d94 --- /dev/null +++ b/tests/API/Endpoints/AttachmentTypeEndpointTest.php @@ -0,0 +1,75 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class AttachmentTypeEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/attachment_types'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 7, + ]); + } + + public function testGetChildrenCollection(): void + { + $this->_testGetChildrenCollection(1); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test Part', + 'parent' => '/api/attachment_types/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(1, [ + 'name' => 'Test Part Updated', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(6); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/AttachmentsEndpointTest.php b/tests/API/Endpoints/AttachmentsEndpointTest.php new file mode 100644 index 00000000..8f4d7e77 --- /dev/null +++ b/tests/API/Endpoints/AttachmentsEndpointTest.php @@ -0,0 +1,107 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\AuthenticatedApiTestCase; + +class AttachmentsEndpointTest extends AuthenticatedApiTestCase +{ + public function testGetCollection(): void + { + $response = static::createAuthenticatedClient()->request('GET', '/api/attachments'); + self::assertResponseIsSuccessful(); + //There should be 2 attachments in the database yet + self::assertJsonContains([ + '@context' => '/api/contexts/Attachment', + '@id' => '/api/attachments', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 2, + ]); + } + + public function testCreateAttachmentGuessTypeFromElement(): void + { + $response = static::createAuthenticatedClient()->request('POST', '/api/attachments', ['json' => [ + 'name' => 'test', + 'element' => '/api/parts/1', + 'attachment_type' => '/api/attachment_types/1' + ]]); + + //The attachment should be created successfully + self::assertResponseIsSuccessful(); + self::assertJsonContains([ + 'name' => 'test', + ]); + } + + public function testCreateAttachmentDiscriminatorColumn(): void + { + $response = static::createAuthenticatedClient()->request('POST', '/api/attachments', ['json' => [ + 'name' => 'test', + 'element' => '/api/parts/1', + 'attachment_type' => '/api/attachment_types/1', + '_type' => "Part", + ]]); + + //The attachment should be created successfully + self::assertResponseIsSuccessful(); + self::assertJsonContains([ + 'name' => 'test', + ]); + } + + public function testUploadFile(): void + { + $response = static::createAuthenticatedClient()->request('POST', '/api/attachments', ['json' => [ + 'name' => 'test', + 'element' => '/api/parts/1', + 'attachment_type' => '/api/attachment_types/1', + '_type' => "Part", + "upload" => [ + "data" => "data:@file/octet-stream;base64,LS0gcGhwTXlB", + "filename" => "test.csv", + "private" => true + ], + ]]); + + //The attachment should be created successfully + self::assertResponseIsSuccessful(); + + //Attachment must be set (not null) + $array = json_decode($response->getContent(), true); + + self::assertNotNull($array['internal_path']); + + //Attachment must be private + self::assertJsonContains([ + 'private' => true, + ]); + } + + public function testRemoveAttachment(): void + { + $response = static::createAuthenticatedClient()->request('DELETE', '/api/attachments/1'); + self::assertResponseIsSuccessful(); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/CategoryEndpointTest.php b/tests/API/Endpoints/CategoryEndpointTest.php new file mode 100644 index 00000000..68f4fd2d --- /dev/null +++ b/tests/API/Endpoints/CategoryEndpointTest.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class CategoryEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/categories'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 7, + ]); + } + + public function testGetChildrenCollection(): void + { + $this->_testGetChildrenCollection(1); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test API', + 'parent' => '/api/categories/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(1, [ + 'name' => 'Updated', + 'parent' => '/api/categories/2', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(5); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/CrudEndpointTestCase.php b/tests/API/Endpoints/CrudEndpointTestCase.php new file mode 100644 index 00000000..2431f836 --- /dev/null +++ b/tests/API/Endpoints/CrudEndpointTestCase.php @@ -0,0 +1,112 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\AuthenticatedApiTestCase; +use Symfony\Contracts\HttpClient\ResponseInterface; + +abstract class CrudEndpointTestCase extends AuthenticatedApiTestCase +{ + /** + * Returns the base path of the endpoint. + * @return string + */ + abstract protected function getBasePath(): string; + + protected function getItemPath(int $id): string + { + $basePath = $this->getBasePath(); + if (!str_ends_with($basePath, '/')) { + $basePath .= '/'; + } + + return $basePath . $id; + } + + /** + * Returns the id of the created element from the response. + * @param ResponseInterface $response + * @return int + */ + protected function getIdOfCreatedElement(ResponseInterface $response): int + { + return $response->toArray(true)['id']; + } + + protected function _testGetCollection(): ResponseInterface + { + $response = self::createAuthenticatedClient()->request('GET', $this->getBasePath()); + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + + return $response; + } + + protected function _testGetChildrenCollection(int $id): ResponseInterface + { + $response = self::createAuthenticatedClient()->request('GET', $this->getItemPath($id) . '/children'); + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + + return $response; + } + + protected function _testGetItem(int $id): ResponseInterface + { + $response = self::createAuthenticatedClient()->request('GET', $this->getItemPath($id)); + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + + return $response; + } + + protected function _testPostItem(array $data): ResponseInterface + { + $response = self::createAuthenticatedClient()->request('POST', $this->getBasePath(), ['json' => $data]); + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + + return $response; + } + + protected function _testPatchItem(int $id, array $data): ResponseInterface + { + $response = self::createAuthenticatedClient()->request('PATCH', $this->getItemPath($id), [ + 'json' => $data, + 'headers' => ['Content-Type' => 'application/merge-patch+json'] + ]); + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + + return $response; + } + + protected function _testDeleteItem(int $id): ResponseInterface + { + $response = self::createAuthenticatedClient()->request('DELETE', $this->getItemPath($id)); + self::assertResponseIsSuccessful(); + + return $response; + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/CurrencyEndpointTest.php b/tests/API/Endpoints/CurrencyEndpointTest.php new file mode 100644 index 00000000..78434ea3 --- /dev/null +++ b/tests/API/Endpoints/CurrencyEndpointTest.php @@ -0,0 +1,64 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + + +class CurrencyEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/currencies'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 0, + ]); + } + + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test API', + 'iso_code' => 'USD', + ]); + } + + /*public function testUpdateItem(): void + { + $this->_testPatchItem(1, [ + 'name' => 'Updated', + + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(5); + }*/ +} \ No newline at end of file diff --git a/tests/API/Endpoints/FootprintsEndpointTest.php b/tests/API/Endpoints/FootprintsEndpointTest.php new file mode 100644 index 00000000..f3f359a2 --- /dev/null +++ b/tests/API/Endpoints/FootprintsEndpointTest.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class FootprintsEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/footprints'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 7, + ]); + } + + public function testGetChildrenCollection(): void + { + $this->_testGetChildrenCollection(1); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test API', + 'parent' => '/api/footprints/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(1, [ + 'name' => 'Updated', + 'parent' => '/api/footprints/2', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(5); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/InfoEndpointTest.php b/tests/API/Endpoints/InfoEndpointTest.php new file mode 100644 index 00000000..09f02e8a --- /dev/null +++ b/tests/API/Endpoints/InfoEndpointTest.php @@ -0,0 +1,40 @@ +. + */ + +declare(strict_types=1); + + +namespace API\Endpoints; + +use App\Tests\API\AuthenticatedApiTestCase; + +class InfoEndpointTest extends AuthenticatedApiTestCase +{ + public function testGetInfo(): void + { + $response = self::createAuthenticatedClient()->request('GET', '/api/info'); + + self::assertResponseIsSuccessful(); + self::assertJsonContains([ + '@id' => '/api/info', + 'title' => 'Part-DB', + ]); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/ManufacturersEndpointTest.php b/tests/API/Endpoints/ManufacturersEndpointTest.php new file mode 100644 index 00000000..482ec98d --- /dev/null +++ b/tests/API/Endpoints/ManufacturersEndpointTest.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class ManufacturersEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/manufacturers'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 7, + ]); + } + + public function testGetChildrenCollection(): void + { + $this->_testGetChildrenCollection(1); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test API', + 'parent' => '/api/manufacturers/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(5, [ + 'name' => 'Updated', + 'parent' => '/api/manufacturers/2', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(7); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/MeasurementUnitsEndpointTest.php b/tests/API/Endpoints/MeasurementUnitsEndpointTest.php new file mode 100644 index 00000000..db7341db --- /dev/null +++ b/tests/API/Endpoints/MeasurementUnitsEndpointTest.php @@ -0,0 +1,74 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +class MeasurementUnitsEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/measurement_units'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 7, + ]); + } + + public function testGetChildrenCollection(): void + { + $this->_testGetChildrenCollection(1); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test API', + 'parent' => '/api/measurement_units/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(5, [ + 'name' => 'Updated', + 'parent' => '/api/measurement_units/2', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(4); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/OrderdetailsEndpointTest.php b/tests/API/Endpoints/OrderdetailsEndpointTest.php new file mode 100644 index 00000000..92823103 --- /dev/null +++ b/tests/API/Endpoints/OrderdetailsEndpointTest.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class OrderdetailsEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/orderdetails'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 2, + ]); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'supplier' => '/api/suppliers/1', + 'part' => '/api/parts/2', + ]); + } + + public function testUpdateItem(): void + { + $response = $this->_testPostItem([ + 'supplier' => '/api/suppliers/1', + 'part' => '/api/parts/2', + ]); + + $id = $this->getIdOfCreatedElement($response); + + $this->_testPatchItem($id, [ + 'supplierpartnr' => 'API test', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(2); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/ParametersEndpointTest.php b/tests/API/Endpoints/ParametersEndpointTest.php new file mode 100644 index 00000000..733df59a --- /dev/null +++ b/tests/API/Endpoints/ParametersEndpointTest.php @@ -0,0 +1,62 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +class ParametersEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/parameters'; + } + + public function testElementLifecycle(): void + { + //Type should be automatically guessed from the element + $this->_testPostItem([ + 'name' => 'test', + 'element' => '/api/parts/1', + ]); + + //Or manually set + $response = $this->_testPostItem([ + 'name' => 'test', + 'element' => '/api/footprints/1', + '_type' => 'Footprint' + ]); + + $id = $this->getIdOfCreatedElement($response); + + //Check if the new item is in the database + $this->_testGetItem($id); + + //Check if we can change the item + $this->_testPatchItem($id, [ + 'name' => 'test2', + ]); + + //Check if we can delete the item + $this->_testDeleteItem($id); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/PartAssociationsEndpointTest.php b/tests/API/Endpoints/PartAssociationsEndpointTest.php new file mode 100644 index 00000000..62408dbb --- /dev/null +++ b/tests/API/Endpoints/PartAssociationsEndpointTest.php @@ -0,0 +1,62 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class PartAssociationsEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/part_associations'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 0, + ]); + } + + public function testLifeCycle(): void + { + $response = $this->_testPostItem([ + "owner" => '/api/parts/1', + "other" => '/api/parts/2', + 'type' => 1, + ]); + + $id = $this->getIdOfCreatedElement($response); + + $this->_testGetItem($id); + + $this->_testPatchItem($id, [ + 'comment' => 'Test comment' + ]); + + $this->_testDeleteItem($id); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/PartEndpointTest.php b/tests/API/Endpoints/PartEndpointTest.php new file mode 100644 index 00000000..9406fc78 --- /dev/null +++ b/tests/API/Endpoints/PartEndpointTest.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +class PartEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/parts'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 3, + ]); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test Part', + 'description' => 'This is a test part', + 'category' => '/api/categories/1', + 'manufacturer' => '/api/manufacturers/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(1, [ + 'name' => 'Test Part Updated', + 'category' => '/api/categories/2', + 'manufacturer' => '/api/manufacturers/2', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(1); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/PartLotsEndpointTest.php b/tests/API/Endpoints/PartLotsEndpointTest.php new file mode 100644 index 00000000..38aa6b18 --- /dev/null +++ b/tests/API/Endpoints/PartLotsEndpointTest.php @@ -0,0 +1,71 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class PartLotsEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/part_lots'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 2, + ]); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'API test', + 'part' => '/api/parts/1', + 'storage_location' => '/api/storage_locations/1', + 'amount' => 100, + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(1, [ + 'amount' => 220 + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(1); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/PricedetailsEndpointTest.php b/tests/API/Endpoints/PricedetailsEndpointTest.php new file mode 100644 index 00000000..8895365f --- /dev/null +++ b/tests/API/Endpoints/PricedetailsEndpointTest.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class PricedetailsEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/pricedetails'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 4, + ]); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'price' => '2.0', + 'orderdetail' => '/api/orderdetails/1', + 'min_discount_quantity' => 1000, + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(1, [ + 'price' => '3.5', + 'min_discount_quantity' => 10, + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(1); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/ProjectBOMEntriesEndpointTest.php b/tests/API/Endpoints/ProjectBOMEntriesEndpointTest.php new file mode 100644 index 00000000..cafb57dc --- /dev/null +++ b/tests/API/Endpoints/ProjectBOMEntriesEndpointTest.php @@ -0,0 +1,66 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +class ProjectBOMEntriesEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/project_bom_entries'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + } + + public function testItemLifecycle(): void + { + $response = $this->_testPostItem([ + 'project' => '/api/projects/1', + 'part' => '/api/parts/1', + 'quantity' => 1, + ]); + + $new_id = $this->getIdOfCreatedElement($response); + + //Check if the new item is in the database + $this->_testGetItem($new_id); + + //Check if we can change the item + $this->_testPatchItem($new_id, [ + 'quantity' => 2, + ]); + + //Check if we can delete the item + $this->_testDeleteItem($new_id); + } + + public function testGetBomOfProject(): void + { + $response = self::createAuthenticatedClient()->request('GET', '/api/projects/1/bom'); + self::assertResponseIsSuccessful(); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/ProjectsEndpointTest.php b/tests/API/Endpoints/ProjectsEndpointTest.php new file mode 100644 index 00000000..9daf584a --- /dev/null +++ b/tests/API/Endpoints/ProjectsEndpointTest.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class ProjectsEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/projects'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 7, + ]); + } + + public function testGetChildrenCollection(): void + { + $this->_testGetChildrenCollection(1); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test API', + 'parent' => '/api/projects/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(5, [ + 'name' => 'Updated', + 'parent' => '/api/projects/2', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(7); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/StorageLocationsEndpointTest.php b/tests/API/Endpoints/StorageLocationsEndpointTest.php new file mode 100644 index 00000000..8d9641c4 --- /dev/null +++ b/tests/API/Endpoints/StorageLocationsEndpointTest.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + + +namespace API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class StorageLocationsEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/storage_locations'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 7, + ]); + } + + public function testGetChildrenCollection(): void + { + $this->_testGetChildrenCollection(1); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test API', + 'parent' => '/api/storage_locations/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(5, [ + 'name' => 'Updated', + 'parent' => '/api/storage_locations/2', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(7); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/SuppliersEndpointTest.php b/tests/API/Endpoints/SuppliersEndpointTest.php new file mode 100644 index 00000000..1941f849 --- /dev/null +++ b/tests/API/Endpoints/SuppliersEndpointTest.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +use App\Tests\API\Endpoints\CrudEndpointTestCase; + +class SuppliersEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/suppliers'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + self::assertJsonContains([ + 'hydra:totalItems' => 7, + ]); + } + + public function testGetChildrenCollection(): void + { + $this->_testGetChildrenCollection(1); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + $this->_testGetItem(2); + $this->_testGetItem(3); + } + + public function testCreateItem(): void + { + $this->_testPostItem([ + 'name' => 'Test API', + 'parent' => '/api/suppliers/1', + ]); + } + + public function testUpdateItem(): void + { + $this->_testPatchItem(5, [ + 'name' => 'Updated', + 'parent' => '/api/suppliers/2', + ]); + } + + public function testDeleteItem(): void + { + $this->_testDeleteItem(7); + } +} \ No newline at end of file diff --git a/tests/API/Endpoints/UsersEndpointTest.php b/tests/API/Endpoints/UsersEndpointTest.php new file mode 100644 index 00000000..0f075a7c --- /dev/null +++ b/tests/API/Endpoints/UsersEndpointTest.php @@ -0,0 +1,43 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\API\Endpoints; + +class UsersEndpointTest extends CrudEndpointTestCase +{ + + protected function getBasePath(): string + { + return '/api/users'; + } + + public function testGetCollection(): void + { + $this->_testGetCollection(); + } + + public function testGetItem(): void + { + $this->_testGetItem(1); + } +} \ No newline at end of file diff --git a/tests/ApplicationAvailabilityFunctionalTest.php b/tests/ApplicationAvailabilityFunctionalTest.php index 1fa08660..519f0cd5 100644 --- a/tests/ApplicationAvailabilityFunctionalTest.php +++ b/tests/ApplicationAvailabilityFunctionalTest.php @@ -91,9 +91,14 @@ class ApplicationAvailabilityFunctionalTest extends WebTestCase yield ['/part/3/clone']; + yield ['/part/1/merge/2']; + yield ['/part/new']; yield ['/part/new?category=1&footprint=1&manufacturer=1&storelocation=1&supplier=1']; + //Parts import + yield ['/parts/import']; + //Statistics yield ['/statistics']; @@ -114,12 +119,14 @@ class ApplicationAvailabilityFunctionalTest extends WebTestCase yield ['/select_api/measurement_unit']; //Label test - yield ['/scan']; yield ['/label/dialog']; yield ['/label/dialog?target_id=1&target_type=part']; yield ['/label/1/dialog']; yield ['/label/1/dialog?target_id=1&target_type=part&generate=1']; + //Scan test + yield ['/scan']; //Interactive scan dialog + //Tools yield ['/tools/reel_calc']; yield ['/tools/server_infos']; @@ -134,5 +141,13 @@ class ApplicationAvailabilityFunctionalTest extends WebTestCase yield ['/project/1/add_parts']; yield ['/project/1/add_parts?parts=1,2']; yield ['/project/1/build?n=1']; + yield ['/project/1/import_bom']; + + //Test info provider system + yield ['/tools/info_providers/providers']; //List all providers + yield ['/tools/info_providers/search']; //Search page + yield['/tools/info_providers/update/1']; //Update search for part from info provider + yield ['/part/from_info_provider/test/element1/create']; //Create part from info provider + yield ['/part/1/from_info_provider/test/element1/update']; //Update part from info provider } } diff --git a/tests/Controller/AdminPages/AbstractAdminControllerTest.php b/tests/Controller/AdminPages/AbstractAdminControllerTest.php index 532f0e92..af3aefee 100644 --- a/tests/Controller/AdminPages/AbstractAdminControllerTest.php +++ b/tests/Controller/AdminPages/AbstractAdminControllerTest.php @@ -31,17 +31,15 @@ use Symfony\Component\Security\Core\Exception\AccessDeniedException; */ abstract class AbstractAdminControllerTest extends WebTestCase { - protected static $base_path = 'not_valid'; - protected static $entity_class = 'not valid'; + protected static string $base_path = 'not_valid'; + protected static string $entity_class = 'not valid'; - public function readDataProvider(): array + public function readDataProvider(): \Iterator { - return [ - ['noread', false], - ['anonymous', true], - ['user', true], - ['admin', true], - ]; + yield ['noread', false]; + yield ['anonymous', true]; + yield ['user', true]; + yield ['admin', true]; } /** @@ -76,7 +74,7 @@ abstract class AbstractAdminControllerTest extends WebTestCase /** * @dataProvider readDataProvider * @group slow - * Tests if it possible to access an specific entity. Checks if permissions are working. + * Tests if it is possible to access a specific entity. Checks if permissions are working. */ public function testReadEntity(string $user, bool $read): void { @@ -98,14 +96,12 @@ abstract class AbstractAdminControllerTest extends WebTestCase $this->assertSame($read, !$client->getResponse()->isForbidden(), 'Permission Checking not working!'); } - public function deleteDataProvider(): array + public function deleteDataProvider(): \Iterator { - return [ - ['noread', false], - ['anonymous', false], - ['user', true], - ['admin', true], - ]; + yield ['noread', false]; + yield ['anonymous', false]; + yield ['user', true]; + yield ['admin', true]; } /** diff --git a/tests/Controller/AdminPages/AttachmentTypeControllerTest.php b/tests/Controller/AdminPages/AttachmentTypeControllerTest.php index 12233942..b98650bf 100644 --- a/tests/Controller/AdminPages/AttachmentTypeControllerTest.php +++ b/tests/Controller/AdminPages/AttachmentTypeControllerTest.php @@ -30,6 +30,6 @@ use App\Entity\Attachments\AttachmentType; */ class AttachmentTypeControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/attachment_type'; - protected static $entity_class = AttachmentType::class; + protected static string $base_path = '/en/attachment_type'; + protected static string $entity_class = AttachmentType::class; } diff --git a/tests/Controller/AdminPages/CategoryControllerTest.php b/tests/Controller/AdminPages/CategoryControllerTest.php index 1a9efee7..aa325b31 100644 --- a/tests/Controller/AdminPages/CategoryControllerTest.php +++ b/tests/Controller/AdminPages/CategoryControllerTest.php @@ -30,6 +30,6 @@ use App\Entity\Parts\Category; */ class CategoryControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/category'; - protected static $entity_class = Category::class; + protected static string $base_path = '/en/category'; + protected static string $entity_class = Category::class; } diff --git a/tests/Controller/AdminPages/FootprintControllerTest.php b/tests/Controller/AdminPages/FootprintControllerTest.php index ac5d8242..6381ecd6 100644 --- a/tests/Controller/AdminPages/FootprintControllerTest.php +++ b/tests/Controller/AdminPages/FootprintControllerTest.php @@ -30,6 +30,6 @@ use App\Entity\Parts\Footprint; */ class FootprintControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/footprint'; - protected static $entity_class = Footprint::class; + protected static string $base_path = '/en/footprint'; + protected static string $entity_class = Footprint::class; } diff --git a/tests/Controller/AdminPages/LabelProfileControllerTest.php b/tests/Controller/AdminPages/LabelProfileControllerTest.php index 80cf8348..b4ce79cc 100644 --- a/tests/Controller/AdminPages/LabelProfileControllerTest.php +++ b/tests/Controller/AdminPages/LabelProfileControllerTest.php @@ -46,8 +46,8 @@ use Symfony\Component\Security\Core\Exception\AccessDeniedException; class LabelProfileControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/label_profile'; - protected static $entity_class = LabelProfile::class; + protected static string $base_path = '/en/label_profile'; + protected static string $entity_class = LabelProfile::class; /** * Tests if deleting an entity is working. diff --git a/tests/Controller/AdminPages/ManufacturerControllerTest.php b/tests/Controller/AdminPages/ManufacturerControllerTest.php index 56b2cd1a..d454c604 100644 --- a/tests/Controller/AdminPages/ManufacturerControllerTest.php +++ b/tests/Controller/AdminPages/ManufacturerControllerTest.php @@ -30,6 +30,6 @@ use App\Entity\Parts\Manufacturer; */ class ManufacturerControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/manufacturer'; - protected static $entity_class = Manufacturer::class; + protected static string $base_path = '/en/manufacturer'; + protected static string $entity_class = Manufacturer::class; } diff --git a/tests/Controller/AdminPages/MeasurementUnitControllerTest.php b/tests/Controller/AdminPages/MeasurementUnitControllerTest.php index 53302e22..83eec70b 100644 --- a/tests/Controller/AdminPages/MeasurementUnitControllerTest.php +++ b/tests/Controller/AdminPages/MeasurementUnitControllerTest.php @@ -30,6 +30,6 @@ use App\Entity\Parts\MeasurementUnit; */ class MeasurementUnitControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/measurement_unit'; - protected static $entity_class = MeasurementUnit::class; + protected static string $base_path = '/en/measurement_unit'; + protected static string $entity_class = MeasurementUnit::class; } diff --git a/tests/Controller/AdminPages/ProjectControllerTest.php b/tests/Controller/AdminPages/ProjectControllerTest.php index 586c1b93..c0f5c288 100644 --- a/tests/Controller/AdminPages/ProjectControllerTest.php +++ b/tests/Controller/AdminPages/ProjectControllerTest.php @@ -31,6 +31,6 @@ use App\Entity\ProjectSystem\Project; */ class ProjectControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/project'; - protected static $entity_class = Project::class; + protected static string $base_path = '/en/project'; + protected static string $entity_class = Project::class; } diff --git a/tests/Controller/AdminPages/StorelocationControllerTest.php b/tests/Controller/AdminPages/StorelocationControllerTest.php index 1844c75e..65a9fe34 100644 --- a/tests/Controller/AdminPages/StorelocationControllerTest.php +++ b/tests/Controller/AdminPages/StorelocationControllerTest.php @@ -22,7 +22,7 @@ declare(strict_types=1); namespace App\Tests\Controller\AdminPages; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; /** * @group slow @@ -30,6 +30,6 @@ use App\Entity\Parts\Storelocation; */ class StorelocationControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/store_location'; - protected static $entity_class = Storelocation::class; + protected static string $base_path = '/en/store_location'; + protected static string $entity_class = StorageLocation::class; } diff --git a/tests/Controller/AdminPages/SupplierControllerTest.php b/tests/Controller/AdminPages/SupplierControllerTest.php index de6f8d7c..d98a7f69 100644 --- a/tests/Controller/AdminPages/SupplierControllerTest.php +++ b/tests/Controller/AdminPages/SupplierControllerTest.php @@ -30,6 +30,6 @@ use App\Entity\Parts\Supplier; */ class SupplierControllerTest extends AbstractAdminControllerTest { - protected static $base_path = '/en'.'/supplier'; - protected static $entity_class = Supplier::class; + protected static string $base_path = '/en/supplier'; + protected static string $entity_class = Supplier::class; } diff --git a/tests/Controller/KiCadApiControllerTest.php b/tests/Controller/KiCadApiControllerTest.php new file mode 100644 index 00000000..a66cb8a4 --- /dev/null +++ b/tests/Controller/KiCadApiControllerTest.php @@ -0,0 +1,250 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\Controller; + +use App\DataFixtures\APITokenFixtures; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class KiCadApiControllerTest extends WebTestCase +{ + private const BASE_URL = '/en/kicad-api/v1'; + + protected function createClientWithCredentials(string $token = APITokenFixtures::TOKEN_READONLY): KernelBrowser + { + return static::createClient([], ['headers' => ['authorization' => 'Bearer '.$token]]); + } + + public function testRoot(): void + { + $client = $this->createClientWithCredentials(); + $client->request('GET', self::BASE_URL.'/'); + + self::assertResponseIsSuccessful(); + $content = $client->getResponse()->getContent(); + self::assertJson($content); + + //Check if the response contains the expected keys + $array = json_decode($content, true); + self::assertArrayHasKey('categories', $array); + self::assertArrayHasKey('parts', $array); + } + + public function testCategories(): void + { + $client = $this->createClientWithCredentials(); + $client->request('GET', self::BASE_URL.'/categories.json'); + + self::assertResponseIsSuccessful(); + $content = $client->getResponse()->getContent(); + self::assertJson($content); + + $data = json_decode($content, true); + //There should be only one category, as the other ones contain no parts + self::assertCount(1, $data); + + //Check if the response contains the expected keys + $category = $data[0]; + self::assertArrayHasKey('name', $category); + self::assertArrayHasKey('id', $category); + } + + public function testCategoryParts(): void + { + $client = $this->createClientWithCredentials(); + $client->request('GET', self::BASE_URL.'/parts/category/1.json'); + + self::assertResponseIsSuccessful(); + $content = $client->getResponse()->getContent(); + self::assertJson($content); + + $data = json_decode($content, true); + //There should be 3 parts in the category + self::assertCount(3, $data); + + //Check if the response contains the expected keys + $part = $data[0]; + self::assertArrayHasKey('name', $part); + self::assertArrayHasKey('id', $part); + self::assertArrayHasKey('description', $part); + } + + public function testCategoryPartsForEmptyCategory(): void + { + $client = $this->createClientWithCredentials(); + $client->request('GET', self::BASE_URL.'/parts/category/2.json'); + + //Response should still be successful, but the result should be empty + self::assertResponseIsSuccessful(); + $content = $client->getResponse()->getContent(); + self::assertJson($content); + self::assertEmpty(json_decode($content, true)); + } + + public function testPartDetailsPart1(): void + { + $client = $this->createClientWithCredentials(); + $client->request('GET', self::BASE_URL.'/parts/1.json'); + + //Response should still be successful, but the result should be empty + self::assertResponseIsSuccessful(); + $content = $client->getResponse()->getContent(); + self::assertJson($content); + + $data = json_decode($content, true); + + $expected = array( + 'id' => '1', + 'name' => 'Part 1', + 'symbolIdStr' => 'Part:1', + 'exclude_from_bom' => 'False', + 'exclude_from_board' => 'True', + 'exclude_from_sim' => 'False', + 'fields' => + array( + 'footprint' => + array( + 'value' => 'Part:1', + 'visible' => 'False', + ), + 'reference' => + array( + 'value' => 'P', + 'visible' => 'True', + ), + 'value' => + array( + 'value' => 'Part 1', + 'visible' => 'True', + ), + 'keywords' => + array( + 'value' => '', + 'visible' => 'False', + ), + 'datasheet' => + array( + 'value' => 'http://localhost/en/part/1/info', + 'visible' => 'False', + ), + 'description' => + array( + 'value' => '', + 'visible' => 'False', + ), + 'Category' => + array( + 'value' => 'Node 1', + 'visible' => 'False', + ), + 'Manufacturing Status' => + array( + 'value' => '', + 'visible' => 'False', + ), + 'Part-DB ID' => + array( + 'value' => '1', + 'visible' => 'False', + ), + ), + ); + + self::assertEquals($expected, $data); + } + + public function testPartDetailsPart2(): void + { + $client = $this->createClientWithCredentials(); + $client->request('GET', self::BASE_URL.'/parts/1.json'); + + //Response should still be successful, but the result should be empty + self::assertResponseIsSuccessful(); + $content = $client->getResponse()->getContent(); + self::assertJson($content); + + $data = json_decode($content, true); + + //For part 2 things info should be taken from the category and footprint + $expected = array ( + 'id' => '1', + 'name' => 'Part 1', + 'symbolIdStr' => 'Part:1', + 'exclude_from_bom' => 'False', + 'exclude_from_board' => 'True', + 'exclude_from_sim' => 'False', + 'fields' => + array ( + 'footprint' => + array ( + 'value' => 'Part:1', + 'visible' => 'False', + ), + 'reference' => + array ( + 'value' => 'P', + 'visible' => 'True', + ), + 'value' => + array ( + 'value' => 'Part 1', + 'visible' => 'True', + ), + 'keywords' => + array ( + 'value' => '', + 'visible' => 'False', + ), + 'datasheet' => + array ( + 'value' => 'http://localhost/en/part/1/info', + 'visible' => 'False', + ), + 'description' => + array ( + 'value' => '', + 'visible' => 'False', + ), + 'Category' => + array ( + 'value' => 'Node 1', + 'visible' => 'False', + ), + 'Manufacturing Status' => + array ( + 'value' => '', + 'visible' => 'False', + ), + 'Part-DB ID' => + array ( + 'value' => '1', + 'visible' => 'False', + ), + ), + ); + + self::assertEquals($expected, $data); + } + +} \ No newline at end of file diff --git a/tests/Controller/RedirectControllerTest.php b/tests/Controller/RedirectControllerTest.php index 69b197e3..f62c05c3 100644 --- a/tests/Controller/RedirectControllerTest.php +++ b/tests/Controller/RedirectControllerTest.php @@ -22,7 +22,9 @@ declare(strict_types=1); namespace App\Tests\Controller; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; use App\Entity\UserSystem\User; +use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; @@ -32,9 +34,9 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; */ class RedirectControllerTest extends WebTestCase { - protected $em; - protected $userRepo; - protected $client; + protected EntityManagerInterface $em; + protected UserRepository $userRepo; + protected KernelBrowser $client; protected function setUp(): void { @@ -48,18 +50,16 @@ class RedirectControllerTest extends WebTestCase $this->userRepo = $this->em->getRepository(User::class); } - public function urlMatchDataProvider(): array + public function urlMatchDataProvider(): \Iterator { - return [ - ['/', true], - ['/part/2/info', true], - ['/part/de/2', true], - ['/part/en/', true], - ['/de/', false], - ['/de_DE/', false], - ['/en/', false], - ['/en_US/', false], - ]; + yield ['/', true]; + yield ['/part/2/info', true]; + yield ['/part/de/2', true]; + yield ['/part/en/', true]; + yield ['/de/', false]; + yield ['/de_DE/', false]; + yield ['/en/', false]; + yield ['/en_US/', false]; } /** @@ -74,24 +74,25 @@ class RedirectControllerTest extends WebTestCase $this->client->request('GET', $url); $response = $this->client->getResponse(); if ($expect_redirect) { - $this->assertSame(302, $response->getStatusCode()); + $this->assertResponseStatusCodeSame(302); } $this->assertSame($expect_redirect, $response->isRedirect()); } - public function urlAddLocaleDataProvider(): array + public function urlAddLocaleDataProvider(): \Iterator { - return [ - //User locale, original target, redirect target - ['de', '/', '/de/'], - ['de', '/part/3', '/de/part/3'], - ['en', '/', '/en/'], - ['en', '/category/new', '/en/category/new'], - ['en_US', '/part/3', '/en_US/part/3'], - //Without an explicit set value, the user should be redirect to english version - [null, '/', '/en/'], - ['en_US', '/part/3', '/en_US/part/3'], - ]; + //User locale, original target, redirect target + yield ['de', '/', '/de/']; + yield ['de', '/part/3', '/de/part/3']; + yield ['en', '/', '/en/']; + yield ['en', '/category/new', '/en/category/new']; + yield ['en_US', '/part/3', '/en_US/part/3']; + //Without an explicit set value, the user should be redirected to english version + yield [null, '/', '/en/']; + yield ['en_US', '/part/3', '/en_US/part/3']; + //Test that query parameters work + yield ['de', '/dialog?target_id=133&target_type=part', '/de/dialog?target_id=133&target_type=part']; + yield ['en', '/dialog?storelocation=1', '/en/dialog?storelocation=1']; } /** @@ -99,13 +100,9 @@ class RedirectControllerTest extends WebTestCase * * @dataProvider urlAddLocaleDataProvider * @group slow - * @depends testUrlMatch - * - * @param $user_locale - * @param $input_path - * @param $redirect_path + * @depends testUrlMatch */ - public function testAddLocale($user_locale, $input_path, $redirect_path): void + public function testAddLocale(?string $user_locale, string $input_path, string $redirect_path): void { //Redirect path is absolute $redirect_path = 'http://localhost'.$redirect_path; @@ -118,6 +115,62 @@ class RedirectControllerTest extends WebTestCase $this->client->followRedirects(false); $this->client->request('GET', $input_path); - $this->assertSame($redirect_path, $this->client->getResponse()->headers->get('Location')); + self::assertResponseRedirects($redirect_path); } + + /** + * Test if the user is redirected to the localized version of a page, based on his settings. + * We simulate the situation of a reverse proxy here, by adding a prefix to the path. + * + * @dataProvider urlAddLocaleDataProvider + * @group slow + */ + public function testAddLocaleReverseProxy(?string $user_locale, string $input_path, string $redirect_path): void + { + //Input path remains unchanged, as this is what the server receives from the proxy + + //Redirect path must contain the proxy prefix + $redirect_path = 'http://localhost'. '/proxy' . $redirect_path; + + /** @var User $user */ + $user = $this->userRepo->findOneBy(['name' => 'user']); + //Set user locale + $user->setLanguage($user_locale); + $this->em->flush(); + + $this->client->followRedirects(false); + $this->client->request('GET', $input_path, [], [], ['HTTP_X_FORWARDED_PREFIX' => '/proxy']); + self::assertResponseRedirects($redirect_path); + } + + + /** + * Test if the user is redirected to the localized version of a page, based on his settings. + * We simulate the situation of serving Part-DB in a subfolder here. + * + * @dataProvider urlAddLocaleDataProvider + * @group slow + */ + public function testAddLocaleSubfolder(?string $user_locale, string $input_path, string $redirect_path): void + { + //Prefix our path with the proxy prefix + $input_path = '/folder'.$input_path; + + //Redirect path is absolute + $redirect_path = 'http://localhost'. '/folder' . $redirect_path; + + /** @var User $user */ + $user = $this->userRepo->findOneBy(['name' => 'user']); + //Set user locale + $user->setLanguage($user_locale); + $this->em->flush(); + + $this->client->followRedirects(false); + $this->client->request('GET', $input_path, [], [], [ + 'SCRIPT_FILENAME' => '/var/www/html/folder/public/index.php', + 'PHP_SELF' => '/folder/index.php', + ]); + self::assertResponseRedirects($redirect_path); + } + } diff --git a/tests/Controller/ScanControllerTest.php b/tests/Controller/ScanControllerTest.php new file mode 100644 index 00000000..98992e09 --- /dev/null +++ b/tests/Controller/ScanControllerTest.php @@ -0,0 +1,54 @@ +. + */ +namespace App\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class ScanControllerTest extends WebTestCase +{ + + private ?KernelBrowser $client = null; + + public function setUp(): void + { + $this->client = static::createClient([], [ + 'PHP_AUTH_USER' => 'admin', + 'PHP_AUTH_PW' => 'test', + ]); + $this->client->disableReboot(); + $this->client->catchExceptions(false); + } + + public function testRedirectOnInputParameter(): void + { + $this->client->request('GET', '/en/scan?input=0000001'); + $this->assertResponseRedirects('/en/part/1'); + } + + public function testScanQRCode(): void + { + $this->client->request('GET', '/scan/part/1'); + $this->assertResponseRedirects('/en/part/1'); + } +} diff --git a/tests/DataTables/Filters/CompoundFilterTraitTest.php b/tests/DataTables/Filters/CompoundFilterTraitTest.php index 455d7da5..93f3c1e1 100644 --- a/tests/DataTables/Filters/CompoundFilterTraitTest.php +++ b/tests/DataTables/Filters/CompoundFilterTraitTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\DataTables\Filters; use App\DataTables\Filters\CompoundFilterTrait; @@ -54,16 +56,8 @@ class CompoundFilterTraitTest extends TestCase $filter = new class($f1, $f2, $f3, null) { use CompoundFilterTrait; - protected $filter1; - private $filter2; - public $filter3; - protected $filter4; - - public function __construct($f1, $f2, $f3, $f4) { - $this->filter1 = $f1; - $this->filter2 = $f2; - $this->filter3 = $f3; - $this->filter4 = $f4; + public function __construct(protected $filter1, private $filter2, public $filter3, protected $filter4) + { } public function _findAllChildFilters() @@ -104,16 +98,8 @@ class CompoundFilterTraitTest extends TestCase $filter = new class($f1, $f2, $f3, null) { use CompoundFilterTrait; - protected $filter1; - private $filter2; - public $filter3; - protected $filter4; - - public function __construct($f1, $f2, $f3, $f4) { - $this->filter1 = $f1; - $this->filter2 = $f2; - $this->filter3 = $f3; - $this->filter4 = $f4; + public function __construct(protected $filter1, private $filter2, public $filter3, protected $filter4) + { } public function _applyAllChildFilters(QueryBuilder $queryBuilder): void diff --git a/tests/DataTables/Filters/Constraints/FilterTraitTest.php b/tests/DataTables/Filters/Constraints/FilterTraitTest.php index 5d6442ec..1e32fdd8 100644 --- a/tests/DataTables/Filters/Constraints/FilterTraitTest.php +++ b/tests/DataTables/Filters/Constraints/FilterTraitTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\DataTables\Filters\Constraints; use App\DataTables\Filters\Constraints\FilterTrait; -use App\Entity\Parts\MeasurementUnit; use PHPUnit\Framework\TestCase; class FilterTraitTest extends TestCase diff --git a/tests/DatatablesAvailabilityTest.php b/tests/DatatablesAvailabilityTest.php index 3cb34d9b..5af04627 100644 --- a/tests/DatatablesAvailabilityTest.php +++ b/tests/DatatablesAvailabilityTest.php @@ -48,7 +48,7 @@ class DatatablesAvailabilityTest extends WebTestCase /** * @dataProvider urlProvider */ - public function testDataTable(string $url): void + public function testDataTable(string $url, ?array $ordering = null): void { //We have localized routes $url = '/en'.$url; @@ -68,7 +68,14 @@ class DatatablesAvailabilityTest extends WebTestCase 'PHP_AUTH_PW' => 'test', ]); $client->catchExceptions(false); - $client->request('POST', $url, ['_dt' => 'dt']); + + $post = ['_dt' => 'dt']; + + if ($ordering) { + $post['order'] = $ordering; + } + + $client->request('POST', $url, $post); $this->assertTrue($client->getResponse()->isSuccessful()); $this->assertJson($client->getResponse()->getContent()); } @@ -92,5 +99,18 @@ class DatatablesAvailabilityTest extends WebTestCase //Test using filters yield ['/category/1/parts?part_filter%5Bname%5D%5Boperator%5D=%3D&part_filter%5Bname%5D%5Bvalue%5D=BC547&part_filter%5Bcategory%5D%5Boperator%5D=INCLUDING_CHILDREN&part_filter%5Btags%5D%5Boperator%5D=ANY&part_filter%5Btags%5D%5Bvalue%5D=Test&part_filter%5Bsubmit%5D=']; yield ['/category/1/parts?part_filter%5Bcategory%5D%5Boperator%5D=INCLUDING_CHILDREN&part_filter%5Bstorelocation%5D%5Boperator%5D=%3D&part_filter%5Bstorelocation%5D%5Bvalue%5D=1&part_filter%5BattachmentsCount%5D%5Boperator%5D=%3D&part_filter%5BattachmentsCount%5D%5Bvalue1%5D=3&part_filter%5Bsubmit%5D=']; + //Filter over total amount + yield ['/parts?part_filter%5BamountSum%5D%5Boperator%5D=>&part_filter%5BamountSum%5D%5Bvalue1%5D=1&part_filter%5Bsubmit%5D=']; + //Less than desired filter: + yield ['/parts?part_filter%5BlessThanDesired%5D%5Bvalue%5D=true&part_filter%5Bsubmit%5D=']; + + //Test regex search + yield ['/parts/search?category=1&comment=1&description=1&ipn=1&keyword=test&mpn=1&name=1&ordernr=1®ex=1&storelocation=1&tags=1']; + } + + public function testOrdering(): void + { + //Amount ordering (which uses the dynamic amount calculation) + $this->testDataTable('/parts', [['column' => 9, 'dir' => 'asc']]); } } diff --git a/tests/Doctrine/SQLiteRegexMiddlewareTest.php b/tests/Doctrine/SQLiteRegexMiddlewareTest.php new file mode 100644 index 00000000..01abb16e --- /dev/null +++ b/tests/Doctrine/SQLiteRegexMiddlewareTest.php @@ -0,0 +1,93 @@ +. + */ +namespace App\Tests\Doctrine; + +use App\Doctrine\Middleware\SQLiteRegexExtensionMiddlewareDriver; +use PHPUnit\Framework\TestCase; + +class SQLiteRegexMiddlewareTest extends TestCase +{ + + public function regexpDataProvider(): \Generator + { + yield [1, 'a', 'a']; + yield [0, 'a', 'b']; + yield [1, 'a', 'ba']; + yield [1, 'a', 'ab']; + yield [1, 'a', 'baa']; + + yield [1, '^a$', 'a']; + yield [0, '^a$', 'ab']; + yield [1, '^a\d+$', 'a123']; + } + + /** + * @dataProvider regexpDataProvider + */ + public function testRegexp(int $expected, string $pattern, string $value): void + { + $this->assertSame($expected, SQLiteRegexExtensionMiddlewareDriver::regexp($pattern, $value)); + } + + public function fieldDataProvider(): \Generator + { + + // Null cases + yield [0, null, []]; + yield [0, null, [1]]; + yield [0, 42, [1, 2]]; + + // Ints + yield [1, 1, [1]]; + yield [1, 2, [2, 1]]; + yield [2, 1, [2, 1]]; + yield [6, 3, [2, 1, 2, 1, 2, 3]]; + yield [1, 2, [2, 1, 2, 1, 2, 1, 2, 1, 2, 1]]; + yield [3, 5, [2, 1, 5, 3]]; + + // Strings + yield [1, 'a', ['a']]; + yield [1, 'b', ['b', 'a']]; + yield [2, 'a', ['b', 'a']]; + yield [1, 'b', ['b', 'a', 'b']]; + yield [6, 'c', ['b', 'a', 'b', 'a', 'b', 'c']]; + } + + /** + * @dataProvider fieldDataProvider + */ + public function testField(int $expected, string|int|null $value, array $array): void + { + $this->assertSame($expected, SQLiteRegexExtensionMiddlewareDriver::field($value, ...$array)); + } + + /** + * @dataProvider fieldDataProvider + */ + public function testField2(int $expected, string|int|null $value, array $array): void + { + //Should be the same as field, but with the array comma imploded + $string = implode(',', $array); + $this->assertSame($expected, SQLiteRegexExtensionMiddlewareDriver::field2($value, $string)); + } +} diff --git a/tests/Entity/Attachments/AttachmentTest.php b/tests/Entity/Attachments/AttachmentTest.php index 91a3cb53..bac28fd4 100644 --- a/tests/Entity/Attachments/AttachmentTest.php +++ b/tests/Entity/Attachments/AttachmentTest.php @@ -33,7 +33,7 @@ use App\Entity\Attachments\GroupAttachment; use App\Entity\Attachments\ManufacturerAttachment; use App\Entity\Attachments\MeasurementUnitAttachment; use App\Entity\Attachments\PartAttachment; -use App\Entity\Attachments\StorelocationAttachment; +use App\Entity\Attachments\StorageLocationAttachment; use App\Entity\Attachments\SupplierAttachment; use App\Entity\Attachments\UserAttachment; use App\Entity\ProjectSystem\Project; @@ -42,7 +42,7 @@ use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\UserSystem\Group; @@ -59,35 +59,34 @@ class AttachmentTest extends TestCase $this->assertNull($attachment->getAttachmentType()); $this->assertFalse($attachment->isPicture()); - $this->assertFalse($attachment->isExternal()); + $this->assertFalse($attachment->hasExternal()); + $this->assertFalse($attachment->hasInternal()); $this->assertFalse($attachment->isSecure()); $this->assertFalse($attachment->isBuiltIn()); $this->assertFalse($attachment->is3DModel()); $this->assertFalse($attachment->getShowInTable()); - $this->assertEmpty($attachment->getPath()); + $this->assertEmpty($attachment->getInternalPath()); + $this->assertEmpty($attachment->getExternalPath()); $this->assertEmpty($attachment->getName()); - $this->assertEmpty($attachment->getURL()); $this->assertEmpty($attachment->getExtension()); $this->assertNull($attachment->getElement()); $this->assertEmpty($attachment->getFilename()); } - public function subClassesDataProvider(): array + public function subClassesDataProvider(): \Iterator { - return [ - [AttachmentTypeAttachment::class, AttachmentType::class], - [CategoryAttachment::class, Category::class], - [CurrencyAttachment::class, Currency::class], - [ProjectAttachment::class, Project::class], - [FootprintAttachment::class, Footprint::class], - [GroupAttachment::class, Group::class], - [ManufacturerAttachment::class, Manufacturer::class], - [MeasurementUnitAttachment::class, MeasurementUnit::class], - [PartAttachment::class, Part::class], - [StorelocationAttachment::class, Storelocation::class], - [SupplierAttachment::class, Supplier::class], - [UserAttachment::class, User::class], - ]; + yield [AttachmentTypeAttachment::class, AttachmentType::class]; + yield [CategoryAttachment::class, Category::class]; + yield [CurrencyAttachment::class, Currency::class]; + yield [ProjectAttachment::class, Project::class]; + yield [FootprintAttachment::class, Footprint::class]; + yield [GroupAttachment::class, Group::class]; + yield [ManufacturerAttachment::class, Manufacturer::class]; + yield [MeasurementUnitAttachment::class, MeasurementUnit::class]; + yield [PartAttachment::class, Part::class]; + yield [StorageLocationAttachment::class, StorageLocation::class]; + yield [SupplierAttachment::class, Supplier::class]; + yield [UserAttachment::class, User::class]; } /** @@ -105,7 +104,7 @@ class AttachmentTest extends TestCase } /** - * Test that all attachment subclasses like PartAttachment or similar returns an exception, when an not allowed + * Test that all attachment subclasses like PartAttachment or similar returns an exception, when a not allowed * element is passed. * * @dataProvider subClassesDataProvider @@ -117,151 +116,150 @@ class AttachmentTest extends TestCase /** @var Attachment $attachment */ $attachment = new $attachment_class(); - if (Project::class !== $allowed_class) { - $element = new Project(); - } else { - $element = new Category(); - } + $element = Project::class !== $allowed_class ? new Project() : new Category(); $attachment->setElement($element); } - public function externalDataProvider(): array - { - return [ - ['', false], - ['%MEDIA%/foo/bar.txt', false], - ['%BASE%/foo/bar.jpg', false], - ['%FOOTPRINTS%/foo/bar.jpg', false], - ['%FOOTPRINTS3D%/foo/bar.jpg', false], - ['%SECURE%/test.txt', false], - ['%test%/foo/bar.ghp', true], - ['foo%MEDIA%/foo.jpg', true], - ['foo%MEDIA%/%BASE%foo.jpg', true], - ]; - } - /** - * @dataProvider externalDataProvider - */ - public function testIsExternal($path, $expected): void + public static function extensionDataProvider(): \Iterator { - $attachment = new PartAttachment(); - $this->setProtectedProperty($attachment, 'path', $path); - $this->assertSame($expected, $attachment->isExternal()); - } - - public function extensionDataProvider(): array - { - return [ - ['%MEDIA%/foo/bar.txt', null, 'txt'], - ['%MEDIA%/foo/bar.JPeg', null, 'jpeg'], - ['%MEDIA%/foo/bar.JPeg', 'test.txt', 'txt'], - ['%MEDIA%/foo/bar', null, ''], - ['%MEDIA%/foo.bar', 'bar', ''], - ['http://google.de', null, null], - ['https://foo.bar', null, null], - ['https://foo.bar/test.jpeg', null, null], - ['test', null, null], - ['test.txt', null, null], - ]; + yield ['%MEDIA%/foo/bar.txt', 'http://google.de', null, 'txt']; + yield ['%MEDIA%/foo/bar.JPeg', 'https://foo.bar', null, 'jpeg']; + yield ['%MEDIA%/foo/bar.JPeg', null, 'test.txt', 'txt']; + yield ['%MEDIA%/foo/bar', 'https://foo.bar/test.jpeg', null, '']; + yield ['%MEDIA%/foo.bar', 'test.txt', 'bar', '']; + yield [null, 'http://google.de', null, null]; + yield [null, 'https://foo.bar', null, null]; + yield [null, ',https://foo.bar/test.jpeg', null, null]; + yield [null, 'test', null, null]; + yield [null, 'test.txt', null, null]; } /** * @dataProvider extensionDataProvider */ - public function testGetExtension($path, $originalFilename, $expected): void + public function testGetExtension(?string $internal_path, ?string $external_path, ?string $originalFilename, ?string $expected): void { $attachment = new PartAttachment(); - $this->setProtectedProperty($attachment, 'path', $path); + $this->setProtectedProperty($attachment, 'internal_path', $internal_path); + $this->setProtectedProperty($attachment, 'external_path', $external_path); $this->setProtectedProperty($attachment, 'original_filename', $originalFilename); $this->assertSame($expected, $attachment->getExtension()); } - public function pictureDataProvider(): array + public static function pictureDataProvider(): \Iterator { - return [ - ['%MEDIA%/foo/bar.txt', false], - ['https://test.de/picture.jpeg', true], - ['https://test.de', true], - ['http://test.de/google.de', true], - ['%MEDIA%/foo/bar.jpeg', true], - ['%MEDIA%/foo/bar.webp', true], - ['%MEDIA%/foo', false], - ['%SECURE%/foo.txt/test', false], - ]; + yield [null, '%MEDIA%/foo/bar.txt', false]; + yield [null, 'https://test.de/picture.jpeg', true]; + yield [null, 'https://test.de/picture.png?test=fdsj&width=34', true]; + yield [null, 'https://invalid.invalid/file.txt', false]; + yield [null, 'http://infsf.inda/file.zip?test', false]; + yield [null, 'https://test.de', true]; + yield [null, 'https://invalid.com/invalid/pic', true]; + yield ['%MEDIA%/foo/bar.jpeg', 'https://invalid.invalid/file.txt', true]; + yield ['%MEDIA%/foo/bar.webp', '', true]; + yield ['%MEDIA%/foo', '', false]; + yield ['%SECURE%/foo.txt/test', 'https://test.de/picture.jpeg', false]; } /** * @dataProvider pictureDataProvider */ - public function testIsPicture($path, $expected): void + public function testIsPicture(?string $internal_path, ?string $external_path, bool $expected): void { $attachment = new PartAttachment(); - $this->setProtectedProperty($attachment, 'path', $path); + $this->setProtectedProperty($attachment, 'internal_path', $internal_path); + $this->setProtectedProperty($attachment, 'external_path', $external_path); $this->assertSame($expected, $attachment->isPicture()); } - public function builtinDataProvider(): array + public static function builtinDataProvider(): \Iterator { - return [ - ['', false], - ['%MEDIA%/foo/bar.txt', false], - ['%BASE%/foo/bar.txt', false], - ['/', false], - ['https://google.de', false], - ['%FOOTPRINTS%/foo/bar.txt', true], - ]; + yield ['', false]; + yield [null, false]; + yield ['%MEDIA%/foo/bar.txt', false]; + yield ['%BASE%/foo/bar.txt', false]; + yield ['/', false]; + yield ['https://google.de', false]; + yield ['%FOOTPRINTS%/foo/bar.txt', true]; } /** * @dataProvider builtinDataProvider */ - public function testIsBuiltIn($path, $expected): void + public function testIsBuiltIn(?string $path, $expected): void { $attachment = new PartAttachment(); - $this->setProtectedProperty($attachment, 'path', $path); + $this->setProtectedProperty($attachment, 'internal_path', $path); $this->assertSame($expected, $attachment->isBuiltIn()); } - public function hostDataProvider(): array + public static function hostDataProvider(): \Iterator { - return [ - ['%MEDIA%/foo/bar.txt', null], - ['https://www.google.de/test.txt', 'www.google.de'], - ['https://foo.bar/test?txt=test', 'foo.bar'], - ]; + yield ['%MEDIA%/foo/bar.txt', null]; + yield ['https://www.google.de/test.txt', 'www.google.de']; + yield ['https://foo.bar/test?txt=test', 'foo.bar']; } /** * @dataProvider hostDataProvider */ - public function testGetHost($path, $expected): void + public function testGetHost(?string $path, ?string $expected): void { $attachment = new PartAttachment(); - $this->setProtectedProperty($attachment, 'path', $path); + $this->setProtectedProperty($attachment, 'external_path', $path); $this->assertSame($expected, $attachment->getHost()); } - public function filenameProvider(): array + public static function filenameProvider(): \Iterator { - return [ - ['%MEDIA%/foo/bar.txt', null, 'bar.txt'], - ['%MEDIA%/foo/bar.JPeg', 'test.txt', 'test.txt'], - ['https://www.google.de/test.txt', null, null], - ]; + yield ['%MEDIA%/foo/bar.txt', 'https://www.google.de/test.txt', null, 'bar.txt']; + yield ['%MEDIA%/foo/bar.JPeg', 'https://www.google.de/foo.txt', 'test.txt', 'test.txt']; + yield ['', 'https://www.google.de/test.txt', null, null]; + yield [null, 'https://www.google.de/test.txt', null, null]; } /** * @dataProvider filenameProvider */ - public function testGetFilename($path, $original_filename, $expected): void + public function testGetFilename(?string $internal_path, ?string $external_path, ?string $original_filename, ?string $expected): void { $attachment = new PartAttachment(); - $this->setProtectedProperty($attachment, 'path', $path); + $this->setProtectedProperty($attachment, 'internal_path', $internal_path); + $this->setProtectedProperty($attachment, 'external_path', $external_path); $this->setProtectedProperty($attachment, 'original_filename', $original_filename); $this->assertSame($expected, $attachment->getFilename()); } + public function testSetExternalPath(): void + { + $attachment = new PartAttachment(); + + //Set URL + $attachment->setExternalPath('https://google.de'); + $this->assertSame('https://google.de', $attachment->getExternalPath()); + + //Ensure that changing the external path does reset the internal one + $attachment->setInternalPath('%MEDIA%/foo/bar.txt'); + $attachment->setExternalPath('https://example.de'); + $this->assertSame(null, $attachment->getInternalPath()); + + //Ensure that setting the same value to the external path again doesn't reset the internal one + $attachment->setExternalPath('https://google.de'); + $attachment->setInternalPath('%MEDIA%/foo/bar.txt'); + $attachment->setExternalPath('https://google.de'); + $this->assertSame('%MEDIA%/foo/bar.txt', $attachment->getInternalPath()); + + //Ensure that resetting the external path doesn't reset the internal one + $attachment->setInternalPath('%MEDIA%/foo/bar.txt'); + $attachment->setExternalPath(''); + $this->assertSame('%MEDIA%/foo/bar.txt', $attachment->getInternalPath()); + + //Ensure that spaces get replaced by %20 + $attachment->setExternalPath('https://example.de/test file.txt'); + $this->assertSame('https://example.de/test%20file.txt', $attachment->getExternalPath()); + } + public function testIsURL(): void { $url = '%MEDIA%/test.txt'; @@ -282,7 +280,7 @@ class AttachmentTest extends TestCase * @param string $property - property on instance being modified * @param mixed $value - new value of the property being modified */ - public function setProtectedProperty($object, $property, $value): void + public function setProtectedProperty(object $object, string $property, mixed $value): void { $reflection = new ReflectionClass($object); $reflection_property = $reflection->getProperty($property); diff --git a/tests/Entity/Base/AbstractStructuralDBElementTest.php b/tests/Entity/Base/AbstractStructuralDBElementTest.php index 4b124053..3f8157ad 100644 --- a/tests/Entity/Base/AbstractStructuralDBElementTest.php +++ b/tests/Entity/Base/AbstractStructuralDBElementTest.php @@ -33,16 +33,16 @@ use PHPUnit\Framework\TestCase; */ class AbstractStructuralDBElementTest extends TestCase { - protected $root; - protected $child1; - protected $child2; - protected $child3; - protected $child1_1; - protected $child1_2; + protected AttachmentType $root; + protected AttachmentType $child1; + protected AttachmentType $child2; + protected AttachmentType $child3; + protected AttachmentType $child1_1; + protected AttachmentType $child1_2; protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub //Build a simple hierachy $this->root = new AttachmentType(); @@ -144,4 +144,15 @@ class AbstractStructuralDBElementTest extends TestCase $this->assertSame([$this->root, $this->child1], $this->child1->getPathArray()); $this->assertSame([$this->root], $this->root->getPathArray()); } + + public function testGetSubelements(): void + { + $this->assertSame([$this->child1, $this->child2, $this->child3], $this->root->getSubelements()->toArray()); + $this->assertSame([$this->child1_1, $this->child1_2], $this->child1->getSubelements()->toArray()); + $this->assertSame([], $this->child1_1->getSubelements()->toArray()); + + //If an element is set as its own parent, it should not be returned as a subelement + $this->child1->setParent($this->child1); + $this->assertSame([], $this->child1->getSubelements()->toArray()); + } } diff --git a/tests/Entity/LogSystem/AbstractLogEntryTest.php b/tests/Entity/LogSystem/AbstractLogEntryTest.php index fe4cdb58..3f223693 100644 --- a/tests/Entity/LogSystem/AbstractLogEntryTest.php +++ b/tests/Entity/LogSystem/AbstractLogEntryTest.php @@ -44,6 +44,7 @@ namespace App\Tests\Entity\LogSystem; use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentType; use App\Entity\Attachments\PartAttachment; +use App\Entity\LogSystem\UserLoginLogEntry; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; use App\Entity\LogSystem\AbstractLogEntry; @@ -51,7 +52,7 @@ use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; @@ -59,91 +60,6 @@ use PHPUnit\Framework\TestCase; class AbstractLogEntryTest extends TestCase { - public function levelDataProvider(): array - { - return [ - [0, 'emergency'], - [1, 'alert'], - [2, 'critical'], - [3, 'error'], - [4, 'warning'], - [5, 'notice'], - [6, 'info'], - [7, 'debug'], - [8, 'blabla', true], - [-1, 'test', true], - ]; - } - - public function targetTypeDataProvider(): array - { - return [ - [1, User::class], - [2, Attachment::class], - [3, AttachmentType::class], - [4, Category::class], - [5, Project::class], - [6, ProjectBOMEntry::class], - [7, Footprint::class], - [8, Group::class], - [9, Manufacturer::class], - [10, Part::class], - [11, Storelocation::class], - [12, Supplier::class], - [-1, 'blablub', true], - ]; - } - - /** - * @dataProvider levelDataProvider - */ - public function testLevelIntToString(int $int, string $expected_string, bool $expect_exception = false): void - { - if ($expect_exception) { - $this->expectException(\InvalidArgumentException::class); - } - $this->assertSame($expected_string, AbstractLogEntry::levelIntToString($int)); - } - - /** - * @dataProvider levelDataProvider - */ - public function testLevelStringToInt(int $expected_int, string $string, bool $expect_exception = false): void - { - if ($expect_exception) { - $this->expectException(\InvalidArgumentException::class); - } - $this->assertSame($expected_int, AbstractLogEntry::levelStringToInt($string)); - } - - /** - * @dataProvider targetTypeDataProvider - */ - public function testTargetTypeIdToClass(int $int, string $expected_class, bool $expect_exception = false): void - { - if ($expect_exception) { - $this->expectException(\InvalidArgumentException::class); - } - $this->assertSame($expected_class, AbstractLogEntry::targetTypeIdToClass($int)); - } - - /** - * @dataProvider targetTypeDataProvider - */ - public function testTypeClassToID(int $expected_id, string $class, bool $expect_exception = false): void - { - if ($expect_exception) { - $this->expectException(\InvalidArgumentException::class); - } - $this->assertSame($expected_id, AbstractLogEntry::targetTypeClassToID($class)); - } - - public function testTypeClassToIDSubclasses(): void - { - //Test if class mapping works for subclasses - $this->assertSame(2, AbstractLogEntry::targetTypeClassToID(PartAttachment::class)); - } - public function testSetGetTarget(): void { $part = $this->createMock(Part::class); @@ -160,4 +76,25 @@ class AbstractLogEntryTest extends TestCase $this->assertNull($log->getTargetClass()); $this->assertNull($log->getTargetID()); } + + public function testCLIUsername(): void + { + $log = new UserLoginLogEntry('1.1.1.1'); + + //By default, no CLI username is set + $this->assertNull($log->getCLIUsername()); + $this->assertFalse($log->isCLIEntry()); + + $user = new User(); + $user->setName('test'); + $log->setUser($user); + + //Set a CLI username + $log->setCLIUsername('root'); + $this->assertSame('root', $log->getCLIUsername()); + $this->assertTrue($log->isCLIEntry()); + + //Normal user must be null now + $this->assertNull($log->getUser()); + } } diff --git a/tests/Entity/LogSystem/LogLevelTest.php b/tests/Entity/LogSystem/LogLevelTest.php new file mode 100644 index 00000000..402942e1 --- /dev/null +++ b/tests/Entity/LogSystem/LogLevelTest.php @@ -0,0 +1,86 @@ +. + */ +namespace App\Tests\Entity\LogSystem; + +use App\Entity\LogSystem\LogLevel; +use PHPUnit\Framework\TestCase; + +class LogLevelTest extends TestCase +{ + + public function testToPSR3LevelString(): void + { + $this->assertSame('debug', LogLevel::DEBUG->toPSR3LevelString()); + $this->assertSame('info', LogLevel::INFO->toPSR3LevelString()); + $this->assertSame('notice', LogLevel::NOTICE->toPSR3LevelString()); + $this->assertSame('warning', LogLevel::WARNING->toPSR3LevelString()); + $this->assertSame('error', LogLevel::ERROR->toPSR3LevelString()); + $this->assertSame('critical', LogLevel::CRITICAL->toPSR3LevelString()); + $this->assertSame('alert', LogLevel::ALERT->toPSR3LevelString()); + $this->assertSame('emergency', LogLevel::EMERGENCY->toPSR3LevelString()); + } + + public function testFromPSR3LevelString(): void + { + $this->assertSame(LogLevel::DEBUG, LogLevel::fromPSR3LevelString('debug')); + $this->assertSame(LogLevel::INFO, LogLevel::fromPSR3LevelString('info')); + $this->assertSame(LogLevel::NOTICE, LogLevel::fromPSR3LevelString('notice')); + $this->assertSame(LogLevel::WARNING, LogLevel::fromPSR3LevelString('warning')); + $this->assertSame(LogLevel::ERROR, LogLevel::fromPSR3LevelString('error')); + $this->assertSame(LogLevel::CRITICAL, LogLevel::fromPSR3LevelString('critical')); + $this->assertSame(LogLevel::ALERT, LogLevel::fromPSR3LevelString('alert')); + $this->assertSame(LogLevel::EMERGENCY, LogLevel::fromPSR3LevelString('emergency')); + } + + public function testMoreImportOrEqualThan(): void + { + $this->assertTrue(LogLevel::DEBUG->moreImportOrEqualThan(LogLevel::DEBUG)); + $this->assertFalse(LogLevel::DEBUG->moreImportOrEqualThan(LogLevel::INFO)); + $this->assertFalse(LogLevel::DEBUG->moreImportOrEqualThan(LogLevel::NOTICE)); + $this->assertTrue(LogLevel::EMERGENCY->moreImportOrEqualThan(LogLevel::DEBUG)); + } + + public function testMoreImportThan(): void + { + $this->assertFalse(LogLevel::DEBUG->moreImportThan(LogLevel::DEBUG)); + $this->assertFalse(LogLevel::DEBUG->moreImportThan(LogLevel::INFO)); + $this->assertFalse(LogLevel::DEBUG->moreImportThan(LogLevel::NOTICE)); + $this->assertTrue(LogLevel::EMERGENCY->moreImportThan(LogLevel::DEBUG)); + } + + public function testLessImportThan(): void + { + $this->assertFalse(LogLevel::DEBUG->lessImportThan(LogLevel::DEBUG)); + $this->assertTrue(LogLevel::DEBUG->lessImportThan(LogLevel::INFO)); + $this->assertTrue(LogLevel::DEBUG->lessImportThan(LogLevel::NOTICE)); + $this->assertFalse(LogLevel::EMERGENCY->lessImportThan(LogLevel::DEBUG)); + } + + public function testLessImportOrEqualThan(): void + { + $this->assertTrue(LogLevel::DEBUG->lessImportOrEqualThan(LogLevel::DEBUG)); + $this->assertTrue(LogLevel::DEBUG->lessImportOrEqualThan(LogLevel::INFO)); + $this->assertTrue(LogLevel::DEBUG->lessImportOrEqualThan(LogLevel::NOTICE)); + $this->assertFalse(LogLevel::EMERGENCY->lessImportOrEqualThan(LogLevel::DEBUG)); + } +} diff --git a/tests/Entity/LogSystem/LogTargetTypeTest.php b/tests/Entity/LogSystem/LogTargetTypeTest.php new file mode 100644 index 00000000..46682496 --- /dev/null +++ b/tests/Entity/LogSystem/LogTargetTypeTest.php @@ -0,0 +1,64 @@ +. + */ +namespace App\Tests\Entity\LogSystem; + +use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\PartAttachment; +use App\Entity\LogSystem\LogTargetType; +use App\Entity\Parameters\PartParameter; +use App\Entity\Parts\Category; +use App\Entity\UserSystem\User; +use PHPUnit\Framework\TestCase; + +class LogTargetTypeTest extends TestCase +{ + + public function testToClass(): void + { + $this->assertNull(LogTargetType::NONE->toClass()); + $this->assertSame(User::class, LogTargetType::USER->toClass()); + $this->assertSame(Category::class, LogTargetType::CATEGORY->toClass()); + $this->assertSame(Attachment::class, LogTargetType::ATTACHMENT->toClass()); + } + + public function testFromElementClass(): void + { + //Test creation from string class + $this->assertSame(LogTargetType::CATEGORY, LogTargetType::fromElementClass(Category::class)); + $this->assertSame(LogTargetType::USER, LogTargetType::fromElementClass(User::class)); + + //Test creation from object + $this->assertSame(LogTargetType::CATEGORY, LogTargetType::fromElementClass(new Category())); + $this->assertSame(LogTargetType::USER, LogTargetType::fromElementClass(new User())); + + //Test creation from subclass + $this->assertSame(LogTargetType::ATTACHMENT, LogTargetType::fromElementClass(new PartAttachment())); + $this->assertSame(LogTargetType::PARAMETER, LogTargetType::fromElementClass(new PartParameter())); + } + + public function testFromElementClassInvalid(): void + { + $this->expectException(\InvalidArgumentException::class); + LogTargetType::fromElementClass(new \stdClass()); + } +} diff --git a/tests/Entity/Parameters/PartParameterTest.php b/tests/Entity/Parameters/PartParameterTest.php index c8005d6c..0e426d52 100644 --- a/tests/Entity/Parameters/PartParameterTest.php +++ b/tests/Entity/Parameters/PartParameterTest.php @@ -46,29 +46,38 @@ use PHPUnit\Framework\TestCase; class PartParameterTest extends TestCase { - public function valueWithUnitDataProvider(): array + public function valueWithUnitDataProvider(): \Iterator { - return [ - ['1', 1.0, ''], - ['1 V', 1.0, 'V'], - ['1.23', 1.23, ''], - ['1.23 V', 1.23, 'V'], - ]; + yield ['1', 1.0, '']; + yield ['1 V', 1.0, 'V']; + yield ['1.23', 1.23, '']; + yield ['1.23 V', 1.23, 'V']; } - public function formattedValueDataProvider(): array + public function formattedValueDataProvider(): \Iterator { - return [ - ['Text Test', null, null, null, 'V', 'Text Test'], - ['10.23 V', null, 10.23, null, 'V', ''], - ['10.23 V [Text]', null, 10.23, null, 'V', 'Text'], - ['max. 10.23 V', null, null, 10.23, 'V', ''], - ['max. 10.23 [Text]', null, null, 10.23, '', 'Text'], - ['min. 10.23 V', 10.23, null, null, 'V', ''], - ['10.23 V ... 11 V', 10.23, null, 11, 'V', ''], - ['10.23 V (9 V ... 11 V)', 9, 10.23, 11, 'V', ''], - ['10.23 V (9 V ... 11 V) [Test]', 9, 10.23, 11, 'V', 'Test'], - ]; + yield ['Text Test', null, null, null, 'V', 'Text Test']; + yield ['10.23 V', null, 10.23, null, 'V', '']; + yield ['10.23 V [Text]', null, 10.23, null, 'V', 'Text']; + yield ['max. 10.23 V', null, null, 10.23, 'V', '']; + yield ['max. 10.23 [Text]', null, null, 10.23, '', 'Text']; + yield ['min. 10.23 V', 10.23, null, null, 'V', '']; + yield ['10.23 V ... 11 V', 10.23, null, 11, 'V', '']; + yield ['10.23 V (9 V ... 11 V)', 9, 10.23, 11, 'V', '']; + yield ['10.23 V (9 V ... 11 V) [Test]', 9, 10.23, 11, 'V', 'Test']; + } + + public function formattedValueWithLatexDataProvider(): \Iterator + { + yield ['Text Test', null, null, null, 'V', 'Text Test']; + yield ['10.23 $\mathrm{V}$', null, 10.23, null, 'V', '']; + yield ['10.23 $\mathrm{V}$ [Text]', null, 10.23, null, 'V', 'Text']; + yield ['max. 10.23 $\mathrm{V}$', null, null, 10.23, 'V', '']; + yield ['max. 10.23 [Text]', null, null, 10.23, '', 'Text']; + yield ['min. 10.23 $\mathrm{V}$', 10.23, null, null, 'V', '']; + yield ['10.23 $\mathrm{V}$ ... 11 $\mathrm{V}$', 10.23, null, 11, 'V', '']; + yield ['10.23 $\mathrm{V}$ (9 $\mathrm{V}$ ... 11 $\mathrm{V}$)', 9, 10.23, 11, 'V', '']; + yield ['10.23 $\mathrm{V}$ (9 $\mathrm{V}$ ... 11 $\mathrm{V}$) [Test]', 9, 10.23, 11, 'V', 'Test']; } /** @@ -121,4 +130,22 @@ class PartParameterTest extends TestCase $param->setValueText($text); $this->assertSame($expected, $param->getFormattedValue()); } + + /** + * @dataProvider formattedValueWithLatexDataProvider + * + * @param float $min + * @param float $typical + * @param float $max + */ + public function testGetFormattedValueWithLatex(string $expected, ?float $min, ?float $typical, ?float $max, string $unit, string $text): void + { + $param = new PartParameter(); + $param->setUnit($unit); + $param->setValueMin($min); + $param->setValueTypical($typical); + $param->setValueMax($max); + $param->setValueText($text); + $this->assertSame($expected, $param->getFormattedValue(true)); + } } diff --git a/tests/Entity/Parts/InfoProviderReferenceTest.php b/tests/Entity/Parts/InfoProviderReferenceTest.php new file mode 100644 index 00000000..a1a8d5de --- /dev/null +++ b/tests/Entity/Parts/InfoProviderReferenceTest.php @@ -0,0 +1,70 @@ +. + */ +namespace App\Tests\Entity\Parts; + +use App\Entity\Parts\InfoProviderReference; +use App\Services\InfoProviderSystem\DTOs\PartDetailDTO; +use PHPUnit\Framework\TestCase; + +class InfoProviderReferenceTest extends TestCase +{ + public function testNoProvider(): void + { + $provider = InfoProviderReference::noProvider(); + + //The no provider instance should return false for the providerCreated method + $this->assertFalse($provider->isProviderCreated()); + //And null for all other methods + $this->assertNull($provider->getProviderKey()); + $this->assertNull($provider->getProviderId()); + $this->assertNull($provider->getProviderUrl()); + $this->assertNull($provider->getLastUpdated()); + } + + public function testProviderReference(): void + { + $provider = InfoProviderReference::providerReference('test', 'id', 'url'); + + //The provider reference instance should return true for the providerCreated method + $this->assertTrue($provider->isProviderCreated()); + //And the correct values for all other methods + $this->assertSame('test', $provider->getProviderKey()); + $this->assertSame('id', $provider->getProviderId()); + $this->assertSame('url', $provider->getProviderUrl()); + $this->assertNotNull($provider->getLastUpdated()); + } + + public function testFromPartDTO(): void + { + $dto = new PartDetailDTO(provider_key: 'test', provider_id: 'id', name: 'name', description: 'description', provider_url: 'url'); + $reference = InfoProviderReference::fromPartDTO($dto); + + //The provider reference instance should return true for the providerCreated method + $this->assertTrue($reference->isProviderCreated()); + //And the correct values for all other methods + $this->assertSame('test', $reference->getProviderKey()); + $this->assertSame('id', $reference->getProviderId()); + $this->assertSame('url', $reference->getProviderUrl()); + $this->assertNotNull($reference->getLastUpdated()); + } +} diff --git a/tests/Entity/Parts/PartAssociationTest.php b/tests/Entity/Parts/PartAssociationTest.php new file mode 100644 index 00000000..e002846e --- /dev/null +++ b/tests/Entity/Parts/PartAssociationTest.php @@ -0,0 +1,45 @@ +. + */ +namespace App\Tests\Entity\Parts; + +use App\Entity\Parts\AssociationType; +use App\Entity\Parts\PartAssociation; +use PHPUnit\Framework\TestCase; + +class PartAssociationTest extends TestCase +{ + + public function testGetTypeTranslationKey(): void + { + $assoc = new PartAssociation(); + $assoc->setType(AssociationType::COMPATIBLE); + $assoc->setOtherType('Custom Type'); + + //If the type is not OTHER the translation key should be the same as the type + $this->assertSame($assoc->getType()->getTranslationKey(), $assoc->getTypeTranslationKey()); + + //If the type is OTHER the translation key should be the other type + $assoc->setType(AssociationType::OTHER); + $this->assertEquals($assoc->getOtherType(), $assoc->getTypeTranslationKey()); + } +} diff --git a/tests/Entity/Parts/PartLotTest.php b/tests/Entity/Parts/PartLotTest.php index dee09aae..30687b72 100644 --- a/tests/Entity/Parts/PartLotTest.php +++ b/tests/Entity/Parts/PartLotTest.php @@ -33,12 +33,11 @@ class PartLotTest extends TestCase $lot = new PartLot(); $this->assertNull($lot->isExpired(), 'Lot must be return null when no Expiration date is set!'); - $datetime = new DateTime(); - $lot->setExpirationDate($datetime->setTimestamp(strtotime('now +1 hour'))); + $lot->setExpirationDate(new \DateTimeImmutable('+1 hour')); $this->assertFalse($lot->isExpired(), 'Lot with expiration date in the future must not be expired!'); - $lot->setExpirationDate($datetime->setTimestamp(strtotime('now -1 hour'))); + $lot->setExpirationDate(new \DateTimeImmutable('-1 hour')); $this->assertTrue($lot->isExpired(), 'Lot with expiration date in the past must be expired!'); } } diff --git a/tests/Entity/Parts/PartTest.php b/tests/Entity/Parts/PartTest.php index 11a9ae19..3e9233c1 100644 --- a/tests/Entity/Parts/PartTest.php +++ b/tests/Entity/Parts/PartTest.php @@ -53,17 +53,17 @@ class PartTest extends TestCase $part = new Part(); $measurement_unit = new MeasurementUnit(); - //Without an set measurement unit the part must return an int + //Without a set measurement unit the part must return an int $part->setMinAmount(1.345); - $this->assertSame(1.0, $part->getMinAmount()); + $this->assertEqualsWithDelta(1.0, $part->getMinAmount(), PHP_FLOAT_EPSILON); - //If an non int-based unit is assigned, an float is returned + //If a non-int-based unit is assigned, a float is returned $part->setPartUnit($measurement_unit); - $this->assertSame(1.345, $part->getMinAmount()); + $this->assertEqualsWithDelta(1.345, $part->getMinAmount(), PHP_FLOAT_EPSILON); //If an int-based unit is assigned an int is returned $measurement_unit->setIsInteger(true); - $this->assertSame(1.0, $part->getMinAmount()); + $this->assertEqualsWithDelta(1.0, $part->getMinAmount(), PHP_FLOAT_EPSILON); } public function testUseFloatAmount(): void @@ -87,7 +87,7 @@ class PartTest extends TestCase $measurement_unit = new MeasurementUnit(); $datetime = new DateTime(); - $this->assertSame(0.0, $part->getAmountSum()); + $this->assertEqualsWithDelta(0.0, $part->getAmountSum(), PHP_FLOAT_EPSILON); $part->addPartLot((new PartLot())->setAmount(3.141)); $part->addPartLot((new PartLot())->setAmount(10.0)); @@ -95,18 +95,18 @@ class PartTest extends TestCase $part->addPartLot( (new PartLot()) ->setAmount(6) - ->setExpirationDate($datetime->setTimestamp(strtotime('now -1 hour'))) + ->setExpirationDate(new \DateTimeImmutable('-1 hour')) ); - $this->assertSame(13.0, $part->getAmountSum()); + $this->assertEqualsWithDelta(13.0, $part->getAmountSum(), PHP_FLOAT_EPSILON); $part->setPartUnit($measurement_unit); - $this->assertSame(13.141, $part->getAmountSum()); + $this->assertEqualsWithDelta(13.141, $part->getAmountSum(), PHP_FLOAT_EPSILON); //1 billion part lot - $part->addPartLot((new PartLot())->setAmount(1000000000)); - $this->assertSame(1000000013.141, $part->getAmountSum()); + $part->addPartLot((new PartLot())->setAmount(1_000_000_000)); + $this->assertEqualsWithDelta(1_000_000_013.141, $part->getAmountSum(), PHP_FLOAT_EPSILON); $measurement_unit->setIsInteger(true); - $this->assertSame(1000000013.0, $part->getAmountSum()); + $this->assertEqualsWithDelta(1_000_000_013.0, $part->getAmountSum(), PHP_FLOAT_EPSILON); } } diff --git a/tests/Entity/PriceSystem/CurrencyTest.php b/tests/Entity/PriceSystem/CurrencyTest.php index 0274e5a0..0058d501 100644 --- a/tests/Entity/PriceSystem/CurrencyTest.php +++ b/tests/Entity/PriceSystem/CurrencyTest.php @@ -32,7 +32,7 @@ class CurrencyTest extends TestCase { $currency = new Currency(); - //By default the inverse exchange rate is not set: + //By default, the inverse exchange rate is not set: $this->assertNull($currency->getInverseExchangeRate()); $currency->setExchangeRate(BigDecimal::zero()); diff --git a/tests/Entity/PriceSystem/PricedetailTest.php b/tests/Entity/PriceSystem/PricedetailTest.php index 75009029..8a3cf328 100644 --- a/tests/Entity/PriceSystem/PricedetailTest.php +++ b/tests/Entity/PriceSystem/PricedetailTest.php @@ -60,18 +60,18 @@ class PricedetailTest extends TestCase $orderdetail2->method('getPart')->willReturn($part2); //By default a price detail returns 1 - $this->assertSame(1.0, $pricedetail->getPriceRelatedQuantity()); + $this->assertEqualsWithDelta(1.0, $pricedetail->getPriceRelatedQuantity(), PHP_FLOAT_EPSILON); $pricedetail->setOrderdetail($orderdetail); $pricedetail->setPriceRelatedQuantity(10.23); - $this->assertSame(10.0, $pricedetail->getPriceRelatedQuantity()); + $this->assertEqualsWithDelta(10.0, $pricedetail->getPriceRelatedQuantity(), PHP_FLOAT_EPSILON); //Price related quantity must not be zero! $pricedetail->setPriceRelatedQuantity(0.23); - $this->assertSame(1.0, $pricedetail->getPriceRelatedQuantity()); + $this->assertEqualsWithDelta(1.0, $pricedetail->getPriceRelatedQuantity(), PHP_FLOAT_EPSILON); - //With an part that has an float amount unit, also values like 0.23 can be returned + //With a part that has a float amount unit, also values like 0.23 can be returned $pricedetail->setOrderdetail($orderdetail2); - $this->assertSame(0.23, $pricedetail->getPriceRelatedQuantity()); + $this->assertEqualsWithDelta(0.23, $pricedetail->getPriceRelatedQuantity(), PHP_FLOAT_EPSILON); } public function testGetMinDiscountQuantity(): void @@ -88,17 +88,17 @@ class PricedetailTest extends TestCase $orderdetail2->method('getPart')->willReturn($part2); //By default a price detail returns 1 - $this->assertSame(1.0, $pricedetail->getMinDiscountQuantity()); + $this->assertEqualsWithDelta(1.0, $pricedetail->getMinDiscountQuantity(), PHP_FLOAT_EPSILON); $pricedetail->setOrderdetail($orderdetail); $pricedetail->setMinDiscountQuantity(10.23); - $this->assertSame(10.0, $pricedetail->getMinDiscountQuantity()); + $this->assertEqualsWithDelta(10.0, $pricedetail->getMinDiscountQuantity(), PHP_FLOAT_EPSILON); //Price related quantity must not be zero! $pricedetail->setMinDiscountQuantity(0.23); - $this->assertSame(1.0, $pricedetail->getMinDiscountQuantity()); + $this->assertEqualsWithDelta(1.0, $pricedetail->getMinDiscountQuantity(), PHP_FLOAT_EPSILON); - //With an part that has an float amount unit, also values like 0.23 can be returned + //With a part that has a float amount unit, also values like 0.23 can be returned $pricedetail->setOrderdetail($orderdetail2); - $this->assertSame(0.23, $pricedetail->getMinDiscountQuantity()); + $this->assertEqualsWithDelta(0.23, $pricedetail->getMinDiscountQuantity(), PHP_FLOAT_EPSILON); } } diff --git a/tests/Entity/UserSystem/ApiTokenTypeTest.php b/tests/Entity/UserSystem/ApiTokenTypeTest.php new file mode 100644 index 00000000..21d14b69 --- /dev/null +++ b/tests/Entity/UserSystem/ApiTokenTypeTest.php @@ -0,0 +1,52 @@ +. + */ +namespace App\Tests\Entity\UserSystem; + +use App\Entity\UserSystem\ApiTokenType; +use PHPUnit\Framework\TestCase; + +class ApiTokenTypeTest extends TestCase +{ + + public function testGetTokenPrefix(): void + { + $this->assertSame('tcp_', ApiTokenType::PERSONAL_ACCESS_TOKEN->getTokenPrefix()); + } + + public function testGetTypeFromToken(): void + { + $this->assertEquals(ApiTokenType::PERSONAL_ACCESS_TOKEN, ApiTokenType::getTypeFromToken('tcp_123')); + } + + public function testGetTypeFromTokenInvalid(): void + { + $this->expectException(\InvalidArgumentException::class); + ApiTokenType::getTypeFromToken('tcp123'); + } + + public function testGetTypeFromTokenNonExisting(): void + { + $this->expectException(\ValueError::class); + ApiTokenType::getTypeFromToken('abc_123'); + } +} diff --git a/tests/Entity/UserSystem/PermissionDataTest.php b/tests/Entity/UserSystem/PermissionDataTest.php index 5107c2a0..4fd8c5ce 100644 --- a/tests/Entity/UserSystem/PermissionDataTest.php +++ b/tests/Entity/UserSystem/PermissionDataTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\Entity\UserSystem; use App\Entity\UserSystem\PermissionData; @@ -26,7 +28,7 @@ use PHPUnit\Framework\TestCase; class PermissionDataTest extends TestCase { - public function testGetSetIs() + public function testGetSetIs(): void { $perm_data = new PermissionData(); @@ -60,7 +62,7 @@ class PermissionDataTest extends TestCase $this->assertFalse($perm_data->isPermissionSet('p1', 'op1')); } - public function testJSONSerialization() + public function testJSONSerialization(): void { $perm_data = new PermissionData(); @@ -99,7 +101,7 @@ class PermissionDataTest extends TestCase } - public function testFromJSON() + public function testFromJSON(): void { $json = json_encode([ 'perm1' => [ @@ -120,7 +122,7 @@ class PermissionDataTest extends TestCase $this->assertFalse($perm_data->getPermissionValue('perm2', 'op2')); } - public function testResetPermissions() + public function testResetPermissions(): void { $data = new PermissionData(); @@ -147,19 +149,19 @@ class PermissionDataTest extends TestCase $this->assertFalse($data->isPermissionSet('perm1', 'op3')); } - public function testGetSchemaVersion() + public function testGetSchemaVersion(): void { $data = new PermissionData(); //By default the schema version must be the CURRENT_SCHEMA_VERSION - $this->assertEquals(PermissionData::CURRENT_SCHEMA_VERSION, $data->getSchemaVersion()); + $this->assertSame(PermissionData::CURRENT_SCHEMA_VERSION, $data->getSchemaVersion()); //Ensure that the schema version can be set $data->setSchemaVersion(12345); - $this->assertEquals(12345, $data->getSchemaVersion()); + $this->assertSame(12345, $data->getSchemaVersion()); } - public function testIsAnyOperationOfPermissionSet() + public function testIsAnyOperationOfPermissionSet(): void { $data = new PermissionData(); @@ -170,7 +172,7 @@ class PermissionDataTest extends TestCase $this->assertTrue($data->isAnyOperationOfPermissionSet('perm1')); } - public function testGetAllDefinedOperationsOfPermission() + public function testGetAllDefinedOperationsOfPermission(): void { $data = new PermissionData(); @@ -185,7 +187,7 @@ class PermissionDataTest extends TestCase $data->getAllDefinedOperationsOfPermission('perm1')); } - public function testSetAllOperationsOfPermission() + public function testSetAllOperationsOfPermission(): void { $data = new PermissionData(); @@ -200,7 +202,7 @@ class PermissionDataTest extends TestCase $data->getAllDefinedOperationsOfPermission('perm1')); } - public function testRemovePermission() + public function testRemovePermission(): void { $data = new PermissionData(); diff --git a/tests/Entity/UserSystem/UserTest.php b/tests/Entity/UserSystem/UserTest.php index 89aab673..f11eec0f 100644 --- a/tests/Entity/UserSystem/UserTest.php +++ b/tests/Entity/UserSystem/UserTest.php @@ -22,13 +22,11 @@ declare(strict_types=1); namespace App\Tests\Entity\UserSystem; -use App\Entity\UserSystem\U2FKey; use App\Entity\UserSystem\User; use App\Entity\UserSystem\WebauthnKey; -use DateTime; use Doctrine\Common\Collections\Collection; use PHPUnit\Framework\TestCase; -use Ramsey\Uuid\Uuid; +use Symfony\Component\Uid\Uuid; use Webauthn\TrustPath\EmptyTrustPath; class UserTest extends TestCase @@ -47,13 +45,11 @@ class UserTest extends TestCase $this->assertSame('John (@username)', $user->getFullName(true)); } - public function googleAuthenticatorEnabledDataProvider(): array + public function googleAuthenticatorEnabledDataProvider(): \Iterator { - return [ - [null, false], - ['', false], - ['SSSk38498', true], - ]; + yield [null, false]; + yield ['', false]; + yield ['SSSk38498', true]; } /** @@ -77,7 +73,7 @@ class UserTest extends TestCase $codes = ['test', 'invalid', 'test']; $user->setBackupCodes($codes); // Backup Codes generation date must be changed! - $this->assertInstanceOf(\DateTime::class, $user->getBackupCodesGenerationDate()); + $this->assertNotNull($user->getBackupCodesGenerationDate()); $this->assertSame($codes, $user->getBackupCodes()); //Test what happens if we delete the backup keys @@ -108,12 +104,12 @@ class UserTest extends TestCase //Ensure the code is valid $this->assertTrue($user->isBackupCode('aaaa')); $this->assertTrue($user->isBackupCode('bbbb')); - //Invalidate code, afterwards the code has to be invalid! + //Invalidate code, afterward the code has to be invalid! $user->invalidateBackupCode('bbbb'); $this->assertFalse($user->isBackupCode('bbbb')); $this->assertTrue($user->isBackupCode('aaaa')); - //No exception must happen, when we try to invalidate an not existing backup key! + //No exception must happen, when we try to invalidate a not existing backup key! $user->invalidateBackupCode('zzzz'); } @@ -135,7 +131,7 @@ class UserTest extends TestCase [], "Test", new EmptyTrustPath(), - Uuid::fromDateTime(new \DateTime()), + Uuid::v4(), "", "", 0 @@ -148,4 +144,40 @@ class UserTest extends TestCase } $this->assertFalse($user->isWebAuthnAuthenticatorEnabled()); } + + public function testSetSAMLAttributes(): void + { + $data = [ + 'firstName' => ['John'], + 'lastName' => ['Doe'], + 'email' => ['j.doe@invalid.invalid'], + 'department' => ['Test Department'], + ]; + + $user = new User(); + $user->setSAMLAttributes($data); + + //Test if the data was set correctly + $this->assertSame('John', $user->getFirstName()); + $this->assertSame('Doe', $user->getLastName()); + $this->assertSame('j.doe@invalid.invalid', $user->getEmail()); + $this->assertSame('Test Department', $user->getDepartment()); + + //Test that it works for X500 attributes + $data = [ + 'urn:oid:2.5.4.42' => ['Jane'], + 'urn:oid:2.5.4.4' => ['Dane'], + 'urn:oid:1.2.840.113549.1.9.1' => ['mail@invalid.invalid'], + ]; + + $user->setSAMLAttributes($data); + + //Data must be changed + $this->assertSame('Jane', $user->getFirstName()); + $this->assertSame('Dane', $user->getLastName()); + $this->assertSame('mail@invalid.invalid', $user->getEmail()); + + //Department must not be changed + $this->assertSame('Test Department', $user->getDepartment()); + } } diff --git a/tests/EventSubscriber/PasswordChangeNeededSubscriberTest.php b/tests/EventSubscriber/PasswordChangeNeededSubscriberTest.php index 491b01ff..0eaf931c 100644 --- a/tests/EventSubscriber/PasswordChangeNeededSubscriberTest.php +++ b/tests/EventSubscriber/PasswordChangeNeededSubscriberTest.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace App\Tests\EventSubscriber; use App\Entity\UserSystem\Group; -use App\Entity\UserSystem\U2FKey; use App\Entity\UserSystem\User; use App\Entity\UserSystem\WebauthnKey; use App\EventSubscriber\UserSystem\PasswordChangeNeededSubscriber; use PHPUnit\Framework\TestCase; -use Ramsey\Uuid\Uuid; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\Uid\Uuid; use Webauthn\TrustPath\EmptyTrustPath; class PasswordChangeNeededSubscriberTest extends TestCase @@ -38,23 +40,35 @@ class PasswordChangeNeededSubscriberTest extends TestCase $user = new User(); $group = new Group(); + $request = new Request(); + $session = new Session(new MockArraySessionStorage()); + $request->setSession($session); + //A user without a group must not redirect $user->setGroup(null); - $this->assertFalse(PasswordChangeNeededSubscriber::TFARedirectNeeded($user)); + $this->assertFalse(PasswordChangeNeededSubscriber::TFARedirectNeeded($user, $request)); + + $session->clear(); //When the group does not enforce the redirect the user must not be redirected $user->setGroup($group); - $this->assertFalse(PasswordChangeNeededSubscriber::TFARedirectNeeded($user)); + $this->assertFalse(PasswordChangeNeededSubscriber::TFARedirectNeeded($user, $request)); - //The user must be redirected if the group enforces 2FA and it does not have a method + $session->clear(); + + //The user must be redirected if the group enforces 2FA, and it does not have a method $group->setEnforce2FA(true); - $this->assertTrue(PasswordChangeNeededSubscriber::TFARedirectNeeded($user)); + $this->assertTrue(PasswordChangeNeededSubscriber::TFARedirectNeeded($user, $request)); - //User must not be redirect if google authenticator is setup + $session->clear(); + + //User must not be redirect if google authenticator is set up $user->setGoogleAuthenticatorSecret('abcd'); - $this->assertFalse(PasswordChangeNeededSubscriber::TFARedirectNeeded($user)); + $this->assertFalse(PasswordChangeNeededSubscriber::TFARedirectNeeded($user, $request)); - //User must not be redirect if 2FA is setup + $session->clear(); + + //User must not be redirect if 2FA is set up $user->setGoogleAuthenticatorSecret(null); $user->addWebauthnKey(new WebauthnKey( "Test", @@ -62,11 +76,14 @@ class PasswordChangeNeededSubscriberTest extends TestCase [], "Test", new EmptyTrustPath(), - Uuid::fromDateTime(new \DateTime()), + Uuid::v4(), "", "", 0 )); - $this->assertFalse(PasswordChangeNeededSubscriber::TFARedirectNeeded($user)); + + $session->clear(); + + $this->assertFalse(PasswordChangeNeededSubscriber::TFARedirectNeeded($user, $request)); } } diff --git a/tests/Exceptions/TwigModeExceptionTest.php b/tests/Exceptions/TwigModeExceptionTest.php new file mode 100644 index 00000000..c5a8ef94 --- /dev/null +++ b/tests/Exceptions/TwigModeExceptionTest.php @@ -0,0 +1,49 @@ +. + */ + +namespace App\Tests\Exceptions; + +use App\Exceptions\TwigModeException; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Twig\Error\Error; + +class TwigModeExceptionTest extends KernelTestCase +{ + + private string $projectPath; + + public function setUp(): void + { + self::bootKernel(); + + $this->projectPath = self::getContainer()->getParameter('kernel.project_dir'); + } + + public function testGetSafeMessage(): void + { + $testException = new Error("Error at : " . $this->projectPath . "/src/dir/path/file.php"); + + $twigModeException = new TwigModeException($testException); + + $this->assertSame("Error at : " . $this->projectPath . "/src/dir/path/file.php", $testException->getMessage()); + $this->assertSame("Error at : [Part-DB Root Folder]/src/dir/path/file.php", $twigModeException->getSafeMessage()); + } +} diff --git a/tests/Helpers/BBCodeToMarkdownConverterTest.php b/tests/Helpers/BBCodeToMarkdownConverterTest.php index 35dea60d..093ff98f 100644 --- a/tests/Helpers/BBCodeToMarkdownConverterTest.php +++ b/tests/Helpers/BBCodeToMarkdownConverterTest.php @@ -27,24 +27,22 @@ use PHPUnit\Framework\TestCase; class BBCodeToMarkdownConverterTest extends TestCase { - protected $converter; + protected BBCodeToMarkdownConverter $converter; protected function setUp(): void { $this->converter = new BBCodeToMarkdownConverter(); } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - ['[b]Bold[/b]', '**Bold**'], - ['[i]Italic[/i]', '*Italic*'], - ['[s]Strike[/s]', 'Strike'], - ['[url]https://foo.bar[/url]', ''], - ['[url=https://foo.bar]test[/url]', '[test](https://foo.bar)'], - ['[center]Centered[/center]', '
Centered
'], - ['test no change', 'test no change'], - ]; + yield ['[b]Bold[/b]', '**Bold**']; + yield ['[i]Italic[/i]', '*Italic*']; + yield ['[s]Strike[/s]', 'Strike']; + yield ['[url]https://foo.bar[/url]', '']; + yield ['[url=https://foo.bar]test[/url]', '[test](https://foo.bar)']; + yield ['[center]Centered[/center]', '
Centered
']; + yield ['test no change', 'test no change']; } /** diff --git a/tests/Helpers/IPAnonymizerTest.php b/tests/Helpers/IPAnonymizerTest.php new file mode 100644 index 00000000..40030ea7 --- /dev/null +++ b/tests/Helpers/IPAnonymizerTest.php @@ -0,0 +1,44 @@ +. + */ + +namespace App\Tests\Helpers; + +use App\Helpers\IPAnonymizer; +use PHPUnit\Framework\TestCase; + +class IPAnonymizerTest extends TestCase +{ + + public function anonymizeDataProvider(): \Generator + { + yield ['127.0.0.0', '127.0.0.23']; + yield ['2001:db8:85a3::', '2001:0db8:85a3:0000:0000:8a2e:0370:7334']; + //RFC 4007 format + yield ['fe80::', 'fe80::1fc4:15d8:78db:2319%enp4s0']; + } + + /** + * @dataProvider anonymizeDataProvider + */ + public function testAnonymize(string $expected, string $input): void + { + $this->assertSame($expected, IPAnonymizer::anonymize($input)); + } +} diff --git a/tests/Helpers/Projects/ProjectBuildRequestTest.php b/tests/Helpers/Projects/ProjectBuildRequestTest.php index 0af0aa72..baccfcd1 100644 --- a/tests/Helpers/Projects/ProjectBuildRequestTest.php +++ b/tests/Helpers/Projects/ProjectBuildRequestTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\Helpers\Projects; use App\Entity\Parts\MeasurementUnit; @@ -35,24 +37,22 @@ class ProjectBuildRequestTest extends TestCase private MeasurementUnit $float_unit; /** @var Project */ - private $project1; + private Project $project1; /** @var ProjectBOMEntry */ - private $bom_entry1a; + private ProjectBOMEntry $bom_entry1a; /** @var ProjectBOMEntry */ - private $bom_entry1b; + private ProjectBOMEntry $bom_entry1b; /** @var ProjectBOMEntry */ - private $bom_entry1c; + private ProjectBOMEntry $bom_entry1c; - /** @var PartLot $lot1a */ - private $lot1a; - /** @var PartLot $lot1b */ - private $lot1b; - private $lot2; + private PartLot $lot1a; + private PartLot $lot1b; + private PartLot $lot2; /** @var Part */ - private $part1; + private Part $part1; /** @var Part */ - private $part2; + private Part $part2; public function setUp(): void @@ -115,39 +115,39 @@ class ProjectBuildRequestTest extends TestCase $this->project1->addBomEntry($this->bom_entry1c); } - public function testInitialization() + public function testInitialization(): void { //The values should be already prefilled correctly $request = new ProjectBuildRequest($this->project1, 10); //We need totally 20: Take 10 from the first (maximum 10) and 10 from the second (maximum 20) - $this->assertEquals(10, $request->getLotWithdrawAmount($this->lot1a)); - $this->assertEquals(10, $request->getLotWithdrawAmount($this->lot1b)); + $this->assertEqualsWithDelta(10.0, $request->getLotWithdrawAmount($this->lot1a), PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(10.0, $request->getLotWithdrawAmount($this->lot1b), PHP_FLOAT_EPSILON); //If the needed amount is higher than the maximum, we should get the maximum - $this->assertEquals(2.5, $request->getLotWithdrawAmount($this->lot2)); + $this->assertEqualsWithDelta(2.5, $request->getLotWithdrawAmount($this->lot2), PHP_FLOAT_EPSILON); } - public function testGetNumberOfBuilds() + public function testGetNumberOfBuilds(): void { $build_request = new ProjectBuildRequest($this->project1, 5); - $this->assertEquals(5, $build_request->getNumberOfBuilds()); + $this->assertSame(5, $build_request->getNumberOfBuilds()); } - public function testGetProject() + public function testGetProject(): void { $build_request = new ProjectBuildRequest($this->project1, 5); $this->assertEquals($this->project1, $build_request->getProject()); } - public function testGetNeededAmountForBOMEntry() + public function testGetNeededAmountForBOMEntry(): void { $build_request = new ProjectBuildRequest($this->project1, 5); - $this->assertEquals(10, $build_request->getNeededAmountForBOMEntry($this->bom_entry1a)); - $this->assertEquals(7.5, $build_request->getNeededAmountForBOMEntry($this->bom_entry1b)); - $this->assertEquals(20, $build_request->getNeededAmountForBOMEntry($this->bom_entry1c)); + $this->assertEqualsWithDelta(10.0, $build_request->getNeededAmountForBOMEntry($this->bom_entry1a), PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(7.5, $build_request->getNeededAmountForBOMEntry($this->bom_entry1b), PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(20.0, $build_request->getNeededAmountForBOMEntry($this->bom_entry1c), PHP_FLOAT_EPSILON); } - public function testGetSetLotWithdrawAmount() + public function testGetSetLotWithdrawAmount(): void { $build_request = new ProjectBuildRequest($this->project1, 5); @@ -156,11 +156,11 @@ class ProjectBuildRequestTest extends TestCase $build_request->setLotWithdrawAmount($this->lot1b->getID(), 3); //And it should be possible to get the amount via the lot object or via the ID - $this->assertEquals(2, $build_request->getLotWithdrawAmount($this->lot1a->getID())); - $this->assertEquals(3, $build_request->getLotWithdrawAmount($this->lot1b)); + $this->assertEqualsWithDelta(2.0, $build_request->getLotWithdrawAmount($this->lot1a->getID()), PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(3.0, $build_request->getLotWithdrawAmount($this->lot1b), PHP_FLOAT_EPSILON); } - public function testGetWithdrawAmountSum() + public function testGetWithdrawAmountSum(): void { //The sum of all withdraw amounts for an BOM entry (over all lots of the associated part) should be correct $build_request = new ProjectBuildRequest($this->project1, 5); @@ -168,9 +168,9 @@ class ProjectBuildRequestTest extends TestCase $build_request->setLotWithdrawAmount($this->lot1a, 2); $build_request->setLotWithdrawAmount($this->lot1b, 3); - $this->assertEquals(5, $build_request->getWithdrawAmountSum($this->bom_entry1a)); + $this->assertEqualsWithDelta(5.0, $build_request->getWithdrawAmountSum($this->bom_entry1a), PHP_FLOAT_EPSILON); $build_request->setLotWithdrawAmount($this->lot2, 1.5); - $this->assertEquals(1.5, $build_request->getWithdrawAmountSum($this->bom_entry1b)); + $this->assertEqualsWithDelta(1.5, $build_request->getWithdrawAmountSum($this->bom_entry1b), PHP_FLOAT_EPSILON); } diff --git a/tests/Helpers/TreeViewNodeTest.php b/tests/Helpers/TreeViewNodeTest.php index d7ee81f9..9005651d 100644 --- a/tests/Helpers/TreeViewNodeTest.php +++ b/tests/Helpers/TreeViewNodeTest.php @@ -30,11 +30,11 @@ class TreeViewNodeTest extends TestCase /** * @var TreeViewNode */ - protected $node1; + protected TreeViewNode $node1; /** * @var TreeViewNode */ - protected $node2; + protected TreeViewNode $node2; protected function setUp(): void { diff --git a/tests/Helpers/TrinaryLogicHelperTest.php b/tests/Helpers/TrinaryLogicHelperTest.php new file mode 100644 index 00000000..3082571b --- /dev/null +++ b/tests/Helpers/TrinaryLogicHelperTest.php @@ -0,0 +1,89 @@ +. + */ +namespace App\Tests\Helpers; + +use App\Helpers\TrinaryLogicHelper; +use PHPUnit\Framework\TestCase; + +class TrinaryLogicHelperTest extends TestCase +{ + + public function testNot() + { + $this->assertTrue(TrinaryLogicHelper::not(false)); + $this->assertFalse(TrinaryLogicHelper::not(true)); + $this->assertNull(TrinaryLogicHelper::not(null)); + } + + public function testOr(): void + { + $this->assertFalse(TrinaryLogicHelper::or(false, false)); + $this->assertNull(TrinaryLogicHelper::or(null, false)); + $this->assertTrue(TrinaryLogicHelper::or(false, true)); + + $this->assertNull(TrinaryLogicHelper::or(null, false)); + $this->assertNull(TrinaryLogicHelper::or(null, null)); + $this->assertTrue(TrinaryLogicHelper::or(false, true)); + + $this->assertTrue(TrinaryLogicHelper::or(true, false)); + $this->assertTrue(TrinaryLogicHelper::or(true, null)); + $this->assertTrue(TrinaryLogicHelper::or(true, true)); + + //Should work for longer arrays too + $this->assertTrue(TrinaryLogicHelper::or(true, true, false, null)); + $this->assertNull(TrinaryLogicHelper::or(false, false, false, false, null)); + $this->assertFalse(TrinaryLogicHelper::or(false, false, false)); + + //Test for one argument + $this->assertTrue(TrinaryLogicHelper::or(true)); + $this->assertFalse(TrinaryLogicHelper::or(false)); + $this->assertNull(TrinaryLogicHelper::or(null)); + + } + + public function testAnd(): void + { + $this->assertFalse(TrinaryLogicHelper::and(false, false)); + $this->assertFalse(TrinaryLogicHelper::and(false, null)); + $this->assertFalse(TrinaryLogicHelper::and(false, true)); + $this->assertFalse(TrinaryLogicHelper::and(null, false)); + $this->assertNull(TrinaryLogicHelper::and(null, null)); + $this->assertNull(TrinaryLogicHelper::and(null, true)); + $this->assertFalse(TrinaryLogicHelper::and(true, false)); + $this->assertNull(TrinaryLogicHelper::and(true, null)); + $this->assertTrue(TrinaryLogicHelper::and(true, true)); + + + //Should work for longer arrays too + $this->assertFalse(TrinaryLogicHelper::and(true, true, false, null)); + $this->assertFalse(TrinaryLogicHelper::and(false, false, false, false, null)); + $this->assertFalse(TrinaryLogicHelper::and(false, false, false)); + $this->assertNull(TrinaryLogicHelper::and(true, true, null)); + $this->assertTrue(TrinaryLogicHelper::and(true, true, true)); + + //Test for one argument + $this->assertTrue(TrinaryLogicHelper::and(true)); + $this->assertFalse(TrinaryLogicHelper::and(false)); + $this->assertNull(TrinaryLogicHelper::and(null)); + } +} diff --git a/tests/Repository/AttachmentContainingDBElementRepositoryTest.php b/tests/Repository/AttachmentContainingDBElementRepositoryTest.php new file mode 100644 index 00000000..f61750d9 --- /dev/null +++ b/tests/Repository/AttachmentContainingDBElementRepositoryTest.php @@ -0,0 +1,58 @@ +. + */ +namespace App\Tests\Repository; + +use App\Entity\Parts\Category; +use App\Repository\AttachmentContainingDBElementRepository; +use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; + +class AttachmentContainingDBElementRepositoryTest extends KernelTestCase +{ + + private EntityManagerInterface $entityManager; + + protected function setUp(): void + { + $kernel = self::bootKernel(); + + $this->entityManager = $kernel->getContainer() + ->get('doctrine')->getManager(); + } + + + public function testGetElementsAndPreviewAttachmentByIDs(): void + { + $repo = $this->entityManager->getRepository(Category::class); + + $elements = $repo->getElementsAndPreviewAttachmentByIDs([2, 1, 5, 3]); + + //Elements are ordered the same way as the ID array + $this->assertCount(4, $elements); + $this->assertSame(2, $elements[0]->getId()); + $this->assertSame(1, $elements[1]->getId()); + $this->assertSame(5, $elements[2]->getId()); + $this->assertSame(3, $elements[3]->getId()); + } +} diff --git a/tests/Repository/DBElementRepositoryTest.php b/tests/Repository/DBElementRepositoryTest.php new file mode 100644 index 00000000..05ede7e2 --- /dev/null +++ b/tests/Repository/DBElementRepositoryTest.php @@ -0,0 +1,94 @@ +. + */ +namespace App\Tests\Repository; + +use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\PartAttachment; +use App\Entity\Parts\Category; +use App\Entity\Parts\Part; +use App\Entity\UserSystem\Group; +use App\Entity\UserSystem\User; +use App\Repository\DBElementRepository; +use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; + +class DBElementRepositoryTest extends KernelTestCase +{ + + private EntityManagerInterface $entityManager; + + protected function setUp(): void + { + $kernel = self::bootKernel(); + + $this->entityManager = $kernel->getContainer() + ->get('doctrine')->getManager(); + } + + public function testFindByIDInMatchingOrder(): void + { + $repo = $this->entityManager->getRepository(Category::class); + + $elements = $repo->findByIDInMatchingOrder([2, 1, 5, 3]); + + //Elements are ordered the same way as the ID array + $this->assertCount(4, $elements); + $this->assertSame(2, $elements[0]->getId()); + $this->assertSame(1, $elements[1]->getId()); + $this->assertSame(5, $elements[2]->getId()); + $this->assertSame(3, $elements[3]->getId()); + } + + public function testChangeID(): void + { + $repo = $this->entityManager->getRepository(User::class); + + $noread_user = $repo->findOneBy(['name' => 'noread']); + + $this->assertNotNull($noread_user); + $old_id = $noread_user->getId(); + + $repo->changeID($noread_user, 999999); + $this->assertSame(999999, $noread_user->getId()); + $this->assertNotSame($old_id, $noread_user->getId()); + + + //ID should be persistent + $this->entityManager->refresh($noread_user); + $this->assertSame(999999, $noread_user->getId()); + } + + public function testGetElementsFromIDArray(): void + { + $repo = $this->entityManager->getRepository(Category::class); + + $elements = $repo->getElementsFromIDArray([2, 1, 3]); + + //Elements are ordered by ID or + $this->assertCount(3, $elements); + $this->assertSame(1, $elements[0]->getId()); + $this->assertSame(2, $elements[1]->getId()); + $this->assertSame(3, $elements[2]->getId()); + } +} diff --git a/tests/Repository/LogEntryRepositoryTest.php b/tests/Repository/LogEntryRepositoryTest.php new file mode 100644 index 00000000..c2329bc7 --- /dev/null +++ b/tests/Repository/LogEntryRepositoryTest.php @@ -0,0 +1,157 @@ +. + */ + +namespace App\Tests\Repository; + +use App\Entity\LogSystem\AbstractLogEntry; +use App\Entity\LogSystem\ElementEditedLogEntry; +use App\Entity\Parts\Category; +use App\Entity\Parts\Part; +use App\Entity\UserSystem\User; +use App\Repository\LogEntryRepository; +use App\Tests\Entity\LogSystem\AbstractLogEntryTest; +use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; + +class LogEntryRepositoryTest extends KernelTestCase +{ + + private EntityManagerInterface $entityManager; + private LogEntryRepository $repo; + + protected function setUp(): void + { + $kernel = self::bootKernel(); + + $this->entityManager = $kernel->getContainer() + ->get('doctrine')->getManager(); + + $this->repo = $this->entityManager->getRepository(AbstractLogEntry::class); + } + + public function testFindBy(): void + { + //The findBy method should be able the target criteria and split it into the needed criterias + + $part = $this->entityManager->find(Part::class, 3); + $elements = $this->repo->findBy(['target' => $part]); + + //It should only contain one log entry, where the part was created. + $this->assertCount(1, $elements); + } + + public function testGetTargetElement(): void + { + $part = $this->entityManager->find(Part::class, 3); + $logEntry = $this->repo->findBy(['target' => $part])[0]; + + $element = $this->repo->getTargetElement($logEntry); + //The target element, must be the part we searched for + $this->assertSame($part, $element); + } + + public function testGetLastEditingUser(): void + { + //We have a edit log entry for the category with ID 1 + $category = $this->entityManager->find(Category::class, 1); + $adminUser = $this->entityManager->getRepository(User::class)->findOneBy(['name' => 'admin']); + + $user = $this->repo->getLastEditingUser($category); + + //The last editing user should be the admin user + $this->assertSame($adminUser, $user); + + //For the category 2, the user must be null + $category = $this->entityManager->find(Category::class, 2); + $user = $this->repo->getLastEditingUser($category); + $this->assertNull($user); + } + + public function testGetCreatingUser(): void + { + //We have a edit log entry for the category with ID 1 + $category = $this->entityManager->find(Category::class, 1); + $adminUser = $this->entityManager->getRepository(User::class)->findOneBy(['name' => 'admin']); + + $user = $this->repo->getCreatingUser($category); + + //The last editing user should be the admin user + $this->assertSame($adminUser, $user); + + //For the category 2, the user must be null + $category = $this->entityManager->find(Category::class, 2); + $user = $this->repo->getCreatingUser($category); + $this->assertNull($user); + } + + public function testGetLogsOrderedByTimestamp(): void + { + $logs = $this->repo->getLogsOrderedByTimestamp('DESC', 2, 0); + + //We have 2 log entries + $this->assertCount(2, $logs); + + //The first one must be newer than the second one + $this->assertGreaterThanOrEqual($logs[0]->getTimestamp(), $logs[1]->getTimestamp()); + } + + public function testGetElementExistedAtTimestamp(): void + { + $part = $this->entityManager->find(Part::class, 3); + + //Assume that the part is existing now + $this->assertTrue($this->repo->getElementExistedAtTimestamp($part, new \DateTimeImmutable())); + + //Assume that the part was not existing long time ago + $this->assertFalse($this->repo->getElementExistedAtTimestamp($part, new \DateTimeImmutable('2000-01-01'))); + } + + public function testGetElementHistory(): void + { + $category = $this->entityManager->find(Category::class, 1); + + $history = $this->repo->getElementHistory($category); + + //We have 4 log entries for the category + $this->assertCount(4, $history); + } + + + public function testGetTimetravelDataForElement(): void + { + $category = $this->entityManager->find(Category::class, 1); + $data = $this->repo->getTimetravelDataForElement($category, new \DateTimeImmutable('2020-01-01')); + + //The data must contain only ElementChangedLogEntry + $this->assertCount(2, $data); + $this->assertInstanceOf(ElementEditedLogEntry::class, $data[0]); + $this->assertInstanceOf(ElementEditedLogEntry::class, $data[1]); + } + + + public function testGetUndeleteDataForElement(): void + { + $undeleteData = $this->repo->getUndeleteDataForElement(Category::class, 100); + + //This must be the delete log entry we created in the fixtures + $this->assertSame('Node 100', $undeleteData->getOldName()); + } +} diff --git a/tests/Repository/StructuralDBElementRepositoryTest.php b/tests/Repository/StructuralDBElementRepositoryTest.php index b5e60605..47c0cb45 100644 --- a/tests/Repository/StructuralDBElementRepositoryTest.php +++ b/tests/Repository/StructuralDBElementRepositoryTest.php @@ -92,7 +92,7 @@ class StructuralDBElementRepositoryTest extends WebTestCase public function testToNodesListRoot(): void { //List all root nodes and their children - $nodes = $this->repo->toNodesList(); + $nodes = $this->repo->getFlatList(); $this->assertCount(7, $nodes); $this->assertContainsOnlyInstancesOf(AttachmentType::class, $nodes); @@ -109,7 +109,7 @@ class StructuralDBElementRepositoryTest extends WebTestCase { //List all nodes that are children to Node 1 $node1 = $this->repo->find(1); - $nodes = $this->repo->toNodesList($node1); + $nodes = $this->repo->getFlatList($node1); $this->assertCount(3, $nodes); $this->assertContainsOnlyInstancesOf(AttachmentType::class, $nodes); diff --git a/tests/Repository/UserRepositoryTest.php b/tests/Repository/UserRepositoryTest.php new file mode 100644 index 00000000..0e6f3c2d --- /dev/null +++ b/tests/Repository/UserRepositoryTest.php @@ -0,0 +1,88 @@ +. + */ +namespace App\Tests\Repository; + +use App\Entity\UserSystem\User; +use App\Repository\UserRepository; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class UserRepositoryTest extends WebTestCase +{ + + private $entityManager; + /** + * @var UserRepository + */ + private $repo; + + protected function setUp(): void + { + $kernel = self::bootKernel(); + + $this->entityManager = $kernel->getContainer() + ->get('doctrine') + ->getManager(); + + $this->repo = $this->entityManager->getRepository(User::class); + } + + + public function testGetAnonymousUser() + { + $user = $this->repo->getAnonymousUser(); + + $this->assertInstanceOf(User::class, $user); + $this->assertSame(User::ID_ANONYMOUS, $user->getId()); + $this->assertSame('anonymous', $user->getUsername()); + } + + public function testFindByEmailOrName() + { + //Test for email + $u = $this->repo->findByEmailOrName('user@invalid.invalid'); + $this->assertInstanceOf(User::class, $u); + $this->assertSame('user', $u->getUsername()); + + //Test for name + $u = $this->repo->findByEmailOrName('user'); + $this->assertInstanceOf(User::class, $u); + $this->assertSame('user', $u->getUsername()); + + //Check what happens for unknown user + $u = $this->repo->findByEmailOrName('unknown'); + $this->assertNull($u); + + } + + public function testFindByUsername() + { + $u = $this->repo->findByUsername('user'); + $this->assertInstanceOf(User::class, $u); + $this->assertSame('user', $u->getUsername()); + + //Check what happens for unknown user + $u = $this->repo->findByEmailOrName('unknown'); + $this->assertNull($u); + } +} diff --git a/tests/Security/EnsureSAMLUserForSAMLLoginCheckerTest.php b/tests/Security/EnsureSAMLUserForSAMLLoginCheckerTest.php new file mode 100644 index 00000000..c9a14426 --- /dev/null +++ b/tests/Security/EnsureSAMLUserForSAMLLoginCheckerTest.php @@ -0,0 +1,71 @@ +. + */ +namespace App\Tests\Security; + +use App\Entity\UserSystem\User; +use App\Security\EnsureSAMLUserForSAMLLoginChecker; +use Nbgrp\OneloginSamlBundle\Security\Http\Authenticator\Token\SamlToken; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; +use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; + +class EnsureSAMLUserForSAMLLoginCheckerTest extends WebTestCase +{ + /** @var EnsureSAMLUserForSAMLLoginChecker */ + protected $service; + + protected function setUp(): void + { + self::bootKernel(); + $this->service = self::getContainer()->get(EnsureSAMLUserForSAMLLoginChecker::class); + } + + public function testOnAuthenticationSuccessFailsOnSSOLoginWithLocalUser(): void + { + $local_user = new User(); + + $saml_token = $this->createMock(SamlToken::class); + $saml_token->method('getUser')->willReturn($local_user); + + $event = new AuthenticationSuccessEvent($saml_token); + + $this->expectException(CustomUserMessageAccountStatusException::class); + + $this->service->onAuthenticationSuccess($event); + } + + public function testOnAuthenticationSuccessFailsOnLocalLoginWithSAMLUser(): void + { + $saml_user = (new User())->setSamlUser(true); + + $saml_token = $this->createMock(UsernamePasswordToken::class); + $saml_token->method('getUser')->willReturn($saml_user); + + $event = new AuthenticationSuccessEvent($saml_token); + + $this->expectException(CustomUserMessageAccountStatusException::class); + + $this->service->onAuthenticationSuccess($event); + } +} diff --git a/tests/Security/SamlUserFactoryTest.php b/tests/Security/SamlUserFactoryTest.php new file mode 100644 index 00000000..7780b4be --- /dev/null +++ b/tests/Security/SamlUserFactoryTest.php @@ -0,0 +1,91 @@ +. + */ +namespace App\Tests\Security; + +use App\Entity\UserSystem\User; +use App\Security\SamlUserFactory; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class SamlUserFactoryTest extends WebTestCase +{ + + /** @var SamlUserFactory */ + protected $service; + + protected function setUp(): void + { + self::bootKernel(); + $this->service = self::getContainer()->get(SamlUserFactory::class); + } + + public function testCreateUser(): void + { + $user = $this->service->createUser('sso_user', [ + 'email' => ['j.doe@invalid.invalid'], + 'urn:oid:2.5.4.42' => ['John'], + 'urn:oid:2.5.4.4' => ['Doe'], + 'department' => ['IT'] + ]); + + $this->assertInstanceOf(User::class, $user); + + $this->assertSame('sso_user', $user->getUserIdentifier()); + //User must not change his password + $this->assertFalse($user->isNeedPwChange()); + //And must not be disabled + $this->assertFalse($user->isDisabled()); + //Password should not be set + $this->assertSame('!!SAML!!', $user->getPassword()); + + //Info should be set + $this->assertSame('John', $user->getFirstName()); + $this->assertSame('Doe', $user->getLastName()); + $this->assertSame('IT', $user->getDepartment()); + $this->assertSame('j.doe@invalid.invalid', $user->getEmail()); + } + + public function testMapSAMLRolesToLocalGroupID(): void + { + $mapping = [ + 'admin' => 2, //This comes first, as this should have higher priority + 'employee' => 1, + 'manager' => 3, + 'administrator' => 2, + '*' => 4, + ]; + + //Test if mapping works + $this->assertSame(1, $this->service->mapSAMLRolesToLocalGroupID(['employee'], $mapping)); + //Only the first valid mapping should be used + $this->assertSame(2, $this->service->mapSAMLRolesToLocalGroupID(['employee', 'admin'], $mapping)); + $this->assertSame(2, $this->service->mapSAMLRolesToLocalGroupID(['does_not_matter', 'admin', 'employee'], $mapping)); + $this->assertSame(1, $this->service->mapSAMLRolesToLocalGroupID(['employee', 'does_not_matter', 'manager'], $mapping)); + $this->assertSame(3, $this->service->mapSAMLRolesToLocalGroupID(['administrator', 'does_not_matter', 'manager'], $mapping)); + //Test if mapping is case-sensitive + $this->assertSame(4, $this->service->mapSAMLRolesToLocalGroupID(['ADMIN'], $mapping)); + + //Test that wildcard mapping works + $this->assertSame(4, $this->service->mapSAMLRolesToLocalGroupID(['entry1', 'entry2'], $mapping)); + $this->assertSame(4, $this->service->mapSAMLRolesToLocalGroupID([], $mapping)); + } +} diff --git a/tests/Security/UserCheckerTest.php b/tests/Security/UserCheckerTest.php index 97d7e412..35c2e1e5 100644 --- a/tests/Security/UserCheckerTest.php +++ b/tests/Security/UserCheckerTest.php @@ -24,10 +24,8 @@ namespace App\Tests\Security; use App\Entity\UserSystem\User; use App\Security\UserChecker; -use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; -use Symfony\Component\Security\Core\Exception\DisabledException; class UserCheckerTest extends WebTestCase { @@ -44,10 +42,10 @@ class UserCheckerTest extends WebTestCase $user = new User(); $user->setDisabled(false); - //An user that is not disabled should not throw an exception + //A user that is not disabled should not throw an exception $this->service->checkPostAuth($user); - //An disabled user must throw an exception + //A disabled user must throw an exception $user->setDisabled(true); $this->expectException(CustomUserMessageAccountStatusException::class); $this->service->checkPostAuth($user); diff --git a/tests/Serializer/BigNumberNormalizerTest.php b/tests/Serializer/BigNumberNormalizerTest.php new file mode 100644 index 00000000..f64347ee --- /dev/null +++ b/tests/Serializer/BigNumberNormalizerTest.php @@ -0,0 +1,87 @@ +. + */ +namespace App\Tests\Serializer; + +use App\Serializer\BigNumberNormalizer; +use Brick\Math\BigInteger; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Brick\Math\BigDecimal; +use Brick\Math\BigNumber; + +class BigNumberNormalizerTest extends WebTestCase +{ + /** @var BigNumberNormalizer */ + protected $service; + + protected function setUp(): void + { + //Get an service instance. + self::bootKernel(); + $this->service = self::getContainer()->get(BigNumberNormalizer::class); + } + + public function testNormalize(): void + { + $bigDecimal = BigDecimal::of('1.23456789'); + $this->assertSame('1.23456789', $this->service->normalize($bigDecimal)); + } + + public function testSupportsNormalization(): void + { + //Normalizer must only support BigNumber objects (and child classes) + $this->assertFalse($this->service->supportsNormalization(new \stdClass())); + + $bigNumber = BigNumber::of(1); + $this->assertTrue($this->service->supportsNormalization($bigNumber)); + + $bigDecimal = BigDecimal::of(1); + $this->assertTrue($this->service->supportsNormalization($bigDecimal)); + } + + public function testSupportsDenormalization(): void + { + //Denormalizer must only support BigNumber objects (and child classes) + $this->assertFalse($this->service->supportsDenormalization("1.23", \stdClass::class)); + + //Denormalizer must only support number like input data + $this->assertFalse($this->service->supportsDenormalization(new \stdClass(), BigDecimal::class)); + + //Using the right class and data type + $this->assertTrue($this->service->supportsDenormalization("1.23", BigDecimal::class)); + $this->assertTrue($this->service->supportsDenormalization("123", BigInteger::class)); + $this->assertTrue($this->service->supportsDenormalization(123, BigInteger::class)); + $this->assertTrue($this->service->supportsDenormalization(12.3, BigDecimal::class)); + } + + public function testDenormalize(): void + { + $bigDecimal = $this->service->denormalize("1.23456789", BigDecimal::class); + $this->assertInstanceOf(BigDecimal::class, $bigDecimal); + $this->assertSame('1.23456789', (string) $bigDecimal); + + $bigInteger = $this->service->denormalize(1234, BigInteger::class); + $this->assertInstanceOf(BigInteger::class, $bigInteger); + $this->assertSame('1234', (string) $bigInteger); + } +} diff --git a/tests/Serializer/PartNormalizerTest.php b/tests/Serializer/PartNormalizerTest.php new file mode 100644 index 00000000..9baff750 --- /dev/null +++ b/tests/Serializer/PartNormalizerTest.php @@ -0,0 +1,139 @@ +. + */ +namespace App\Tests\Serializer; + +use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; +use App\Entity\PriceInformations\Orderdetail; +use App\Entity\PriceInformations\Pricedetail; +use App\Serializer\PartNormalizer; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +class PartNormalizerTest extends WebTestCase +{ + /** @var PartNormalizer */ + protected DenormalizerInterface&NormalizerInterface $service; + + protected function setUp(): void + { + //Get a service instance. + self::bootKernel(); + $this->service = self::getContainer()->get(PartNormalizer::class); + + //We need to inject the serializer into the normalizer, as we use it directly + $serializer = self::getContainer()->get('serializer'); + $this->service->setNormalizer($serializer); + $this->service->setDenormalizer($serializer); + } + + public function testSupportsNormalization(): void + { + //Normalizer must only support Part objects (and child classes) + $this->assertFalse($this->service->supportsNormalization(new \stdClass())); + //Part serialization should only work with csv + $this->assertFalse($this->service->supportsNormalization(new Part())); + $this->assertTrue($this->service->supportsNormalization(new Part(), 'csv')); + } + + public function testNormalize(): void + { + $part = new Part(); + $part->setName('Test Part'); + $partLot1 = new PartLot(); + $partLot1->setAmount(1); + $partLot2 = new PartLot(); + $partLot2->setAmount(5); + $part->addPartLot($partLot1); + $part->addPartLot($partLot2); + + //Check that type field is not present in CSV export + $data = $this->service->normalize($part, 'csv', ['groups' => ['simple']]); + $this->assertSame('Test Part', $data['name']); + $this->assertArrayNotHasKey('type', $data); + } + + public function testSupportsDenormalization(): void + { + //Normalizer must only support Part type with array as input + $this->assertFalse($this->service->supportsDenormalization(new \stdClass(), Part::class)); + $this->assertFalse($this->service->supportsDenormalization('string', Part::class)); + $this->assertFalse($this->service->supportsDenormalization(['a' => 'b'], \stdClass::class)); + + //Only support denormalization, if CSV import + $this->assertFalse($this->service->supportsDenormalization(['a' => 'b'], Part::class)); + $this->assertTrue($this->service->supportsDenormalization(['a' => 'b'], Part::class, null, ['partdb_import' => true])); + } + + public function testDenormalize(): void + { + $input = [ + 'name' => 'Test Part', + 'description' => 'Test Description', + 'notes' => 'Test Note', //Test key normalization + 'ipn' => 'Test IPN', + 'mpn' => 'Test MPN', + 'instock' => '5', + 'storage_location' => 'Test Storage Location', + 'supplier' => 'Test Supplier', + 'price' => '5.5', + 'supplier_part_number' => 'TEST123' + ]; + + $part = $this->service->denormalize($input, Part::class, 'json', ['groups' => ['import'], 'create_unknown_datastructures' => true]); + $this->assertInstanceOf(Part::class, $part); + $this->assertSame('Test Part', $part->getName()); + $this->assertSame('Test Description', $part->getDescription()); + $this->assertSame('Test Note', $part->getComment()); + $this->assertSame('Test IPN', $part->getIpn()); + $this->assertSame('Test MPN', $part->getManufacturerProductNumber()); + + //Check that a new PartLot was created + $this->assertCount(1, $part->getPartLots()); + /** @var PartLot $partLot */ + $partLot = $part->getPartLots()->first(); + $this->assertEqualsWithDelta(5.0, $partLot->getAmount(), PHP_FLOAT_EPSILON); + $this->assertNotNull($partLot->getStorageLocation()); + $this->assertSame('Test Storage Location', $partLot->getStorageLocation()->getName()); + + //Check that a new orderdetail was created + $this->assertCount(1, $part->getOrderdetails()); + /** @var Orderdetail $orderDetail */ + $orderDetail = $part->getOrderdetails()->first(); + $this->assertNotNull($orderDetail->getSupplier()); + $this->assertSame('Test Supplier', $orderDetail->getSupplier()->getName()); + $this->assertSame('TEST123', $orderDetail->getSupplierPartNr()); + + //Check that a pricedetail was created + $this->assertCount(1, $orderDetail->getPricedetails()); + /** @var Pricedetail $priceDetail */ + $priceDetail = $orderDetail->getPricedetails()->first(); + $this->assertSame("5.50000", (string) $priceDetail->getPrice()); + //Must be in base currency + $this->assertNull($priceDetail->getCurrency()); + //Must be for 1 part and 1 minimum order quantity + $this->assertEqualsWithDelta(1.0, $priceDetail->getPriceRelatedQuantity(), PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(1.0, $priceDetail->getMinDiscountQuantity(), PHP_FLOAT_EPSILON); + } +} diff --git a/tests/Serializer/StructuralElementDenormalizerTest.php b/tests/Serializer/StructuralElementDenormalizerTest.php new file mode 100644 index 00000000..2543c4f3 --- /dev/null +++ b/tests/Serializer/StructuralElementDenormalizerTest.php @@ -0,0 +1,89 @@ +. + */ +namespace App\Tests\Serializer; + +use App\Entity\Parts\Category; +use App\Serializer\StructuralElementDenormalizer; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class StructuralElementDenormalizerTest extends WebTestCase +{ + + /** @var StructuralElementDenormalizer */ + protected $service; + + protected function setUp(): void + { + //Get a service instance. + self::bootKernel(); + $this->service = self::getContainer()->get(StructuralElementDenormalizer::class); + + //We need to inject the serializer into the normalizer, as we use it directly + $serializer = self::getContainer()->get('serializer'); + $this->service->setDenormalizer($serializer); + } + + public function testSupportsDenormalization(): void + { + $this->assertFalse($this->service->supportsDenormalization('doesnt_matter', Category::class, 'json', ['groups' => ['import']])); + $this->assertFalse($this->service->supportsDenormalization(['name' => 'Test'], Category::class, 'json', ['groups' => ['simple']])); + + //Denormalizer should only be active, when we use the import function and partdb_import is set + $this->assertFalse($this->service->supportsDenormalization(['name' => 'Test'], Category::class, 'json', ['groups' => ['import']])); + $this->assertTrue($this->service->supportsDenormalization(['name' => 'Test'], Category::class, 'json', ['groups' => ['import'], 'partdb_import' => true])); + } + + /** + * @group DB + */ + public function testDenormalize(): void + { + //Check that we retrieve DB elements via the name + $data = ['name' => 'Node 1']; + $result = $this->service->denormalize($data, Category::class, 'json', ['groups' => ['import']]); + $this->assertInstanceOf(Category::class, $result); + $this->assertSame('Node 1', $result->getName()); + $this->assertNotNull($result->getID()); //ID should be set, because we retrieved the element from the DB + + //Check that we can retrieve nested DB elements + $data = ['name' => 'Node 1.1', 'parent' => ['name' => 'Node 1']]; + $result = $this->service->denormalize($data, Category::class, 'json', ['groups' => ['import']]); + $this->assertInstanceOf(Category::class, $result); + $this->assertSame('Node 1.1', $result->getName()); + $this->assertSame('Node 1', $result->getParent()->getName()); + $this->assertNotNull($result->getID()); //ID should be set, because we retrieved the element from the DB + $this->assertNotNull($result->getParent()->getID()); //ID should be set, because we retrieved the element from the DB + + //Check that we can create new elements + $data = ['name' => 'New Node 1.1', 'parent' => ['name' => 'New Node 1']]; + $result = $this->service->denormalize($data, Category::class, 'json', ['groups' => ['import']]); + $this->assertInstanceOf(Category::class, $result); + $this->assertSame('New Node 1.1', $result->getName()); + $this->assertSame('New Node 1', $result->getParent()->getName()); + $this->assertNull($result->getID()); //ID should be null, because we created a new element + + //Check that when we retrieve this element again, we get the same instance + $result2 = $this->service->denormalize($data, Category::class, 'json', ['groups' => ['import']]); + $this->assertSame($result, $result2); + } +} diff --git a/tests/Serializer/StructuralElementFromNameDenormalizerTest.php b/tests/Serializer/StructuralElementFromNameDenormalizerTest.php new file mode 100644 index 00000000..b344508c --- /dev/null +++ b/tests/Serializer/StructuralElementFromNameDenormalizerTest.php @@ -0,0 +1,124 @@ +. + */ +namespace App\Tests\Serializer; + +use App\Entity\Parts\Category; +use App\Serializer\StructuralElementFromNameDenormalizer; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class StructuralElementFromNameDenormalizerTest extends WebTestCase +{ + + /** @var StructuralElementFromNameDenormalizer */ + protected $service; + + protected function setUp(): void + { + //Get a service instance. + self::bootKernel(); + $this->service = self::getContainer()->get(StructuralElementFromNameDenormalizer::class); + } + + public function testSupportsDenormalization(): void + { + //Only the combination of string data and StructuralElement class as type is supported. + $this->assertFalse($this->service->supportsDenormalization('doesnt_matter', \stdClass::class)); + $this->assertFalse($this->service->supportsDenormalization(['a' => 'b'], Category::class)); + + //The denormalizer should only be active, when we are doing a file import operation + $this->assertFalse($this->service->supportsDenormalization('doesnt_matter', Category::class)); + $this->assertTrue($this->service->supportsDenormalization('doesnt_matter', Category::class, 'json', ['partdb_import' => true])); + } + + public function testDenormalizeCreateNew(): void + { + $context = [ + 'groups' => ['simple'], + 'path_delimiter' => '->', + 'create_unknown_datastructures' => true, + ]; + + //Test for simple category + $category = $this->service->denormalize('New Category', Category::class, null, $context); + $this->assertInstanceOf(Category::class, $category); + $this->assertSame('New Category', $category->getName()); + + //Test for nested category + $category = $this->service->denormalize('New Category->Sub Category', Category::class, null, $context); + $this->assertInstanceOf(Category::class, $category); + $this->assertSame('Sub Category', $category->getName()); + $this->assertInstanceOf(Category::class, $category->getParent()); + $this->assertSame('New Category', $category->getParent()->getName()); + + //Test with existing category + $category = $this->service->denormalize('Node 1->Node 1.1', Category::class, null, $context); + $this->assertInstanceOf(Category::class, $category); + $this->assertSame('Node 1.1', $category->getName()); + $this->assertInstanceOf(Category::class, $category->getParent()); + $this->assertSame('Node 1', $category->getParent()->getName()); + //Both categories should be in DB (have an ID) + $this->assertNotNull($category->getID()); + $this->assertNotNull($category->getParent()->getID()); + + //Test with other path_delimiter + $context['path_delimiter'] = '/'; + $category = $this->service->denormalize('New Category/Sub Category', Category::class, null, $context); + $this->assertInstanceOf(Category::class, $category); + $this->assertSame('Sub Category', $category->getName()); + $this->assertInstanceOf(Category::class, $category->getParent()); + $this->assertSame('New Category', $category->getParent()->getName()); + + //Test with empty path + $category = $this->service->denormalize('', Category::class, null, $context); + $this->assertNull($category); + } + + public function testDenormalizeOnlyExisting(): void + { + $context = [ + 'groups' => ['simple'], + 'path_delimiter' => '->', + 'create_unknown_datastructures' => false, + ]; + + //Test with existing category + $category = $this->service->denormalize('Node 1->Node 1.1', Category::class, null, $context); + $this->assertInstanceOf(Category::class, $category); + $this->assertSame('Node 1.1', $category->getName()); + $this->assertInstanceOf(Category::class, $category->getParent()); + $this->assertSame('Node 1', $category->getParent()->getName()); + //Both categories should be in DB (have an ID) + $this->assertNotNull($category->getID()); + $this->assertNotNull($category->getParent()->getID()); + + //Test with non-existing category + $category = $this->service->denormalize('New category', Category::class, null, $context); + $this->assertNull($category); + + //Test with empty path + $category = $this->service->denormalize('', Category::class, null, $context); + $this->assertNull($category); + } + + +} diff --git a/tests/Serializer/StructuralElementNormalizerTest.php b/tests/Serializer/StructuralElementNormalizerTest.php new file mode 100644 index 00000000..b151f249 --- /dev/null +++ b/tests/Serializer/StructuralElementNormalizerTest.php @@ -0,0 +1,81 @@ +. + */ +namespace App\Tests\Serializer; + +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; +use App\Entity\Parts\Part; +use App\Serializer\BigNumberNormalizer; +use App\Serializer\StructuralElementNormalizer; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class StructuralElementNormalizerTest extends WebTestCase +{ + + /** @var StructuralElementNormalizer */ + protected $service; + + protected function setUp(): void + { + //Get an service instance. + self::bootKernel(); + $this->service = self::getContainer()->get(StructuralElementNormalizer::class); + } + + public function testNormalize(): void + { + $category1 = (new Category())->setName('Category 1'); + $category11 = (new Category())->setName('Category 1.1'); + $category11->setParent($category1); + + //Serialize category 1 + $data1 = $this->service->normalize($category1, 'json', ['groups' => ['simple']]); + $this->assertArrayHasKey('full_name', $data1); + $this->assertSame('Category 1', $data1['full_name']); + //Json export must contain type attribute + $this->assertArrayHasKey('type', $data1); + + //Serialize category 1.1 + $data11 = $this->service->normalize($category11, 'json', ['groups' => ['simple']]); + $this->assertArrayHasKey('full_name', $data11); + $this->assertSame('Category 1->Category 1.1', $data11['full_name']); + + //Test that type attribute is removed for CSV export + $data11 = $this->service->normalize($category11, 'csv', ['groups' => ['simple']]); + $this->assertArrayNotHasKey('type', $data11); + } + + public function testSupportsNormalization(): void + { + //Normalizer must only support StructuralElement objects (and child classes) + $this->assertFalse($this->service->supportsNormalization(new \stdClass())); + $this->assertFalse($this->service->supportsNormalization(new Part())); + + //Must only be active when export is enabled + $this->assertFalse($this->service->supportsNormalization(new Category())); + $this->assertTrue($this->service->supportsNormalization(new Category(), null, ['partdb_export' => true])); + $this->assertTrue($this->service->supportsNormalization(new Footprint(), null, ['partdb_export' => true])); + + } +} diff --git a/tests/Services/Attachments/AttachmentPathResolverTest.php b/tests/Services/Attachments/AttachmentPathResolverTest.php index 9edde560..3c432f48 100644 --- a/tests/Services/Attachments/AttachmentPathResolverTest.php +++ b/tests/Services/Attachments/AttachmentPathResolverTest.php @@ -29,8 +29,8 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class AttachmentPathResolverTest extends WebTestCase { - protected $media_path; - protected $footprint_path; + protected string $media_path; + protected string $footprint_path; protected $projectDir_orig; protected $projectDir; /** @@ -40,9 +40,7 @@ class AttachmentPathResolverTest extends WebTestCase public function setUp(): void { - parent::setUp(); - - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->projectDir_orig = realpath(self::$kernel->getProjectDir()); @@ -71,7 +69,7 @@ class AttachmentPathResolverTest extends WebTestCase $this->assertNull($this->service->parameterToAbsolutePath('/./this/one/too')); } - public function placeholderDataProvider(): array + public function placeholderDataProvider(): \Iterator { //We need to do initialization (again), as dataprovider is called before setUp() self::bootKernel(); @@ -79,28 +77,28 @@ class AttachmentPathResolverTest extends WebTestCase $this->projectDir = str_replace('\\', '/', $this->projectDir_orig); $this->media_path = $this->projectDir.'/public/media'; $this->footprint_path = $this->projectDir.'/public/img/footprints'; - - return [ - ['%FOOTPRINTS%/test/test.jpg', $this->footprint_path.'/test/test.jpg'], - ['%FOOTPRINTS%/test/', $this->footprint_path.'/test/'], - ['%MEDIA%/test', $this->media_path.'/test'], - ['%MEDIA%', $this->media_path], - ['%FOOTPRINTS%', $this->footprint_path], - //Footprints 3D are disabled - ['%FOOTPRINTS_3D%', null], - //Check that invalid pathes return null - ['/no/placeholder', null], - ['%INVALID_PLACEHOLDER%', null], - ['%FOOTPRINTS/test/', null], //Malformed placeholder - ['/wrong/%FOOTRPINTS%/', null], //Placeholder not at beginning - ['%FOOTPRINTS%/%MEDIA%', null], //No more than one placholder - ['%FOOTPRINTS%/%FOOTPRINTS%', null], - ['%FOOTPRINTS%/../../etc/passwd', null], - ['%FOOTPRINTS%/0\..\test', null], - ]; + yield ['%FOOTPRINTS%/test/test.jpg', $this->footprint_path.'/test/test.jpg']; + yield ['%FOOTPRINTS%/test/', $this->footprint_path.'/test/']; + yield ['%MEDIA%/test', $this->media_path.'/test']; + yield ['%MEDIA%', $this->media_path]; + yield ['%FOOTPRINTS%', $this->footprint_path]; + //Footprints 3D are disabled + yield ['%FOOTPRINTS_3D%', null]; + //Check that invalid pathes return null + yield ['/no/placeholder', null]; + yield ['%INVALID_PLACEHOLDER%', null]; + yield ['%FOOTPRINTS/test/', null]; + //Malformed placeholder + yield ['/wrong/%FOOTRPINTS%/', null]; + //Placeholder not at beginning + yield ['%FOOTPRINTS%/%MEDIA%', null]; + //No more than one placholder + yield ['%FOOTPRINTS%/%FOOTPRINTS%', null]; + yield ['%FOOTPRINTS%/../../etc/passwd', null]; + yield ['%FOOTPRINTS%/0\..\test', null]; } - public function realPathDataProvider(): array + public function realPathDataProvider(): \Iterator { //We need to do initialization (again), as dataprovider is called before setUp() self::bootKernel(); @@ -108,20 +106,17 @@ class AttachmentPathResolverTest extends WebTestCase $this->projectDir = str_replace('\\', '/', $this->projectDir_orig); $this->media_path = $this->projectDir.'/public/media'; $this->footprint_path = $this->projectDir.'/public/img/footprints'; - - return [ - [$this->media_path.'/test/img.jpg', '%MEDIA%/test/img.jpg'], - [$this->media_path.'/test/img.jpg', '%BASE%/data/media/test/img.jpg', true], - [$this->footprint_path.'/foo.jpg', '%FOOTPRINTS%/foo.jpg'], - [$this->footprint_path.'/foo.jpg', '%FOOTPRINTS%/foo.jpg', true], - //Every kind of absolute path, that is not based with our placeholder dirs must be invald - ['/etc/passwd', null], - ['C:\\not\\existing.txt', null], - //More then one placeholder is not allowed - [$this->footprint_path.'/test/'.$this->footprint_path, null], - //Path must begin with path - ['/not/root'.$this->footprint_path, null], - ]; + yield [$this->media_path.'/test/img.jpg', '%MEDIA%/test/img.jpg']; + yield [$this->media_path.'/test/img.jpg', '%BASE%/data/media/test/img.jpg', true]; + yield [$this->footprint_path.'/foo.jpg', '%FOOTPRINTS%/foo.jpg']; + yield [$this->footprint_path.'/foo.jpg', '%FOOTPRINTS%/foo.jpg', true]; + //Every kind of absolute path, that is not based with our placeholder dirs must be invald + yield ['/etc/passwd', null]; + yield ['C:\\not\\existing.txt', null]; + //More than one placeholder is not allowed + yield [$this->footprint_path.'/test/'.$this->footprint_path, null]; + //Path must begin with path + yield ['/not/root'.$this->footprint_path, null]; } /** @@ -140,7 +135,7 @@ class AttachmentPathResolverTest extends WebTestCase $this->assertSame($expected, $this->service->realPathToPlaceholder($param, $old_method)); } - public function germanFootprintPathdDataProvider() + public function germanFootprintPathdDataProvider(): ?\Generator { self::bootKernel(); $this->projectDir_orig = realpath(self::$kernel->getProjectDir()); @@ -161,7 +156,6 @@ class AttachmentPathResolverTest extends WebTestCase /** * @dataProvider germanFootprintPathdDataProvider - * @return void */ public function testConversionOfGermanFootprintPaths(string $expected, string $input): void { diff --git a/tests/Services/Attachments/AttachmentURLGeneratorTest.php b/tests/Services/Attachments/AttachmentURLGeneratorTest.php index 1588a951..0db57f29 100644 --- a/tests/Services/Attachments/AttachmentURLGeneratorTest.php +++ b/tests/Services/Attachments/AttachmentURLGeneratorTest.php @@ -33,20 +33,18 @@ class AttachmentURLGeneratorTest extends WebTestCase public static function setUpBeforeClass(): void { - //Get an service instance. + //Get a service instance. self::bootKernel(); self::$service = self::getContainer()->get(AttachmentURLGenerator::class); } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - ['/public/test.jpg', 'test.jpg'], - ['/public/folder/test.jpg', 'folder/test.jpg'], - ['/not/public/test.jpg', null], - ['/public/', ''], - ['not/absolute/test.jpg', null], - ]; + yield ['/public/test.jpg', 'test.jpg']; + yield ['/public/folder/test.jpg', 'folder/test.jpg']; + yield ['/not/public/test.jpg', null]; + yield ['/public/', '']; + yield ['not/absolute/test.jpg', null]; } /** diff --git a/tests/Services/Attachments/BuiltinAttachmentsFinderTest.php b/tests/Services/Attachments/BuiltinAttachmentsFinderTest.php index 499ab8f4..5ca656e3 100644 --- a/tests/Services/Attachments/BuiltinAttachmentsFinderTest.php +++ b/tests/Services/Attachments/BuiltinAttachmentsFinderTest.php @@ -27,7 +27,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class BuiltinAttachmentsFinderTest extends WebTestCase { - protected static $mock_list = [ + protected static array $mock_list = [ '%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS%/test/test.png', '%FOOTPRINTS%/123.jpg', '%FOOTPRINTS%/123.jpeg', '%FOOTPRINTS_3D%/test.jpg', '%FOOTPRINTS_3D%/hallo.txt', ]; @@ -38,25 +38,20 @@ class BuiltinAttachmentsFinderTest extends WebTestCase public static function setUpBeforeClass(): void { - //Get an service instance. + //Get a service instance. self::bootKernel(); self::$service = self::getContainer()->get(BuiltinAttachmentsFinder::class); } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - //No value should return empty array - ['', [], []], - ['', ['empty_returns_all' => true], static::$mock_list], - //Basic search for keyword - ['test', [], ['%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS%/test/test.png', '%FOOTPRINTS_3D%/test.jpg']], - ['%FOOTPRINTS_3D%', [], ['%FOOTPRINTS_3D%/test.jpg', '%FOOTPRINTS_3D%/hallo.txt']], - ['.txt', [], ['%FOOTPRINTS_3D%/hallo.txt']], - //Filter extensions - //['test', ['allowed_extensions' => ['jpeg', 'jpg']], ['%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS%/123.jpeg', '%FOOTPRINTS_3D%/test.jpg']], - //['test.jpg', ['allowed_extensions' => ['jpeg', 'jpg']], ['%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS_3D%/test.jpg']] - ]; + //No value should return empty array + yield ['', [], []]; + yield ['', ['empty_returns_all' => true], static::$mock_list]; + //Basic search for keyword + yield ['test', [], ['%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS%/test/test.png', '%FOOTPRINTS_3D%/test.jpg']]; + yield ['%FOOTPRINTS_3D%', [], ['%FOOTPRINTS_3D%/test.jpg', '%FOOTPRINTS_3D%/hallo.txt']]; + yield ['.txt', [], ['%FOOTPRINTS_3D%/hallo.txt']]; } /** diff --git a/tests/Services/Attachments/FileTypeFilterToolsTest.php b/tests/Services/Attachments/FileTypeFilterToolsTest.php index 63aa8bde..a6032a4b 100644 --- a/tests/Services/Attachments/FileTypeFilterToolsTest.php +++ b/tests/Services/Attachments/FileTypeFilterToolsTest.php @@ -35,56 +35,52 @@ class FileTypeFilterToolsTest extends WebTestCase self::$service = self::getContainer()->get(FileTypeFilterTools::class); } - public function validateDataProvider(): array + public function validateDataProvider(): \Iterator { - return [ - ['', true], //Empty string is valid - ['.jpeg,.png, .gif', true], //Only extensions are valid - ['image/*, video/*, .mp4, video/x-msvideo, application/vnd.amazon.ebook', true], - ['application/vnd.amazon.ebook, audio/opus', true], - - ['*.notvalid, .png', false], //No stars in extension - ['test.png', false], //No full filename - ['application/*', false], //Only certain placeholders are allowed - ['.png;.png,.jpg', false], //Wrong separator - ['.png .jpg .gif', false], - ]; + yield ['', true]; + //Empty string is valid + yield ['.jpeg,.png, .gif', true]; + //Only extensions are valid + yield ['image/*, video/*, .mp4, video/x-msvideo, application/vnd.amazon.ebook', true]; + yield ['application/vnd.amazon.ebook, audio/opus', true]; + yield ['*.notvalid, .png', false]; + //No stars in extension + yield ['test.png', false]; + //No full filename + yield ['application/*', false]; + //Only certain placeholders are allowed + yield ['.png;.png,.jpg', false]; + //Wrong separator + yield ['.png .jpg .gif', false]; } - public function normalizeDataProvider(): array + public function normalizeDataProvider(): \Iterator { - return [ - ['', ''], - ['.jpeg,.png,.gif', '.jpeg,.png,.gif'], - ['.jpeg, .png, .gif,', '.jpeg,.png,.gif'], - ['jpg, *.gif', '.jpg,.gif'], - ['video, image/', 'video/*,image/*'], - ['video/*', 'video/*'], - ['video/x-msvideo,.jpeg', 'video/x-msvideo,.jpeg'], - ['.video', '.video'], - //Remove duplicate entries - ['png, .gif, .png,', '.png,.gif'], - ]; + yield ['', '']; + yield ['.jpeg,.png,.gif', '.jpeg,.png,.gif']; + yield ['.jpeg, .png, .gif,', '.jpeg,.png,.gif']; + yield ['jpg, *.gif', '.jpg,.gif']; + yield ['video, image/', 'video/*,image/*']; + yield ['video/*', 'video/*']; + yield ['video/x-msvideo,.jpeg', 'video/x-msvideo,.jpeg']; + yield ['.video', '.video']; + //Remove duplicate entries + yield ['png, .gif, .png,', '.png,.gif']; } - public function extensionAllowedDataProvider(): array + public function extensionAllowedDataProvider(): \Iterator { - return [ - ['', 'txt', true], - ['', 'everything_should_match', true], - - ['.jpg,.png', 'jpg', true], - ['.jpg,.png', 'png', true], - ['.jpg,.png', 'txt', false], - - ['image/*', 'jpeg', true], - ['image/*', 'png', true], - ['image/*', 'txt', false], - - ['application/pdf,.txt', 'pdf', true], - ['application/pdf,.txt', 'txt', true], - ['application/pdf,.txt', 'jpg', false], - ]; + yield ['', 'txt', true]; + yield ['', 'everything_should_match', true]; + yield ['.jpg,.png', 'jpg', true]; + yield ['.jpg,.png', 'png', true]; + yield ['.jpg,.png', 'txt', false]; + yield ['image/*', 'jpeg', true]; + yield ['image/*', 'png', true]; + yield ['image/*', 'txt', false]; + yield ['application/pdf,.txt', 'pdf', true]; + yield ['application/pdf,.txt', 'txt', true]; + yield ['application/pdf,.txt', 'jpg', false]; } /** diff --git a/tests/Services/ElementTypeNameGeneratorTest.php b/tests/Services/ElementTypeNameGeneratorTest.php index 2fbca494..934a3bbd 100644 --- a/tests/Services/ElementTypeNameGeneratorTest.php +++ b/tests/Services/ElementTypeNameGeneratorTest.php @@ -41,11 +41,8 @@ class ElementTypeNameGeneratorTest extends WebTestCase protected function setUp(): void { - parent::setUp(); - //Get an service instance. - self::bootKernel(); - $this->service = self::$container->get(ElementTypeNameGenerator::class); + $this->service = self::getContainer()->get(ElementTypeNameGenerator::class); } public function testGetLocalizedTypeNameCombination(): void diff --git a/tests/Services/EntityMergers/Mergers/EntityMergerHelperTraitTest.php b/tests/Services/EntityMergers/Mergers/EntityMergerHelperTraitTest.php new file mode 100644 index 00000000..22fa220b --- /dev/null +++ b/tests/Services/EntityMergers/Mergers/EntityMergerHelperTraitTest.php @@ -0,0 +1,246 @@ +. + */ +namespace App\Tests\Services\EntityMergers\Mergers; + +use App\Entity\Parts\Part; +use App\Services\EntityMergers\Mergers\EntityMergerHelperTrait; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +class EntityMergerHelperTraitTest extends KernelTestCase +{ + use EntityMergerHelperTrait; + + public function setUp(): void + { + self::bootKernel(); + $this->property_accessor = self::getContainer()->get(PropertyAccessorInterface::class); + } + + public function testUseCallback(): void + { + $obj1 = new MergeTestClass(); + $obj1->non_nullable_string = 'obj1'; + $obj2 = new MergeTestClass(); + $obj2->non_nullable_string = 'obj2'; + + $tmp = $this->useCallback(function ($target_value, $other_value, $target, $other, $field) use ($obj1, $obj2) { + $this->assertSame($obj1, $target); + $this->assertSame($obj2, $other); + $this->assertSame('non_nullable_string', $field); + $this->assertSame('obj1', $target_value); + $this->assertSame('obj2', $other_value); + + return 'callback'; + + }, $obj1, $obj2, 'non_nullable_string'); + + //merge should return the target object + $this->assertSame($obj1, $tmp); + //And it should have the value from the callback set + $this->assertSame('callback', $obj1->non_nullable_string); + } + + public function testOtherFunctionIfNotNull(): void + { + $obj1 = new MergeTestClass(); + $obj1->string_property = null; + $obj2 = new MergeTestClass(); + $obj2->string_property = 'obj2'; + + $tmp = $this->useOtherValueIfNotNull($obj1, $obj2, 'string_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame('obj2', $obj1->string_property); + + $obj1->string_property = 'obj1'; + $tmp = $this->useOtherValueIfNotNull($obj1, $obj2, 'string_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame('obj1', $tmp->string_property); + + $obj1->string_property = null; + $obj2->string_property = null; + $this->assertSame($obj1, $this->useOtherValueIfNotNull($obj1, $obj2, 'string_property')); + $this->assertNull($obj1->string_property); + } + + public function testOtherFunctionIfNotEmpty(): void + { + $obj1 = new MergeTestClass(); + $obj1->string_property = null; + $obj2 = new MergeTestClass(); + $obj2->string_property = 'obj2'; + + $tmp = $this->useOtherValueIfNotEmtpy($obj1, $obj2, 'string_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame('obj2', $obj1->string_property); + + $obj1->string_property = 'obj1'; + $tmp = $this->useOtherValueIfNotEmtpy($obj1, $obj2, 'string_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame('obj1', $tmp->string_property); + + $obj1->string_property = null; + $obj2->string_property = null; + $this->assertSame($obj1, $this->useOtherValueIfNotEmtpy($obj1, $obj2, 'string_property')); + $this->assertNull($obj1->string_property); + + $obj1->string_property = ''; + $obj2->string_property = 'test'; + $this->assertSame($obj1, $this->useOtherValueIfNotEmtpy($obj1, $obj2, 'string_property')); + $this->assertSame('test', $obj1->string_property); + } + + public function testUseLargerValue(): void + { + $obj1 = new MergeTestClass(); + $obj1->int_property = 1; + $obj2 = new MergeTestClass(); + $obj2->int_property = 2; + + $tmp = $this->useLargerValue($obj1, $obj2, 'int_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame(2, $obj1->int_property); + + $obj1->int_property = 3; + $obj2->int_property = 2; + + $tmp = $this->useLargerValue($obj1, $obj2, 'int_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame(3, $obj1->int_property); + } + + public function testUseSmallerValue(): void + { + $obj1 = new MergeTestClass(); + $obj1->int_property = 1; + $obj2 = new MergeTestClass(); + $obj2->int_property = 2; + + $tmp = $this->useSmallerValue($obj1, $obj2, 'int_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame(1, $obj1->int_property); + + $obj1->int_property = 3; + $obj2->int_property = 2; + + $tmp = $this->useSmallerValue($obj1, $obj2, 'int_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame(2, $obj1->int_property); + } + + public function testUseTrueValue(): void + { + $obj1 = new MergeTestClass(); + $obj1->bool_property = false; + $obj2 = new MergeTestClass(); + $obj2->bool_property = true; + + $tmp = $this->useTrueValue($obj1, $obj2, 'bool_property'); + $this->assertSame($obj1, $tmp); + $this->assertTrue($obj1->bool_property); + + $obj1->bool_property = true; + $obj2->bool_property = false; + $this->assertTrue($this->useTrueValue($obj1, $obj2, 'bool_property')->bool_property); + + $obj1->bool_property = false; + $obj2->bool_property = false; + $this->assertFalse($this->useTrueValue($obj1, $obj2, 'bool_property')->bool_property); + } + + public function testMergeTags(): void + { + $obj1 = new MergeTestClass(); + $obj1->string_property = 'tag1,tag2,tag3'; + $obj2 = new MergeTestClass(); + $obj2->string_property = 'tag2,tag3,tag4'; + + $tmp = $this->mergeTags($obj1, $obj2, 'string_property'); + $this->assertSame($obj1, $tmp); + $this->assertSame('tag1,tag2,tag3,tag4', $obj1->string_property); + } + + public function testAreStringsEqual(): void + { + $this->assertTrue($this->areStringsEqual('test', 'test')); + $this->assertTrue($this->areStringsEqual('test', 'TEST')); + $this->assertTrue($this->areStringsEqual('test', 'Test')); + $this->assertTrue($this->areStringsEqual('test', ' Test ')); + $this->assertTrue($this->areStringsEqual('Test ', 'test')); + + $this->assertFalse($this->areStringsEqual('test', 'test2')); + $this->assertFalse($this->areStringsEqual('test', 'test 1')); + } + + public function testMergeTextWithSeparator(): void + { + $obj1 = new MergeTestClass(); + $obj1->string_property = 'Test1'; + $obj2 = new MergeTestClass(); + $obj2->string_property = 'Test2'; + + $tmp = $this->mergeTextWithSeparator($obj1, $obj2, 'string_property', ' # '); + $this->assertSame($obj1, $tmp); + $this->assertSame('Test1 # Test2', $obj1->string_property); + + //If thee text is the same, it should not be duplicated + $obj1->string_property = 'Test1'; + $obj2->string_property = 'Test1'; + $this->assertSame($obj1, $this->mergeTextWithSeparator($obj1, $obj2, 'string_property', ' # ')); + $this->assertSame('Test1', $obj1->string_property); + + //Test what happens if the second text is empty + $obj1->string_property = 'Test1'; + $obj2->string_property = ''; + $this->assertSame($obj1, $this->mergeTextWithSeparator($obj1, $obj2, 'string_property', ' # ')); + $this->assertSame('Test1', $obj1->string_property); + + } + + public function testMergeComment(): void + { + $obj1 = new Part(); + $obj1->setName('Test1'); + $obj1->setComment('Comment1'); + $obj2 = new Part(); + $obj2->setName('Test2'); + $obj2->setComment('Comment2'); + + $tmp = $this->mergeComment($obj1, $obj2); + $this->assertSame($obj1, $tmp); + $this->assertSame("Comment1\n\nTest2:\nComment2", $obj1->getComment()); + + //If the comment is the same, it should not be duplicated + $obj1->setComment('Comment1'); + $obj2->setComment('Comment1'); + $this->assertSame($obj1, $this->mergeComment($obj1, $obj2)); + $this->assertSame('Comment1', $obj1->getComment()); + + //Test what happens if the second comment is empty + $obj1->setComment('Comment1'); + $obj2->setComment(''); + $this->assertSame($obj1, $this->mergeComment($obj1, $obj2)); + $this->assertSame('Comment1', $obj1->getComment()); + } +} diff --git a/tests/Services/EntityMergers/Mergers/MergeTestClass.php b/tests/Services/EntityMergers/Mergers/MergeTestClass.php new file mode 100644 index 00000000..73fa9314 --- /dev/null +++ b/tests/Services/EntityMergers/Mergers/MergeTestClass.php @@ -0,0 +1,46 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Tests\Services\EntityMergers\Mergers; + +use App\Entity\Parts\Category; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; + +class MergeTestClass +{ + public int $int_property = 0; + public ?float $float_property = null; + public ?string $string_property = null; + public string $non_nullable_string = ''; + public bool $bool_property = false; + + public Collection $collection; + + public ?Category $category = null; + + public function __construct() + { + $this->collection = new ArrayCollection(); + } +} \ No newline at end of file diff --git a/tests/Services/EntityMergers/Mergers/PartMergerTest.php b/tests/Services/EntityMergers/Mergers/PartMergerTest.php new file mode 100644 index 00000000..bf02744a --- /dev/null +++ b/tests/Services/EntityMergers/Mergers/PartMergerTest.php @@ -0,0 +1,192 @@ +. + */ +namespace App\Tests\Services\EntityMergers\Mergers; + +use App\Entity\Parts\AssociationType; +use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; +use App\Entity\Parts\Manufacturer; +use App\Entity\Parts\MeasurementUnit; +use App\Entity\Parts\Part; +use App\Entity\Parts\PartAssociation; +use App\Entity\Parts\PartLot; +use App\Entity\PriceInformations\Orderdetail; +use App\Services\EntityMergers\Mergers\PartMerger; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; + +class PartMergerTest extends KernelTestCase +{ + + /** @var PartMerger|null */ + protected ?PartMerger $merger = null; + + protected function setUp(): void + { + self::bootKernel(); + $this->merger = self::getContainer()->get(PartMerger::class); + } + + public function testMergeOfEntityRelations(): void + { + $category = new Category(); + $footprint = new Footprint(); + $manufacturer1 = new Manufacturer(); + $manufacturer2 = new Manufacturer(); + $unit = new MeasurementUnit(); + + $part1 = (new Part()) + ->setCategory($category) + ->setManufacturer($manufacturer1); + + $part2 = (new Part()) + ->setFootprint($footprint) + ->setManufacturer($manufacturer2) + ->setPartUnit($unit); + + $merged = $this->merger->merge($part1, $part2); + $this->assertSame($merged, $part1); + $this->assertSame($category, $merged->getCategory()); + $this->assertSame($footprint, $merged->getFootprint()); + $this->assertSame($manufacturer1, $merged->getManufacturer()); + $this->assertSame($unit, $merged->getPartUnit()); + } + + public function testMergeOfTags(): void + { + $part1 = (new Part()) + ->setTags('tag1,tag2,tag3'); + + $part2 = (new Part()) + ->setTags('tag2,tag3,tag4'); + + $merged = $this->merger->merge($part1, $part2); + $this->assertSame($merged, $part1); + $this->assertSame('tag1,tag2,tag3,tag4', $merged->getTags()); + } + + public function testMergeOfBoolFields(): void + { + $part1 = (new Part()) + ->setFavorite(false) + ->setNeedsReview(true); + + $part2 = (new Part()) + ->setFavorite(true) + ->setNeedsReview(false); + + $merged = $this->merger->merge($part1, $part2); + //Favorite and needs review should be true, as it is true in one of the parts + $this->assertTrue($merged->isFavorite()); + $this->assertTrue($merged->isNeedsReview()); + } + + public function testMergeOfAssociatedPartsAsOther(): void + { + //Part1 is associated with part2 and part3: + $part1 = (new Part()) + ->setName('part1'); + $part2 = (new Part()) + ->setName('part2'); + $part3 = (new Part()) + ->setName('part3'); + + $association1 = (new PartAssociation()) + ->setOther($part2) + ->setType(AssociationType::COMPATIBLE); + + $association2 = (new PartAssociation()) + ->setOther($part2) + ->setType(AssociationType::SUPERSEDES); + + $association3 = (new PartAssociation()) + ->setOther($part3) + ->setType(AssociationType::SUPERSEDES); + + $part1->addAssociatedPartsAsOwner($association1); + $part1->addAssociatedPartsAsOwner($association2); + $part1->addAssociatedPartsAsOwner($association3); + //Fill the other side of the association manually, as we have no entity manager + $part2->getAssociatedPartsAsOther()->add($association1); + $part2->getAssociatedPartsAsOther()->add($association2); + $part3->getAssociatedPartsAsOther()->add($association3); + + //Now we merge part2 into part3: + $merged = $this->merger->merge($part3, $part2); + $this->assertSame($merged, $part3); + + //Now part1 should have 4 associations, 2 with part2 and 2 with part3 + $this->assertCount(4, $part1->getAssociatedPartsAsOwner()); + $this->assertCount(2, $part1->getAssociatedPartsAsOwner()->filter(fn(PartAssociation $a) => $a->getOther() === $part2)); + $this->assertCount(2, $part1->getAssociatedPartsAsOwner()->filter(fn(PartAssociation $a) => $a->getOther() === $part3)); + } + + /** + * This test also functions as test for EntityMergerHelperTrait::mergeCollections() so its pretty long. + * @return void + */ + public function testMergeOfPartLots(): void + { + $lot1 = (new PartLot())->setAmount(2)->setNeedsRefill(true); + $lot2 = (new PartLot())->setInstockUnknown(true)->setUserBarcode('test'); + $lot3 = (new PartLot())->setDescription('lot3')->setAmount(3); + $lot4 = (new PartLot())->setDescription('lot4')->setComment('comment'); + + $part1 = (new Part()) + ->setName('Part 1') + ->addPartLot($lot1) + ->addPartLot($lot2); + + $part2 = (new Part()) + ->setName('Part 2') + ->addPartLot($lot3) + ->addPartLot($lot4); + + $merged = $this->merger->merge($part1, $part2); + + $this->assertInstanceOf(Part::class, $merged); + //We should now have all 4 lots + $this->assertCount(4, $merged->getPartLots()); + + //The existing lots should be the same instance as before + $this->assertSame($lot1, $merged->getPartLots()->get(0)); + $this->assertSame($lot2, $merged->getPartLots()->get(1)); + //While the new lots should be new instances + $this->assertNotSame($lot3, $merged->getPartLots()->get(2)); + $this->assertNotSame($lot4, $merged->getPartLots()->get(3)); + + //But the new lots, should be assigned to the target part and contain the same info + $clone3 = $merged->getPartLots()->get(2); + $clone4 = $merged->getPartLots()->get(3); + $this->assertSame($merged, $clone3->getPart()); + $this->assertSame($merged, $clone4->getPart()); + + } + + public function testSupports() + { + $this->assertFalse($this->merger->supports(new \stdClass(), new \stdClass())); + $this->assertFalse($this->merger->supports(new \stdClass(), new Part())); + $this->assertTrue($this->merger->supports(new Part(), new Part())); + } +} diff --git a/tests/Services/Formatters/AmountFormatterTest.php b/tests/Services/Formatters/AmountFormatterTest.php index 40df0af7..40f9b7cf 100644 --- a/tests/Services/Formatters/AmountFormatterTest.php +++ b/tests/Services/Formatters/AmountFormatterTest.php @@ -36,9 +36,9 @@ class AmountFormatterTest extends WebTestCase protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(AmountFormatter::class); } @@ -47,7 +47,7 @@ class AmountFormatterTest extends WebTestCase { $this->assertSame('2', $this->service->format(2.321)); $this->assertSame('1002', $this->service->format(1002.356)); - $this->assertSame('1000454', $this->service->format(1000454.0)); + $this->assertSame('1000454', $this->service->format(1_000_454.0)); $this->assertSame('0', $this->service->format(0.01)); $this->assertSame('0', $this->service->format(0)); } diff --git a/tests/Services/ImportExportSystem/BOMImporterTest.php b/tests/Services/ImportExportSystem/BOMImporterTest.php new file mode 100644 index 00000000..b9aba1d4 --- /dev/null +++ b/tests/Services/ImportExportSystem/BOMImporterTest.php @@ -0,0 +1,122 @@ +. + */ +namespace App\Tests\Services\ImportExportSystem; + +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use App\Services\ImportExportSystem\BOMImporter; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\HttpFoundation\File\File; + +class BOMImporterTest extends WebTestCase +{ + + /** + * @var BOMImporter + */ + protected $service; + + protected function setUp(): void + { + //Get a service instance. + self::bootKernel(); + $this->service = self::getContainer()->get(BOMImporter::class); + } + + public function testImportFileIntoProject(): void + { + $input = <<createMock(File::class); + $file->method('getContent')->willReturn($input); + + $project = new Project(); + $this->assertCount(0, $project->getBOMEntries()); + + $bom_entries = $this->service->importFileIntoProject($file, $project, ['type' => 'kicad_pcbnew']); + $this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom_entries); + $this->assertCount(4, $bom_entries); + + //Check that the BOM entries are added to the project + $this->assertCount(4, $project->getBOMEntries()); + } + + public function testStringToBOMEntriesKiCADPCB(): void + { + //Test for german input + $input = <<service->stringToBOMEntries($input, ['type' => 'kicad_pcbnew']); + + $this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom); + $this->assertCount(4, $bom); + + $this->assertSame('R19,R17', $bom[0]->getMountnames()); + $this->assertEqualsWithDelta(2.0, $bom[0]->getQuantity(), PHP_FLOAT_EPSILON); + $this->assertSame('4.7k (R_0805_2012Metric_Pad1.20x1.40mm_HandSolder)', $bom[0]->getName()); + $this->assertSame('Test', $bom[0]->getComment()); + + //Test for english input + $input = <<service->stringToBOMEntries($input, ['type' => 'kicad_pcbnew']); + + $this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom); + $this->assertCount(4, $bom); + + $this->assertSame('R19,R17', $bom[0]->getMountnames()); + $this->assertEqualsWithDelta(2.0, $bom[0]->getQuantity(), PHP_FLOAT_EPSILON); + $this->assertSame('4.7k (R_0805_2012Metric_Pad1.20x1.40mm_HandSolder)', $bom[0]->getName()); + $this->assertSame('Test', $bom[0]->getComment()); + } + + public function testStringToBOMEntriesKiCADPCBError(): void + { + $input = <<expectException(\UnexpectedValueException::class); + + $this->service->stringToBOMEntries($input, ['type' => 'kicad_pcbnew']); + } +} diff --git a/tests/Services/ImportExportSystem/EntityExporterTest.php b/tests/Services/ImportExportSystem/EntityExporterTest.php new file mode 100644 index 00000000..004971ab --- /dev/null +++ b/tests/Services/ImportExportSystem/EntityExporterTest.php @@ -0,0 +1,82 @@ +. + */ +namespace App\Tests\Services\ImportExportSystem; + +use App\Entity\Parts\Category; +use App\Services\ImportExportSystem\EntityExporter; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\HttpFoundation\Request; + +class EntityExporterTest extends WebTestCase +{ + /** + * @var EntityExporter + */ + protected $service; + + protected function setUp(): void + { + self::bootKernel(); + $this->service = self::getContainer()->get(EntityExporter::class); + } + + private function getEntities(): array + { + $entity1 = (new Category())->setName('Enitity 1')->setComment('Test'); + $entity1_1 = (new Category())->setName('Enitity 1.1')->setParent($entity1); + $entity2 = (new Category())->setName('Enitity 2'); + + return [$entity1, $entity1_1, $entity2]; + } + + public function testExportStructuralEntities(): void + { + $entities = $this->getEntities(); + + $json_without_children = $this->service->exportEntities($entities, ['format' => 'json', 'level' => 'simple']); + $this->assertJsonStringEqualsJsonString('[{"name":"Enitity 1","type":"category","full_name":"Enitity 1"},{"name":"Enitity 1.1","type":"category","full_name":"Enitity 1->Enitity 1.1"},{"name":"Enitity 2","type":"category","full_name":"Enitity 2"}]', + $json_without_children); + + $json_with_children = $this->service->exportEntities($entities, + ['format' => 'json', 'level' => 'simple', 'include_children' => true]); + $this->assertJsonStringEqualsJsonString('[{"children":[{"children":[],"name":"Enitity 1.1","type":"category","full_name":"Enitity 1->Enitity 1.1"}],"name":"Enitity 1","type":"category","full_name":"Enitity 1"},{"children":[],"name":"Enitity 1.1","type":"category","full_name":"Enitity 1->Enitity 1.1"},{"children":[],"name":"Enitity 2","type":"category","full_name":"Enitity 2"}]', + $json_with_children); + } + + public function testExportEntityFromRequest(): void + { + $entities = $this->getEntities(); + + $request = new Request(); + $request->request->set('format', 'json'); + $request->request->set('level', 'simple'); + $response = $this->service->exportEntityFromRequest($entities, $request); + + $this->assertJson($response->getContent()); + + $this->assertSame('application/json', $response->headers->get('Content-Type')); + $this->assertNotEmpty($response->headers->get('Content-Disposition')); + + + } +} diff --git a/tests/Services/ImportExportSystem/EntityImporterTest.php b/tests/Services/ImportExportSystem/EntityImporterTest.php index 79599c8e..31859b6a 100644 --- a/tests/Services/ImportExportSystem/EntityImporterTest.php +++ b/tests/Services/ImportExportSystem/EntityImporterTest.php @@ -22,11 +22,18 @@ declare(strict_types=1); namespace App\Tests\Services\ImportExportSystem; +use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\AttachmentType; +use App\Entity\LabelSystem\LabelProfile; +use App\Entity\Parts\Category; +use App\Entity\Parts\Part; +use App\Entity\ProjectSystem\Project; use App\Entity\UserSystem\User; -use App\Services\Formatters\AmountFormatter; use App\Services\ImportExportSystem\EntityImporter; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\ConstraintViolationListInterface; /** * @group DB @@ -34,15 +41,13 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class EntityImporterTest extends WebTestCase { /** - * @var AmountFormatter + * @var EntityImporter */ protected $service; protected function setUp(): void { - parent::setUp(); - - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(EntityImporter::class); } @@ -67,10 +72,24 @@ class EntityImporterTest extends WebTestCase //Check parent $this->assertNull($results[0]->getMasterPictureAttachment()); - $parent = new AttachmentType(); + $em = self::getContainer()->get(EntityManagerInterface::class); + $parent = $em->find(AttachmentType::class, 1); $results = $this->service->massCreation($lines, AttachmentType::class, $parent, $errors); $this->assertCount(3, $results); $this->assertSame($parent, $results[0]->getParent()); + + //Test for addition of existing elements + $errors = []; + $lines = "Node 3\n Node 3.new"; + $results = $this->service->massCreation($lines, AttachmentType::class, null, $errors); + $this->assertCount(2, $results); + $this->assertCount(0, $errors); + $this->assertSame('Node 3', $results[0]->getName()); + //Node 3 must be an existing entity + $this->assertNotNull($results[0]->getId()); + $this->assertSame('Node 3.new', $results[1]->getName()); + //Parent must be Node 3 + $this->assertSame($results[0], $results[1]->getParent()); } public function testNonStructuralClass(): void @@ -80,9 +99,9 @@ Test1 Test1.1 Test2 EOT; - $errors = []; - $results = $this->service->massCreation($input, User::class, null, $errors); + + $results = $this->service->massCreation($input, LabelProfile::class, null, $errors); //Import must not fail, even with non-structural classes $this->assertCount(3, $results); @@ -107,10 +126,11 @@ Test 2 EOT; $errors = []; - $parent = new AttachmentType(); + $em = self::getContainer()->get(EntityManagerInterface::class); + $parent = $em->find(AttachmentType::class, 1); $results = $this->service->massCreation($input, AttachmentType::class, $parent, $errors); - //We have 7 elements, an now errros + //We have 7 elements, and 0 errors $this->assertCount(0, $errors); $this->assertCount(7, $results); @@ -143,12 +163,160 @@ EOT; public function testMassCreationErrors(): void { $errors = []; - //Node 1 and Node 2 are created in datafixtures, so their attemp to create them again must fail. - $lines = "Test 1\nNode 1\nNode 2"; + $longName = str_repeat('a', 256); + + //The last node is too long, this must trigger a validation error + $lines = "Test 1\nNode 1\n " . $longName; $results = $this->service->massCreation($lines, AttachmentType::class, null, $errors); - $this->assertCount(1, $results); + $this->assertCount(2, $results); $this->assertSame('Test 1', $results[0]->getName()); - $this->assertCount(2, $errors); - $this->assertSame('Node 1', $errors[0]['entity']->getName()); + $this->assertCount(1, $errors); + $this->assertSame($longName, $errors[0]['entity']->getName()); + } + + public function formatDataProvider(): \Iterator + { + yield ['csv', 'csv']; + yield ['csv', 'CSV']; + yield ['xml', 'Xml']; + yield ['json', 'json']; + yield ['yaml', 'yml']; + yield ['yaml', 'YAML']; + } + + /** + * @dataProvider formatDataProvider + */ + public function testDetermineFormat(string $expected, string $extension): void + { + $this->assertSame($expected, $this->service->determineFormat($extension)); + } + + public function testImportStringProjects(): void + { + $input = <<service->importString($input, [ + 'class' => Project::class, + 'format' => 'csv', + 'csv_delimiter' => ';', + ], $errors); + + $this->assertCount(2, $results); + + //No errors must be present + $this->assertEmpty($errors); + + + $this->assertContainsOnlyInstancesOf(Project::class, $results); + + $this->assertSame('Test 1', $results[0]->getName()); + $this->assertSame('Test 1 notes', $results[0]->getComment()); + } + + public function testImportStringProjectWithErrors(): void + { + $input = <<service->importString($input, [ + 'class' => Project::class, + 'format' => 'csv', + 'csv_delimiter' => ';', + ], $errors); + + $this->assertCount(1, $results); + $this->assertCount(1, $errors); + + //Validate shape of error output + + $this->assertArrayHasKey('Row 0', $errors); + $this->assertArrayHasKey('entity', $errors['Row 0']); + $this->assertArrayHasKey('violations', $errors['Row 0']); + + $this->assertInstanceOf(ConstraintViolationListInterface::class, $errors['Row 0']['violations']); + $this->assertInstanceOf(Project::class, $errors['Row 0']['entity']); + } + + public function testImportStringParts(): void + { + $input = <<setName('Test category'); + + $errors = []; + $results = $this->service->importString($input, [ + 'class' => Part::class, + 'format' => 'csv', + 'csv_delimiter' => ',', + 'create_unknown_datastructures' => true, + 'part_category' => $category, + ], $errors); + + $this->assertCount(2, $results); + //No errors must be present + $this->assertEmpty($errors); + $this->assertContainsOnlyInstancesOf(Part::class, $results); + + $this->assertSame('Test 1', $results[0]->getName()); + $this->assertSame('Test 1 description', $results[0]->getDescription()); + $this->assertSame('Test 1 notes', $results[0]->getComment()); + $this->assertSame('Test 1 manufacturer', $results[0]->getManufacturer()->getName()); + $this->assertSame($category, $results[0]->getCategory()); + + $this->assertSame('Test 2', $results[1]->getName()); + $this->assertSame('Test 2 description', $results[1]->getDescription()); + $this->assertSame('Test 2 notes', $results[1]->getComment()); + $this->assertSame('Test 2 manufacturer', $results[1]->getManufacturer()->getName()); + $this->assertSame($category, $results[1]->getCategory()); + + $input = <<service->importString($input, [ + 'class' => Part::class, + 'format' => 'json', + 'create_unknown_datastructures' => true, + 'part_category' => $category, + ], $errors); + + //We have 2 elements, but one is invalid + $this->assertCount(1, $results); + $this->assertCount(1, $errors); + $this->assertContainsOnlyInstancesOf(Part::class, $results); + + //Check the format of the error + $error = reset($errors); + $this->assertInstanceOf(Part::class, $error['entity']); + $this->assertSame('', $error['entity']->getName()); + $this->assertContainsOnlyInstancesOf(ConstraintViolation::class, $error['violations']); + //Element name must be element name + $this->assertArrayHasKey('Row 1', $errors); + + //Check the valid element + $this->assertSame('Test 1', $results[0]->getName()); + $this->assertSame('Test 1 description', $results[0]->getDescription()); + $this->assertSame('Test 1 notes', $results[0]->getComment()); + $this->assertSame('Test 1 manufacturer', $results[0]->getManufacturer()->getName()); + $this->assertSame($category, $results[0]->getCategory()); + $this->assertSame('test,test2', $results[0]->getTags()); } } diff --git a/tests/Services/InfoProviderSystem/DTOs/FileDTOTest.php b/tests/Services/InfoProviderSystem/DTOs/FileDTOTest.php new file mode 100644 index 00000000..f6e145f9 --- /dev/null +++ b/tests/Services/InfoProviderSystem/DTOs/FileDTOTest.php @@ -0,0 +1,51 @@ +. + */ +namespace App\Tests\Services\InfoProviderSystem\DTOs; + +use App\Services\InfoProviderSystem\DTOs\FileDTO; +use PHPUnit\Framework\TestCase; + +class FileDTOTest extends TestCase +{ + + + public static function escapingDataProvider(): \Iterator + { + //Normal URLs must be unchanged, even if they contain special characters + yield ["https://localhost:8000/en/part/1335/edit#attachments", "https://localhost:8000/en/part/1335/edit#attachments"]; + yield ["https://localhost:8000/en/part/1335/edit?test=%20%20&sfee_aswer=test-223!*()", "https://localhost:8000/en/part/1335/edit?test=%20%20&sfee_aswer=test-223!*()"]; + //Remaining URL unsafe characters must be escaped + yield ["test%5Ese", "test^se"]; + yield ["test%20se", "test se"]; + yield ["test%7Cse", "test|se"]; + } + + /** + * @dataProvider escapingDataProvider + */ + public function testURLEscaping(string $expected, string $input): void + { + $fileDTO = new FileDTO( $input); + self::assertSame($expected, $fileDTO->url); + } +} diff --git a/tests/Services/InfoProviderSystem/DTOs/ParameterDTOTest.php b/tests/Services/InfoProviderSystem/DTOs/ParameterDTOTest.php new file mode 100644 index 00000000..ee4ca39a --- /dev/null +++ b/tests/Services/InfoProviderSystem/DTOs/ParameterDTOTest.php @@ -0,0 +1,272 @@ +. + */ +namespace App\Tests\Services\InfoProviderSystem\DTOs; + +use App\Services\InfoProviderSystem\DTOs\ParameterDTO; +use PHPUnit\Framework\TestCase; + +class ParameterDTOTest extends TestCase +{ + + public function parseValueFieldDataProvider(): \Generator + { + //Text value + yield [ + new ParameterDTO('test', value_text: 'test', unit: 'm', symbol: 'm', group: 'test'), + 'test', + 'test', + 'm', + 'm', + 'test' + ]; + + //Numerical value + yield [ + new ParameterDTO('test', value_typ: 1.0, unit: 'm', symbol: 'm', group: 'test'), + 'test', + 1.0, + 'm', + 'm', + 'test' + ]; + + //Numerical value with unit should be parsed as text value + yield [ + new ParameterDTO('test', value_text: '1.0 m', unit: 'm', symbol: 'm', group: 'test'), + 'test', + '1.0 m', + 'm', + 'm', + 'test' + ]; + + //Test ranges + yield [ + new ParameterDTO('test', value_min: 1.0, value_max: 2.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '1.0...2.0', + 'kg', + 'm', + 'test' + ]; + + //Test ranges + yield [ + new ParameterDTO('test', value_min: 1.0, value_max: 2.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '1.0..2.0', + 'kg', + 'm', + 'test' + ]; + + //Test ranges with tilde + yield [ + new ParameterDTO('test', value_min: -1.0, value_max: 2.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '-1.0~+2.0', //Leading signs are parsed correctly + 'kg', + 'm', + 'test' + ]; + + //Test ranges with comment + yield [ + new ParameterDTO('test', value_text: "Test", value_min: -1.0, value_max: 2.0, unit: 'kg', symbol: 'm', + group: 'test'), + 'test', + '-1.0~+2.0 kg Test', //Leading signs are parsed correctly + 'kg', + 'm', + 'test' + ]; + + //Test @comment + yield [ + new ParameterDTO('test', value_text: "@comment", value_typ: 1.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '1.0@comment', + 'kg', + 'm', + 'test' + ]; + + //Test plus minus range (without unit) + yield [ + new ParameterDTO('test', value_min: -1.0, value_max: +1.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '±1.0', + 'kg', + 'm', + 'test' + ]; + + yield [ //And with unit + new ParameterDTO('test', value_min: -1.0, value_max: +1.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '±1.0kg', + 'kg', + 'm', + 'test' + ]; + } + + public function parseValueIncludingUnitDataProvider(): \Generator + { + //Text value + yield [ + new ParameterDTO('test', value_text: 'test', unit: null, symbol: 'm', group: 'test'), + 'test', + 'test', + 'm', + 'test' + ]; + + //Numerical value + yield [ + new ParameterDTO('test', value_typ: 1.0, unit: null, symbol: 'm', group: 'test'), + 'test', + 1.0, + 'm', + 'test' + ]; + + //Numerical value with unit should extract unit correctly + yield [ + new ParameterDTO('test', value_typ: 1.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '1.0 kg', + 'm', + 'test' + ]; + + //Should work without space between value and unit + yield [ + new ParameterDTO('test', value_typ: 1.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '1.0kg', + 'm', + 'test' + ]; + + //Allow ° as unit symbol + yield [ + new ParameterDTO('test', value_typ: 1.0, unit: '°C', symbol: 'm', group: 'test'), + 'test', + '1.0°C', + 'm', + 'test' + ]; + + //Allow _ in units + yield [ + new ParameterDTO('test', value_typ: 1.0, unit: 'C_m', symbol: 'm', group: 'test'), + 'test', + '1.0C_m', + 'm', + 'test' + ]; + + //Allow a single space in units + yield [ + new ParameterDTO('test', value_typ: 1.0, unit: 'C m', symbol: 'm', group: 'test'), + 'test', + '1.0C m', + 'm', + 'test' + ]; + + //Test ranges + yield [ + new ParameterDTO('test', value_min: 1.0, value_max: 2.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '1.0...2.0 kg', + 'm', + 'test' + ]; + + //Test ranges with tilde + yield [ + new ParameterDTO('test', value_min: -1.0, value_max: 2.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '-1.0kg~+2.0kg', //Leading signs are parsed correctly + 'm', + 'test' + ]; + + //Test @comment + yield [ + new ParameterDTO('test', value_text: "@comment", value_typ: 1.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '1.0 kg@comment', + 'm', + 'test' + ]; + + //Test plus minus range (without unit) + yield [ + new ParameterDTO('test', value_min: -1.0, value_max: +1.0, unit: 'kg', symbol: 'm', group: 'test'), + 'test', + '±1.0 kg', + 'm', + 'test' + ]; + } + + /** + * @dataProvider parseValueFieldDataProvider + * @return void + */ + public function testParseValueField(ParameterDTO $expected, string $name, string|float $value, ?string $unit = null, ?string $symbol = null, ?string $group = null) + { + $this->assertEquals($expected, ParameterDTO::parseValueField($name, $value, $unit, $symbol, $group)); + } + + /** + * @dataProvider parseValueIncludingUnitDataProvider + * @return void + */ + public function testParseValueIncludingUnit(ParameterDTO $expected, string $name, string|float $value, ?string $symbol = null, ?string $group = null) + { + $this->assertEquals($expected, ParameterDTO::parseValueIncludingUnit($name, $value, $symbol, $group)); + } + + public function testSplitIntoValueAndUnit(): void + { + $this->assertSame(['1.0', 'kg'], ParameterDTO::splitIntoValueAndUnit('1.0 kg')); + $this->assertSame(['1.0', 'kg'], ParameterDTO::splitIntoValueAndUnit('1.0kg')); + $this->assertSame(['1', 'kg'], ParameterDTO::splitIntoValueAndUnit('1 kg')); + + $this->assertSame(['1.0', '°C'], ParameterDTO::splitIntoValueAndUnit('1.0°C')); + $this->assertSame(['1.0', '°C'], ParameterDTO::splitIntoValueAndUnit('1.0 °C')); + + $this->assertSame(['1.0', 'C_m'], ParameterDTO::splitIntoValueAndUnit('1.0C_m')); + $this->assertSame(["70", "℃"], ParameterDTO::splitIntoValueAndUnit("70℃")); + + $this->assertSame(["-5.0", "kg"], ParameterDTO::splitIntoValueAndUnit("-5.0 kg")); + $this->assertSame(["-5.0", "µg"], ParameterDTO::splitIntoValueAndUnit("-5.0 µg")); + + $this->assertNull(ParameterDTO::splitIntoValueAndUnit('kg')); + $this->assertNull(ParameterDTO::splitIntoValueAndUnit('Test')); + } +} diff --git a/tests/Services/InfoProviderSystem/DTOs/PurchaseInfoDTOTest.php b/tests/Services/InfoProviderSystem/DTOs/PurchaseInfoDTOTest.php new file mode 100644 index 00000000..14a3c03f --- /dev/null +++ b/tests/Services/InfoProviderSystem/DTOs/PurchaseInfoDTOTest.php @@ -0,0 +1,36 @@ +. + */ +namespace App\Tests\Services\InfoProviderSystem\DTOs; + +use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO; +use PHPUnit\Framework\TestCase; + +class PurchaseInfoDTOTest extends TestCase +{ + public function testThrowOnInvalidType(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The prices array must only contain PriceDTO instances'); + new PurchaseInfoDTO('test', 'test', [new \stdClass()]); + } +} diff --git a/tests/Services/InfoProviderSystem/DTOs/SearchResultDTOTest.php b/tests/Services/InfoProviderSystem/DTOs/SearchResultDTOTest.php new file mode 100644 index 00000000..dd516c8d --- /dev/null +++ b/tests/Services/InfoProviderSystem/DTOs/SearchResultDTOTest.php @@ -0,0 +1,64 @@ +. + */ +namespace App\Tests\Services\InfoProviderSystem\DTOs; + +use App\Services\InfoProviderSystem\DTOs\SearchResultDTO; +use PHPUnit\Framework\TestCase; + +class SearchResultDTOTest extends TestCase +{ + public function testPreviewImageURL(): void + { + //For null preview_image_url, the url and file dto should be null + $searchResultDTO = new SearchResultDTO( + 'provider_key', + 'provider_id', + 'name', + 'description' + ); + $this->assertNull($searchResultDTO->preview_image_url); + $this->assertNull($searchResultDTO->preview_image_file); + + //If a value is passed then the url and file dto should be the same + $searchResultDTO = new SearchResultDTO( + 'provider_key', + 'provider_id', + 'name', + 'description', + preview_image_url: 'https://invalid.com/preview_image_url.jpg' + ); + $this->assertSame('https://invalid.com/preview_image_url.jpg', $searchResultDTO->preview_image_url); + $this->assertSame('https://invalid.com/preview_image_url.jpg', $searchResultDTO->preview_image_file->url); + + //Invalid url characters should be replaced with their URL encoded version (similar to FileDTO) + $searchResultDTO = new SearchResultDTO( + 'provider_key', + 'provider_id', + 'name', + 'description', + preview_image_url: 'https://invalid.com/preview_image^url.jpg?param1=1¶m2=2' + ); + $this->assertSame('https://invalid.com/preview_image%5Eurl.jpg?param1=1¶m2=2', $searchResultDTO->preview_image_url); + $this->assertSame('https://invalid.com/preview_image%5Eurl.jpg?param1=1¶m2=2', $searchResultDTO->preview_image_file->url); + } +} diff --git a/tests/Services/InfoProviderSystem/DTOtoEntityConverterTest.php b/tests/Services/InfoProviderSystem/DTOtoEntityConverterTest.php new file mode 100644 index 00000000..396969e2 --- /dev/null +++ b/tests/Services/InfoProviderSystem/DTOtoEntityConverterTest.php @@ -0,0 +1,213 @@ +. + */ +namespace App\Tests\Services\InfoProviderSystem; + +use App\Entity\Attachments\AttachmentType; +use App\Entity\Parts\ManufacturingStatus; +use App\Services\InfoProviderSystem\DTOs\FileDTO; +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\Services\InfoProviderSystem\DTOtoEntityConverter; +use PhpParser\Node\Param; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class DTOtoEntityConverterTest extends WebTestCase +{ + + private ?DTOtoEntityConverter $service = null; + + public function setUp(): void + { + self::bootKernel(); + $this->service = self::getContainer()->get(DTOtoEntityConverter::class); + } + + public function testConvertParameter(): void + { + $dto = new ParameterDTO( + name: 'TestParameter', + value_text: 'Text', + value_typ: 10.0, value_min: 0.0, value_max: 100.0, + unit: 'kg', symbol: 'TP', group: 'TestGroup' + ); + + $entity = $this->service->convertParameter($dto); + + $this->assertSame($dto->name, $entity->getName()); + $this->assertEquals($dto->value_text, $entity->getValueText()); + $this->assertEquals($dto->value_typ, $entity->getValueTypical()); + $this->assertEquals($dto->value_min, $entity->getValueMin()); + $this->assertEquals($dto->value_max, $entity->getValueMax()); + $this->assertEquals($dto->unit, $entity->getUnit()); + $this->assertEquals($dto->symbol, $entity->getSymbol()); + $this->assertEquals($dto->group, $entity->getGroup()); + } + + public function testConvertPriceOtherCurrency(): void + { + $dto = new PriceDTO( + minimum_discount_amount: 5, + price: "10.0", + currency_iso_code: 'CNY', + includes_tax: true, + price_related_quantity: 10.0, + ); + + $entity = $this->service->convertPrice($dto); + $this->assertSame($dto->minimum_discount_amount, $entity->getMinDiscountQuantity()); + $this->assertSame((float) $dto->price, (float) (string) $entity->getPrice()); + $this->assertEquals($dto->price_related_quantity, $entity->getPriceRelatedQuantity()); + + //For non-base currencies, a new currency entity is created + $currency = $entity->getCurrency(); + $this->assertEquals($dto->currency_iso_code, $currency->getIsoCode()); + } + + public function testConvertPriceBaseCurrency(): void + { + $dto = new PriceDTO( + minimum_discount_amount: 5, + price: "10.0", + currency_iso_code: 'EUR', + includes_tax: true, + ); + + $entity = $this->service->convertPrice($dto); + + //For base currencies, the currency field is null + $this->assertNull($entity->getCurrency()); + } + + public function testConvertPurchaseInfo(): void + { + $prices = [ + new PriceDTO(1, "10.0", 'EUR'), + new PriceDTO(5, "9.0", 'EUR'), + ]; + + $dto = new PurchaseInfoDTO( + distributor_name: 'TestDistributor', + order_number: 'TestOrderNumber', + prices: $prices, + product_url: 'https://example.com', + ); + + $entity = $this->service->convertPurchaseInfo($dto); + + $this->assertSame($dto->distributor_name, $entity->getSupplier()->getName()); + $this->assertSame($dto->order_number, $entity->getSupplierPartNr()); + $this->assertEquals($dto->product_url, $entity->getSupplierProductUrl()); + } + + public function testConvertFileWithName(): void + { + $dto = new FileDTO(url: 'https://invalid.com/file.pdf', name: 'TestFile'); + $type = new AttachmentType(); + + + $entity = $this->service->convertFile($dto, $type); + + $this->assertEquals($dto->name, $entity->getName()); + $this->assertSame($dto->url, $entity->getUrl()); + $this->assertEquals($type, $entity->getAttachmentType()); + } + + public function testConvertFileWithoutName(): void + { + $dto = new FileDTO(url: 'https://invalid.invalid/file.pdf'); + $type = new AttachmentType(); + + + $entity = $this->service->convertFile($dto, $type); + + //If no name is given, the name is derived from the url + $this->assertSame('file.pdf', $entity->getName()); + $this->assertSame($dto->url, $entity->getUrl()); + $this->assertEquals($type, $entity->getAttachmentType()); + } + + public function testConvertPart(): void + { + $parameters = [new ParameterDTO('Test', 'Test'), new ParameterDTO('Duplicate', 'Test'), new ParameterDTO('Test', 'test', group: "Other"), new ParameterDTO('Duplicate', 'ds')]; + $datasheets = [new FileDTO('https://invalid.invalid/file.pdf'), new FileDTO('https://invalid.invalid/file.pdf', name: 'TestFile'), new FileDTO('https://invalid.invalid/file2.pdf', name: 'Duplicate'), new FileDTO('https://invalid.invalid/file3.pdf', name: 'Duplicate')]; + $images = [new FileDTO('https://invalid.invalid/image.png'), new FileDTO('https://invalid.invalid/image2.png', name: 'TestImage2'), new FileDTO('https://invalid.invalid/image3.png', name: "Duplicate")]; + $shopping_infos = [new PurchaseInfoDTO('TestDistributor', 'TestOrderNumber', [new PriceDTO(1, "10.0", 'EUR')])]; + + $dto = new PartDetailDTO( + provider_key: 'test_provider', provider_id: 'test_id', provider_url: 'https://invalid.invalid/test_id', + name: 'TestPart', description: 'TestDescription', category: 'TestCategory', + manufacturer: 'TestManufacturer', mpn: 'TestMPN', manufacturing_status: ManufacturingStatus::EOL, + preview_image_url: 'https://invalid.invalid/image.png', + footprint: 'DIP8', notes: 'TestNotes', mass: 10.4, + parameters: $parameters, datasheets: $datasheets, vendor_infos: $shopping_infos, images: $images + ); + + $entity = $this->service->convertPart($dto); + + $this->assertSame($dto->name, $entity->getName()); + $this->assertSame($dto->description, $entity->getDescription()); + $this->assertSame($dto->notes, $entity->getComment()); + + $this->assertSame($dto->manufacturer, $entity->getManufacturer()->getName()); + $this->assertSame($dto->mpn, $entity->getManufacturerProductNumber()); + $this->assertSame($dto->manufacturing_status, $entity->getManufacturingStatus()); + + $this->assertEquals($dto->mass, $entity->getMass()); + $this->assertEquals($dto->footprint, $entity->getFootprint()); + + //We just check that the lenghts of parameters, datasheets, images and shopping infos are the same + //The actual content is tested in the corresponding tests + $this->assertCount(count($parameters), $entity->getParameters()); + $this->assertCount(count($shopping_infos), $entity->getOrderdetails()); + + //Test that duplicate parameters get renamed: + $this->assertSame('Test', $entity->getParameters()[0]->getName()); + $this->assertSame('Duplicate', $entity->getParameters()[1]->getName()); + $this->assertSame('Test', $entity->getParameters()[2]->getName()); + $this->assertSame('Duplicate (2)', $entity->getParameters()[3]->getName()); + + //Datasheets and images are stored as attachments and the duplicates, should be filtered out + $this->assertCount(6, $entity->getAttachments()); + //The attachments should have the name of the named duplicate file + $image1 = $entity->getAttachments()[0]; + $this->assertSame('Main image', $image1->getName()); + + $image1 = $entity->getAttachments()[1]; + $this->assertSame('TestImage2', $image1->getName()); + + $datasheet = $entity->getAttachments()[2]; + $this->assertSame('Duplicate', $datasheet->getName()); + + $datasheet = $entity->getAttachments()[3]; + $this->assertSame('TestFile', $datasheet->getName()); + + $datasheet = $entity->getAttachments()[4]; + $this->assertSame('Duplicate (2)', $datasheet->getName()); + + $datasheet = $entity->getAttachments()[5]; + $this->assertSame('Duplicate (3)', $datasheet->getName()); + } +} diff --git a/tests/Services/InfoProviderSystem/ProviderRegistryTest.php b/tests/Services/InfoProviderSystem/ProviderRegistryTest.php new file mode 100644 index 00000000..9026c5bf --- /dev/null +++ b/tests/Services/InfoProviderSystem/ProviderRegistryTest.php @@ -0,0 +1,112 @@ +. + */ +namespace App\Tests\Services\InfoProviderSystem; + +use App\Services\InfoProviderSystem\ProviderRegistry; +use App\Services\InfoProviderSystem\Providers\InfoProviderInterface; +use PHPUnit\Framework\TestCase; + +class ProviderRegistryTest extends TestCase +{ + + /** @var InfoProviderInterface[] */ + private array $providers = []; + + public function setUp(): void + { + //Create some mock providers + $this->providers = [ + $this->getMockProvider('test1'), + $this->getMockProvider('test2'), + $this->getMockProvider('test3', false), + ]; + } + + public function getMockProvider(string $key, bool $active = true): InfoProviderInterface + { + $mock = $this->createMock(InfoProviderInterface::class); + $mock->method('getProviderKey')->willReturn($key); + $mock->method('isActive')->willReturn($active); + + return $mock; + } + + public function testGetProviders(): void + { + $registry = new ProviderRegistry($this->providers); + + $this->assertEquals( + [ + 'test1' => $this->providers[0], + 'test2' => $this->providers[1], + 'test3' => $this->providers[2], + ], + $registry->getProviders()); + } + + public function testGetDisabledProviders(): void + { + $registry = new ProviderRegistry($this->providers); + + $this->assertEquals( + [ + 'test3' => $this->providers[2], + ], + $registry->getDisabledProviders()); + } + + public function testGetActiveProviders(): void + { + $registry = new ProviderRegistry($this->providers); + + $this->assertEquals( + [ + 'test1' => $this->providers[0], + 'test2' => $this->providers[1], + ], + $registry->getActiveProviders()); + } + + public function testGetProviderByKey(): void + { + $registry = new ProviderRegistry($this->providers); + + $this->assertEquals( + $this->providers[0], + $registry->getProviderByKey('test1') + ); + } + + public function testThrowOnDuplicateKeyOfProviders(): void + { + $this->expectException(\LogicException::class); + + $registry = new ProviderRegistry([ + $this->getMockProvider('test1'), + $this->getMockProvider('test2'), + $this->getMockProvider('test1'), + ]); + + $registry->getProviders(); + } +} diff --git a/tests/Services/LabelSystem/Barcodes/BarcodeRedirectorTest.php b/tests/Services/LabelSystem/BarcodeScanner/BarcodeRedirectorTest.php similarity index 66% rename from tests/Services/LabelSystem/Barcodes/BarcodeRedirectorTest.php rename to tests/Services/LabelSystem/BarcodeScanner/BarcodeRedirectorTest.php index 5e7a7ad8..58030f93 100644 --- a/tests/Services/LabelSystem/Barcodes/BarcodeRedirectorTest.php +++ b/tests/Services/LabelSystem/BarcodeScanner/BarcodeRedirectorTest.php @@ -39,18 +39,18 @@ declare(strict_types=1); * along with this program. If not, see . */ -namespace App\Tests\Services\LabelSystem\Barcodes; +namespace App\Tests\Services\LabelSystem\BarcodeScanner; -use App\Services\LabelSystem\Barcodes\BarcodeRedirector; +use App\Entity\LabelSystem\LabelSupportedElement; +use App\Services\LabelSystem\BarcodeScanner\BarcodeRedirector; +use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType; +use App\Services\LabelSystem\BarcodeScanner\LocalBarcodeScanResult; use Doctrine\ORM\EntityNotFoundException; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; -class BarcodeRedirectorTest extends KernelTestCase +final class BarcodeRedirectorTest extends KernelTestCase { - /** - * @var BarcodeRedirector - */ - private $service; + private ?BarcodeRedirector $service = null; protected function setUp(): void { @@ -58,35 +58,28 @@ class BarcodeRedirectorTest extends KernelTestCase $this->service = self::getContainer()->get(BarcodeRedirector::class); } - public function urlDataProvider(): array + public static function urlDataProvider(): \Iterator { - return [ - ['part', '/en/part/1'], - //Part lot redirects to Part info page (Part lot 1 is associated with part 3 - ['lot', '/en/part/3'], - ['location', '/en/store_location/1/parts'], - ]; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 1, BarcodeSourceType::INTERNAL), '/en/part/1']; + //Part lot redirects to Part info page (Part lot 1 is associated with part 3) + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 1, BarcodeSourceType::INTERNAL), '/en/part/3']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 1, BarcodeSourceType::INTERNAL), '/en/store_location/1/parts']; } /** * @dataProvider urlDataProvider * @group DB */ - public function testGetRedirectURL(string $type, string $url): void + public function testGetRedirectURL(LocalBarcodeScanResult $scanResult, string $url): void { - $this->assertSame($url, $this->service->getRedirectURL($type, 1)); + $this->assertSame($url, $this->service->getRedirectURL($scanResult)); } public function testGetRedirectEntityNotFount(): void { $this->expectException(EntityNotFoundException::class); //If we encounter an invalid lot, we must throw an exception - $this->service->getRedirectURL('lot', 12345678); - } - - public function testInvalidType(): void - { - $this->expectException(\InvalidArgumentException::class); - $this->service->getRedirectURL('invalid', 1); + $this->service->getRedirectURL(new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, + 12_345_678, BarcodeSourceType::INTERNAL)); } } diff --git a/tests/Services/LabelSystem/BarcodeScanner/BarcodeScanHelperTest.php b/tests/Services/LabelSystem/BarcodeScanner/BarcodeScanHelperTest.php new file mode 100644 index 00000000..74ae068a --- /dev/null +++ b/tests/Services/LabelSystem/BarcodeScanner/BarcodeScanHelperTest.php @@ -0,0 +1,159 @@ +. + */ + +declare(strict_types=1); + +/** + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2020 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 . + */ + +namespace App\Tests\Services\LabelSystem\BarcodeScanner; + +use App\Entity\LabelSystem\LabelSupportedElement; +use App\Services\LabelSystem\BarcodeScanner\BarcodeScanHelper; +use App\Services\LabelSystem\BarcodeScanner\BarcodeScanResultInterface; +use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType; +use App\Services\LabelSystem\BarcodeScanner\EIGP114BarcodeScanResult; +use App\Services\LabelSystem\BarcodeScanner\LocalBarcodeScanResult; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class BarcodeScanHelperTest extends WebTestCase +{ + private ?BarcodeScanHelper $service = null; + + protected function setUp(): void + { + self::bootKernel(); + $this->service = self::getContainer()->get(BarcodeScanHelper::class); + } + + public static function dataProvider(): \Iterator + { + //QR URL content: + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 1, BarcodeSourceType::INTERNAL), + 'https://localhost:8000/scan/lot/1']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL), + 'https://localhost:8000/scan/part/123']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 4, BarcodeSourceType::INTERNAL), + 'http://foo.bar/part-db/scan/location/4']; + + //Current Code39 format: + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL), + 'L0010']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 123, BarcodeSourceType::INTERNAL), + 'L0123']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 123456, BarcodeSourceType::INTERNAL), + 'L123456']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 2, BarcodeSourceType::INTERNAL), + 'P0002']; + + //Development phase Code39 barcodes: + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL), + 'L-000010']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL), + 'Lß000010']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL), + 'P-000123']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 123, BarcodeSourceType::INTERNAL), + 'S-000123']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 12_345_678, BarcodeSourceType::INTERNAL), + 'L-12345678']; + + //Legacy storelocation format + yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 336, BarcodeSourceType::INTERNAL), + '$L00336']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 12_345_678, BarcodeSourceType::INTERNAL), + '$L12345678']; + + //Legacy Part format + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL), + '0000123']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL), + '00001236']; + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 1_234_567, BarcodeSourceType::INTERNAL), + '12345678']; + + //Test IPN barcode + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 2, BarcodeSourceType::IPN), + 'IPN123']; + + //Test vendor barcode + yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 2,BarcodeSourceType::USER_DEFINED), + 'lot2_vendor_barcode']; + + $eigp114Result = new EIGP114BarcodeScanResult([ + 'P' => '596-777A1-ND', + '1P' => 'XAF4444', + 'Q' => '3', + '10D' => '1452', + '1T' => 'BF1103', + '4L' => 'US', + ]); + + yield [$eigp114Result, "[)>\x1E06\x1DP596-777A1-ND\x1D1PXAF4444\x1DQ3\x1D10D1452\x1D1TBF1103\x1D4LUS\x1E\x04"]; + } + + public static function invalidDataProvider(): \Iterator + { + yield ['https://localhost/part/1']; + //Without scan + yield ['L-']; + //Without number + yield ['L-123']; + //Too short + yield ['X-123456']; + //Unknown prefix + yield ['XXXWADSDF sdf']; + //Garbage + yield ['']; + } + + /** + * @dataProvider dataProvider + */ + public function testNormalizeBarcodeContent(BarcodeScanResultInterface $expected, string $input): void + { + $this->assertEquals($expected, $this->service->scanBarcodeContent($input)); + } + + /** + * @dataProvider invalidDataProvider + */ + public function testInvalidFormats(string $input): void + { + $this->expectException(\InvalidArgumentException::class); + $this->service->scanBarcodeContent($input); + } +} diff --git a/tests/Services/LabelSystem/BarcodeScanner/EIGP114BarcodeScanResultTest.php b/tests/Services/LabelSystem/BarcodeScanner/EIGP114BarcodeScanResultTest.php new file mode 100644 index 00000000..aa5e4fbc --- /dev/null +++ b/tests/Services/LabelSystem/BarcodeScanner/EIGP114BarcodeScanResultTest.php @@ -0,0 +1,154 @@ +. + */ + +namespace App\Tests\Services\LabelSystem\BarcodeScanner; + +use App\Services\LabelSystem\BarcodeScanner\EIGP114BarcodeScanResult; +use PHPUnit\Framework\TestCase; + +class EIGP114BarcodeScanResultTest extends TestCase +{ + + public function testGuessBarcodeVendor(): void + { + //Generic barcode: + + $barcode = new EIGP114BarcodeScanResult([ + 'P' => '596-777A1-ND', + '1P' => 'XAF4444', + 'Q' => '3', + '10D' => '1452', + '1T' => 'BF1103', + '4L' => 'US', + ]); + + $this->assertNull($barcode->guessBarcodeVendor()); + + //Digikey barcode: + $barcode = new EIGP114BarcodeScanResult([ + 'P' => '596-777A1-ND', + '1P' => 'XAF4444', + 'Q' => '3', + '10D' => '1452', + '1T' => 'BF1103', + '4L' => 'US', + '13Z' => 'Digi-Key', + ]); + $this->assertEquals('digikey', $barcode->guessBarcodeVendor()); + + //Mouser barcode: + $barcode = new EIGP114BarcodeScanResult([ + 'P' => '596-777A1-ND', + '1P' => 'XAF4444', + 'Q' => '3', + '10D' => '1452', + '1T' => 'BF1103', + '4L' => 'US', + '1V' => 'Mouser', + ]); + + $this->assertEquals('mouser', $barcode->guessBarcodeVendor()); + + //Farnell barcode: + $barcode = new EIGP114BarcodeScanResult([ + 'P' => '596-777A1-ND', + '1P' => 'XAF4444', + 'Q' => '3', + '10D' => '1452', + '1T' => 'BF1103', + '4L' => 'US', + '3P' => 'Farnell', + ]); + + $this->assertEquals('element14', $barcode->guessBarcodeVendor()); + } + + public function testIsFormat06Code(): void + { + $this->assertFalse(EIGP114BarcodeScanResult::isFormat06Code('')); + $this->assertFalse(EIGP114BarcodeScanResult::isFormat06Code('test')); + $this->assertFalse(EIGP114BarcodeScanResult::isFormat06Code('12232435ew4rf')); + + //Valid code (with trailer) + $this->assertTrue(EIGP114BarcodeScanResult::isFormat06Code("[)>\x1E06\x1DP596-777A1-ND\x1D1PXAF4444\x1DQ3\x1D10D1452\x1D1TBF1103\x1D4LUS\x1E\x04")); + + //Valid code (digikey, without trailer) + $this->assertTrue(EIGP114BarcodeScanResult::isFormat06Code("[)>\x1e06\x1dPQ1045-ND\x1d1P364019-01\x1d30PQ1045-ND\x1dK12432 TRAVIS FOSS P\x1d1K85732873\x1d10K103332956\x1d9D231013\x1d1TQJ13P\x1d11K1\x1d4LTW\x1dQ3\x1d11ZPICK\x1d12Z7360988\x1d13Z999999\x1d20Z0000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + } + + public function testParseFormat06CodeInvalid(): void + { + $this->expectException(\InvalidArgumentException::class); + EIGP114BarcodeScanResult::parseFormat06Code(''); + } + + public function testParseFormat06Code(): void + { + $barcode = EIGP114BarcodeScanResult::parseFormat06Code("[)>\x1E06\x1DP596-777A1-ND\x1D1PXAF4444\x1DQ3\x1D10D1452\x1D1TBF1103\x1D4LUS\x1E\x04"); + $this->assertEquals([ + 'P' => '596-777A1-ND', + '1P' => 'XAF4444', + 'Q' => '3', + '10D' => '1452', + '1T' => 'BF1103', + '4L' => 'US', + ], $barcode->data); + } + + public function testDataParsing(): void + { + $barcode = new EIGP114BarcodeScanResult([ + 'P' => '596-777A1-ND', + '1P' => 'XAF4444', + 'Q' => '3', + '10D' => '1452', + '1T' => 'BF1103', + '4L' => 'US', + ]); + + $this->assertEquals('596-777A1-ND', $barcode->customerPartNumber); + $this->assertEquals('XAF4444', $barcode->supplierPartNumber); + $this->assertEquals(3, $barcode->quantity); + $this->assertEquals('1452', $barcode->alternativeDateCode); + $this->assertEquals('BF1103', $barcode->lotCode); + $this->assertEquals('US', $barcode->countryOfOrigin); + } + + public function testDigikeyParsing(): void + { + $barcode = EIGP114BarcodeScanResult::parseFormat06Code("[)>\x1e06\x1dPQ1045-ND\x1d1P364019-01\x1d30PQ1045-ND\x1dK12432 TRAVIS FOSS P\x1d1K85732873\x1d10K103332956\x1d9D231013\x1d1TQJ13P\x1d11K1\x1d4LTW\x1dQ3\x1d11ZPICK\x1d12Z7360988\x1d13Z999999\x1d20Z0000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + + $this->assertEquals('digikey', $barcode->guessBarcodeVendor()); + + $this->assertEquals('Q1045-ND', $barcode->customerPartNumber); + $this->assertEquals('364019-01', $barcode->supplierPartNumber); + $this->assertEquals(3, $barcode->quantity); + $this->assertEquals('231013', $barcode->dateCode); + $this->assertEquals('QJ13P', $barcode->lotCode); + $this->assertEquals('TW', $barcode->countryOfOrigin); + $this->assertEquals('Q1045-ND', $barcode->digikeyPartNumber); + $this->assertEquals('85732873', $barcode->digikeySalesOrderNumber); + $this->assertEquals('103332956', $barcode->digikeyInvoiceNumber); + $this->assertEquals('PICK', $barcode->digikeyLabelType); + $this->assertEquals('7360988', $barcode->digikeyPartID); + $this->assertEquals('999999', $barcode->digikeyNA); + $this->assertEquals('0000000000000000000000000000000000000000000000000000000000000000000000000000000000000', $barcode->digikeyPadding); + } +} diff --git a/tests/Services/LabelSystem/Barcodes/BarcodeContentGeneratorTest.php b/tests/Services/LabelSystem/Barcodes/BarcodeContentGeneratorTest.php index fe98ccdf..9d0ed7e2 100644 --- a/tests/Services/LabelSystem/Barcodes/BarcodeContentGeneratorTest.php +++ b/tests/Services/LabelSystem/Barcodes/BarcodeContentGeneratorTest.php @@ -43,16 +43,13 @@ namespace App\Tests\Services\LabelSystem\Barcodes; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Services\LabelSystem\Barcodes\BarcodeContentGenerator; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class BarcodeContentGeneratorTest extends KernelTestCase { - /** - * @var BarcodeContentGenerator - */ - private $service; + private ?object $service = null; protected function setUp(): void { @@ -60,22 +57,18 @@ class BarcodeContentGeneratorTest extends KernelTestCase $this->service = self::getContainer()->get(BarcodeContentGenerator::class); } - public function Barcode1DDataProvider(): array + public function Barcode1DDataProvider(): \Iterator { - return [ - ['P0000', Part::class], - ['L0000', PartLot::class], - ['S0000', Storelocation::class], - ]; + yield ['P0000', Part::class]; + yield ['L0000', PartLot::class]; + yield ['S0000', StorageLocation::class]; } - public function Barcode2DDataProvider(): array + public function Barcode2DDataProvider(): \Iterator { - return [ - ['/scan/part/0', Part::class], - ['/scan/lot/0', PartLot::class], - ['/scan/location/0', Storelocation::class], - ]; + yield ['/scan/part/0', Part::class]; + yield ['/scan/lot/0', PartLot::class]; + yield ['/scan/location/0', StorageLocation::class]; } /** diff --git a/tests/Services/LabelSystem/Barcodes/BarcodeHelperTest.php b/tests/Services/LabelSystem/Barcodes/BarcodeHelperTest.php new file mode 100644 index 00000000..d681b3b9 --- /dev/null +++ b/tests/Services/LabelSystem/Barcodes/BarcodeHelperTest.php @@ -0,0 +1,70 @@ +. + */ +namespace App\Tests\Services\LabelSystem\Barcodes; + +use App\Entity\LabelSystem\BarcodeType; +use App\Services\LabelSystem\Barcodes\BarcodeHelper; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class BarcodeHelperTest extends WebTestCase +{ + + protected ?BarcodeHelper $service = null; + + protected function setUp(): void + { + self::bootKernel(); + $this->service = self::getContainer()->get(BarcodeHelper::class); + } + + public function testBarcodeAsHTML(): void + { + $html = $this->service->barcodeAsHTML('Test', BarcodeType::QR); + $this->assertStringStartsWith('assertStringContainsString('alt="Test"', $html); + } + + public function testBarcodeAsSVG(): void + { + //Test that all barcodes types are supported + foreach (BarcodeType::cases() as $type) { + //Skip NONE type + if (BarcodeType::NONE === $type) { + continue; + } + + $svg = $this->service->barcodeAsSVG('1234', $type); + + $this->assertStringContainsStringIgnoringCase('SVG', $svg); + } + } + + public function testBarcodeAsSVGNoneType(): void + { + //On NONE type, service must throw an exception. + $this->expectException(\InvalidArgumentException::class); + + $this->service->barcodeAsSVG('test', BarcodeType::NONE); + } +} diff --git a/tests/Services/LabelSystem/Barcodes/BarcodeNormalizerTest.php b/tests/Services/LabelSystem/Barcodes/BarcodeNormalizerTest.php deleted file mode 100644 index b8d34cc0..00000000 --- a/tests/Services/LabelSystem/Barcodes/BarcodeNormalizerTest.php +++ /dev/null @@ -1,117 +0,0 @@ -. - */ - -declare(strict_types=1); - -/** - * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). - * - * Copyright (C) 2019 - 2020 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 . - */ - -namespace App\Tests\Services\LabelSystem\Barcodes; - -use App\Services\LabelSystem\Barcodes\BarcodeNormalizer; -use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; - -class BarcodeNormalizerTest extends WebTestCase -{ - /** - * @var BarcodeNormalizer - */ - protected $service; - - protected function setUp(): void - { - self::bootKernel(); - $this->service = self::getContainer()->get(BarcodeNormalizer::class); - } - - public function dataProvider(): array - { - return [ - //QR URL content: - [['lot', 1], 'https://localhost:8000/scan/lot/1'], - [['part', 123], 'https://localhost:8000/scan/part/123'], - [['location', 4], 'http://foo.bar/part-db/scan/location/4'], - [['under_score', 10], 'http://test/part-db/sub/scan/under_score/10/'], - //Current Code39 format: - [['lot', 10], 'L0010'], - [['lot', 123], 'L0123'], - [['lot', 123456], 'L123456'], - [['part', 2], 'P0002'], - //Development phase Code39 barcodes: - [['lot', 10], 'L-000010'], - [['lot', 10], 'Lß000010'], - [['part', 123], 'P-000123'], - [['location', 123], 'S-000123'], - [['lot', 12345678], 'L-12345678'], - //Legacy storelocation format - [['location', 336], '$L00336'], - [['location', 12345678], '$L12345678'], - //Legacy Part format - [['part', 123], '0000123'], - [['part', 123], '00001236'], - [['part', 1234567], '12345678'], - ]; - } - - public function invalidDataProvider(): array - { - return [ - ['https://localhost/part/1'], //Without scan - ['L-'], //Without number - ['L-123'], //Too short - ['X-123456'], //Unknown prefix - ['XXXWADSDF sdf'], //Garbage - [''], //Empty - ]; - } - - /** - * @dataProvider dataProvider - */ - public function testNormalizeBarcodeContent(array $expected, string $input): void - { - $this->assertSame($expected, $this->service->normalizeBarcodeContent($input)); - } - - /** - * @dataProvider invalidDataProvider - */ - public function testInvalidFormats(string $input): void - { - $this->expectException(\InvalidArgumentException::class); - $this->service->normalizeBarcodeContent($input); - } -} diff --git a/tests/Services/LabelSystem/BarcodeGeneratorTest.php b/tests/Services/LabelSystem/LabelBarcodeGeneratorTest.php similarity index 82% rename from tests/Services/LabelSystem/BarcodeGeneratorTest.php rename to tests/Services/LabelSystem/LabelBarcodeGeneratorTest.php index a82b630b..4afdd9d2 100644 --- a/tests/Services/LabelSystem/BarcodeGeneratorTest.php +++ b/tests/Services/LabelSystem/LabelBarcodeGeneratorTest.php @@ -41,22 +41,20 @@ declare(strict_types=1); namespace App\Tests\Services\LabelSystem; +use App\Entity\LabelSystem\BarcodeType; use App\Entity\LabelSystem\LabelOptions; use App\Entity\Parts\Part; -use App\Services\LabelSystem\BarcodeGenerator; +use App\Services\LabelSystem\LabelBarcodeGenerator; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; -final class BarcodeGeneratorTest extends WebTestCase +final class LabelBarcodeGeneratorTest extends WebTestCase { - /** - * @var BarcodeGenerator - */ - protected $services; + protected ?LabelBarcodeGenerator $service = null; protected function setUp(): void { self::bootKernel(); - $this->services = self::$container->get(BarcodeGenerator::class); + $this->service = self::getContainer()->get(LabelBarcodeGenerator::class); } public function testGetContent(): void @@ -65,13 +63,13 @@ final class BarcodeGeneratorTest extends WebTestCase $part->setName('Test'); //Test that all barcodes types are supported - foreach (LabelOptions::BARCODE_TYPES as $type) { + foreach (BarcodeType::cases() as $type) { $options = new LabelOptions(); $options->setBarcodeType($type); - $content = $this->services->generateSVG($options, $part); + $content = $this->service->generateSVG($options, $part); //When type is none, service must return null. - if ('none' === $type) { + if (BarcodeType::NONE === $type) { $this->assertNull($content); } else { $this->assertIsString($content); @@ -85,13 +83,13 @@ final class BarcodeGeneratorTest extends WebTestCase $part->setName('Test'); //Test that all barcodes types are supported - foreach (LabelOptions::BARCODE_TYPES as $type) { + foreach (BarcodeType::cases() as $type) { $options = new LabelOptions(); $options->setBarcodeType($type); - $svg = $this->services->generateSVG($options, $part); + $svg = $this->service->generateSVG($options, $part); //When type is none, service must return null. - if ('none' === $type) { + if (BarcodeType::NONE === $type) { $this->assertNull($svg); } else { $this->assertStringContainsStringIgnoringCase('SVG', $svg); diff --git a/tests/Services/LabelSystem/LabelGeneratorTest.php b/tests/Services/LabelSystem/LabelGeneratorTest.php index 68254f3e..88ab97c5 100644 --- a/tests/Services/LabelSystem/LabelGeneratorTest.php +++ b/tests/Services/LabelSystem/LabelGeneratorTest.php @@ -43,9 +43,10 @@ namespace App\Tests\Services\LabelSystem; use App\Entity\Base\AbstractDBElement; use App\Entity\LabelSystem\LabelOptions; +use App\Entity\LabelSystem\LabelSupportedElement; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Services\LabelSystem\LabelGenerator; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; @@ -62,19 +63,17 @@ class LabelGeneratorTest extends WebTestCase $this->service = self::getContainer()->get(LabelGenerator::class); } - public function supportsDataProvider(): array + public static function supportsDataProvider(): \Iterator { - return [ - ['part', Part::class], - ['part_lot', PartLot::class], - ['storelocation', Storelocation::class], - ]; + yield [LabelSupportedElement::PART, Part::class]; + yield [LabelSupportedElement::PART_LOT, PartLot::class]; + yield [LabelSupportedElement::STORELOCATION, StorageLocation::class]; } /** * @dataProvider supportsDataProvider */ - public function testSupports(string $type, string $class): void + public function testSupports(LabelSupportedElement $type, string $class): void { $options = new LabelOptions(); $options->setSupportedElement($type); @@ -102,7 +101,7 @@ class LabelGeneratorTest extends WebTestCase $part = new Part(); $options = new LabelOptions(); $options->setLines('Test'); - $options->setSupportedElement('part'); + $options->setSupportedElement(LabelSupportedElement::PART); //Test for a single passed element: $pdf = $this->service->generateLabel($options, $part); diff --git a/tests/Services/LabelSystem/LabelTextReplacerTest.php b/tests/Services/LabelSystem/LabelTextReplacerTest.php index 09635562..e5c7c26e 100644 --- a/tests/Services/LabelSystem/LabelTextReplacerTest.php +++ b/tests/Services/LabelSystem/LabelTextReplacerTest.php @@ -51,18 +51,16 @@ class LabelTextReplacerTest extends WebTestCase /** * @var LabelTextReplacer */ - protected $service; + protected LabelTextReplacer $service; /** * @var Part */ - protected $target; + protected Part $target; protected function setUp(): void { - parent::setUp(); - - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(LabelTextReplacer::class); @@ -72,32 +70,28 @@ class LabelTextReplacerTest extends WebTestCase $this->target->setComment('P Comment'); } - public function handlePlaceholderDataProvider(): array + public function handlePlaceholderDataProvider(): \Iterator { - return [ - ['Part 1', '[[NAME]]'], - ['P Description', '[[DESCRIPTION]]'], - ['[[UNKNOWN]]', '[[UNKNOWN]]', '[[UNKNOWN]]'], - ['[[INVALID', '[[INVALID'], - ['[[', '[['], - ['NAME', 'NAME'], - ['[[NAME', '[[NAME'], - ['Test [[NAME]]', 'Test [[NAME]]', 'Test [[NAME]]'], - ]; + yield ['Part 1', '[[NAME]]']; + yield ['P Description', '[[DESCRIPTION]]']; + yield ['[[UNKNOWN]]', '[[UNKNOWN]]', '[[UNKNOWN]]']; + yield ['[[INVALID', '[[INVALID']; + yield ['[[', '[[']; + yield ['NAME', 'NAME']; + yield ['[[NAME', '[[NAME']; + yield ['Test [[NAME]]', 'Test [[NAME]]', 'Test [[NAME]]']; } - public function replaceDataProvider(): array + public function replaceDataProvider(): \Iterator { - return [ - ['Part 1', '[[NAME]]'], - ['TestPart 1', 'Test[[NAME]]'], - ["P Description\nPart 1", "[[DESCRIPTION_T]]\n[[NAME]]"], - ['Part 1 Part 1', '[[NAME]] [[NAME]]'], - ['[[UNKNOWN]] Test', '[[UNKNOWN]] Test'], - ["[[NAME\n]] [[NAME ]]", "[[NAME\n]] [[NAME ]]"], - ['[[]]', '[[]]'], - ['TEST[[ ]]TEST', 'TEST[[ ]]TEST'], - ]; + yield ['Part 1', '[[NAME]]']; + yield ['TestPart 1', 'Test[[NAME]]']; + yield ["P Description\nPart 1", "[[DESCRIPTION_T]]\n[[NAME]]"]; + yield ['Part 1 Part 1', '[[NAME]] [[NAME]]']; + yield ['[[UNKNOWN]] Test', '[[UNKNOWN]] Test']; + yield ["[[NAME\n]] [[NAME ]]", "[[NAME\n]] [[NAME ]]"]; + yield ['[[]]', '[[]]']; + yield ['TEST[[ ]]TEST', 'TEST[[ ]]TEST']; } /** diff --git a/tests/Services/LabelSystem/PlaceholderProviders/AbstractElementProviderTest.php b/tests/Services/LabelSystem/PlaceholderProviders/AbstractElementProviderTest.php index ee32ca94..3c40a9ac 100644 --- a/tests/Services/LabelSystem/PlaceholderProviders/AbstractElementProviderTest.php +++ b/tests/Services/LabelSystem/PlaceholderProviders/AbstractElementProviderTest.php @@ -63,11 +63,9 @@ class AbstractElementProviderTest extends WebTestCase }; } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - ['123', '[[ID]]'], - ]; + yield ['123', '[[ID]]']; } /** diff --git a/tests/Services/LabelSystem/PlaceholderProviders/GlobalProvidersTest.php b/tests/Services/LabelSystem/PlaceholderProviders/GlobalProvidersTest.php index 3a66aa87..854e7467 100644 --- a/tests/Services/LabelSystem/PlaceholderProviders/GlobalProvidersTest.php +++ b/tests/Services/LabelSystem/PlaceholderProviders/GlobalProvidersTest.php @@ -61,12 +61,10 @@ class GlobalProvidersTest extends WebTestCase $this->target = new Part(); } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - ['Part-DB', '[[INSTALL_NAME]]'], - ['anonymous', '[[USERNAME]]'], - ]; + yield ['Part-DB', '[[INSTALL_NAME]]']; + yield ['anonymous', '[[USERNAME]]']; } /** diff --git a/tests/Services/LabelSystem/PlaceholderProviders/NamedElementProviderTest.php b/tests/Services/LabelSystem/PlaceholderProviders/NamedElementProviderTest.php index e5644515..7360b2d2 100644 --- a/tests/Services/LabelSystem/PlaceholderProviders/NamedElementProviderTest.php +++ b/tests/Services/LabelSystem/PlaceholderProviders/NamedElementProviderTest.php @@ -66,11 +66,9 @@ class NamedElementProviderTest extends WebTestCase }; } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - ['This is my Name', '[[NAME]]'], - ]; + yield ['This is my Name', '[[NAME]]']; } /** diff --git a/tests/Services/LabelSystem/PlaceholderProviders/PartLotProviderTest.php b/tests/Services/LabelSystem/PlaceholderProviders/PartLotProviderTest.php index cdede869..98285aa5 100644 --- a/tests/Services/LabelSystem/PlaceholderProviders/PartLotProviderTest.php +++ b/tests/Services/LabelSystem/PlaceholderProviders/PartLotProviderTest.php @@ -43,7 +43,8 @@ namespace App\Tests\Services\LabelSystem\PlaceholderProviders; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; +use App\Entity\UserSystem\User; use App\Services\LabelSystem\PlaceholderProviders\PartLotProvider; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; @@ -52,46 +53,52 @@ class PartLotProviderTest extends WebTestCase /** * @var PartLotProvider */ - protected $service; + protected PartLotProvider $service; - protected $target; + protected PartLot $target; protected function setUp(): void { self::bootKernel(); \Locale::setDefault('en'); - $this->service = self::$container->get(PartLotProvider::class); + $this->service = self::getContainer()->get(PartLotProvider::class); $this->target = new PartLot(); $this->target->setDescription('Lot description'); $this->target->setComment('Lot comment'); - $this->target->setExpirationDate(new \DateTime('1999-04-13')); + $this->target->setExpirationDate(new \DateTimeImmutable('1999-04-13')); $this->target->setInstockUnknown(true); - $location = new Storelocation(); + $location = new StorageLocation(); $location->setName('Location'); - $location->setParent((new Storelocation())->setName('Parent')); + $location->setParent((new StorageLocation())->setName('Parent')); $this->target->setStorageLocation($location); $part = new Part(); $part->setName('Part'); $part->setDescription('Part description'); $this->target->setPart($part); + + $user = new User(); + $user->setName('user'); + $user->setFirstName('John'); + $user->setLastName('Doe'); + $this->target->setOwner($user); } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - ['unknown', '[[LOT_ID]]'], - ['Lot description', '[[LOT_NAME]]'], - ['Lot comment', '[[LOT_COMMENT]]'], - ['4/13/99', '[[EXPIRATION_DATE]]'], - ['?', '[[AMOUNT]]'], - ['Location', '[[LOCATION]]'], - ['Parent → Location', '[[LOCATION_FULL]]'], - //Test part inheritance - ['Part', '[[NAME]]'], - ['Part description', '[[DESCRIPTION]]'], - ]; + yield ['unknown', '[[LOT_ID]]']; + yield ['Lot description', '[[LOT_NAME]]']; + yield ['Lot comment', '[[LOT_COMMENT]]']; + yield ['4/13/99', '[[EXPIRATION_DATE]]']; + yield ['?', '[[AMOUNT]]']; + yield ['Location', '[[LOCATION]]']; + yield ['Parent → Location', '[[LOCATION_FULL]]']; + //Test part inheritance + yield ['Part', '[[NAME]]']; + yield ['Part description', '[[DESCRIPTION]]']; + yield ['John Doe', '[[OWNER]]']; + yield ['user', '[[OWNER_USERNAME]]']; } /** diff --git a/tests/Services/LabelSystem/PlaceholderProviders/PartProviderTest.php b/tests/Services/LabelSystem/PlaceholderProviders/PartProviderTest.php index a97cb438..db3ebad1 100644 --- a/tests/Services/LabelSystem/PlaceholderProviders/PartProviderTest.php +++ b/tests/Services/LabelSystem/PlaceholderProviders/PartProviderTest.php @@ -41,6 +41,8 @@ declare(strict_types=1); namespace App\Tests\Services\LabelSystem\PlaceholderProviders; +use App\Entity\Parts\ManufacturingStatus; +use Doctrine\ORM\EntityManager; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Part; @@ -56,12 +58,12 @@ class PartProviderTest extends WebTestCase /** * @var PartProvider */ - protected $service; + protected PartProvider $service; - protected $target; + protected Part $target; /** - * @var \Doctrine\ORM\EntityManager + * @var EntityManager */ protected $em; @@ -79,31 +81,28 @@ class PartProviderTest extends WebTestCase $this->target->setMass(1234.2); $this->target->setTags('SMD, Tag1, Tag2'); $this->target->setManufacturerProductNumber('MPN123'); - $this->target->setManufacturingStatus('active'); + $this->target->setManufacturingStatus(ManufacturingStatus::ACTIVE); $this->target->setDescription('Bold *Italic*'); $this->target->setComment('Bold *Italic*'); } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - ['Node 2.1', '[[CATEGORY]]'], - ['Node 2 → Node 2.1', '[[CATEGORY_FULL]]'], - ['Node 2.1', '[[FOOTPRINT]]'], - ['Node 2 → Node 2.1', '[[FOOTPRINT_FULL]]'], - ['', '[[MANUFACTURER]]'], - ['', '[[MANUFACTURER_FULL]]'], - - ['1.2 kg', '[[MASS]]'], - ['MPN123', '[[MPN]]'], - ['SMD, Tag1, Tag2', '[[TAGS]]'], - ['Active', '[[M_STATUS]]'], - ['Bold Italic', '[[DESCRIPTION]]'], - ['Bold Italic', '[[DESCRIPTION_T]]'], - ['Bold Italic', '[[COMMENT]]'], - ['Bold Italic', '[[COMMENT_T]]'], - ]; + yield ['Node 2.1', '[[CATEGORY]]']; + yield ['Node 2 → Node 2.1', '[[CATEGORY_FULL]]']; + yield ['Node 2.1', '[[FOOTPRINT]]']; + yield ['Node 2 → Node 2.1', '[[FOOTPRINT_FULL]]']; + yield ['', '[[MANUFACTURER]]']; + yield ['', '[[MANUFACTURER_FULL]]']; + yield ['1.2 kg', '[[MASS]]']; + yield ['MPN123', '[[MPN]]']; + yield ['SMD, Tag1, Tag2', '[[TAGS]]']; + yield ['Active', '[[M_STATUS]]']; + yield ['Bold Italic', '[[DESCRIPTION]]']; + yield ['Bold Italic', '[[DESCRIPTION_T]]']; + yield ['Bold Italic', '[[COMMENT]]']; + yield ['Bold Italic', '[[COMMENT_T]]']; } /** diff --git a/tests/Services/LabelSystem/PlaceholderProviders/TimestampableElementProviderTest.php b/tests/Services/LabelSystem/PlaceholderProviders/TimestampableElementProviderTest.php index 79b9a95f..07bb4270 100644 --- a/tests/Services/LabelSystem/PlaceholderProviders/TimestampableElementProviderTest.php +++ b/tests/Services/LabelSystem/PlaceholderProviders/TimestampableElementProviderTest.php @@ -74,14 +74,11 @@ class TimestampableElementProviderTest extends WebTestCase }; } - public function dataProvider(): array + public function dataProvider(): \Iterator { \Locale::setDefault('en'); - - return [ - ['1/1/00, 12:00 AM', '[[LAST_MODIFIED]]'], - ['1/1/00, 12:00 AM', '[[CREATION_DATE]]'], - ]; + yield ['1/1/00, 12:00 AM', '[[LAST_MODIFIED]]']; + yield ['1/1/00, 12:00 AM', '[[CREATION_DATE]]']; } /** diff --git a/tests/Services/LabelSystem/SandboxedTwigProviderTest.php b/tests/Services/LabelSystem/SandboxedTwigFactoryTest.php similarity index 64% rename from tests/Services/LabelSystem/SandboxedTwigProviderTest.php rename to tests/Services/LabelSystem/SandboxedTwigFactoryTest.php index 92a6cd54..495a2f45 100644 --- a/tests/Services/LabelSystem/SandboxedTwigProviderTest.php +++ b/tests/Services/LabelSystem/SandboxedTwigFactoryTest.php @@ -42,63 +42,65 @@ declare(strict_types=1); namespace App\Tests\Services\LabelSystem; use App\Entity\LabelSystem\LabelOptions; +use App\Entity\LabelSystem\LabelProcessMode; +use App\Entity\LabelSystem\LabelSupportedElement; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; -use App\Entity\Parts\Storelocation; -use App\Services\LabelSystem\SandboxedTwigProvider; +use App\Entity\Parts\StorageLocation; +use App\Services\LabelSystem\SandboxedTwigFactory; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Twig\Sandbox\SecurityError; -class SandboxedTwigProviderTest extends WebTestCase +class SandboxedTwigFactoryTest extends WebTestCase { - /** - * @var SandboxedTwigProvider - */ - private $service; + private ?SandboxedTwigFactory $service = null; protected function setUp(): void { self::bootKernel(); - $this->service = self::$container->get(SandboxedTwigProvider::class); + $this->service = self::getContainer()->get(SandboxedTwigFactory::class); } - public function twigDataProvider(): array + public function twigDataProvider(): \Iterator { - return [ - [' {% for i in range(1, 3) %} + yield [' {% for i in range(1, 3) %} {{ part.id }} {{ part.name }} {{ part.lastModified | format_datetime }} {% endfor %} - '], - [' {% if part.category %} + ']; + yield [' {% if part.category %} {{ part.category }} {% endif %} - '], - [' {% set a = random(1, 3) %} + ']; + yield [' {% set a = random(1, 3) %} {{ 1 + 2 | abs }} {{ "test" | capitalize | escape | lower | raw }} {{ "\n" | nl2br | trim | title | url_encode | reverse }} - '], - [' + ']; + yield [' {{ location.isRoot}} {{ location.isChildOf(location) }} {{ location.comment }} {{ location.level }} - {{ location.fullPath }} {% set arr = location.pathArray %} {% set child = location.children %} {{location.childrenNotSelectable}} - '], - [' - {{ part.reviewNeeded }} {{ part.tags }} {{ part.mass }} - '], - ]; + {{ location.fullPath }} {% set arr = location.pathArray %} {% set child = location.children %} {{location.notSelectable}} + ']; + yield [' + {{ part.needsReview }} {{ part.tags }} {{ part.mass }} + ']; + yield [' + {{ entity_type(part) is object }} + ']; + yield [' + {% apply placeholders(part) %}[[NAME]]{% endapply %}
+ {{ placeholder("[[NAME]]", part) }} + ']; } - public function twigNotAllowedDataProvider(): array + public function twigNotAllowedDataProvider(): \Iterator { - return [ - ['{% block test %} {% endblock %}'], - ['{% deprecated test %}'], - ['{% flush %}'], - ["{{ part.setName('test') }}"], - ['{{ part.setCategory(null) }}'], - ]; + yield ['{% block test %} {% endblock %}']; + yield ['{% deprecated test %}']; + yield ['{% flush %}']; + yield ["{{ part.setName('test') }}"]; + yield ['{{ part.setCategory(null) }}']; } /** @@ -107,15 +109,15 @@ class SandboxedTwigProviderTest extends WebTestCase public function testTwigFeatures(string $twig): void { $options = new LabelOptions(); - $options->setSupportedElement('part'); + $options->setSupportedElement(LabelSupportedElement::PART); $options->setLines($twig); - $options->setLinesMode('twig'); + $options->setProcessMode(LabelProcessMode::TWIG); - $twig = $this->service->getTwig($options); + $twig = $this->service->createTwig($options); $str = $twig->render('lines', [ 'part' => new Part(), 'lot' => new PartLot(), - 'location' => new Storelocation(), + 'location' => new StorageLocation(), ]); $this->assertIsString($str); @@ -129,15 +131,17 @@ class SandboxedTwigProviderTest extends WebTestCase $this->expectException(SecurityError::class); $options = new LabelOptions(); - $options->setSupportedElement('part'); + $options->setSupportedElement(LabelSupportedElement::PART); $options->setLines($twig); - $options->setLinesMode('twig'); + $options->setProcessMode(LabelProcessMode::TWIG); - $twig = $this->service->getTwig($options); + $twig = $this->service->createTwig($options); $str = $twig->render('lines', [ 'part' => new Part(), 'lot' => new PartLot(), - 'location' => new Storelocation(), + 'location' => new StorageLocation(), ]); + + $this->assertIsString($str); } } diff --git a/tests/Services/LogSystem/EventCommentHelperTest.php b/tests/Services/LogSystem/EventCommentHelperTest.php index 4cab7bb4..9c78d4c6 100644 --- a/tests/Services/LogSystem/EventCommentHelperTest.php +++ b/tests/Services/LogSystem/EventCommentHelperTest.php @@ -53,9 +53,9 @@ class EventCommentHelperTest extends WebTestCase protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(EventCommentHelper::class); } diff --git a/tests/Services/LogSystem/EventCommentNeededHelperTest.php b/tests/Services/LogSystem/EventCommentNeededHelperTest.php new file mode 100644 index 00000000..2fb3e123 --- /dev/null +++ b/tests/Services/LogSystem/EventCommentNeededHelperTest.php @@ -0,0 +1,45 @@ +. + */ +namespace App\Tests\Services\LogSystem; + +use App\Services\LogSystem\EventCommentNeededHelper; +use PHPUnit\Framework\TestCase; + +class EventCommentNeededHelperTest extends TestCase +{ + public function testIsCommentNeeded(): void + { + $service = new EventCommentNeededHelper(['part_edit', 'part_create']); + $this->assertTrue($service->isCommentNeeded('part_edit')); + $this->assertTrue($service->isCommentNeeded('part_create')); + $this->assertFalse($service->isCommentNeeded('part_delete')); + $this->assertFalse($service->isCommentNeeded('part_stock_operation')); + } + + public function testIsCommentNeededInvalidTypeException(): void + { + $service = new EventCommentNeededHelper(['part_edit', 'part_create']); + $this->expectException(\InvalidArgumentException::class); + $service->isCommentNeeded('this_is_not_valid'); + } +} diff --git a/tests/Services/LogSystem/EventLoggerTest.php b/tests/Services/LogSystem/EventLoggerTest.php index 77be97db..0dbb85a3 100644 --- a/tests/Services/LogSystem/EventLoggerTest.php +++ b/tests/Services/LogSystem/EventLoggerTest.php @@ -42,6 +42,7 @@ declare(strict_types=1); namespace App\Tests\Services\LogSystem; use App\Entity\LogSystem\AbstractLogEntry; +use App\Entity\LogSystem\LogLevel; use App\Entity\LogSystem\UserLoginLogEntry; use App\Entity\LogSystem\UserLogoutLogEntry; use App\Services\LogSystem\EventLogger; @@ -56,9 +57,9 @@ class EventLoggerTest extends WebTestCase protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(EventLogger::class); } @@ -67,21 +68,21 @@ class EventLoggerTest extends WebTestCase { $event1 = new UserLoginLogEntry('127.0.0.1'); $event2 = new UserLogoutLogEntry('127.0.0.1'); - $event2->setLevel(AbstractLogEntry::LEVEL_CRITICAL); + $event2->setLevel(LogLevel::CRITICAL); //Test without restrictions - $this->assertTrue($this->service->shouldBeAdded($event1, 7, [], [])); + $this->assertTrue($this->service->shouldBeAdded($event1, LogLevel::DEBUG, [], [])); //Test minimum log level - $this->assertFalse($this->service->shouldBeAdded($event1, 2, [], [])); - $this->assertTrue($this->service->shouldBeAdded($event2, 2, [], [])); + $this->assertFalse($this->service->shouldBeAdded($event1, LogLevel::CRITICAL, [], [])); + $this->assertTrue($this->service->shouldBeAdded($event2, LogLevel::CRITICAL, [], [])); //Test blacklist - $this->assertFalse($this->service->shouldBeAdded($event1, 7, [UserLoginLogEntry::class], [])); - $this->assertTrue($this->service->shouldBeAdded($event2, 7, [UserLoginLogEntry::class], [])); + $this->assertFalse($this->service->shouldBeAdded($event1, LogLevel::DEBUG, [UserLoginLogEntry::class], [])); + $this->assertTrue($this->service->shouldBeAdded($event2, LogLevel::DEBUG, [UserLoginLogEntry::class], [])); //Test whitelist - $this->assertFalse($this->service->shouldBeAdded($event1, 7, [], [UserLogoutLogEntry::class])); - $this->assertTrue($this->service->shouldBeAdded($event2, 7, [], [UserLogoutLogEntry::class])); + $this->assertFalse($this->service->shouldBeAdded($event1, LogLevel::DEBUG, [], [UserLogoutLogEntry::class])); + $this->assertTrue($this->service->shouldBeAdded($event2, LogLevel::DEBUG, [], [UserLogoutLogEntry::class])); } } diff --git a/tests/Services/LogSystem/TimeTravelTest.php b/tests/Services/LogSystem/TimeTravelTest.php new file mode 100644 index 00000000..f8e9c088 --- /dev/null +++ b/tests/Services/LogSystem/TimeTravelTest.php @@ -0,0 +1,80 @@ +. + */ + +namespace App\Tests\Services\LogSystem; + +use App\Entity\LogSystem\ElementEditedLogEntry; +use App\Entity\Parts\Category; +use App\Services\LogSystem\TimeTravel; +use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; + +class TimeTravelTest extends KernelTestCase +{ + + private TimeTravel $service; + private EntityManagerInterface $em; + + public function setUp(): void + { + self::bootKernel(); + $this->service = self::getContainer()->get(TimeTravel::class); + $this->em = self::getContainer()->get(EntityManagerInterface::class); + } + + public function testUndeleteEntity(): void + { + $undeletedCategory = $this->service->undeleteEntity(Category::class, 100); + + $this->assertInstanceOf(Category::class, $undeletedCategory); + $this->assertEquals(100, $undeletedCategory->getId()); + } + + public function testApplyEntry(): void + { + $category = new Category(); + //Fake an ID + $reflClass = new \ReflectionClass($category); + $reflClass->getProperty('id')->setValue($category, 1000); + + $category->setName('Test Category'); + $category->setComment('Test Comment'); + + $logEntry = new ElementEditedLogEntry($category); + $logEntry->setOldData(['name' => 'Old Category', 'comment' => 'Old Comment']); + + $this->service->applyEntry($category, $logEntry); + + $this->assertEquals('Old Category', $category->getName()); + $this->assertEquals('Old Comment', $category->getComment()); + } + + public function testRevertEntityToTimestamp(): void + { + /** @var Category $category */ + $category = $this->em->find(Category::class, 1); + + $this->service->revertEntityToTimestamp($category, new \DateTime('2022-01-01 00:00:00')); + + //The category with 1 should have the name 'Test' at this timestamp + $this->assertEquals('Test', $category->getName()); + } +} diff --git a/tests/Services/Misc/FAIconGeneratorTest.php b/tests/Services/Misc/FAIconGeneratorTest.php index 91255f68..1bfc740c 100644 --- a/tests/Services/Misc/FAIconGeneratorTest.php +++ b/tests/Services/Misc/FAIconGeneratorTest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace App\Tests\Services\Misc; use App\Services\Misc\FAIconGenerator; -use App\Tests\Services\AmountFormatter; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class FAIconGeneratorTest extends WebTestCase @@ -35,25 +34,65 @@ class FAIconGeneratorTest extends WebTestCase protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(FAIconGenerator::class); } - public function fileExtensionDataProvider(): array + public function fileExtensionDataProvider(): \Iterator { - return [ - ['pdf', 'fa-file-pdf'], - ['jpeg', 'fa-file-image'], - ['txt', 'fa-file-alt'], - ['doc', 'fa-file-word'], - ['zip', 'fa-file-archive'], - ['php', 'fa-file-code'], - ['tmp', 'fa-file'], - ['fgd', 'fa-file'], - ]; + yield ['pdf', 'fa-file-pdf']; + yield ['jpeg','fa-file-image']; + yield ['txt', 'fa-file-lines']; + yield ['doc', 'fa-file-word']; + yield ['zip', 'fa-file-zipper']; + yield ['png', 'fa-file-image']; + yield ['jpg', 'fa-file-image']; + yield ['gif', 'fa-file-image']; + yield ['svg', 'fa-file-image']; + yield ['xls', 'fa-file-excel']; + yield ['xlsx', 'fa-file-excel']; + yield ['ppt', 'fa-file-powerpoint']; + yield ['pptx', 'fa-file-powerpoint']; + yield ['docx', 'fa-file-word']; + yield ['odt', 'fa-file-word']; + yield ['ods', 'fa-file-excel']; + yield ['odp', 'fa-file-powerpoint']; + yield ['py', 'fa-file-code']; + yield ['js', 'fa-file-code']; + yield ['html', 'fa-file-code']; + yield ['css', 'fa-file-code']; + yield ['xml', 'fa-file-code']; + yield ['json', 'fa-file-code']; + yield ['yml', 'fa-file-code']; + yield ['yaml', 'fa-file-code']; + yield ['csv', 'fa-file-csv']; + yield ['sql', 'fa-file-code']; + yield ['sh', 'fa-file-code']; + yield ['bat', 'fa-file-code']; + yield ['exe', 'fa-file-code']; + yield ['dll', 'fa-file-code']; + yield ['lib', 'fa-file-code']; + yield ['so', 'fa-file-code']; + yield ['a', 'fa-file-code']; + yield ['o', 'fa-file-code']; + yield ['class', 'fa-file-code']; + yield ['jar', 'fa-file-code']; + yield ['rar', 'fa-file-zipper']; + yield ['7z', 'fa-file-zipper']; + yield ['tar', 'fa-file-zipper']; + yield ['gz', 'fa-file-zipper']; + yield ['tgz', 'fa-file-zipper']; + yield ['bz2', 'fa-file-zipper']; + yield ['tbz', 'fa-file-zipper']; + yield ['xz', 'fa-file-zipper']; + yield ['txz', 'fa-file-zipper']; + yield ['zip', 'fa-file-zipper']; + yield ['php', 'fa-file-code']; + yield ['tmp', 'fa-file']; + yield ['fgd', 'fa-file']; } /** @@ -61,7 +100,7 @@ class FAIconGeneratorTest extends WebTestCase */ public function testFileExtensionToFAType(string $ext, string $expected): void { - $this->assertSame($expected, $this->service->fileExtensionToFAType($ext)); + $this->assertSame($expected, $this->service->fileExtensionToFAType($ext), 'Failed for extension .'.$ext); } public function testGenerateIconHTML(): void diff --git a/tests/Services/Misc/MySQLDumpXMLConverterTest.php b/tests/Services/Misc/MySQLDumpXMLConverterTest.php new file mode 100644 index 00000000..98614b4b --- /dev/null +++ b/tests/Services/Misc/MySQLDumpXMLConverterTest.php @@ -0,0 +1,56 @@ +. + */ +namespace App\Tests\Services\Misc; + +use App\Services\ImportExportSystem\PartKeeprImporter\MySQLDumpXMLConverter; +use PHPUnit\Framework\TestCase; + +class MySQLDumpXMLConverterTest extends TestCase +{ + + public function testConvertMySQLDumpXMLDataToArrayStructure(): void + { + $service = new MySQLDumpXMLConverter(); + + //Load the test XML file + $xml_string = file_get_contents(__DIR__.'/../../assets/partkeepr_import_test.xml'); + + $result = $service->convertMySQLDumpXMLDataToArrayStructure($xml_string); + + //Check that the result is an array + $this->assertIsArray($result); + + //Must contain 36 tables + $this->assertCount(50, $result); + + //Must have a table called "footprints" + $this->assertArrayHasKey('footprint', $result); + + //Must have 36 entry in the "footprints" table + $this->assertCount(36, $result['footprint']); + + $this->assertSame('1', $result['footprint'][0]['id']); + $this->assertSame('CBGA-32', $result['footprint'][0]['name']); + + } +} diff --git a/tests/Services/Misc/RangeParserTest.php b/tests/Services/Misc/RangeParserTest.php index 2ffa5f04..1a9b2146 100644 --- a/tests/Services/Misc/RangeParserTest.php +++ b/tests/Services/Misc/RangeParserTest.php @@ -57,47 +57,43 @@ class RangeParserTest extends WebTestCase $this->service = self::getContainer()->get(RangeParser::class); } - public function dataProvider(): array + public function dataProvider(): \Iterator { - return [ - [[], ''], - [[], ' '], - [[], "\t"], - [[1], '1'], - [[1, 2, 3], '1,2, 3'], - [[1, 2, 3], '1-3'], - [[1, 2, 3, 4], '1- 3, 4'], - [[1, 2, 3, 4], '1, 2,3 - 4'], - [[1, 2, 3], ' 1; 2, 3'], - [[-1, 0, 1, 2], '-1; 0; 1, 2'], - [[4, 3, 1, 2], '4,3, 1;2'], - [[1, 2, 3, 4], '2-1, 3-4'], - [[1], '1-1'], - [[-3, -2, -1], '-3--1'], - [[1, 2, 3], '1,,2;;,,3'], - [[100, 1000, 1], '100, 1000, 1'], - [[], 'test', true], - [[], '1-2-3-4,5', true], - [[], '1 2 3, 455, 23', true], - [[], '1, 2, test', true], - ]; + yield [[], '']; + yield [[], ' ']; + yield [[], "\t"]; + yield [[1], '1']; + yield [[1, 2, 3], '1,2, 3']; + yield [[1, 2, 3], '1-3']; + yield [[1, 2, 3, 4], '1- 3, 4']; + yield [[1, 2, 3, 4], '1, 2,3 - 4']; + yield [[1, 2, 3], ' 1; 2, 3']; + yield [[-1, 0, 1, 2], '-1; 0; 1, 2']; + yield [[4, 3, 1, 2], '4,3, 1;2']; + yield [[1, 2, 3, 4], '2-1, 3-4']; + yield [[1], '1-1']; + yield [[-3, -2, -1], '-3--1']; + yield [[1, 2, 3], '1,,2;;,,3']; + yield [[100, 1000, 1], '100, 1000, 1']; + yield [[], 'test', true]; + yield [[], '1-2-3-4,5', true]; + yield [[], '1 2 3, 455, 23', true]; + yield [[], '1, 2, test', true]; } - public function validDataProvider(): array + public function validDataProvider(): \Iterator { - return [ - [true, ''], - [true, ' '], - [true, '1, 2, 3'], - [true, '1-2,3, 4- 5'], - [true, '1 -2, 3- 4, 6'], - [true, '1--2'], - [true, '1- -2'], - [true, ',,12,33'], - [false, 'test'], - [false, '1-2-3'], - [false, '1, 2 test'], - ]; + yield [true, '']; + yield [true, ' ']; + yield [true, '1, 2, 3']; + yield [true, '1-2,3, 4- 5']; + yield [true, '1 -2, 3- 4, 6']; + yield [true, '1--2']; + yield [true, '1- -2']; + yield [true, ',,12,33']; + yield [false, 'test']; + yield [false, '1-2-3']; + yield [false, '1, 2 test']; } /** diff --git a/tests/Services/Parameters/ParameterExtractorTest.php b/tests/Services/Parameters/ParameterExtractorTest.php index ceb28456..148470d1 100644 --- a/tests/Services/Parameters/ParameterExtractorTest.php +++ b/tests/Services/Parameters/ParameterExtractorTest.php @@ -51,24 +51,22 @@ class ParameterExtractorTest extends WebTestCase protected function setUp(): void { - parent::setUp(); - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(ParameterExtractor::class); } - public function emptyDataProvider(): array + public function emptyDataProvider(): \Iterator { - return [ - [''], - [' '], - ["\t\n"], - [':;'], - ['NPN Transistor'], - ['=BC547 rewr'], - ['For good, [b]bad[/b], evil'], - ['Param:; Test'], - ]; + yield ['']; + yield [' ']; + yield ["\t\n"]; + yield [':;']; + yield ['NPN Transistor']; + yield ['=BC547 rewr']; + yield ['For good, [b]bad[/b], evil']; + yield ['Param:; Test']; + yield ['A [link](https://demo.part-db.de) should not be matched']; } /** diff --git a/tests/Services/Parts/PartLotWithdrawAddHelperTest.php b/tests/Services/Parts/PartLotWithdrawAddHelperTest.php index b2f8a2f3..697d3983 100644 --- a/tests/Services/Parts/PartLotWithdrawAddHelperTest.php +++ b/tests/Services/Parts/PartLotWithdrawAddHelperTest.php @@ -1,13 +1,13 @@ service = self::getContainer()->get(PartLotWithdrawAddHelper::class); @@ -60,8 +59,8 @@ class PartLotWithdrawAddHelperTest extends WebTestCase { $this->part = new Part(); - $this->storageLocation = new Storelocation(); - $this->full_storageLocation = new Storelocation(); + $this->storageLocation = new StorageLocation(); + $this->full_storageLocation = new StorageLocation(); $this->full_storageLocation->setIsFull(true); $this->partLot1 = new TestPartLot(); @@ -89,7 +88,7 @@ class PartLotWithdrawAddHelperTest extends WebTestCase $this->lotWithUnknownInstock->setStorageLocation($this->storageLocation); } - public function testCanWithdraw() + public function testCanWithdraw(): void { //Normal lots should be withdrawable $this->assertTrue($this->service->canWithdraw($this->partLot1)); @@ -103,7 +102,7 @@ class PartLotWithdrawAddHelperTest extends WebTestCase $this->assertFalse($this->service->canWithdraw($this->lotWithUnknownInstock)); } - public function testCanAdd() + public function testCanAdd(): void { //Normal lots should be addable $this->assertTrue($this->service->canAdd($this->partLot1)); @@ -116,43 +115,43 @@ class PartLotWithdrawAddHelperTest extends WebTestCase $this->assertFalse($this->service->canAdd($this->lotWithUnknownInstock)); } - public function testAdd() + public function testAdd(): void { //Add 5 to lot 1 $this->service->add($this->partLot1, 5, "Test"); - $this->assertEquals(15, $this->partLot1->getAmount()); + $this->assertEqualsWithDelta(15.0, $this->partLot1->getAmount(), PHP_FLOAT_EPSILON); //Add 3.2 to lot 2 $this->service->add($this->partLot2, 3.2, "Test"); - $this->assertEquals(5, $this->partLot2->getAmount()); + $this->assertEqualsWithDelta(5.0, $this->partLot2->getAmount(), PHP_FLOAT_EPSILON); //Add 1.5 to lot 3 $this->service->add($this->partLot3, 1.5, "Test"); - $this->assertEquals(2, $this->partLot3->getAmount()); + $this->assertEqualsWithDelta(2.0, $this->partLot3->getAmount(), PHP_FLOAT_EPSILON); } - public function testWithdraw() + public function testWithdraw(): void { //Withdraw 5 from lot 1 $this->service->withdraw($this->partLot1, 5, "Test"); - $this->assertEquals(5, $this->partLot1->getAmount()); + $this->assertEqualsWithDelta(5.0, $this->partLot1->getAmount(), PHP_FLOAT_EPSILON); //Withdraw 2.2 from lot 2 $this->service->withdraw($this->partLot2, 2.2, "Test"); - $this->assertEquals(0, $this->partLot2->getAmount()); + $this->assertEqualsWithDelta(0.0, $this->partLot2->getAmount(), PHP_FLOAT_EPSILON); } - public function testMove() + public function testMove(): void { //Move 5 from lot 1 to lot 2 $this->service->move($this->partLot1, $this->partLot2, 5, "Test"); - $this->assertEquals(5, $this->partLot1->getAmount()); - $this->assertEquals(7, $this->partLot2->getAmount()); + $this->assertEqualsWithDelta(5.0, $this->partLot1->getAmount(), PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(7.0, $this->partLot2->getAmount(), PHP_FLOAT_EPSILON); //Move 2.2 from lot 2 to lot 3 $this->service->move($this->partLot2, $this->partLot3, 2.2, "Test"); - $this->assertEquals(5, $this->partLot2->getAmount()); - $this->assertEquals(2, $this->partLot3->getAmount()); + $this->assertEqualsWithDelta(5.0, $this->partLot2->getAmount(), PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(2.0, $this->partLot3->getAmount(), PHP_FLOAT_EPSILON); } } diff --git a/tests/Services/Parts/PricedetailHelperTest.php b/tests/Services/Parts/PricedetailHelperTest.php index aff0fdba..cfb4a043 100644 --- a/tests/Services/Parts/PricedetailHelperTest.php +++ b/tests/Services/Parts/PricedetailHelperTest.php @@ -38,8 +38,7 @@ class PricedetailHelperTest extends WebTestCase protected function setUp(): void { - parent::setUp(); - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(PricedetailHelper::class); } diff --git a/tests/Services/ProjectSystem/ProjectBuildHelperTest.php b/tests/Services/ProjectSystem/ProjectBuildHelperTest.php index 0037ae0c..3b73cad1 100644 --- a/tests/Services/ProjectSystem/ProjectBuildHelperTest.php +++ b/tests/Services/ProjectSystem/ProjectBuildHelperTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\Services\ProjectSystem; use App\Entity\Parts\Part; @@ -34,12 +36,11 @@ class ProjectBuildHelperTest extends WebTestCase protected function setUp(): void { - parent::setUp(); self::bootKernel(); $this->service = self::getContainer()->get(ProjectBuildHelper::class); } - public function testGetMaximumBuildableCountForBOMEntryNonPartBomEntry() + public function testGetMaximumBuildableCountForBOMEntryNonPartBomEntry(): void { $bom_entry = new ProjectBOMEntry(); $bom_entry->setPart(null); @@ -50,7 +51,7 @@ class ProjectBuildHelperTest extends WebTestCase $this->service->getMaximumBuildableCountForBOMEntry($bom_entry); } - public function testGetMaximumBuildableCountForBOMEntry() + public function testGetMaximumBuildableCountForBOMEntry(): void { $project_bom_entry = new ProjectBOMEntry(); $project_bom_entry->setQuantity(10); @@ -66,15 +67,15 @@ class ProjectBuildHelperTest extends WebTestCase $project_bom_entry->setPart($part); //We have 125 parts in stock, so we can build 12 times the project (125 / 10 = 12.5) - $this->assertEquals(12, $this->service->getMaximumBuildableCountForBOMEntry($project_bom_entry)); + $this->assertSame(12, $this->service->getMaximumBuildableCountForBOMEntry($project_bom_entry)); $lot1->setAmount(0); //We have 5 parts in stock, so we can build 0 times the project (5 / 10 = 0.5) - $this->assertEquals(0, $this->service->getMaximumBuildableCountForBOMEntry($project_bom_entry)); + $this->assertSame(0, $this->service->getMaximumBuildableCountForBOMEntry($project_bom_entry)); } - public function testGetMaximumBuildableCount() + public function testGetMaximumBuildableCount(): void { $project = new Project(); @@ -102,15 +103,15 @@ class ProjectBuildHelperTest extends WebTestCase $project->addBomEntry((new ProjectBOMEntry())->setName('Non part entry')->setQuantity(1)); //Restricted by the few parts in stock of part2 - $this->assertEquals(2, $this->service->getMaximumBuildableCount($project)); + $this->assertSame(2, $this->service->getMaximumBuildableCount($project)); $lot3->setAmount(1000); //Now the build count is restricted by the few parts in stock of part1 - $this->assertEquals(12, $this->service->getMaximumBuildableCount($project)); + $this->assertSame(12, $this->service->getMaximumBuildableCount($project)); $lot3->setAmount(0); //Now the build count must be 0, as we have no parts in stock - $this->assertEquals(0, $this->service->getMaximumBuildableCount($project)); + $this->assertSame(0, $this->service->getMaximumBuildableCount($project)); } } diff --git a/tests/Services/ProjectSystem/ProjectBuildPartHelperTest.php b/tests/Services/ProjectSystem/ProjectBuildPartHelperTest.php index 41014c8a..4baa7cf3 100644 --- a/tests/Services/ProjectSystem/ProjectBuildPartHelperTest.php +++ b/tests/Services/ProjectSystem/ProjectBuildPartHelperTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\Services\ProjectSystem; use App\Entity\ProjectSystem\Project; -use App\Services\Parts\PricedetailHelper; use App\Services\ProjectSystem\ProjectBuildPartHelper; -use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class ProjectBuildPartHelperTest extends WebTestCase @@ -33,7 +33,6 @@ class ProjectBuildPartHelperTest extends WebTestCase protected function setUp(): void { - parent::setUp(); self::bootKernel(); $this->service = self::getContainer()->get(ProjectBuildPartHelper::class); } diff --git a/tests/Services/Trees/TreeViewGeneratorTest.php b/tests/Services/Trees/TreeViewGeneratorTest.php index c43fe680..f34c855d 100644 --- a/tests/Services/Trees/TreeViewGeneratorTest.php +++ b/tests/Services/Trees/TreeViewGeneratorTest.php @@ -42,9 +42,9 @@ class TreeViewGeneratorTest extends WebTestCase protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(TreeViewGenerator::class); $this->em = self::getContainer()->get(EntityManagerInterface::class); @@ -99,7 +99,7 @@ class TreeViewGeneratorTest extends WebTestCase //First element should link to new category $this->assertStringContainsStringIgnoringCase('New', $tree[0]->getText()); $this->assertSame('/en/category/new', $tree[0]->getHref()); - //By default the new element node is selected + //By default, the new element node is selected $this->assertTrue($tree[0]->getState()->getSelected()); //Next element is spacing diff --git a/tests/Services/UserSystem/PermissionManagerTest.php b/tests/Services/UserSystem/PermissionManagerTest.php index 78ce2850..cfe74ce7 100644 --- a/tests/Services/UserSystem/PermissionManagerTest.php +++ b/tests/Services/UserSystem/PermissionManagerTest.php @@ -24,7 +24,6 @@ namespace App\Tests\Services\UserSystem; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\PermissionData; -use App\Entity\UserSystem\PermissionsEmbed; use App\Entity\UserSystem\User; use App\Services\UserSystem\PermissionManager; use InvalidArgumentException; @@ -32,21 +31,18 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class PermissionManagerTest extends WebTestCase { - protected $user_withoutGroup; + protected ?User $user_withoutGroup = null; - protected $user; - protected $group; + protected ?User $user = null; + protected ?Group $group = null; - /** - * @var PermissionManager - */ - protected $service; + protected ?PermissionManager $service = null; protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(PermissionManager::class); @@ -56,7 +52,11 @@ class PermissionManagerTest extends WebTestCase ->setPermissionValue('parts', 'edit', false) //edit ->setPermissionValue('parts', 'create', null) //create ->setPermissionValue('parts', 'move', null) //move - ->setPermissionValue('parts', 'delete', null); //delete + ->setPermissionValue('parts', 'delete', null) //delete + + ->setPermissionValue('footprints', 'edit', true) + ->setPermissionValue('footprints', 'create', false) + ; $this->user = $this->createMock(User::class); $this->user->method('getPermissions')->willReturn($user_perms); @@ -91,16 +91,14 @@ class PermissionManagerTest extends WebTestCase $this->group->method('getParent')->willReturn($parent_group); } - public function getPermissionNames(): array + public function getPermissionNames(): \Iterator { //List some permission names - return [ - ['parts'], - ['system'], - ['footprints'], - ['suppliers'], - ['tools'] - ]; + yield ['parts']; + yield ['system']; + yield ['footprints']; + yield ['suppliers']; + yield ['tools']; } /** @@ -156,7 +154,7 @@ class PermissionManagerTest extends WebTestCase public function testInherit(): void { - //Not inherited values should be same as dont inherit: + //Not inherited values should be same as don't inherit: $this->assertTrue($this->service->inherit($this->user, 'parts', 'read')); $this->assertFalse($this->service->inherit($this->user, 'parts', 'edit')); //When thing can not be resolved null should be returned @@ -174,6 +172,19 @@ class PermissionManagerTest extends WebTestCase $this->assertNull($this->service->inherit($this->user_withoutGroup, 'parts', 'delete')); } + public function testInheritWithAPILevel(): void + { + //If no API roles are given, access should be prevented + $this->assertFalse($this->service->inheritWithAPILevel($this->user, [], 'parts', 'read')); + //Allow access with roles + $this->assertTrue($this->service->inheritWithAPILevel($this->user, ['ROLE_API_READ_ONLY', 'ROLE_API_FULL'], 'parts', 'read')); + + //Block access if the token has not the sufficient level + $this->assertFalse($this->service->inheritWithAPILevel($this->user, ['ROLE_API_READ_ONLY'], 'footprints', 'edit')); + //And allow with role + $this->assertTrue($this->service->inheritWithAPILevel($this->user, ['ROLE_API_READ_ONLY', 'ROLE_API_EDIT'], 'footprints', 'edit')); + } + public function testSetPermission(): void { $user = new User(); @@ -240,6 +251,36 @@ class PermissionManagerTest extends WebTestCase $this->assertNull($this->service->dontInherit($user, 'parts', 'edit')); } + public function testSetAllOperationsOfPermissionExcept(): void + { + $user = new User(); + + //Set all operations of permission to true (except import and delete) + $this->service->setAllOperationsOfPermissionExcept($user, 'parts', true, ['import', 'delete']); + $this->assertTrue($this->service->dontInherit($user, 'parts', 'read')); + $this->assertTrue($this->service->dontInherit($user, 'parts', 'create')); + $this->assertTrue($this->service->dontInherit($user, 'parts', 'edit')); + $this->assertNull($this->service->dontInherit($user, 'parts', 'import')); + $this->assertNull($this->service->dontInherit($user, 'parts', 'delete')); + + //Set all operations of permission to false + $this->service->setAllOperationsOfPermissionExcept($user, 'parts', false, ['import', 'delete']); + $this->assertFalse($this->service->dontInherit($user, 'parts', 'read')); + $this->assertFalse($this->service->dontInherit($user, 'parts', 'create')); + $this->assertFalse($this->service->dontInherit($user, 'parts', 'edit')); + $this->assertNull($this->service->dontInherit($user, 'parts', 'import')); + $this->assertNull($this->service->dontInherit($user, 'parts', 'delete')); + + + //Set all operations of permission to null + $this->service->setAllOperationsOfPermissionExcept($user, 'parts', null, ['import', 'delete']); + $this->assertNull($this->service->dontInherit($user, 'parts', 'read')); + $this->assertNull($this->service->dontInherit($user, 'parts', 'create')); + $this->assertNull($this->service->dontInherit($user, 'parts', 'edit')); + $this->assertNull($this->service->dontInherit($user, 'parts', 'import')); + $this->assertNull($this->service->dontInherit($user, 'parts', 'delete')); + } + public function testEnsureCorrectSetOperations(): void { //Create an empty user (all permissions are inherit) @@ -265,4 +306,34 @@ class PermissionManagerTest extends WebTestCase $this->assertTrue($this->service->dontInherit($user, 'parts', 'edit')); $this->assertTrue($this->service->dontInherit($user, 'categories', 'read')); } + + public function testHasAnyPermissionSetToAllowInherited(): void + { + //For empty user this should return false + $user = new User(); + $this->assertFalse($this->service->hasAnyPermissionSetToAllowInherited($user)); + + //If all permissions are set to false this should return false + $this->service->setAllPermissions($user, false); + $this->assertFalse($this->service->hasAnyPermissionSetToAllowInherited($user)); + + //If all permissions are set to null this should return false + $this->service->setAllPermissions($user, null); + $this->assertFalse($this->service->hasAnyPermissionSetToAllowInherited($user)); + + //If all permissions are set to true this should return true + $this->service->setAllPermissions($user, true); + $this->assertTrue($this->service->hasAnyPermissionSetToAllowInherited($user)); + + //The test data should return true + $this->assertTrue($this->service->hasAnyPermissionSetToAllowInherited($this->user)); + $this->assertTrue($this->service->hasAnyPermissionSetToAllowInherited($this->user_withoutGroup)); + + //Create a user with a group + $user = new User(); + $user->setGroup($this->group); + //This should return true + $this->assertTrue($this->service->hasAnyPermissionSetToAllowInherited($user)); + + } } diff --git a/tests/Services/UserSystem/PermissionSchemaUpdaterTest.php b/tests/Services/UserSystem/PermissionSchemaUpdaterTest.php index 79abb89c..120fe075 100644 --- a/tests/Services/UserSystem/PermissionSchemaUpdaterTest.php +++ b/tests/Services/UserSystem/PermissionSchemaUpdaterTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\Services\UserSystem; use App\Entity\UserSystem\PermissionData; use App\Security\Interfaces\HasPermissionsInterface; use App\Services\UserSystem\PermissionSchemaUpdater; -use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class TestPermissionHolder implements HasPermissionsInterface { - private PermissionData $perm_data; - - public function __construct(PermissionData $perm_data) + public function __construct(private readonly PermissionData $perm_data) { - $this->perm_data = $perm_data; } public function getPermissions(): PermissionData @@ -50,13 +48,12 @@ class PermissionSchemaUpdaterTest extends WebTestCase public function setUp(): void { - parent::setUp(); self::bootKernel(); - $this->service = self::$container->get(PermissionSchemaUpdater::class); + $this->service = self::getContainer()->get(PermissionSchemaUpdater::class); } - public function testIsSchemaUpdateNeeded() + public function testIsSchemaUpdateNeeded(): void { $perm_data = new PermissionData(); $perm_data->setSchemaVersion(0); @@ -70,7 +67,7 @@ class PermissionSchemaUpdaterTest extends WebTestCase self::assertFalse($this->service->isSchemaUpdateNeeded($user)); } - public function testUpgradeSchema() + public function testUpgradeSchema(): void { $perm_data = new PermissionData(); $perm_data->setSchemaVersion(0); @@ -78,27 +75,27 @@ class PermissionSchemaUpdaterTest extends WebTestCase //With schema version 0, an update should be done and the schema version should be updated self::assertTrue($this->service->upgradeSchema($user)); - self::assertEquals(PermissionData::CURRENT_SCHEMA_VERSION, $user->getPermissions()->getSchemaVersion()); + self::assertSame(PermissionData::CURRENT_SCHEMA_VERSION, $user->getPermissions()->getSchemaVersion()); //If we redo it with the same schema version, no update should be done self::assertFalse($this->service->upgradeSchema($user)); } - public function testUpgradeSchemaToVersion1() + public function testUpgradeSchemaToVersion1(): void { $perm_data = new PermissionData(); $perm_data->setSchemaVersion(0); $perm_data->setPermissionValue('parts', 'edit', PermissionData::ALLOW); $user = new TestPermissionHolder($perm_data); - //Do an upgrade and afterwards the move, add, and withdraw permissions should be set to ALLOW + //Do an upgrade and afterward the move, add, and withdraw permissions should be set to ALLOW self::assertTrue($this->service->upgradeSchema($user, 1)); self::assertEquals(PermissionData::ALLOW, $user->getPermissions()->getPermissionValue('parts_stock', 'move')); self::assertEquals(PermissionData::ALLOW, $user->getPermissions()->getPermissionValue('parts_stock', 'add')); self::assertEquals(PermissionData::ALLOW, $user->getPermissions()->getPermissionValue('parts_stock', 'withdraw')); } - public function testUpgradeSchemaToVersion2() + public function testUpgradeSchemaToVersion2(): void { $perm_data = new PermissionData(); $perm_data->setSchemaVersion(1); @@ -113,4 +110,17 @@ class PermissionSchemaUpdaterTest extends WebTestCase self::assertEquals(PermissionData::INHERIT, $user->getPermissions()->getPermissionValue('projects', 'edit')); self::assertEquals(PermissionData::DISALLOW, $user->getPermissions()->getPermissionValue('projects', 'delete')); } + + public function testUpgradeSchemaToVersion3(): void + { + $perm_data = new PermissionData(); + $perm_data->setSchemaVersion(2); + $perm_data->setPermissionValue('system', 'server_infos', PermissionData::ALLOW); + + $user = new TestPermissionHolder($perm_data); + + //After the upgrade the server.show_updates should be set to ALLOW + self::assertTrue($this->service->upgradeSchema($user, 3)); + self::assertSame(PermissionData::ALLOW, $user->getPermissions()->getPermissionValue('system', 'show_updates')); + } } diff --git a/tests/Services/UserSystem/TFA/BackupCodeGeneratorTest.php b/tests/Services/UserSystem/TFA/BackupCodeGeneratorTest.php index b731ec87..b489eec2 100644 --- a/tests/Services/UserSystem/TFA/BackupCodeGeneratorTest.php +++ b/tests/Services/UserSystem/TFA/BackupCodeGeneratorTest.php @@ -46,9 +46,12 @@ class BackupCodeGeneratorTest extends TestCase new BackupCodeGenerator(4, 10); } - public function codeLengthDataProvider(): array + public function codeLengthDataProvider(): \Iterator { - return [[6], [8], [10], [16]]; + yield [6]; + yield [8]; + yield [10]; + yield [16]; } /** @@ -60,9 +63,11 @@ class BackupCodeGeneratorTest extends TestCase $this->assertMatchesRegularExpression("/^([a-f0-9]){{$code_length}}\$/", $generator->generateSingleCode()); } - public function codeCountDataProvider(): array + public function codeCountDataProvider(): \Iterator { - return [[2], [8], [10]]; + yield [2]; + yield [8]; + yield [10]; } /** diff --git a/tests/Services/UserSystem/TFA/BackupCodeManagerTest.php b/tests/Services/UserSystem/TFA/BackupCodeManagerTest.php index 6e2b744a..35b7f4f8 100644 --- a/tests/Services/UserSystem/TFA/BackupCodeManagerTest.php +++ b/tests/Services/UserSystem/TFA/BackupCodeManagerTest.php @@ -68,7 +68,7 @@ class BackupCodeManagerTest extends WebTestCase { $user = new User(); - //By default nothing other 2FA is activated, so the backup codes should be disabled + //By default, nothing other 2FA is activated, so the backup codes should be disabled $codes = ['aaaa', 'bbbb']; $user->setBackupCodes($codes); $this->service->disableBackupCodesIfUnused($user); diff --git a/tests/Services/UserSystem/VoterHelperTest.php b/tests/Services/UserSystem/VoterHelperTest.php new file mode 100644 index 00000000..53d7ee82 --- /dev/null +++ b/tests/Services/UserSystem/VoterHelperTest.php @@ -0,0 +1,135 @@ +. + */ +namespace App\Tests\Services\UserSystem; + +use App\Entity\UserSystem\ApiToken; +use App\Entity\UserSystem\ApiTokenLevel; +use App\Entity\UserSystem\PermissionData; +use App\Entity\UserSystem\User; +use App\Security\ApiTokenAuthenticatedToken; +use App\Services\UserSystem\VoterHelper; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\Security\Core\Authentication\Token\NullToken; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken; + +class VoterHelperTest extends KernelTestCase +{ + + protected ?VoterHelper $service = null; + + protected ?User $user = null; + + protected function setUp(): void + { + //Get a service instance. + self::bootKernel(); + $this->service = self::getContainer()->get(VoterHelper::class); + + //Set up a mocked user + $user_perms = new PermissionData(); + $user_perms->setPermissionValue('parts', 'read', true) //read + ->setPermissionValue('parts', 'edit', false) //edit + ->setPermissionValue('parts', 'create', null) //create + ->setPermissionValue('parts', 'move', null) //move + ->setPermissionValue('parts', 'delete', null) //delete + + ->setPermissionValue('footprints', 'edit', true) + ->setPermissionValue('footprints', 'create', false) + ; + + $this->user = $this->createMock(User::class); + $this->user->method('getPermissions')->willReturn($user_perms); + } + + public function testResolveUserAnonUser(): void + { + //If the user is null, the anonymous user should be returned. + + $anonymousToken = new NullToken(); + $this->assertNull($anonymousToken->getUser()); + $user = $this->service->resolveUser($anonymousToken); + //Ensure that this is the anonymous user. + $this->assertNotNull($user); + $this->assertTrue($user->isAnonymousUser()); + } + + public function testResolveUser(): void + { + //For a token with a user, the user should be returned. + $token = new PostAuthenticationToken($this->user, 'main', ['ROLE_USER']); + $this->assertSame($this->user, $token->getUser()); + $user = $this->service->resolveUser($token); + $this->assertSame($this->user, $user); + } + + public function testIsGrantedTrinaryNonAPI(): void + { + //For a UserNamePasswordToken everything should work as expected. + $token = new UsernamePasswordToken($this->user, 'main'); + $this->assertTrue($this->service->isGrantedTrinary($token, 'parts', 'read')); + $this->assertFalse($this->service->isGrantedTrinary($token, 'parts', 'edit')); + $this->assertNull($this->service->isGrantedTrinary($token, 'parts', 'create')); + } + + public function testIsGrantedTrinaryReadOnlyAPI(): void + { + //Create a API token + $api_token = new ApiToken(); + $api_token->setLevel(ApiTokenLevel::READ_ONLY)->setName('Test Token'); + //Create an auth token + $token = new ApiTokenAuthenticatedToken($this->user, 'main', ['ROLE_USER'], $api_token); + + //The permissions should be readonly + $this->assertTrue($this->service->isGrantedTrinary($token, 'parts', 'read')); + $this->assertFalse($this->service->isGrantedTrinary($token, 'parts', 'edit')); + $this->assertFalse($this->service->isGrantedTrinary($token, 'parts', 'create')); + $this->assertFalse($this->service->isGrantedTrinary($token, 'footprints', 'edit')); + } + + public function testIsGrantedTrinaryAdminAPI(): void + { + //Create a API token + $api_token = new ApiToken(); + $api_token->setLevel(ApiTokenLevel::FULL)->setName('Test Token'); + //Create an auth token + $token = new ApiTokenAuthenticatedToken($this->user, 'main', ['ROLE_USER'], $api_token); + + //The permissions should be readonly + $this->assertTrue($this->service->isGrantedTrinary($token, 'parts', 'read')); + $this->assertFalse($this->service->isGrantedTrinary($token, 'parts', 'edit')); + $this->assertNull($this->service->isGrantedTrinary($token, 'parts', 'create')); + $this->assertTrue($this->service->isGrantedTrinary($token, 'footprints', 'edit')); + } + + + public function testIsGrantedNonAPI(): void + { + //Same as testIsGrantedTrinaryNonAPI, but every non-true value should return false. + $token = new UsernamePasswordToken($this->user, 'main'); + $this->assertTrue($this->service->isGranted($token, 'parts', 'read')); + $this->assertFalse($this->service->isGranted($token, 'parts', 'edit')); + $this->assertFalse($this->service->isGranted($token, 'parts', 'create')); + } +} diff --git a/tests/Twig/EntityExtensionTest.php b/tests/Twig/EntityExtensionTest.php index 9e951ea6..3adb9ad2 100644 --- a/tests/Twig/EntityExtensionTest.php +++ b/tests/Twig/EntityExtensionTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\Twig; -use App\Entity\Attachments\Attachment; use App\Entity\Attachments\PartAttachment; use App\Entity\ProjectSystem\Project; use App\Entity\LabelSystem\LabelProfile; @@ -29,7 +30,7 @@ use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; -use App\Entity\Parts\Storelocation; +use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\UserSystem\Group; @@ -44,28 +45,28 @@ class EntityExtensionTest extends WebTestCase protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub - //Get an service instance. + //Get a service instance. self::bootKernel(); $this->service = self::getContainer()->get(EntityExtension::class); } - public function testGetEntityType() + public function testGetEntityType(): void { - $this->assertEquals('part', $this->service->getEntityType(new Part())); - $this->assertEquals('footprint', $this->service->getEntityType(new Footprint())); - $this->assertEquals('storelocation', $this->service->getEntityType(new Storelocation())); - $this->assertEquals('manufacturer', $this->service->getEntityType(new Manufacturer())); - $this->assertEquals('category', $this->service->getEntityType(new Category())); - $this->assertEquals('device', $this->service->getEntityType(new Project())); - $this->assertEquals('attachment', $this->service->getEntityType(new PartAttachment())); - $this->assertEquals('supplier', $this->service->getEntityType(new Supplier())); - $this->assertEquals('user', $this->service->getEntityType(new User())); - $this->assertEquals('group', $this->service->getEntityType(new Group())); - $this->assertEquals('currency', $this->service->getEntityType(new Currency())); - $this->assertEquals('measurement_unit', $this->service->getEntityType(new MeasurementUnit())); - $this->assertEquals('label_profile', $this->service->getEntityType(new LabelProfile())); + $this->assertSame('part', $this->service->getEntityType(new Part())); + $this->assertSame('footprint', $this->service->getEntityType(new Footprint())); + $this->assertSame('storelocation', $this->service->getEntityType(new StorageLocation())); + $this->assertSame('manufacturer', $this->service->getEntityType(new Manufacturer())); + $this->assertSame('category', $this->service->getEntityType(new Category())); + $this->assertSame('device', $this->service->getEntityType(new Project())); + $this->assertSame('attachment', $this->service->getEntityType(new PartAttachment())); + $this->assertSame('supplier', $this->service->getEntityType(new Supplier())); + $this->assertSame('user', $this->service->getEntityType(new User())); + $this->assertSame('group', $this->service->getEntityType(new Group())); + $this->assertSame('currency', $this->service->getEntityType(new Currency())); + $this->assertSame('measurement_unit', $this->service->getEntityType(new MeasurementUnit())); + $this->assertSame('label_profile', $this->service->getEntityType(new LabelProfile())); } } diff --git a/tests/Twig/TwigCoreExtensionTest.php b/tests/Twig/TwigCoreExtensionTest.php index dccb43ca..1aa1f7ca 100644 --- a/tests/Twig/TwigCoreExtensionTest.php +++ b/tests/Twig/TwigCoreExtensionTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\Twig; use App\Twig\TwigCoreExtension; @@ -31,7 +33,7 @@ class TwigCoreExtensionTest extends WebTestCase protected function setUp(): void { - parent::setUp(); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub //Get an service instance. self::bootKernel(); @@ -52,10 +54,10 @@ class TwigCoreExtensionTest extends WebTestCase $obj = new class { public $test = 1; protected $test2 = 3; - private $test3 = 5; - private $test4 = 7; + private int $test3 = 5; + private int $test4 = 7; - public function getTest4() + public function getTest4(): int { return $this->test4; } @@ -63,12 +65,4 @@ class TwigCoreExtensionTest extends WebTestCase $this->assertEqualsCanonicalizing(['test' => 1, 'test4' => 7], $this->service->toArray($obj)); } - - public function testToArrayException(): void - { - //When passing a simple scalar value a exception should be thrown. - $this->expectException(\InvalidArgumentException::class); - - $this->service->toArray(1); - } } diff --git a/tests/Twig/UserExtensionTest.php b/tests/Twig/UserExtensionTest.php index 6a469cda..ea024bda 100644 --- a/tests/Twig/UserExtensionTest.php +++ b/tests/Twig/UserExtensionTest.php @@ -1,4 +1,7 @@ . */ - namespace App\Tests\Twig; use App\Twig\UserExtension; @@ -34,23 +36,22 @@ class UserExtensionTest extends WebTestCase $this->service = self::getContainer()->get(UserExtension::class); } - public function removeeLocaleFromPathDataSet() + public function removeLocaleFromPathDataSet(): ?\Generator { yield ['/', '/de/']; yield ['/test', '/de/test']; yield ['/test/foo', '/en/test/foo']; yield ['/test/foo/bar?param1=val1¶m2=val2', '/en/test/foo/bar?param1=val1¶m2=val2']; + //Allow de_DE + yield ['/test/foo/bar?param1=val1¶m2=val2', '/de_DE/test/foo/bar?param1=val1¶m2=val2']; } /** - * @dataProvider removeeLocaleFromPathDataSet - * @param string $expected - * @param string $input - * @return void + * @dataProvider removeLocaleFromPathDataSet */ public function testRemoveLocaleFromPath(string $expected, string $input): void { - $this->assertEquals($expected, $this->service->removeLocaleFromPath($input)); + $this->assertSame($expected, $this->service->removeLocaleFromPath($input)); } public function testRemoveLocaleFromPathException(): void diff --git a/tests/Validator/Constraints/NoneOfItsChildrenValidatorTest.php b/tests/Validator/Constraints/NoneOfItsChildrenValidatorTest.php new file mode 100644 index 00000000..0efcd5de --- /dev/null +++ b/tests/Validator/Constraints/NoneOfItsChildrenValidatorTest.php @@ -0,0 +1,137 @@ +. + */ +namespace App\Tests\Validator\Constraints; + +use App\Entity\Attachments\AttachmentType; +use App\Entity\Base\AbstractStructuralDBElement; +use App\Validator\Constraints\NoneOfItsChildren; +use App\Validator\Constraints\NoneOfItsChildrenValidator; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class NoneOfItsChildrenValidatorTest extends ConstraintValidatorTestCase +{ + + protected AttachmentType $root_node; + protected AttachmentType $child1; + protected AttachmentType $child2; + protected AttachmentType $child3; + protected AttachmentType $child1_1; + protected AttachmentType $child1_2; + + protected function setUp(): void + { + // TODO: Change the autogenerated stub + + parent::setUp(); + + //Build a simple hierachy + $this->root_node = new AttachmentType(); + $this->root_node->setName('root')->setParent(null); + $this->child1 = new AttachmentType(); + $this->child1->setParent($this->root_node)->setName('child1'); + $this->child2 = new AttachmentType(); + $this->child2->setName('child2')->setParent($this->root_node); + $this->child3 = new AttachmentType(); + $this->child3->setName('child3')->setParent($this->root_node); + $this->child1_1 = new AttachmentType(); + $this->child1_1->setName('child1_1')->setParent($this->child1); + $this->child1_2 = new AttachmentType(); + $this->child1_2->setName('child1_2')->setParent($this->child1); + } + + + protected function createValidator(): NoneOfItsChildrenValidator + { + return new NoneOfItsChildrenValidator(); + } + + public function testNullIsValid(): void + { + $this->setObject($this->child1); + $this->validator->validate(null, new NoneOfItsChildren()); + $this->assertNoViolation(); + } + + public function testWithUnrelatedObject(): void + { + $this->setObject($this->child1); + $this->validator->validate(new AttachmentType(), new NoneOfItsChildren()); + $this->assertNoViolation(); + } + + public function testWithParentObject(): void + { + $this->setObject($this->child1); + $this->validator->validate($this->root_node, new NoneOfItsChildren()); + $this->assertNoViolation(); + } + + public function testWithIntermediateChild(): void + { + $this->setObject($this->child1); + $this->validator->validate($this->child1_1, new NoneOfItsChildren()); + $this->buildViolation('validator.noneofitschild.children') + ->assertRaised(); + } + + public function testWithIndirectChild(): void + { + $this->setObject($this->root_node); + $this->validator->validate($this->child1_1, new NoneOfItsChildren()); + $this->buildViolation('validator.noneofitschild.children') + ->assertRaised(); + } + + public function testWithSelfInstance(): void + { + $this->setObject($this->root_node); + $this->validator->validate($this->root_node, new NoneOfItsChildren()); + $this->buildViolation('validator.noneofitschild.self') + ->assertRaised(); + } + + public function testWithSelfByID(): void + { + $obj1 = new class extends AbstractStructuralDBElement { + public function __construct() + { + $this->id = 1; + parent::__construct(); + } + }; + + $obj2 = new class extends AbstractStructuralDBElement { + public function __construct() + { + $this->id = 1; + parent::__construct(); + } + }; + + $this->setObject($obj1); + $this->validator->validate($obj2, new NoneOfItsChildren()); + $this->buildViolation('validator.noneofitschild.self') + ->assertRaised(); + + } +} diff --git a/tests/Validator/Constraints/SelectableValidatorTest.php b/tests/Validator/Constraints/SelectableValidatorTest.php new file mode 100644 index 00000000..bc520621 --- /dev/null +++ b/tests/Validator/Constraints/SelectableValidatorTest.php @@ -0,0 +1,73 @@ +. + */ +namespace App\Tests\Validator\Constraints; + +use App\Entity\Attachments\AttachmentType; +use App\Validator\Constraints\Selectable; +use App\Validator\Constraints\SelectableValidator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class SelectableValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): SelectableValidator + { + return new SelectableValidator(); + } + + public function testNullIsValid(): void + { + $this->validator->validate(null, new Selectable()); + $this->assertNoViolation(); + } + + public function testExpectAbstractStructuralElement(): void + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate('test', new Selectable()); + } + + public function testWithSelectableObj(): void + { + $selectable_obj = new AttachmentType(); + $selectable_obj->setNotSelectable(false); + + $this->validator->validate($selectable_obj, new Selectable()); + $this->assertNoViolation(); + } + + public function testWithNotSelectableObj(): void + { + $selectable_obj = new AttachmentType(); + $selectable_obj->setNotSelectable(true); + $selectable_obj->setName('Test'); + + $this->validator->validate($selectable_obj, new Selectable()); + $this->buildViolation('validator.isSelectable') + ->setParameter('{{ name }}', 'Test') + ->setParameter('{{ full_path }}', 'Test') + ->assertRaised(); + } + +} diff --git a/tests/Validator/Constraints/UniqueObjectCollectionValidatorTest.php b/tests/Validator/Constraints/UniqueObjectCollectionValidatorTest.php new file mode 100644 index 00000000..d9fab6cf --- /dev/null +++ b/tests/Validator/Constraints/UniqueObjectCollectionValidatorTest.php @@ -0,0 +1,159 @@ +. + */ +namespace App\Tests\Validator\Constraints; + +use App\Tests\Validator\DummyUniqueValidatableObject; +use App\Validator\Constraints\UniqueObjectCollection; +use App\Validator\Constraints\UniqueObjectCollectionValidator; +use Doctrine\Common\Collections\ArrayCollection; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class UniqueObjectCollectionValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): UniqueObjectCollectionValidator + { + return new UniqueObjectCollectionValidator(); + } + + public function testEmptyCollection(): void + { + $this->validator->validate(new ArrayCollection([]), new UniqueObjectCollection()); + $this->assertNoViolation(); + } + + public function testUnqiueCollectionDefaultSettings(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 2, 'b' => 1]) + ]), + new UniqueObjectCollection()); + + $this->assertNoViolation(); + } + + public function testUnqiueCollectionSpecifiedFields(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 2, 'b' => 1]) + ]), + new UniqueObjectCollection(fields: ['a'])); + + $this->assertNoViolation(); + } + + public function testExpectsIterableElement(): void + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate('string', new UniqueObjectCollection()); + } + + public function testExpectsUniqueValidatableObject(): void + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate(new ArrayCollection([new \stdClass()]), new UniqueObjectCollection()); + } + + public function testNonUniqueCollectionDefaultSettings(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]) + ]), + new UniqueObjectCollection()); + + $this + ->buildViolation('This value is already used.') + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->setParameter('{{ object }}', 'objectString') + ->atPath('property.path[1].a') + ->assertRaised(); + } + + public function testNonUniqueCollectionSpecifyFields(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]) + ]), + new UniqueObjectCollection(fields: ['b'])); + + $this + ->buildViolation('This value is already used.') + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->setParameter('{{ object }}', 'objectString') + ->atPath('property.path[1].b') + ->assertRaised(); + } + + public function testNonUniqueCollectionFirstFieldIsTarget(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]) + ]), + new UniqueObjectCollection(fields: ['b', 'a'])); + + $this + ->buildViolation('This value is already used.') + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->setParameter('{{ object }}', 'objectString') + ->atPath('property.path[1].b') + ->assertRaised(); + } + + public function testNonUniqueCollectionAllowNull(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => null]), + new DummyUniqueValidatableObject(['a' => 2, 'b' => 2]), + new DummyUniqueValidatableObject(['a' => 3, 'b' => null]) + ]), + new UniqueObjectCollection(fields: ['b'], allowNull: true)); + + $this->assertNoViolation(); + } + + public function testNonUniqueCollectionDoNotAllowNull(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => null]), + new DummyUniqueValidatableObject(['a' => 2, 'b' => 2]), + new DummyUniqueValidatableObject(['a' => 3, 'b' => null]) + ]), + new UniqueObjectCollection(fields: ['b'], allowNull: false)); + + $this + ->buildViolation('This value is already used.') + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->setParameter('{{ object }}', 'objectString') + ->atPath('property.path[2].b') + ->assertRaised(); + } + + + +} diff --git a/tests/Validator/Constraints/UrlOrBuiltinValidatorTest.php b/tests/Validator/Constraints/UrlOrBuiltinValidatorTest.php new file mode 100644 index 00000000..c75754df --- /dev/null +++ b/tests/Validator/Constraints/UrlOrBuiltinValidatorTest.php @@ -0,0 +1,75 @@ +. + */ +namespace App\Tests\Validator\Constraints; + +use App\Validator\Constraints\UrlOrBuiltin; +use App\Validator\Constraints\UrlOrBuiltinValidator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class UrlOrBuiltinValidatorTest extends ConstraintValidatorTestCase +{ + + protected function createValidator(): UrlOrBuiltinValidator + { + return new UrlOrBuiltinValidator(); + } + + public function testNullIsValid(): void + { + $this->validator->validate(null, new UrlOrBuiltin()); + $this->assertNoViolation(); + } + + public function testEmptyStringIsValid(): void + { + $this->validator->validate('', new UrlOrBuiltin()); + $this->assertNoViolation(); + } + + public function testValidUrlIsValid(): void + { + $this->validator->validate('https://example.com', new UrlOrBuiltin()); + $this->assertNoViolation(); + } + + public function testValidBuiltinIsValid(): void + { + $this->validator->validate('%FOOTPRINTS%/test/footprint.png', new UrlOrBuiltin()); + $this->assertNoViolation(); + } + + public function testInvalidUrlIsInvalid(): void + { + $constraint = new UrlOrBuiltin([ + 'message' => 'myMessage', + ]); + + $this->validator->validate('invalid-url', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"invalid-url"') + ->setCode(UrlOrBuiltin::INVALID_URL_ERROR) + ->assertRaised(); + } +} diff --git a/tests/Validator/Constraints/ValidGoogleAuthCodeValidatorTest.php b/tests/Validator/Constraints/ValidGoogleAuthCodeValidatorTest.php new file mode 100644 index 00000000..a1bc1a74 --- /dev/null +++ b/tests/Validator/Constraints/ValidGoogleAuthCodeValidatorTest.php @@ -0,0 +1,144 @@ +. + */ +namespace App\Tests\Validator\Constraints; + +use App\Entity\UserSystem\User; +use App\Validator\Constraints\ValidGoogleAuthCode; +use App\Validator\Constraints\ValidGoogleAuthCodeValidator; +use PHPUnit\Framework\TestCase; +use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; +use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\ConstraintValidatorInterface; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class ValidGoogleAuthCodeValidatorTest extends ConstraintValidatorTestCase +{ + + protected function createValidator(): ConstraintValidatorInterface + { + $googleAuth = new class implements GoogleAuthenticatorInterface + { + + public function checkCode(TwoFactorInterface $user, string $code): bool + { + return $code === '123456'; + } + + public function getQRContent(TwoFactorInterface $user): string + { + return 'not_needed'; + } + + public function generateSecret(): string + { + return 'not_needed'; + } + }; + + $security = new class extends Security { + public function __construct() + { + //Leave empty + } + public function getUser(): ?UserInterface + { + return new class implements TwoFactorInterface, UserInterface { + + public function isGoogleAuthenticatorEnabled(): bool + { + return true; + } + + public function getGoogleAuthenticatorUsername(): string + { + return "test"; + } + + public function getGoogleAuthenticatorSecret(): ?string + { + return "not_needed"; + } + + public function getRoles(): array + { + return []; + } + + public function eraseCredentials() + { + } + + public function getUserIdentifier(): string + { + return 'test'; + } + }; + } + }; + + + return new ValidGoogleAuthCodeValidator($googleAuth, $security); + } + + public function testAllowNull(): void + { + $this->validator->validate(null, new ValidGoogleAuthCode()); + $this->assertNoViolation(); + } + + public function testAllowEmpty(): void + { + $this->validator->validate('', new ValidGoogleAuthCode()); + $this->assertNoViolation(); + } + + public function testValidCode(): void + { + $this->validator->validate('123456', new ValidGoogleAuthCode()); + $this->assertNoViolation(); + } + + public function testInvalidCode(): void + { + $this->validator->validate('111111', new ValidGoogleAuthCode()); + $this->buildViolation('validator.google_code.wrong_code') + ->assertRaised(); + } + + public function testCheckNumerical(): void + { + $this->validator->validate('123456a', new ValidGoogleAuthCode()); + $this->buildViolation('validator.google_code.only_digits_allowed') + ->assertRaised(); + } + + public function testCheckLength(): void + { + $this->validator->validate('12345', new ValidGoogleAuthCode()); + $this->buildViolation('validator.google_code.wrong_digit_count') + ->assertRaised(); + } +} diff --git a/tests/Validator/Constraints/ValidThemeValidatorTest.php b/tests/Validator/Constraints/ValidThemeValidatorTest.php new file mode 100644 index 00000000..9db8f33b --- /dev/null +++ b/tests/Validator/Constraints/ValidThemeValidatorTest.php @@ -0,0 +1,65 @@ +. + */ +namespace App\Tests\Validator\Constraints; + +use App\Validator\Constraints\ValidTheme; +use App\Validator\Constraints\ValidThemeValidator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class ValidThemeValidatorTest extends ConstraintValidatorTestCase +{ + + protected function createValidator(): ValidThemeValidator + { + return new ValidThemeValidator(['bootstrap', 'theme1', 'theme2']); + } + + public function testAllowNull(): void + { + $this->validator->validate(null, new ValidTheme()); + $this->assertNoViolation(); + } + + public function testAllowEmpty(): void + { + $this->validator->validate('', new ValidTheme()); + $this->assertNoViolation(); + } + + public function testValidTheme(): void + { + $this->validator->validate('bootstrap', new ValidTheme()); + $this->assertNoViolation(); + } + + public function testInvalidTheme(): void + { + $this->validator->validate('invalid', new ValidTheme()); + $this->buildViolation('validator.selected_theme_is_invalid') + ->setParameter('{{ value }}', 'invalid') + ->assertRaised(); + } + + +} diff --git a/tests/Validator/DummyUniqueValidatableObject.php b/tests/Validator/DummyUniqueValidatableObject.php new file mode 100644 index 00000000..a7dd9d7a --- /dev/null +++ b/tests/Validator/DummyUniqueValidatableObject.php @@ -0,0 +1,43 @@ +. + */ +namespace App\Tests\Validator; + +use App\Validator\UniqueValidatableInterface; + +class DummyUniqueValidatableObject implements UniqueValidatableInterface, \Stringable +{ + + public function __construct(public readonly array $array = []) + { + } + + public function getComparableFields(): array + { + return $this->array; + } + + public function __toString(): string + { + return 'objectString'; + } +} diff --git a/tests/assets/partkeepr_import_test.xml b/tests/assets/partkeepr_import_test.xml new file mode 100644 index 00000000..4fa497e2 --- /dev/null +++ b/tests/assets/partkeepr_import_test.xml @@ -0,0 +1,8952 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3 + 2023-03-22 23:28:48 + partkeepr:cron:synctips + + + 4 + 2023-03-22 23:28:48 + partkeepr:cron:create-statistic-snapshot + + + + + + + + + + + + + + + + + + + + 1 + Reichelt + Bal + www.ggllg.de + flksl + dsf + fdlsf + dfs + Re + 0 + + + 2 + rewr + dsf + sf + sdf + sf + sf + sf + sdf + 0 + + + + + + + + + + + + + + + 1 + 3 + CBGA-32 + 32-Lead Ceramic Ball Grid Array + + + 2 + 5 + FCBGA-576 + 576-Ball Ball Grid Array, Thermally Enhanced + + + 3 + 7 + PBGA-119 + 119-Ball Plastic Ball Grid Array + + + 4 + 9 + PBGA-169 + 169-Ball Plastic Ball Grid Array + + + 5 + 11 + PBGA-225 + 225-Ball Plastic a Ball Grid Array + + + 6 + 13 + PBGA-260 + 260-Ball Plastic Ball Grid Array + + + 7 + 15 + PBGA-297 + 297-Ball Plastic Ball Grid Array + + + 8 + 17 + PBGA-304 + 304-Lead Plastic Ball Grid Array + + + 9 + 19 + PBGA-316 + 316-Lead Plastic Ball Grid Array + + + 10 + 21 + PBGA-324 + 324-Ball Plastic Ball Grid Array + + + 11 + 23 + PBGA-385 + 385-Lead Ball Grid Array + + + 12 + 25 + PBGA-400 + 400-Ball Plastic Ball Grid Array + + + 13 + 27 + PBGA-484 + 484-Ball Plastic Ball Grid Array + + + 14 + 29 + PBGA-625 + 625-Ball Plastic Ball Grid Array + + + 15 + 31 + PBGA-676 + 676-Ball Plastic Ball Grid Array + + + 16 + 33 + SBGA-256 + 256-Ball Ball Grid Array, Thermally Enhanced + + + 17 + 35 + SBGA-304 + 304-Ball Ball Grid Array, Thermally Enhanced + + + 18 + 37 + SBGA-432 + 432-Ball Ball Grid Array, Thermally Enhanced + + + 19 + 39 + CerDIP-8 + 8-Lead Ceramic Dual In-Line Package + + + 20 + 41 + CerDIP-14 + 14-Lead Ceramic Dual In-Line Package + + + 21 + 43 + CerDIP-16 + 16-Lead Ceramic Dual In-Line Package + + + 22 + 45 + CerDIP-18 + 18-Lead Ceramic Dual In-Line Package + + + 23 + 47 + CerDIP-20 + 20-Lead Ceramic Dual In-Line Package + + + 24 + 49 + CerDIP-24 Narrow + 24-Lead Ceramic Dual In-Line Package - Narrow Body + + + 25 + 51 + CerDIP-24 Wide + 24-Lead Ceramic Dual In-Line Package - Wide Body + + + 26 + 53 + CerDIP-28 + 28-Lead Ceramic Dual In-Line Package + + + 27 + 55 + CerDIP-40 + 40-Lead Ceramic Dual In-Line Package + + + 28 + 57 + PDIP-8 + 8-Lead Plastic Dual In-Line Package + + + 29 + 59 + PDIP-14 + 14-Lead Plastic Dual In-Line Package + + + 30 + 61 + PDIP-16 + 16-Lead Plastic Dual In-Line Package + + + 31 + 63 + PDIP-18 + 18-Lead Plastic Dual In-Line Package + + + 32 + 65 + PDIP-20 + 20-Lead Plastic Dual In-Line Package + + + 33 + 67 + PDIP-24 + 24-Lead Plastic Dual In-Line Package + + + 34 + 69 + PDIP-28 Narrow + 28-Lead Plastic Dual In-Line Package, Narrow Body + + + 35 + 71 + PDIP-28 Wide + 28-Lead Plastic Dual In-Line Package, Wide Body + + + 36 + + SOIC-N-EP-8 + 8-Lead Standard Small Outline Package, with Expose Pad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + 1 + 142 + 0 + 1 + Root Category + + Root Category + + + 2 + 1 + 2 + 5 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 3 + 2 + 3 + 4 + 2 + 1 + CBGA + + Root Category ➤ BGA ➤ CBGA + + + 4 + 1 + 6 + 9 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 5 + 4 + 7 + 8 + 2 + 1 + FCBGA + + Root Category ➤ BGA ➤ FCBGA + + + 6 + 1 + 10 + 13 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 7 + 6 + 11 + 12 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 8 + 1 + 14 + 17 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 9 + 8 + 15 + 16 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 10 + 1 + 18 + 21 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 11 + 10 + 19 + 20 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 12 + 1 + 22 + 25 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 13 + 12 + 23 + 24 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 14 + 1 + 26 + 29 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 15 + 14 + 27 + 28 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 16 + 1 + 30 + 33 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 17 + 16 + 31 + 32 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 18 + 1 + 34 + 37 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 19 + 18 + 35 + 36 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 20 + 1 + 38 + 41 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 21 + 20 + 39 + 40 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 22 + 1 + 42 + 45 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 23 + 22 + 43 + 44 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 24 + 1 + 46 + 49 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 25 + 24 + 47 + 48 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 26 + 1 + 50 + 53 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 27 + 26 + 51 + 52 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 28 + 1 + 54 + 57 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 29 + 28 + 55 + 56 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 30 + 1 + 58 + 61 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 31 + 30 + 59 + 60 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 32 + 1 + 62 + 65 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 33 + 32 + 63 + 64 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 34 + 1 + 66 + 69 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 35 + 34 + 67 + 68 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 36 + 1 + 70 + 73 + 1 + 1 + BGA + + Root Category ➤ BGA + + + 37 + 36 + 71 + 72 + 2 + 1 + PBGA + + Root Category ➤ BGA ➤ PBGA + + + 38 + 1 + 74 + 77 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 39 + 38 + 75 + 76 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 40 + 1 + 78 + 81 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 41 + 40 + 79 + 80 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 42 + 1 + 82 + 85 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 43 + 42 + 83 + 84 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 44 + 1 + 86 + 89 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 45 + 44 + 87 + 88 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 46 + 1 + 90 + 93 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 47 + 46 + 91 + 92 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 48 + 1 + 94 + 97 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 49 + 48 + 95 + 96 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 50 + 1 + 98 + 101 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 51 + 50 + 99 + 100 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 52 + 1 + 102 + 105 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 53 + 52 + 103 + 104 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 54 + 1 + 106 + 109 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 55 + 54 + 107 + 108 + 2 + 1 + CERDIP + + Root Category ➤ DIP ➤ CERDIP + + + 56 + 1 + 110 + 113 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 57 + 56 + 111 + 112 + 2 + 1 + PDIP + + Root Category ➤ DIP ➤ PDIP + + + 58 + 1 + 114 + 117 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 59 + 58 + 115 + 116 + 2 + 1 + PDIP + + Root Category ➤ DIP ➤ PDIP + + + 60 + 1 + 118 + 121 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 61 + 60 + 119 + 120 + 2 + 1 + PDIP + + Root Category ➤ DIP ➤ PDIP + + + 62 + 1 + 122 + 125 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 63 + 62 + 123 + 124 + 2 + 1 + PDIP + + Root Category ➤ DIP ➤ PDIP + + + 64 + 1 + 126 + 129 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 65 + 64 + 127 + 128 + 2 + 1 + PDIP + + Root Category ➤ DIP ➤ PDIP + + + 66 + 1 + 130 + 133 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 67 + 66 + 131 + 132 + 2 + 1 + PDIP + + Root Category ➤ DIP ➤ PDIP + + + 68 + 1 + 134 + 137 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 69 + 68 + 135 + 136 + 2 + 1 + PDIP + + Root Category ➤ DIP ➤ PDIP + + + 70 + 1 + 138 + 141 + 1 + 1 + DIP + + Root Category ➤ DIP + + + 71 + 70 + 139 + 140 + 2 + 1 + PDIP + + Root Category ➤ DIP ➤ PDIP + + + + + + + + + + + + + + + + + + + + 1 + 1 + footprint + 55f695c0-c8ff-11ed-bdcf-59f5f5013ecd + CBGA-32.png + image/png + 23365 + png + + 2023-03-22 23:17:30 + + + 2 + 2 + footprint + 55f7eb50-c8ff-11ed-8d5f-bf44eda6855c + FCBGA-576.png + image/png + 47861 + png + + 2023-03-22 23:17:30 + + + 3 + 3 + footprint + 55f8444c-c8ff-11ed-9ff3-21919bd0b36e + PBGA-119.png + image/png + 32537 + png + + 2023-03-22 23:17:30 + + + 4 + 4 + footprint + 55f8921c-c8ff-11ed-b733-1f9d0f81d4fc + PBGA-169.png + image/png + 36699 + png + + 2023-03-22 23:17:30 + + + 5 + 5 + footprint + 55f8e42e-c8ff-11ed-9d48-a25b23bb490e + PBGA-225.png + image/png + 39366 + png + + 2023-03-22 23:17:30 + + + 6 + 6 + footprint + 55f93226-c8ff-11ed-924b-2b812e93d1f6 + PBGA-260.png + image/png + 61202 + png + + 2023-03-22 23:17:30 + + + 7 + 7 + footprint + 55f97e48-c8ff-11ed-aa4b-1e57132db8dd + PBGA-297.png + image/png + 68013 + png + + 2023-03-22 23:17:30 + + + 8 + 8 + footprint + 55f9ce34-c8ff-11ed-938d-dc132c365553 + PBGA-304.png + image/png + 55833 + png + + 2023-03-22 23:17:30 + + + 9 + 9 + footprint + 55fa1808-c8ff-11ed-9c31-69685fea3a6f + PBGA-316.png + image/png + 55996 + png + + 2023-03-22 23:17:30 + + + 10 + 10 + footprint + 55fa66e6-c8ff-11ed-b7b8-21795f745a53 + PBGA-324.png + image/png + 44882 + png + + 2023-03-22 23:17:30 + + + 11 + 11 + footprint + 55fab330-c8ff-11ed-b5d1-2422489f775a + PBGA-385.png + image/png + 35146 + png + + 2023-03-22 23:17:30 + + + 12 + 12 + footprint + 55fafcaa-c8ff-11ed-ae25-dd7dc4b216e3 + PBGA-400.png + image/png + 67933 + png + + 2023-03-22 23:17:30 + + + 13 + 13 + footprint + 55fb4a16-c8ff-11ed-9bf7-ba491078c6c7 + PBGA-484.png + image/png + 49851 + png + + 2023-03-22 23:17:30 + + + 14 + 14 + footprint + 55fb93ae-c8ff-11ed-bb52-0c76c2603549 + PBGA-625.png + image/png + 65307 + png + + 2023-03-22 23:17:30 + + + 15 + 15 + footprint + 55fbdeae-c8ff-11ed-8e4d-16e3121e623a + PBGA-676.png + image/png + 54708 + png + + 2023-03-22 23:17:30 + + + 16 + 16 + footprint + 55fc2814-c8ff-11ed-ab90-26137e083580 + SBGA-256.png + image/png + 48636 + png + + 2023-03-22 23:17:30 + + + 17 + 17 + footprint + 55fc6fea-c8ff-11ed-8671-25ee225cc8a6 + SBGA-304.png + image/png + 51944 + png + + 2023-03-22 23:17:30 + + + 18 + 18 + footprint + 55fcb91e-c8ff-11ed-ab2c-dcfa807f34fd + SBGA-432.png + image/png + 63247 + png + + 2023-03-22 23:17:30 + + + 19 + 19 + footprint + 55fd0720-c8ff-11ed-bf3f-b34a2375c258 + CERDIP-8.png + image/png + 13544 + png + + 2023-03-22 23:17:30 + + + 20 + 20 + footprint + 55fd4974-c8ff-11ed-8c41-52e85be33ce3 + CERDIP-14.png + image/png + 14226 + png + + 2023-03-22 23:17:30 + + + 21 + 21 + footprint + 55fd92e4-c8ff-11ed-b58b-2e0b4703c07c + CERDIP-16.png + image/png + 14576 + png + + 2023-03-22 23:17:30 + + + 22 + 22 + footprint + 55fdd718-c8ff-11ed-aa74-6ebd8a44b22c + CERDIP-18.png + image/png + 9831 + png + + 2023-03-22 23:17:30 + + + 23 + 23 + footprint + 55fe28f8-c8ff-11ed-9be0-45de90527ab0 + CERDIP-20.png + image/png + 10209 + png + + 2023-03-22 23:17:30 + + + 24 + 24 + footprint + 55fe7088-c8ff-11ed-9cbd-225ebb21aad1 + CERDIP-24-N.png + image/png + 11582 + png + + 2023-03-22 23:17:30 + + + 25 + 25 + footprint + 55feb516-c8ff-11ed-82b1-60555b199ba1 + CERDIP-24-W.png + image/png + 12407 + png + + 2023-03-22 23:17:30 + + + 26 + 26 + footprint + 55fefb3e-c8ff-11ed-bfbd-8ff7ca6d8389 + CERDIP-28.png + image/png + 12233 + png + + 2023-03-22 23:17:30 + + + 27 + 27 + footprint + 55ff4346-c8ff-11ed-8e5d-3cb198a6abbe + CERDIP-40.png + image/png + 12421 + png + + 2023-03-22 23:17:30 + + + 28 + 28 + footprint + 55ff87d4-c8ff-11ed-b606-58804e50e34e + PDIP-8.png + image/png + 13537 + png + + 2023-03-22 23:17:30 + + + 29 + 29 + footprint + 55ffcdca-c8ff-11ed-9a42-f03c778f9d7b + PDIP-14.png + image/png + 13779 + png + + 2023-03-22 23:17:30 + + + 30 + 30 + footprint + 56001370-c8ff-11ed-8386-42396433ea07 + PDIP-16.png + image/png + 18305 + png + + 2023-03-22 23:17:30 + + + 31 + 31 + footprint + 56005c5e-c8ff-11ed-b2ce-1252ddf0e3d3 + PDIP-18.png + image/png + 14893 + png + + 2023-03-22 23:17:30 + + + 32 + 32 + footprint + 5600abbe-c8ff-11ed-8cdf-327a4488b1d8 + PDIP-20.png + image/png + 14429 + png + + 2023-03-22 23:17:30 + + + 33 + 33 + footprint + 5600eeb2-c8ff-11ed-b438-41b6b6a9c181 + PDIP-24.png + image/png + 14647 + png + + 2023-03-22 23:17:30 + + + 34 + 34 + footprint + 560130a2-c8ff-11ed-9c9e-b5df2af3c7c7 + PDIP-28-N.png + image/png + 18703 + png + + 2023-03-22 23:17:30 + + + 35 + 35 + footprint + 560176e8-c8ff-11ed-bbe8-26c989058206 + PDIP-28-W.png + image/png + 15728 + png + + 2023-03-22 23:17:30 + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + partkeepr + partkeepr + 1 + flbvx2z49kgs04gw0cs08gc4g88kcgk + 9Nxsm8JzMh+zZ+seR5wv/r42NGtzJmeL1IC9LD1IaX19z+YRewwUs0UrIx7sjOhjRn8bkAusm/IKlRGNizkKhw== + + 0 + 0 + + + + a:1:{i:0;s:16:"ROLE_SUPER_ADMIN";} + 0 + + partkeepr@test.org + partkeepr@test.org + + + 2 + partkeepr2 + partkeepr2 + 1 + ocy7yd6dxlw4wo84c0sosw0kg8k8gcs + a+h/FoVvBEoEGq9wAwbsXyD8RywH1qzciav8FR6ekv+UP6UHl+h+TCN2JTlIK8emTkRhyE3sXPtbH8TAQ0XOYw== + + 0 + 0 + + + + a:0:{} + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + Integrated Circuit Designs + + + + + + + + + 2 + ACTEL + + + + + + + + + 3 + ALTINC + + + + + + + + + 4 + Aeroflex + + + + + + + + + 5 + Agilent Technologies + + + + + + + + + 6 + AKM Semiconductor + + + + + + + + + 7 + Alesis Semiconductor + + + + + + + + + 8 + ALi (Acer Laboratories Inc.) + + + + + + + + + 9 + Allayer Communications + + + + + + + + + 10 + Allegro Microsystems + + + + + + + + + 11 + Alliance Semiconductor + + + + + + + + + 12 + Alpha Industries + + + + + + + + + 13 + Alpha Microelectronics + + + + + + + + + 14 + Altera + + + + + + + + + 15 + Advanced Micro Devices (AMD) + + + + + + + + + 16 + American Microsystems, Inc. (AMI) + + + + + + + + + 17 + Amic Technology + + + + + + + + + 18 + Amphus + + + + + + + + + 19 + Anachip Corp. + + + + + + + + + 20 + ANADIGICs + + + + + + + + + 21 + Analog Devices + + + + + + + + + 22 + Analog Systems + + + + + + + + + 23 + Anchor Chips + + + + + + + + + 24 + Apex Microtechnology + + + + + + + + + 25 + ARK Logic + + + + + + + + + 26 + ASD + + + + + + + + + 27 + Astec Semiconductor + + + + + + + + + 28 + ATC (Analog Technologie) + + + + + + + + + 29 + ATecoM + + + + + + + + + 30 + ATI Technologies + + + + + + + + + 31 + Atmel + + + + + + + + + 32 + AT&T + + + + + + + + + 33 + AudioCodes + + + + + + + + + 34 + Aura Vision + + + + + + + + + 35 + Aureal + + + + + + + + + 36 + Austin Semiconductor + + + + + + + + + 37 + Avance Logic + + + + + + + + + 38 + Bel Fuse + + + + + + + + + 39 + Benchmarq Microelectronics + + + + + + + + + 40 + BI Technologies + + + + + + + + + 41 + Bowmar/White + + + + + + + + + 42 + Brightflash + + + + + + + + + 43 + Broadcom + + + + + + + + + 44 + Brooktree(now Rockwell) + + + + + + + + + 45 + Burr Brown + + + + + + + + + 46 + California Micro Devices + + + + + + + + + 47 + Calogic + + + + + + + + + 48 + Catalyst Semiconductor + + + + + + + + + 49 + Centon Electronics + + + + + + + + + 50 + Ceramate Technical + + + + + + + + + 51 + Cherry Semiconductor + + + + + + + + + 52 + Chipcon AS + + + + + + + + + 53 + Chips + + + + + + + + + 54 + Chrontel + + + + + + + + + 55 + Cirrus Logic + + + + + + + + + 56 + ComCore Semiconductor + + + + + + + + + 57 + Conexant + + + + + + + + + 58 + Cosmo Electronics + + + + + + + + + 59 + Chrystal + + + + + + + + + 60 + Cygnal + + + + + + + + + 61 + Cypress Semiconductor + + + + + + + + + 62 + Cyrix Corporation + + + + + + + + + 63 + Daewoo Electronics Semiconductor + + + + + + + + + 64 + Dallas Semiconductor + + + + + + + + + 65 + Davicom Semiconductor + + + + + + + + + 66 + Data Delay Devices + + + + + + + + + 67 + Diamond Technologies + + + + + + + + + 68 + DIOTEC + + + + + + + + + 69 + DTC Data Technology + + + + + + + + + 70 + DVDO + + + + + + + + + 71 + EG&G + + + + + + + + + 72 + Elan Microelectronics + + + + + + + + + 73 + ELANTEC + + + + + + + + + 74 + Electronic Arrays + + + + + + + + + 75 + Elite Flash Storage Technology Inc. (EFST) + + + + + + + + + 76 + EM Microelectronik - Marin + + + + + + + + + 77 + Enhanced Memory Systems + + + + + + + + + 78 + Ensoniq Corp + + + + + + + + + 79 + EON Silicon Devices + + + + + + + + + 80 + Epson + + + + + + + + + 81 + Ericsson + + + + + + + + + 82 + ESS Technology + + + + + + + + + 83 + Electronic Technology + + + + + + + + + 84 + EXAR + + + + + + + + + 85 + Excel Semiconductor Inc. + + + + + + + + + 86 + Fairschild + + + + + + + + + 87 + Freescale Semiconductor + + + + + + + + + 88 + Fujitsu + + + + + + + + + 89 + Galileo Technology + + + + + + + + + 90 + Galvantech + + + + + + + + + 91 + GEC Plessey + + + + + + + + + 92 + Gennum + + + + + + + + + 93 + General Electric (Harris) + + + + + + + + + 94 + General Instruments + + + + + + + + + 95 + G-Link Technology + + + + + + + + + 96 + Goal Semiconductor + + + + + + + + + 97 + Goldstar + + + + + + + + + 98 + Gould + + + + + + + + + 99 + Greenwich Instruments + + + + + + + + + 100 + General Semiconductor + + + + + + + + + 101 + Harris Semiconductor + + + + + + + + + 102 + VEB + + + + + + + + + 103 + Hitachi Semiconductor + + + + + + + + + 104 + Holtek + + + + + + + + + 105 + Hewlett Packard + + + + + + + + + 106 + Hualon + + + + + + + + + 107 + Hynix Semiconductor + + + + + + + + + 108 + Hyundai + + + + + + + + + 109 + IC Design + + + + + + + + + 110 + Integrated Circuit Systems (ICS) + + + + + + + + + 111 + IC - Haus + + + + + + + + + 112 + ICSI (Integrated Circuit Solution Inc.) + + + + + + + + + 113 + I-Cube + + + + + + + + + 114 + IC Works + + + + + + + + + 115 + Integrated Device Technology (IDT) + + + + + + + + + 116 + IGS Technologies + + + + + + + + + 117 + IMPALA Linear + + + + + + + + + 118 + IMP + + + + + + + + + 119 + Infineon + + + + + + + + + 120 + INMOS + + + + + + + + + 121 + Intel + + + + + + + + + 122 + Intersil + + + + + + + + + 123 + International Rectifier + + + + + + + + + 124 + Information Storage Devices + + + + + + + + + 125 + ISSI (Integrated Silicon Solution, Inc.) + + + + + + + + + 126 + Integrated Technology Express + + + + + + + + + 127 + ITT Semiconductor (Micronas Intermetall) + + + + + + + + + 128 + IXYS + + + + + + + + + 129 + Korea Electronics (KEC) + + + + + + + + + 130 + Kota Microcircuits + + + + + + + + + 131 + Lattice Semiconductor Corp. + + + + + + + + + 132 + Lansdale Semiconductor + + + + + + + + + 133 + Level One Communications + + + + + + + + + 134 + LG Semicon (Lucky Goldstar Electronic Co.) + + + + + + + + + 135 + Linear Technology + + + + + + + + + 136 + Linfinity Microelectronics + + + + + + + + + 137 + Lite-On + + + + + + + + + 138 + Lucent Technologies (AT&T Microelectronics) + + + + + + + + + 139 + Macronix International + + + + + + + + + 140 + Marvell Semiconductor + + + + + + + + + 141 + Matsushita Panasonic + + + + + + + + + 142 + Maxim Dallas + + + + + + + + + 143 + Media Vision + + + + + + + + + 144 + Microchip (Arizona Michrochip Technology) + + + + + + + + + 145 + Matra MHS + + + + + + + + + 146 + Micrel Semiconductor + + + + + + + + + 147 + Micronas + + + + + + + + + 148 + Micronix Integrated Systems + + + + + + + + + 149 + Micron Technology, Inc. + + + + + + + + + 150 + Microsemi + + + + + + + + + 151 + Mini-Circuits + + + + + + + + + 152 + Mitel Semiconductor + + + + + + + + + 153 + Mitsubishi Semiconductor + + + + + + + + + 154 + Micro Linear + + + + + + + + + 155 + MMI (Monolithic Memories, Inc.) + + + + + + + + + 156 + Mosaic Semiconductor + + + + + + + + + 157 + Mosel Vitelic + + + + + + + + + 158 + MOS Technologies + + + + + + + + + 159 + Mostek + + + + + + + + + 160 + MoSys + + + + + + + + + 161 + Motorola + + + + + + + + + 162 + Microtune + + + + + + + + + 163 + M-Systems + + + + + + + + + 164 + Murata Manufacturing + + + + + + + + + 165 + MWave (IBM) + + + + + + + + + 166 + Myson Technology + + + + + + + + + 167 + NEC Electronics + + + + + + + + + 168 + NexFlash Technologies + + + + + + + + + 169 + New Japan Radio + + + + + + + + + 170 + National Semiconductor + + + + + + + + + 171 + NVidia Corporation + + + + + + + + + 172 + Oak Technology + + + + + + + + + 173 + Oki Semiconductor + + + + + + + + + 174 + Opti + + + + + + + + + 175 + Orbit Semiconductor + + + + + + + + + 176 + Oren Semiconductor + + + + + + + + + 177 + Performance Semiconductor + + + + + + + + + 178 + Pericom Semiconductor + + + + + + + + + 179 + PhaseLink Laboratories + + + + + + + + + 180 + Philips Semiconductor + + + + + + + + + 181 + PLX Technology + + + + + + + + + 182 + PMC- Sierra + + + + + + + + + 183 + Precision Monolithics + + + + + + + + + 184 + Princeton Technology + + + + + + + + + 185 + PowerSmart + + + + + + + + + 186 + QuickLogic + + + + + + + + + 187 + Qlogic + + + + + + + + + 188 + Quality Semiconductor + + + + + + + + + 189 + Rabbit Semiconductor + + + + + + + + + 190 + Ramtron International Co. + + + + + + + + + 191 + Raytheon Semiconductor + + + + + + + + + 192 + RCA Solid State + + + + + + + + + 193 + Realtek Semiconductor + + + + + + + + + 194 + Rectron + + + + + + + + + 195 + Rendition + + + + + + + + + 196 + Renesas Technology + + + + + + + + + 197 + Rockwell + + + + + + + + + 198 + Rohm Corp. + + + + + + + + + 199 + S3 + + + + + + + + + 200 + Sage + + + + + + + + + 201 + Saifun Semiconductors Ltd. + + + + + + + + + 202 + Sames + + + + + + + + + 203 + Samsung + + + + + + + + + 204 + Sanken + + + + + + + + + 205 + Sanyo + + + + + + + + + 206 + Scenix + + + + + + + + + 207 + Samsung Electronics + + + + + + + + + 208 + SEEQ Technology + + + + + + + + + 209 + Seiko Instruments + + + + + + + + + 210 + Semtech + + + + + + + + + 211 + SGS-Ates + + + + + + + + + 212 + SGS-Thomson Microelectonics ST-M) + + + + + + + + + 213 + Sharp Microelectronics (USA) + + + + + + + + + 214 + Shindengen + + + + + + + + + 215 + Siemens Microelectronics, Inc. + + + + + + + + + 216 + Sierra + + + + + + + + + 217 + Sigma Tel + + + + + + + + + 218 + Signetics + + + + + + + + + 219 + Silicon Laboratories + + + + + + + + + 220 + Silicon Magic + + + + + + + + + 221 + Simtec Corp. + + + + + + + + + 222 + Siliconix + + + + + + + + + 223 + Siliconians + + + + + + + + + 224 + Sipex + + + + + + + + + 225 + Silicon Integrated Systems + + + + + + + + + 226 + SMC + + + + + + + + + 227 + Standard Microsystems + + + + + + + + + 228 + Sony Semiconductor + + + + + + + + + 229 + Space Electronics + + + + + + + + + 230 + Spectek + + + + + + + + + 231 + Signal Processing Technologies + + + + + + + + + 232 + Solid State Scientific + + + + + + + + + 233 + Silicon Storage Technology (SST) + + + + + + + + + 234 + STMicroelectronics + + + + + + + + + 235 + SUMMIT Microelectronics + + + + + + + + + 236 + Synergy Semiconductor + + + + + + + + + 237 + Synertek + + + + + + + + + 238 + Taiwan Semiconductor + + + + + + + + + 239 + TDK Semiconductor + + + + + + + + + 240 + Teccor Electronics + + + + + + + + + 241 + TelCom Semiconductor + + + + + + + + + 242 + Teledyne + + + + + + + + + 243 + Telefunken + + + + + + + + + 244 + Teltone + + + + + + + + + 245 + Thomson-CSF + + + + + + + + + 246 + Texas Instruments + + + + + + + + + 247 + Toko Amerika + + + + + + + + + 248 + Toshiba (US) + + + + + + + + + 249 + Trident + + + + + + + + + 250 + TriQuint Semiconductor + + + + + + + + + 251 + Triscend + + + + + + + + + 252 + Tseng Labs + + + + + + + + + 253 + Tundra + + + + + + + + + 254 + Turbo IC + + + + + + + + + 255 + Ubicom + + + + + + + + + 256 + United Microelectronics Corp (UMC) + + + + + + + + + 257 + Unitrode + + + + + + + + + 258 + USAR Systems + + + + + + + + + 259 + United Technologies Microelectronics Center (UTMC) + + + + + + + + + 260 + Utron + + + + + + + + + 261 + V3 Semiconductor + + + + + + + + + 262 + Vadem + + + + + + + + + 263 + Vanguard International Semiconductor + + + + + + + + + 264 + Vantis + + + + + + + + + 265 + Via Technologies + + + + + + + + + 266 + Virata + + + + + + + + + 267 + Vishay + + + + + + + + + 268 + Vision Tech + + + + + + + + + 269 + Vitelic + + + + + + + + + 270 + VLSI Technology + + + + + + + + + 271 + Volterra + + + + + + + + + 272 + VTC + + + + + + + + + 273 + Waferscale Integration (WSI) + + + + + + + + + 274 + Western Digital + + + + + + + + + 275 + Weitek + + + + + + + + + 276 + Winbond + + + + + + + + + 277 + Wofson Microelectronics + + + + + + + + + 278 + Xwmics + + + + + + + + + 279 + Xicor + + + + + + + + + 280 + Xilinx + + + + + + + + + 281 + Yamaha + + + + + + + + + 282 + Zetex Semiconductors + + + + + + + + + 283 + Zilog + + + + + + + + + 284 + ZMD (Zentrum Mikroelektronik Dresden) + + + + + + + + + 285 + Zoran + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + iclogo + 56736e10-c8ff-11ed-9738-a49472685cd5 + acer.png + image/png + 2195 + png + + 2023-03-22 23:17:31 + + + 2 + 2 + iclogo + 5673df12-c8ff-11ed-95b5-5825d19874bd + actel.png + image/png + 5003 + png + + 2023-03-22 23:17:31 + + + 3 + 3 + iclogo + 56740a14-c8ff-11ed-8a7a-e6d789965eb7 + advldev.png + image/png + 1835 + png + + 2023-03-22 23:17:31 + + + 4 + 4 + iclogo + 56743106-c8ff-11ed-9369-adae9b0aee92 + aeroflex1.png + image/png + 9649 + png + + 2023-03-22 23:17:31 + + + 5 + 4 + iclogo + 56744056-c8ff-11ed-9b85-834fc0efcc0c + aeroflex2.png + image/png + 4562 + png + + 2023-03-22 23:17:31 + + + 6 + 5 + iclogo + 56746626-c8ff-11ed-90f1-e9f5ae8d1b89 + agilent.png + image/png + 5264 + png + + 2023-03-22 23:17:31 + + + 7 + 6 + iclogo + 56748fde-c8ff-11ed-9322-3aae2ee0bd68 + akm.png + image/png + 2204 + png + + 2023-03-22 23:17:31 + + + 8 + 7 + iclogo + 5674b694-c8ff-11ed-b38e-10b020c2f3f7 + alesis.png + image/png + 1475 + png + + 2023-03-22 23:17:31 + + + 9 + 8 + iclogo + 5674de4e-c8ff-11ed-9606-a90fbac9c900 + ali1.png + image/png + 2462 + png + + 2023-03-22 23:17:31 + + + 10 + 8 + iclogo + 5674ea88-c8ff-11ed-8f21-b848e9e67a0f + ali2.png + image/png + 1784 + png + + 2023-03-22 23:17:31 + + + 11 + 9 + iclogo + 56751468-c8ff-11ed-bc15-3950afd683d4 + allayer.png + image/png + 1869 + png + + 2023-03-22 23:17:31 + + + 12 + 10 + iclogo + 56753d3a-c8ff-11ed-8973-2a1853f53aa3 + allegro.png + image/png + 1475 + png + + 2023-03-22 23:17:31 + + + 13 + 11 + iclogo + 567564e0-c8ff-11ed-8a16-05ef910cc7d3 + alliance.png + image/png + 1949 + png + + 2023-03-22 23:17:31 + + + 14 + 12 + iclogo + 56758ae2-c8ff-11ed-9328-593d60e003b6 + alphaind.png + image/png + 1403 + png + + 2023-03-22 23:17:31 + + + 15 + 13 + iclogo + 5675b22e-c8ff-11ed-a6f1-f0044c7db7ef + alphamic.png + image/png + 2989 + png + + 2023-03-22 23:17:31 + + + 16 + 13 + iclogo + 5675bdc8-c8ff-11ed-a4d8-e936fcf8a6f4 + alpha.png + image/png + 1534 + png + + 2023-03-22 23:17:31 + + + 17 + 14 + iclogo + 5675e406-c8ff-11ed-9f4d-85fa9b728109 + altera.png + image/png + 4064 + png + + 2023-03-22 23:17:31 + + + 18 + 15 + iclogo + 56760b2a-c8ff-11ed-9d5d-b60428ce67da + amd.png + image/png + 1709 + png + + 2023-03-22 23:17:31 + + + 19 + 16 + iclogo + 567631f4-c8ff-11ed-beda-6fc6e0010be3 + ami1.png + image/png + 2399 + png + + 2023-03-22 23:17:31 + + + 20 + 16 + iclogo + 56763d02-c8ff-11ed-916d-a5dc5999c8e1 + ami2.png + image/png + 1706 + png + + 2023-03-22 23:17:31 + + + 21 + 17 + iclogo + 56778702-c8ff-11ed-827a-4888d34a5221 + amic.png + image/png + 2228 + png + + 2023-03-22 23:17:31 + + + 22 + 18 + iclogo + 5677b72c-c8ff-11ed-9ea1-e94b2864ef6c + ampus.png + image/png + 6150 + png + + 2023-03-22 23:17:31 + + + 23 + 19 + iclogo + 5677e4cc-c8ff-11ed-a8c7-4149b345f008 + anachip.png + image/png + 3549 + png + + 2023-03-22 23:17:31 + + + 24 + 20 + iclogo + 56780bf0-c8ff-11ed-ad69-bead7e1692f4 + anadigic.png + image/png + 5147 + png + + 2023-03-22 23:17:31 + + + 25 + 21 + iclogo + 56783242-c8ff-11ed-a430-b6c923d43e60 + analog1.png + image/png + 1262 + png + + 2023-03-22 23:17:31 + + + 26 + 21 + iclogo + 56783c60-c8ff-11ed-86fa-f7503d83026f + analog.png + image/png + 1403 + png + + 2023-03-22 23:17:31 + + + 27 + 22 + iclogo + 56786b9a-c8ff-11ed-a379-889ffabbe28f + anasys.png + image/png + 3309 + png + + 2023-03-22 23:17:31 + + + 28 + 23 + iclogo + 56789250-c8ff-11ed-b016-86e9cd015aa9 + anchorch.png + image/png + 1475 + png + + 2023-03-22 23:17:31 + + + 29 + 24 + iclogo + 5678b9d8-c8ff-11ed-8f71-89d4834df732 + apex1.png + image/png + 2627 + png + + 2023-03-22 23:17:31 + + + 30 + 24 + iclogo + 5678c8ba-c8ff-11ed-8deb-a9146eb405f2 + apex.png + image/png + 3974 + png + + 2023-03-22 23:17:31 + + + 31 + 25 + iclogo + 5678eeee-c8ff-11ed-815b-c08fc768f909 + ark.png + image/png + 2089 + png + + 2023-03-22 23:17:31 + + + 32 + 26 + iclogo + 56791c7a-c8ff-11ed-bdec-09b2ccd10f57 + asd.png + image/png + 5024 + png + + 2023-03-22 23:17:31 + + + 33 + 27 + iclogo + 567945b0-c8ff-11ed-a0d1-638200f30d7e + astec.png + image/png + 3369 + png + + 2023-03-22 23:17:31 + + + 34 + 28 + iclogo + 56796dba-c8ff-11ed-a83f-16b5def8c7d2 + atc.png + image/png + 8660 + png + + 2023-03-22 23:17:31 + + + 35 + 29 + iclogo + 56799a1a-c8ff-11ed-b586-b9bcffe8ade4 + atecom.png + image/png + 1709 + png + + 2023-03-22 23:17:31 + + + 36 + 30 + iclogo + 5679bdba-c8ff-11ed-ace8-9745eb1c428a + ati.png + image/png + 2630 + png + + 2023-03-22 23:17:31 + + + 37 + 31 + iclogo + 5679e20e-c8ff-11ed-a786-dd09c1a44a45 + atmel.png + image/png + 2843 + png + + 2023-03-22 23:17:31 + + + 38 + 32 + iclogo + 567a0c48-c8ff-11ed-9074-34f1bcfe6e71 + att.png + image/png + 2816 + png + + 2023-03-22 23:17:31 + + + 39 + 33 + iclogo + 567a2fd4-c8ff-11ed-a43a-959314ab1731 + audiocod.png + image/png + 2429 + png + + 2023-03-22 23:17:31 + + + 40 + 34 + iclogo + 567a5414-c8ff-11ed-874a-b1fa27b6759f + auravis.png + image/png + 2281 + png + + 2023-03-22 23:17:31 + + + 41 + 35 + iclogo + 567a782c-c8ff-11ed-b1e0-163c79aca6f7 + aureal.png + image/png + 2109 + png + + 2023-03-22 23:17:31 + + + 42 + 36 + iclogo + 567a9eba-c8ff-11ed-ac82-563388522721 + austin.png + image/png + 2464 + png + + 2023-03-22 23:17:31 + + + 43 + 37 + iclogo + 567ac1c4-c8ff-11ed-b710-3f02bf90a788 + averlog.png + image/png + 1552 + png + + 2023-03-22 23:17:31 + + + 44 + 38 + iclogo + 567ae5f0-c8ff-11ed-8686-515d834485c9 + belfuse.png + image/png + 2204 + png + + 2023-03-22 23:17:31 + + + 45 + 39 + iclogo + 567b0ac6-c8ff-11ed-ba65-ddfaf11024ac + benchmrq.png + image/png + 1370 + png + + 2023-03-22 23:17:31 + + + 46 + 40 + iclogo + 567b2f60-c8ff-11ed-bb76-4a08e20c9f53 + bi.png + image/png + 2008 + png + + 2023-03-22 23:17:31 + + + 47 + 41 + iclogo + 567b529c-c8ff-11ed-bf78-8f32f9163954 + bowmar_white.png + image/png + 4652 + png + + 2023-03-22 23:17:31 + + + 48 + 42 + iclogo + 567b786c-c8ff-11ed-a2dc-555306aca428 + bright.png + image/png + 6839 + png + + 2023-03-22 23:17:31 + + + 49 + 43 + iclogo + 567b9db0-c8ff-11ed-8bfe-99239254da53 + broadcom.png + image/png + 6056 + png + + 2023-03-22 23:17:31 + + + 50 + 44 + iclogo + 567bc344-c8ff-11ed-994c-b5cf21979ad4 + brooktre.png + image/png + 1364 + png + + 2023-03-22 23:17:31 + + + 51 + 45 + iclogo + 567be96e-c8ff-11ed-9f51-6cab3d998613 + burrbrwn.png + image/png + 3563 + png + + 2023-03-22 23:17:31 + + + 52 + 46 + iclogo + 567c0d18-c8ff-11ed-b35c-4cab50b0cd73 + calmicro.png + image/png + 2109 + png + + 2023-03-22 23:17:31 + + + 53 + 47 + iclogo + 567c7532-c8ff-11ed-a3ea-b93c3e9c4635 + calogic.png + image/png + 3367 + png + + 2023-03-22 23:17:31 + + + 54 + 48 + iclogo + 567c9e0e-c8ff-11ed-8847-a0b673d5e1ab + catalys1.png + image/png + 1922 + png + + 2023-03-22 23:17:31 + + + 55 + 48 + iclogo + 567ca9bc-c8ff-11ed-8bbb-027650d63cd5 + catalyst.png + image/png + 2228 + png + + 2023-03-22 23:17:31 + + + 56 + 49 + iclogo + 567ccf14-c8ff-11ed-8b32-854f12739943 + ccube.png + image/png + 1309 + png + + 2023-03-22 23:17:31 + + + 57 + 50 + iclogo + 567cf700-c8ff-11ed-877f-fd2bffe83a5e + ceramate1.png + image/png + 2917 + png + + 2023-03-22 23:17:31 + + + 58 + 50 + iclogo + 567d00ba-c8ff-11ed-b7f4-5f940585884e + ceramate2.png + image/png + 2917 + png + + 2023-03-22 23:17:31 + + + 59 + 51 + iclogo + 567d21b2-c8ff-11ed-bf52-26280437fbc7 + cherry.png + image/png + 2507 + png + + 2023-03-22 23:17:31 + + + 60 + 52 + iclogo + 567d46e2-c8ff-11ed-906c-72aa9a6fbc85 + chipcon1.png + image/png + 8655 + png + + 2023-03-22 23:17:31 + + + 61 + 52 + iclogo + 567d533a-c8ff-11ed-bd42-4e0ad6fe5289 + chipcon2.png + image/png + 2923 + png + + 2023-03-22 23:17:31 + + + 62 + 53 + iclogo + 567d7a86-c8ff-11ed-9ebc-093fefd63927 + chips.png + image/png + 2864 + png + + 2023-03-22 23:17:31 + + + 63 + 54 + iclogo + 567d9f5c-c8ff-11ed-abd4-04b03743df6b + chrontel.png + image/png + 1476 + png + + 2023-03-22 23:17:31 + + + 64 + 55 + iclogo + 567dc4b4-c8ff-11ed-ac12-2ff653599de6 + cirrus.png + image/png + 3218 + png + + 2023-03-22 23:17:31 + + + 65 + 56 + iclogo + 567de854-c8ff-11ed-97a9-60bd3a522823 + comcore.png + image/png + 1709 + png + + 2023-03-22 23:17:31 + + + 66 + 57 + iclogo + 567e0bc2-c8ff-11ed-9c75-c05721ac16a9 + conexant.png + image/png + 2051 + png + + 2023-03-22 23:17:31 + + + 67 + 58 + iclogo + 567e3214-c8ff-11ed-aaef-57a02609532f + cosmo.png + image/png + 1709 + png + + 2023-03-22 23:17:31 + + + 68 + 59 + iclogo + 567e5618-c8ff-11ed-8a30-b4f5ca6ac96c + crystal.png + image/png + 3605 + png + + 2023-03-22 23:17:31 + + + 69 + 60 + iclogo + 567e7c2e-c8ff-11ed-b3d3-315f48719dc9 + cygnal.png + image/png + 2135 + png + + 2023-03-22 23:17:31 + + + 70 + 61 + iclogo + 567ee5ce-c8ff-11ed-87bd-3a00ca7c85ff + cypres1.png + image/png + 2504 + png + + 2023-03-22 23:17:31 + + + 71 + 61 + iclogo + 567ef9e2-c8ff-11ed-8ffd-6085c95108d8 + cypress.png + image/png + 4275 + png + + 2023-03-22 23:17:31 + + + 72 + 62 + iclogo + 567f29da-c8ff-11ed-91ba-029df33a5d24 + cyrix.png + image/png + 2204 + png + + 2023-03-22 23:17:31 + + + 73 + 63 + iclogo + 567f4f46-c8ff-11ed-9f77-98250e474335 + daewoo.png + image/png + 1907 + png + + 2023-03-22 23:17:31 + + + 74 + 64 + iclogo + 567f73d6-c8ff-11ed-9c34-bc9f54818bc6 + dallas1.png + image/png + 1469 + png + + 2023-03-22 23:17:31 + + + 75 + 64 + iclogo + 567f801a-c8ff-11ed-8430-b8998c93e36f + dallas2.png + image/png + 1309 + png + + 2023-03-22 23:17:31 + + + 76 + 64 + iclogo + 567f8a92-c8ff-11ed-a8fb-004a0856ee44 + dallas3.png + image/png + 1869 + png + + 2023-03-22 23:17:31 + + + 77 + 65 + iclogo + 567faef0-c8ff-11ed-b5bd-538ac90d1fd5 + davicom.png + image/png + 4589 + png + + 2023-03-22 23:17:31 + + + 78 + 66 + iclogo + 567fd3da-c8ff-11ed-b336-02ccc0a4e663 + ddd.png + image/png + 3235 + png + + 2023-03-22 23:17:31 + + + 79 + 67 + iclogo + 567ffa5e-c8ff-11ed-8db8-ac892f4a9795 + diamond.png + image/png + 2504 + png + + 2023-03-22 23:17:31 + + + 80 + 68 + iclogo + 568021f0-c8ff-11ed-a5b8-b53005039505 + diotec.png + image/png + 1454 + png + + 2023-03-22 23:17:31 + + + 81 + 69 + iclogo + 5680464e-c8ff-11ed-a2c3-a6b9353d79d0 + dtc1.png + image/png + 2513 + png + + 2023-03-22 23:17:31 + + + 82 + 69 + iclogo + 568050f8-c8ff-11ed-b7ad-db2eaa536a4e + dtc2.png + image/png + 1670 + png + + 2023-03-22 23:17:31 + + + 83 + 70 + iclogo + 5680bf70-c8ff-11ed-a042-1831bb8be007 + dvdo.png + image/png + 2357 + png + + 2023-03-22 23:17:31 + + + 84 + 71 + iclogo + 5680e66c-c8ff-11ed-af0e-c9899039e293 + egg.png + image/png + 1628 + png + + 2023-03-22 23:17:31 + + + 85 + 72 + iclogo + 56810cfa-c8ff-11ed-8fd5-bd7b625183a9 + elan.png + image/png + 13826 + png + + 2023-03-22 23:17:31 + + + 86 + 73 + iclogo + 5681384c-c8ff-11ed-ac8f-13c6d94c8c74 + elantec1.png + image/png + 1400 + png + + 2023-03-22 23:17:31 + + + 87 + 73 + iclogo + 56814350-c8ff-11ed-a12e-7b278c9ddb6e + elantec.png + image/png + 3274 + png + + 2023-03-22 23:17:31 + + + 88 + 74 + iclogo + 568165ce-c8ff-11ed-8c79-efdb39322df7 + elec_arrays.png + image/png + 5602 + png + + 2023-03-22 23:17:31 + + + 89 + 75 + iclogo + 56818b26-c8ff-11ed-9ee1-7ccf365558d1 + elite[1].png + image/png + 8285 + png + + 2023-03-22 23:17:31 + + + 90 + 76 + iclogo + 5681af16-c8ff-11ed-b0c2-eae2efd1511a + emmicro.png + image/png + 3599 + png + + 2023-03-22 23:17:31 + + + 91 + 77 + iclogo + 5681d2fc-c8ff-11ed-9255-dbd0817dbade + enhmemsy.png + image/png + 1403 + png + + 2023-03-22 23:17:31 + + + 92 + 78 + iclogo + 5681f570-c8ff-11ed-a813-f668082c0eab + ensoniq.png + image/png + 3557 + png + + 2023-03-22 23:17:31 + + + 93 + 79 + iclogo + 56821992-c8ff-11ed-91e9-cb9f497faf3e + eon.png + image/png + 5393 + png + + 2023-03-22 23:17:31 + + + 94 + 80 + iclogo + 56823eea-c8ff-11ed-8258-778fb9c27989 + epson1.png + image/png + 2349 + png + + 2023-03-22 23:17:31 + + + 95 + 80 + iclogo + 56824aa2-c8ff-11ed-a738-8dd2b09d2cb0 + epson2.png + image/png + 2405 + png + + 2023-03-22 23:17:31 + + + 96 + 81 + iclogo + 568279aa-c8ff-11ed-b1e9-b3441cd92dcb + ericsson.png + image/png + 4184 + png + + 2023-03-22 23:17:31 + + + 97 + 82 + iclogo + 5682a984-c8ff-11ed-b137-4ec668ffac51 + ess.png + image/png + 3030 + png + + 2023-03-22 23:17:31 + + + 98 + 83 + iclogo + 5682d7ce-c8ff-11ed-9bdf-fba8f1d0ec89 + etc.png + image/png + 2189 + png + + 2023-03-22 23:17:31 + + + 99 + 84 + iclogo + 5683046a-c8ff-11ed-8190-aebb6953623c + exar.png + image/png + 2771 + png + + 2023-03-22 23:17:31 + + + 100 + 85 + iclogo + 56833232-c8ff-11ed-b3a7-2060823541e9 + excelsemi1.png + image/png + 7632 + png + + 2023-03-22 23:17:31 + + + 101 + 85 + iclogo + 56833da4-c8ff-11ed-9e27-7f7898b11cf8 + excelsemi2.png + image/png + 2339 + png + + 2023-03-22 23:17:31 + + + 102 + 85 + iclogo + 5683488a-c8ff-11ed-99a6-dbbb65360908 + exel.png + image/png + 2771 + png + + 2023-03-22 23:17:31 + + + 103 + 86 + iclogo + 56836aea-c8ff-11ed-bd24-4cbbf7fc3d24 + fairchil.png + image/png + 1552 + png + + 2023-03-22 23:17:31 + + + 104 + 87 + iclogo + 56838f0c-c8ff-11ed-ad0a-62c536c263e8 + freescale.png + image/png + 3840 + png + + 2023-03-22 23:17:31 + + + 105 + 88 + iclogo + 5683b108-c8ff-11ed-a0fe-21e98a327412 + fujielec.png + image/png + 5048 + png + + 2023-03-22 23:17:31 + + + 106 + 88 + iclogo + 5683bbe4-c8ff-11ed-a164-bb108f090d77 + fujitsu2.png + image/png + 1860 + png + + 2023-03-22 23:17:31 + + + 107 + 89 + iclogo + 5683e060-c8ff-11ed-9d97-e097c8a106fd + galileo.png + image/png + 3779 + png + + 2023-03-22 23:17:31 + + + 108 + 90 + iclogo + 568402d4-c8ff-11ed-b834-4e88d7741e15 + galvant.png + image/png + 2669 + png + + 2023-03-22 23:17:31 + + + 109 + 91 + iclogo + 56842638-c8ff-11ed-95ac-73b2d4057ca6 + gecples.png + image/png + 2312 + png + + 2023-03-22 23:17:31 + + + 110 + 92 + iclogo + 56848f92-c8ff-11ed-999c-6f752c409f65 + gennum.png + image/png + 2614 + png + + 2023-03-22 23:17:31 + + + 111 + 93 + iclogo + 5684b9d6-c8ff-11ed-8e78-5217bce7d59a + ge.png + image/png + 2321 + png + + 2023-03-22 23:17:31 + + + 112 + 94 + iclogo + 5684e1ea-c8ff-11ed-80bc-a1b40bb97c23 + gi1.png + image/png + 1385 + png + + 2023-03-22 23:17:31 + + + 113 + 94 + iclogo + 5684eca8-c8ff-11ed-a7fe-b2538daeb6f7 + gi.png + image/png + 1691 + png + + 2023-03-22 23:17:31 + + + 114 + 95 + iclogo + 568511ba-c8ff-11ed-9986-8acdf1e13ae5 + glink.png + image/png + 1706 + png + + 2023-03-22 23:17:31 + + + 115 + 96 + iclogo + 56853abe-c8ff-11ed-b19f-b21d77131e35 + goal1.png + image/png + 9092 + png + + 2023-03-22 23:17:31 + + + 116 + 96 + iclogo + 56854766-c8ff-11ed-a12b-77f2a2c8cc6b + goal2.png + image/png + 9649 + png + + 2023-03-22 23:17:31 + + + 117 + 97 + iclogo + 56856c5a-c8ff-11ed-974f-953d09d40956 + goldstar1.png + image/png + 2923 + png + + 2023-03-22 23:17:31 + + + 118 + 97 + iclogo + 568577fe-c8ff-11ed-9958-083abcd834b1 + goldstar2.png + image/png + 11387 + png + + 2023-03-22 23:17:31 + + + 119 + 98 + iclogo + 56859d74-c8ff-11ed-a155-dd29dab1a09c + gould.png + image/png + 1549 + png + + 2023-03-22 23:17:31 + + + 120 + 99 + iclogo + 5685c164-c8ff-11ed-8dba-fa4e2521e98c + greenwich.png + image/png + 9761 + png + + 2023-03-22 23:17:31 + + + 121 + 100 + iclogo + 5685e5ae-c8ff-11ed-ab49-4ae5feb9550d + gsemi.png + image/png + 1704 + png + + 2023-03-22 23:17:31 + + + 122 + 101 + iclogo + 56860a2a-c8ff-11ed-a645-a6c651179b34 + harris1.png + image/png + 1549 + png + + 2023-03-22 23:17:31 + + + 123 + 101 + iclogo + 568615e2-c8ff-11ed-9f83-08d670bcbb46 + harris2.png + image/png + 1874 + png + + 2023-03-22 23:17:31 + + + 124 + 102 + iclogo + 56863856-c8ff-11ed-9510-9409bd91334e + hfo.png + image/png + 1958 + png + + 2023-03-22 23:17:31 + + + 125 + 103 + iclogo + 56866f4c-c8ff-11ed-8460-cbfd6ff948f0 + hitachi.png + image/png + 2611 + png + + 2023-03-22 23:17:31 + + + 126 + 104 + iclogo + 56869418-c8ff-11ed-b2ca-b0fc0d7dec8c + holtek.png + image/png + 2160 + png + + 2023-03-22 23:17:31 + + + 127 + 105 + iclogo + 5686baa6-c8ff-11ed-8413-6b45ea7860c4 + hp.png + image/png + 2464 + png + + 2023-03-22 23:17:31 + + + 128 + 106 + iclogo + 5686e044-c8ff-11ed-8e4f-18fe52f91fd4 + hualon.png + image/png + 2864 + png + + 2023-03-22 23:17:31 + + + 129 + 107 + iclogo + 568702cc-c8ff-11ed-8eb3-2427c2a58056 + hynix.png + image/png + 8444 + png + + 2023-03-22 23:17:31 + + + 130 + 108 + iclogo + 56872ee6-c8ff-11ed-8c68-27075e0b74fe + hyundai2.png + image/png + 2269 + png + + 2023-03-22 23:17:31 + + + 131 + 109 + iclogo + 56875380-c8ff-11ed-997e-cb42d307c52b + icdesign.png + image/png + 3014 + png + + 2023-03-22 23:17:31 + + + 132 + 110 + iclogo + 56877860-c8ff-11ed-b541-88b27e33d0e8 + icd.png + image/png + 1641 + png + + 2023-03-22 23:17:31 + + + 133 + 110 + iclogo + 568782ec-c8ff-11ed-8bcb-1a6fbffe8d9a + ics.png + image/png + 2042 + png + + 2023-03-22 23:17:31 + + + 134 + 111 + iclogo + 5687aa42-c8ff-11ed-a4cf-cf5827b5d295 + ichaus1.png + image/png + 3370 + png + + 2023-03-22 23:17:31 + + + 135 + 111 + iclogo + 5687b51e-c8ff-11ed-ae42-654971fe0b86 + ichaus.png + image/png + 1552 + png + + 2023-03-22 23:17:31 + + + 136 + 112 + iclogo + 5687d7ec-c8ff-11ed-b867-bf966a38be76 + icsi.png + image/png + 4049 + png + + 2023-03-22 23:17:31 + + + 137 + 113 + iclogo + 5687ff10-c8ff-11ed-806e-0ac441837823 + icube.png + image/png + 1629 + png + + 2023-03-22 23:17:31 + + + 138 + 114 + iclogo + 56882832-c8ff-11ed-85ef-218e037c424b + icworks.png + image/png + 1874 + png + + 2023-03-22 23:17:31 + + + 139 + 115 + iclogo + 56884e20-c8ff-11ed-8f03-8bc418e54276 + idt1.png + image/png + 3995 + png + + 2023-03-22 23:17:31 + + + 140 + 115 + iclogo + 5688587a-c8ff-11ed-896a-6eb01b1e8b6a + idt.png + image/png + 1553 + png + + 2023-03-22 23:17:31 + + + 141 + 116 + iclogo + 56887c88-c8ff-11ed-b382-388c27f86648 + igstech.png + image/png + 3832 + png + + 2023-03-22 23:17:31 + + + 142 + 117 + iclogo + 5688a3b6-c8ff-11ed-958a-57bcf30f0e8e + impala.png + image/png + 1628 + png + + 2023-03-22 23:17:31 + + + 143 + 118 + iclogo + 5688c95e-c8ff-11ed-974c-2becd8d95cfe + imp.png + image/png + 2175 + png + + 2023-03-22 23:17:31 + + + 144 + 119 + iclogo + 5688efce-c8ff-11ed-bd6a-3a3f23feed22 + infineon.png + image/png + 4511 + png + + 2023-03-22 23:17:31 + + + 145 + 120 + iclogo + 568915c6-c8ff-11ed-9828-99f8f9466667 + inmos.png + image/png + 3365 + png + + 2023-03-22 23:17:31 + + + 146 + 121 + iclogo + 56893c18-c8ff-11ed-addc-4d17b44d41a9 + intel2.png + image/png + 2010 + png + + 2023-03-22 23:17:31 + + + 147 + 122 + iclogo + 56896026-c8ff-11ed-8bb4-690773d704b3 + intresil4.png + image/png + 2614 + png + + 2023-03-22 23:17:31 + + + 148 + 122 + iclogo + 56896b20-c8ff-11ed-a162-fc27a1f080ee + intrsil1.png + image/png + 1874 + png + + 2023-03-22 23:17:31 + + + 149 + 122 + iclogo + 5689757a-c8ff-11ed-a642-c1ff2b88b16d + intrsil2.png + image/png + 2520 + png + + 2023-03-22 23:17:31 + + + 150 + 122 + iclogo + 56897fd4-c8ff-11ed-8b61-ecdd2f4375cf + intrsil3.png + image/png + 3295 + png + + 2023-03-22 23:17:31 + + + 151 + 123 + iclogo + 5689a914-c8ff-11ed-8471-671c4d3526a2 + ir.png + image/png + 2729 + png + + 2023-03-22 23:17:31 + + + 152 + 124 + iclogo + 5689cdae-c8ff-11ed-8640-2c6e9bf8b270 + isd.png + image/png + 2554 + png + + 2023-03-22 23:17:31 + + + 153 + 125 + iclogo + 5689f3ec-c8ff-11ed-962a-2fc58c1f358f + issi.png + image/png + 3030 + png + + 2023-03-22 23:17:31 + + + 154 + 126 + iclogo + 568a1912-c8ff-11ed-bd7e-d179c93e6b00 + ite.png + image/png + 3302 + png + + 2023-03-22 23:17:31 + + + 155 + 127 + iclogo + 568a3dc0-c8ff-11ed-8f3d-6c1dd312337c + itt.png + image/png + 2483 + png + + 2023-03-22 23:17:31 + + + 156 + 128 + iclogo + 568a6228-c8ff-11ed-b9c8-00902f96aeb6 + ixys.png + image/png + 3575 + png + + 2023-03-22 23:17:31 + + + 157 + 129 + iclogo + 568a85d2-c8ff-11ed-b548-8f4b43deb58f + kec.png + image/png + 2567 + png + + 2023-03-22 23:17:31 + + + 158 + 130 + iclogo + 568aa7e2-c8ff-11ed-ab2f-b53ec050a242 + kota.png + image/png + 1552 + png + + 2023-03-22 23:17:31 + + + 159 + 131 + iclogo + 568acb82-c8ff-11ed-aebb-42187109e347 + lattice1.png + image/png + 1768 + png + + 2023-03-22 23:17:31 + + + 160 + 131 + iclogo + 568ad58c-c8ff-11ed-aa8d-592ab1c33b53 + lattice2.png + image/png + 1519 + png + + 2023-03-22 23:17:31 + + + 161 + 131 + iclogo + 568adea6-c8ff-11ed-8ff6-d407a412924f + lattice3.png + image/png + 1216 + png + + 2023-03-22 23:17:31 + + + 162 + 132 + iclogo + 568b025a-c8ff-11ed-8b03-92c6d544d1f4 + lds1.png + image/png + 2136 + png + + 2023-03-22 23:17:31 + + + 163 + 132 + iclogo + 568b0c82-c8ff-11ed-91b1-cf8307f47c99 + lds.png + image/png + 1959 + png + + 2023-03-22 23:17:31 + + + 164 + 133 + iclogo + 568b2e42-c8ff-11ed-98f1-c29c79404afe + levone.png + image/png + 4189 + png + + 2023-03-22 23:17:31 + + + 165 + 134 + iclogo + 568b544e-c8ff-11ed-8b7a-50043605a2b6 + lgs1.png + image/png + 2417 + png + + 2023-03-22 23:17:31 + + + 166 + 134 + iclogo + 568b5f0c-c8ff-11ed-a373-1898c7e0a30c + lgs.png + image/png + 737 + png + + 2023-03-22 23:17:31 + + + 167 + 135 + iclogo + 568b81ee-c8ff-11ed-a86f-8114cf642140 + linear.png + image/png + 2486 + png + + 2023-03-22 23:17:31 + + + 168 + 136 + iclogo + 568ba5ac-c8ff-11ed-b29d-165934da4046 + linfin.png + image/png + 4844 + png + + 2023-03-22 23:17:31 + + + 169 + 137 + iclogo + 568bc9e2-c8ff-11ed-a3f0-c10bfee4f5a5 + liteon.png + image/png + 2388 + png + + 2023-03-22 23:17:31 + + + 170 + 138 + iclogo + 568beeea-c8ff-11ed-a2f1-7df5051d824f + lucent.png + image/png + 1709 + png + + 2023-03-22 23:17:31 + + + 171 + 139 + iclogo + 568c112c-c8ff-11ed-9884-378bfa8cd82f + macronix.png + image/png + 2324 + png + + 2023-03-22 23:17:31 + + + 172 + 140 + iclogo + 568c354e-c8ff-11ed-9ded-8e10fddeba15 + marvell.png + image/png + 3131 + png + + 2023-03-22 23:17:31 + + + 173 + 141 + iclogo + 568c592a-c8ff-11ed-b6a8-887699641615 + matsush1.png + image/png + 1709 + png + + 2023-03-22 23:17:31 + + + 174 + 141 + iclogo + 568c644c-c8ff-11ed-bdf5-a1555770b909 + matsushi.png + image/png + 2029 + png + + 2023-03-22 23:17:31 + + + 175 + 142 + iclogo + 568c881e-c8ff-11ed-aa7c-fa4001861926 + maxim.png + image/png + 2690 + png + + 2023-03-22 23:17:31 + + + 176 + 143 + iclogo + 568cac5e-c8ff-11ed-8b66-7e90a640da46 + mediavi1.png + image/png + 2189 + png + + 2023-03-22 23:17:31 + + + 177 + 143 + iclogo + 568cb604-c8ff-11ed-8003-6edfe1893ad6 + mediavi2.png + image/png + 2487 + png + + 2023-03-22 23:17:31 + + + 178 + 144 + iclogo + 568cd8b4-c8ff-11ed-8b6a-9380b4a1402f + me.png + image/png + 2411 + png + + 2023-03-22 23:17:31 + + + 179 + 144 + iclogo + 568ce3c2-c8ff-11ed-8aee-b8f55cd58993 + microchp.png + image/png + 2814 + png + + 2023-03-22 23:17:31 + + + 180 + 145 + iclogo + 568d0758-c8ff-11ed-a08d-98ede1f64041 + mhs2.png + image/png + 2036 + png + + 2023-03-22 23:17:31 + + + 181 + 145 + iclogo + 568d12ac-c8ff-11ed-92de-bed578696e3c + mhs.png + image/png + 1870 + png + + 2023-03-22 23:17:31 + + + 182 + 146 + iclogo + 568d3638-c8ff-11ed-9213-ac7f3ed2ffda + micrel1.png + image/png + 9695 + png + + 2023-03-22 23:17:31 + + + 183 + 146 + iclogo + 568d4268-c8ff-11ed-8860-a64981c73948 + micrel2.png + image/png + 9695 + png + + 2023-03-22 23:17:31 + + + 184 + 147 + iclogo + 568d67ac-c8ff-11ed-93b0-302d67844227 + micronas.png + image/png + 1871 + png + + 2023-03-22 23:17:31 + + + 185 + 148 + iclogo + 568d8f34-c8ff-11ed-8c81-056ec8ba5c17 + micronix.png + image/png + 1856 + png + + 2023-03-22 23:17:31 + + + 186 + 149 + iclogo + 568db1b2-c8ff-11ed-8e67-e47603874a4b + micron.png + image/png + 1763 + png + + 2023-03-22 23:17:31 + + + 187 + 150 + iclogo + 568dd5ca-c8ff-11ed-9106-d633745d51c5 + microsemi1.png + image/png + 3714 + png + + 2023-03-22 23:17:31 + + + 188 + 150 + iclogo + 568de0ce-c8ff-11ed-859a-9f73181a3e5c + microsemi2.png + image/png + 11992 + png + + 2023-03-22 23:17:31 + + + 189 + 151 + iclogo + 568e0568-c8ff-11ed-8bd2-bd4a8fea36d6 + minicirc.png + image/png + 1391 + png + + 2023-03-22 23:17:31 + + + 190 + 152 + iclogo + 568e2a0c-c8ff-11ed-9b90-ff9eb99be150 + mitel.png + image/png + 2819 + png + + 2023-03-22 23:17:31 + + + 191 + 153 + iclogo + 568e4d5c-c8ff-11ed-8622-0e50f183e8fa + mitsubis.png + image/png + 2311 + png + + 2023-03-22 23:17:31 + + + 192 + 154 + iclogo + 568e72f0-c8ff-11ed-8568-09f01a3a2670 + mlinear.png + image/png + 3377 + png + + 2023-03-22 23:17:31 + + + 193 + 155 + iclogo + 568e96f4-c8ff-11ed-86ea-8d3a071d766d + mmi.png + image/png + 2692 + png + + 2023-03-22 23:17:31 + + + 194 + 156 + iclogo + 568ebbac-c8ff-11ed-a4d7-8eca7f888ba4 + mosaic.png + image/png + 2959 + png + + 2023-03-22 23:17:31 + + + 195 + 157 + iclogo + 568ee064-c8ff-11ed-90d5-084340453793 + moselvit.png + image/png + 2504 + png + + 2023-03-22 23:17:31 + + + 196 + 158 + iclogo + 568f0594-c8ff-11ed-a5e8-df73bc079b7c + mos.png + image/png + 2857 + png + + 2023-03-22 23:17:31 + + + 197 + 159 + iclogo + 568f37d0-c8ff-11ed-80d1-a7dd8dce594b + mostek1.png + image/png + 7502 + png + + 2023-03-22 23:17:31 + + + 198 + 159 + iclogo + 568f43ba-c8ff-11ed-8a49-a8e9c5b40903 + mostek2.png + image/png + 7502 + png + + 2023-03-22 23:17:31 + + + 199 + 159 + iclogo + 568f4ec8-c8ff-11ed-bc4c-848264d621d5 + mostek3.png + image/png + 2514 + png + + 2023-03-22 23:17:31 + + + 200 + 160 + iclogo + 568f7718-c8ff-11ed-b5cb-fec4c4f0de0e + mosys.png + image/png + 2321 + png + + 2023-03-22 23:17:31 + + + 201 + 161 + iclogo + 568f9b3a-c8ff-11ed-a51e-a346b463cb1f + motorol1.png + image/png + 999 + png + + 2023-03-22 23:17:31 + + + 202 + 161 + iclogo + 568fa616-c8ff-11ed-aa80-42f6c9e69760 + motorol2.png + image/png + 2417 + png + + 2023-03-22 23:17:31 + + + 203 + 162 + iclogo + 568fc970-c8ff-11ed-a093-161f51674dcb + mpd.png + image/png + 2663 + png + + 2023-03-22 23:17:31 + + + 204 + 163 + iclogo + 568fefae-c8ff-11ed-9d67-64959a0c66d9 + msystem.png + image/png + 1670 + png + + 2023-03-22 23:17:31 + + + 205 + 164 + iclogo + 569012a4-c8ff-11ed-8fe4-04a628989fd4 + murata1.png + image/png + 4874 + png + + 2023-03-22 23:17:31 + + + 206 + 164 + iclogo + 56901e48-c8ff-11ed-a434-cfdc4d826d34 + murata.png + image/png + 4777 + png + + 2023-03-22 23:17:31 + + + 207 + 165 + iclogo + 56904422-c8ff-11ed-abc1-98d52541c298 + mwave.png + image/png + 3370 + png + + 2023-03-22 23:17:31 + + + 208 + 166 + iclogo + 56906b50-c8ff-11ed-a39a-db0fbab6e9d0 + myson.png + image/png + 1932 + png + + 2023-03-22 23:17:31 + + + 209 + 167 + iclogo + 5690901c-c8ff-11ed-b105-9611ce907bee + nec1.png + image/png + 3166 + png + + 2023-03-22 23:17:31 + + + 210 + 167 + iclogo + 56909ad0-c8ff-11ed-9ef5-3fd91a35cadc + nec2.png + image/png + 3071 + png + + 2023-03-22 23:17:31 + + + 211 + 168 + iclogo + 5690bc5e-c8ff-11ed-bc71-4eda4a9c6182 + nexflash.png + image/png + 7789 + png + + 2023-03-22 23:17:31 + + + 212 + 169 + iclogo + 5690e1a2-c8ff-11ed-b344-ae7ef2663dd1 + njr.png + image/png + 3419 + png + + 2023-03-22 23:17:31 + + + 213 + 170 + iclogo + 569105f6-c8ff-11ed-9ace-8448b0d43dc7 + ns1.png + image/png + 1959 + png + + 2023-03-22 23:17:31 + + + 214 + 170 + iclogo + 5691103c-c8ff-11ed-88d0-4889d374d080 + ns2.png + image/png + 1952 + png + + 2023-03-22 23:17:31 + + + 215 + 171 + iclogo + 569134b8-c8ff-11ed-9371-8171479e9467 + nvidia.png + image/png + 1874 + png + + 2023-03-22 23:17:31 + + + 216 + 172 + iclogo + 56915812-c8ff-11ed-a88a-1ee3e984ee1e + oak.png + image/png + 2614 + png + + 2023-03-22 23:17:31 + + + 217 + 173 + iclogo + 56917be4-c8ff-11ed-945d-d0d25bc0c57a + oki1.png + image/png + 2267 + png + + 2023-03-22 23:17:31 + + + 218 + 173 + iclogo + 5691862a-c8ff-11ed-8300-2b2dbc7b9070 + oki.png + image/png + 2546 + png + + 2023-03-22 23:17:31 + + + 219 + 174 + iclogo + 5691a98e-c8ff-11ed-ab20-b034f44ad412 + opti.png + image/png + 1684 + png + + 2023-03-22 23:17:31 + + + 220 + 175 + iclogo + 5691cd24-c8ff-11ed-a148-64e44275e7c9 + orbit.png + image/png + 3347 + png + + 2023-03-22 23:17:31 + + + 221 + 176 + iclogo + 5691f10a-c8ff-11ed-a8aa-c6598f17b11f + oren.png + image/png + 3497 + png + + 2023-03-22 23:17:31 + + + 222 + 177 + iclogo + 5692134c-c8ff-11ed-9646-44382c2039c7 + perform.png + image/png + 3284 + png + + 2023-03-22 23:17:31 + + + 223 + 178 + iclogo + 56923796-c8ff-11ed-88e2-75a8035a68cd + pericom.png + image/png + 2311 + png + + 2023-03-22 23:17:31 + + + 224 + 179 + iclogo + 56925bf4-c8ff-11ed-b290-e6e40eef9273 + phaslink.png + image/png + 2669 + png + + 2023-03-22 23:17:31 + + + 225 + 180 + iclogo + 569280ca-c8ff-11ed-9f14-9152a84119d5 + philips.png + image/png + 8690 + png + + 2023-03-22 23:17:31 + + + 226 + 181 + iclogo + 5692ae92-c8ff-11ed-ade5-83e922a02acc + plx.png + image/png + 4749 + png + + 2023-03-22 23:17:31 + + + 227 + 182 + iclogo + 5692d1c4-c8ff-11ed-bf7d-6fbac519872c + pmc.png + image/png + 3497 + png + + 2023-03-22 23:17:31 + + + 228 + 183 + iclogo + 5692f686-c8ff-11ed-9d7b-6564d4e66103 + pmi.png + image/png + 3807 + png + + 2023-03-22 23:17:31 + + + 229 + 184 + iclogo + 56931dc8-c8ff-11ed-95c1-4c6f225a64e4 + ptc.png + image/png + 2669 + png + + 2023-03-22 23:17:31 + + + 230 + 185 + iclogo + 569342b2-c8ff-11ed-a1ce-a1013da9bf84 + pwrsmart.png + image/png + 1389 + png + + 2023-03-22 23:17:31 + + + 231 + 186 + iclogo + 5693679c-c8ff-11ed-9863-94fd3160386d + qlogic.png + image/png + 1709 + png + + 2023-03-22 23:17:31 + + + 232 + 187 + iclogo + 56938b96-c8ff-11ed-92a8-2ba0a126c643 + qualcomm.png + image/png + 3326 + png + + 2023-03-22 23:17:31 + + + 233 + 188 + iclogo + 5693b076-c8ff-11ed-8e1a-21c0bab343d6 + quality.png + image/png + 1309 + png + + 2023-03-22 23:17:31 + + + 234 + 189 + iclogo + 5693d600-c8ff-11ed-bcce-4a2083ba53fe + rabbit.png + image/png + 2857 + png + + 2023-03-22 23:17:31 + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 3 + Max Current + > + + + + numeric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 7 + 3 + 100k Ohm + Resistor 100 + Test + 0 + 10 + 0.0000 + Active + 1 + Test + Hallo + 2023-03-22 23:38:06 + IPN1 + 0 + 1 + 0 + 1 + 1 + + + 2 + 5 + + Test + + + 0 + 0 + 0.0000 + + 0 + + + 2023-03-22 23:40:07 + + 0 + 0 + 1 + 1 + + + + 3 + 6 + + Test + + + 50 + 0 + 0.0000 + + 0 + + + 2023-03-22 23:40:40 + + 0 + 0 + 0 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + 1 + 1 + PartAttachment + 75d71f60-c902-11ed-8740-1bd85445aac2 + Lückenauskunft.pdf + application/pdf + 7763 + + + 2023-03-22 23:39:52 + + + + + + + + + + + + + + + + + + + + + + 1 + + 1 + 14 + 0 + 1 + Root Category + + Root Category + + + 2 + 1 + 2 + 9 + 1 + 1 + Active Parts + + Root Category ➤ Active Parts + + + 3 + 1 + 10 + 13 + 1 + 1 + Passive Parts + + Root Category ➤ Passive Parts + + + 4 + 2 + 3 + 8 + 2 + 1 + Transistors + + Root Category ➤ Active Parts ➤ Transistors + + + 5 + 4 + 4 + 5 + 3 + 1 + NPN + + Root Category ➤ Active Parts ➤ Transistors ➤ NPN + + + 6 + 4 + 6 + 7 + 3 + 1 + PNP + + Root Category ➤ Active Parts ➤ Transistors ➤ PNP + + + 7 + 3 + 11 + 12 + 2 + 1 + Resistors + + Root Category ➤ Passive Parts ➤ Resistors + + + + + + + + + + + + + + + + + + + + 1 + 1 + 1 + BC547 + 2 + 2.4000 + + + + + + 2 + 1 + 1 + BC547 + 1 + 1.0000 + + Test + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + partkeepr + + + 0 + 0 + + 1 + 0 + + + 2 + 1 + partkeepr2 + + + 0 + 0 + + 1 + 0 + + + + + + + + + + + + + + + 1 + 1 + 5 + SDFKds + + + 2 + 1 + 5 + Marcoos + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + 3 + Max Current + Test + 1 + 1 + 23 + 23 + 10 + 10 + + numeric + + + + + + + + + + + + + + + + 1 + Pieces + pcs + 1 + + + 2 + Meter + m + 0 + + + + + + + + + + + + + + 1 + + Test Project + Test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + 1 + 1 + Test + absolute + 0 + Test234 + + + 2 + + 1 + 10 + PCB + absolute + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20150608120000 + + + 20150708120022 + + + 20150724174030 + + + 20151001180120 + + + 20151002183125 + + + 20151031163951 + + + 20151208162723 + + + 20160103145302 + + + 20170108122512 + + + 20170108143802 + + + 20170113203042 + + + 20170601175559 + + + + + + + + + + + + + + 1 + yotta + Y + 24 + 10 + + + 2 + zetta + Z + 21 + 10 + + + 3 + exa + E + 18 + 10 + + + 4 + peta + P + 15 + 10 + + + 5 + tera + T + 12 + 10 + + + 6 + giga + G + 9 + 10 + + + 7 + mega + M + 6 + 10 + + + 8 + kilo + k + 3 + 10 + + + 9 + hecto + h + 2 + 10 + + + 10 + deca + da + 1 + 10 + + + 11 + - + + 0 + 10 + + + 12 + deci + d + -1 + 10 + + + 13 + centi + c + -2 + 10 + + + 14 + milli + m + -3 + 10 + + + 15 + micro + μ + -6 + 10 + + + 16 + nano + n + -9 + 10 + + + 17 + pico + p + -12 + 10 + + + 18 + femto + f + -15 + 10 + + + 19 + atto + a + -18 + 10 + + + 20 + zepto + z + -21 + 10 + + + 21 + yocto + y + -24 + 10 + + + 22 + kibi + Ki + 1 + 1024 + + + 23 + mebi + Mi + 2 + 1024 + + + 24 + gibi + Gi + 3 + 1024 + + + 25 + tebi + Ti + 4 + 1024 + + + 26 + pebi + Pi + 5 + 1024 + + + 27 + exbi + Ei + 6 + 1024 + + + 28 + zebi + Zi + 7 + 1024 + + + 29 + yobi + Yi + 8 + 1024 + + + + + + + + + + + + + 1 + 2023-03-22 23:27:34 + 0 + 1 + + + 2 + 2023-03-22 23:28:48 + 0 + 1 + + + + + + + + + + + + + + + 1 + 0 + 1 + 1 + + + 2 + 0 + 2 + 1 + + + + + + + + + + + + + + + + + + + 1 + 3 + + 50 + 0.0000 + 2023-03-22 23:40:40 + 0 + + + + + + + + + + + + + + + 1 + 1 + Location 1 + + + 2 + 1 + Location 2 + + + 3 + 8 + Sublocation 1 + + + 4 + 8 + Sublocation 2 + + + 5 + 9 + Hallo 2 + + + + + + + + + + + + + + + + + + + + + 1 + + 1 + 6 + 0 + 1 + Root Category + + Root Category + + + 8 + 1 + 2 + 5 + 1 + 1 + Test + + Root Category ➤ Test + + + 9 + 8 + 3 + 4 + 2 + 1 + Level 2 + + Root Category ➤ Test ➤ Level 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + tempfile + 9cd27bec-c901-11ed-ba1f-d163ec6b8aa1 + export (1).csv + text/plain + 12 + + + 2023-03-22 23:33:48 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + Meter + m + + + 2 + Gram + g + + + 3 + Second + s + + + 4 + Kelvin + K + + + 5 + Mol + mol + + + 6 + Candela + cd + + + 7 + Ampere + A + + + 8 + Ohm + Ω + + + 9 + Volt + V + + + 10 + Hertz + Hz + + + 11 + Newton + N + + + 12 + Pascal + Pa + + + 13 + Joule + J + + + 14 + Watt + W + + + 15 + Coulomb + C + + + 16 + Farad + F + + + 17 + Siemens + S + + + 18 + Weber + Wb + + + 19 + Tesla + T + + + 20 + Henry + H + + + 21 + Celsius + °C + + + 22 + Lumen + lm + + + 23 + Lux + lx + + + 24 + Becquerel + Bq + + + 25 + Gray + Gy + + + 26 + Sievert + Sv + + + 27 + Katal + kat + + + 28 + Ampere Hour + Ah + + + + + + + + + + + + + + 1 + 8 + + + 1 + 11 + + + 1 + 12 + + + 1 + 13 + + + 1 + 14 + + + 1 + 15 + + + 1 + 16 + + + 2 + 8 + + + 2 + 11 + + + 2 + 14 + + + 3 + 11 + + + 3 + 14 + + + 4 + 11 + + + 5 + 11 + + + 6 + 14 + + + 7 + 8 + + + 7 + 11 + + + 7 + 14 + + + 7 + 15 + + + 7 + 16 + + + 7 + 17 + + + 8 + 5 + + + 8 + 6 + + + 8 + 8 + + + 8 + 11 + + + 8 + 14 + + + 8 + 15 + + + 9 + 8 + + + 9 + 11 + + + 9 + 14 + + + 10 + 5 + + + 10 + 6 + + + 10 + 8 + + + 10 + 11 + + + 10 + 14 + + + 11 + 8 + + + 11 + 11 + + + 12 + 8 + + + 12 + 11 + + + 12 + 14 + + + 13 + 8 + + + 13 + 11 + + + 13 + 14 + + + 13 + 15 + + + 14 + 6 + + + 14 + 7 + + + 14 + 8 + + + 14 + 11 + + + 14 + 14 + + + 14 + 15 + + + 15 + 8 + + + 15 + 11 + + + 16 + 11 + + + 16 + 14 + + + 16 + 15 + + + 16 + 16 + + + 16 + 17 + + + 17 + 11 + + + 17 + 14 + + + 18 + 11 + + + 19 + 11 + + + 20 + 11 + + + 20 + 14 + + + 20 + 15 + + + 21 + 11 + + + 22 + 11 + + + 23 + 11 + + + 24 + 11 + + + 25 + 11 + + + 26 + 11 + + + 26 + 14 + + + 26 + 15 + + + 27 + 11 + + + 28 + 8 + + + 28 + 11 + + + 28 + 14 + + + + + + + + + + + + + + 1 + partkeepr.tipoftheday.showtips + s:4:"true"; + + + + + + + + + + + + + 1 + Builtin + 1 + + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3181151d..ecec14bf 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,15 +1,14 @@ bootEnv(dirname(__DIR__).'/.env'); } - if ($_SERVER['APP_DEBUG']) { umask(0000); } diff --git a/tests/object-manager.php b/tests/object-manager.php new file mode 100644 index 00000000..1e335268 --- /dev/null +++ b/tests/object-manager.php @@ -0,0 +1,32 @@ +. + */ + +declare(strict_types=1); + +use App\Kernel; +use Symfony\Component\Dotenv\Dotenv; + +require __DIR__ . '/../vendor/autoload.php'; + +(new Dotenv())->bootEnv(__DIR__ . '/../.env'); + +$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); +$kernel->boot(); +return $kernel->getContainer()->get('doctrine')->getManager(); \ No newline at end of file diff --git a/tests/symfony-container.php b/tests/symfony-container.php new file mode 100644 index 00000000..be2ff108 --- /dev/null +++ b/tests/symfony-container.php @@ -0,0 +1,9 @@ +boot(); +return $appKernel->getContainer(); diff --git a/translations/messages.cs.xlf b/translations/messages.cs.xlf new file mode 100644 index 00000000..bde090df --- /dev/null +++ b/translations/messages.cs.xlf @@ -0,0 +1,12213 @@ + + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + + + attachment_type.caption + Typy souborů pro přílohy + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 + new + + + attachment_type.edit + Upravit typ souboru + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 + new + + + attachment_type.new + Nový typ souboru + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + templates\AdminPages\CategoryAdmin.html.twig:4 + templates\base.html.twig:163 + templates\base.html.twig:170 + templates\base.html.twig:197 + templates\base.html.twig:225 + + + category.labelp + Kategorie + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:11 + templates\AdminPages\CategoryAdmin.html.twig:8 + + + admin.options + Možnosti + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + templates\AdminPages\CategoryAdmin.html.twig:9 + + + admin.advanced + Pokročilé + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 + new + + + category.edit + Upravit kategorii + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 + new + + + category.new + Nová kategorie + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + + + currency.caption + Měna + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + + + currency.iso_code.caption + Kód ISO + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + + + currency.symbol.caption + Symbol měny + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 + new + + + currency.edit + Upravit měnu + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 + new + + + currency.new + Nová měna + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + templates\AdminPages\DeviceAdmin.html.twig:4 + + + project.caption + Projekt + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 + new + + + project.edit + Upravit projekt + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 + new + + + project.new + Nový projekt + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:67 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + templates\AdminPages\EntityAdminBase.html.twig:9 + templates\base.html.twig:80 + templates\base.html.twig:179 + templates\base.html.twig:206 + templates\base.html.twig:237 + + + search.placeholder + Hledat + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + templates\AdminPages\EntityAdminBase.html.twig:13 + templates\base.html.twig:166 + templates\base.html.twig:193 + templates\base.html.twig:221 + + + expandAll + Rozbalit vše + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + templates\AdminPages\EntityAdminBase.html.twig:17 + templates\base.html.twig:167 + templates\base.html.twig:194 + templates\base.html.twig:222 + + + reduceAll + Sbalit vše + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + + + part.info.timetravel_hint + Takto vypadal díl před %timestamp%. <i>Upozorňujeme, že tato funkce je experimentální, takže informace nemusí být správné.</i> + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + templates\AdminPages\EntityAdminBase.html.twig:42 + + + standard.label + Vlastnosti + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + templates\AdminPages\EntityAdminBase.html.twig:43 + + + infos.label + Informace + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + new + + + history.label + Historie + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + templates\AdminPages\EntityAdminBase.html.twig:45 + + + export.label + Export + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + templates\AdminPages\EntityAdminBase.html.twig:47 + + + import_export.label + Import / Export + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + + + mass_creation.label + Hromadné vytváření + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + templates\AdminPages\EntityAdminBase.html.twig:59 + + + admin.common + Obecné + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + + + admin.attachments + Přílohy + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 + + + admin.parameters + Parametr + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 + templates\AdminPages\EntityAdminBase.html.twig:142 + + + export_all.label + Exportovat všechny prvky + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 + + + mass_creation.help + Každý řádek bude interpretován jako název prvku, který bude vytvořen. Vnořené struktury můžete vytvářet pomocí odsazení. + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + templates\AdminPages\EntityAdminBase.html.twig:35 + + + edit.caption + Upravit prvek "%name" + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + templates\AdminPages\EntityAdminBase.html.twig:37 + + + new.caption + Nový prvek + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + templates\base.html.twig:172 + templates\base.html.twig:199 + templates\base.html.twig:227 + + + footprint.labelp + Otisky + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 + new + + + footprint.edit + Upravit otisk + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 + new + + + footprint.new + Nový otisk + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + + + group.edit.caption + Skupiny + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + + + user.edit.permissions + Oprávnění + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 + new + + + group.edit + Upravit skupinu + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 + new + + + group.new + Nová skupina + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 + + + label_profile.caption + Profily štítků + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 + + + label_profile.advanced + Pokročilé + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 + + + label_profile.comment + Poznámky + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 + new + + + label_profile.edit + Upravit profil štítku + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 + new + + + label_profile.new + Nový profil štítku + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + templates\AdminPages\ManufacturerAdmin.html.twig:4 + + + manufacturer.caption + Výrobci + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 + new + + + manufacturer.edit + Upravit výrobce + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 + new + + + manufacturer.new + Nový výrobce + + + + + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + + + measurement_unit.caption + Měrné jednotky + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 + Part-DB1\templates\_sidebar.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:8 + templates\base.html.twig:171 + templates\base.html.twig:198 + templates\base.html.twig:226 + + + storelocation.labelp + Umístění + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 + new + + + storelocation.edit + Upravit umístění + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 + new + + + storelocation.new + Nové místo skladování + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + templates\AdminPages\SupplierAdmin.html.twig:4 + + + supplier.caption + Dodavatelé + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 + new + + + supplier.edit + Upravit dodavatele + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 + new + + + supplier.new + Nový dodavatel + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + + + user.edit.caption + Uživatelé + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + + + user.edit.configuration + Konfigurace + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + + + user.edit.password + Heslo + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + + + user.edit.tfa.caption + Dvoufaktorové ověřování + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + + + user.edit.tfa.google_active + Aplikace Authenticator aktivní + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + + + tfa_backup.remaining_tokens + Počet zbývajících záložních kódů + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + + + tfa_backup.generation_date + Datum generování záložních kódů + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + + + user.edit.tfa.disabled + Metoda není povolena + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + + + user.edit.tfa.u2f_keys_count + Aktivní bezpečnostní klíče + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_title + Opravdu chcete pokračovat? + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_message + Tím deaktivujete <b>všechny aktivní dvoufaktorové metody ověřování uživatele</b> a odstraníte <b>záložní kódy</b>! +<br> +Uživatel bude muset znovu nastavit všechny metody dvoufaktorového ověřování a vytisknout nové záložní kódy! <br><br> +<b>Toto proveďte pouze v případě, že jste si naprosto jisti identitou uživatele (hledajícího pomoc), jinak by mohl být účet kompromitován útočníkem!</b> + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + + + user.edit.tfa.disable_tfa.btn + Zakázat všechny metody dvoufaktorového ověřování + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 + new + + + user.edit + Upravit uživatele + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 + new + + + user.new + Nový uživatel + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:63 + + + attachment.delete + Smazat + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:41 + Part-DB1\templates\Parts\edit\_attachments.html.twig:38 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:35 + Part-DB1\src\DataTables\AttachmentDataTable.php:159 + Part-DB1\templates\Parts\edit\_attachments.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:159 + + + attachment.external + Externí + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:49 + Part-DB1\templates\Parts\edit\_attachments.html.twig:47 + Part-DB1\templates\AdminPages\_attachments.html.twig:47 + Part-DB1\templates\Parts\edit\_attachments.html.twig:45 + + + attachment.preview.alt + Náhled přílohy + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:52 + Part-DB1\templates\Parts\edit\_attachments.html.twig:50 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + Part-DB1\templates\AdminPages\_attachments.html.twig:50 + Part-DB1\templates\Parts\edit\_attachments.html.twig:48 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:45 + + + attachment.view + Zobrazit + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:58 + Part-DB1\templates\Parts\edit\_attachments.html.twig:56 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:43 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + Part-DB1\templates\AdminPages\_attachments.html.twig:56 + Part-DB1\templates\Parts\edit\_attachments.html.twig:54 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + + + attachment.file_not_found + Soubor nebyl nalezen + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:66 + Part-DB1\templates\Parts\edit\_attachments.html.twig:64 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:48 + Part-DB1\templates\Parts\edit\_attachments.html.twig:62 + + + attachment.secure + Soukromá příloha + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:79 + Part-DB1\templates\Parts\edit\_attachments.html.twig:77 + Part-DB1\templates\AdminPages\_attachments.html.twig:77 + Part-DB1\templates\Parts\edit\_attachments.html.twig:75 + + + attachment.create + Přidat přílohu + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:84 + Part-DB1\templates\Parts\edit\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + Part-DB1\templates\AdminPages\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_attachments.html.twig:80 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + + + part_lot.edit.delete.confirm + Opravdu chcete tuto zásobu smazat? To nelze vzít zpět! + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + templates\AdminPages\_delete_form.html.twig:2 + + + entity.delete.confirm_title + Opravdu chcete smazat %name%? + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + templates\AdminPages\_delete_form.html.twig:3 + + + entity.delete.message + To nelze vrátit zpět! +<br> +Související prvky budou přesunuty nahoru. + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + templates\AdminPages\_delete_form.html.twig:9 + + + entity.delete + Odstranit prvek + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:45 + Part-DB1\src\Form\Part\PartBaseType.php:286 + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:43 + Part-DB1\src\Form\Part\PartBaseType.php:267 + new + + + edit.log_comment + Změnit komentář + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + templates\AdminPages\_delete_form.html.twig:12 + + + entity.delete.recursive + Odstranit rekurzivně (všechny související prvky) + + + + + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 + + + entity.duplicate + Duplikovat prvek + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + templates\AdminPages\_export_form.html.twig:4 + src\Form\ImportType.php:67 + + + export.format + Formát souboru + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + templates\AdminPages\_export_form.html.twig:16 + + + export.level + Úroveň podrobností + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + templates\AdminPages\_export_form.html.twig:19 + + + export.level.simple + Jednoduchý + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + templates\AdminPages\_export_form.html.twig:20 + + + export.level.extended + Rozšířený + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + templates\AdminPages\_export_form.html.twig:21 + + + export.level.full + Úplný + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + templates\AdminPages\_export_form.html.twig:31 + + + export.include_children + Zahrnutí podřízených prvků do exportu + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + templates\AdminPages\_export_form.html.twig:39 + + + export.btn + Export + + + + + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + templates\AdminPages\EntityAdminBase.html.twig:94 + templates\Parts\edit_part_info.html.twig:12 + templates\Parts\show_part_info.html.twig:11 + + + id.label + ID + + + + + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:77 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:77 + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:59 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:60 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:53 + templates\AdminPages\EntityAdminBase.html.twig:101 + templates\Parts\show_part_info.html.twig:248 + + + createdAt + Vytvořeno + + + + + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:73 + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:49 + templates\AdminPages\EntityAdminBase.html.twig:114 + templates\Parts\show_part_info.html.twig:263 + + + lastModified + Naposledy upraveno + + + + + Part-DB1\templates\AdminPages\_info.html.twig:38 + Part-DB1\templates\AdminPages\_info.html.twig:38 + + + entity.info.parts_count + Počet dílů s tímto prvkem + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:6 + Part-DB1\templates\helper.twig:125 + Part-DB1\templates\Parts\edit\_specifications.html.twig:6 + + + specifications.property + Parametr + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:7 + Part-DB1\templates\Parts\edit\_specifications.html.twig:7 + + + specifications.symbol + Symbol + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:8 + Part-DB1\templates\Parts\edit\_specifications.html.twig:8 + + + specifications.value_min + Min. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:9 + Part-DB1\templates\Parts\edit\_specifications.html.twig:9 + + + specifications.value_typ + Typ. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:10 + Part-DB1\templates\Parts\edit\_specifications.html.twig:10 + + + specifications.value_max + Max. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:11 + Part-DB1\templates\Parts\edit\_specifications.html.twig:11 + + + specifications.unit + Jednotka + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:12 + Part-DB1\templates\Parts\edit\_specifications.html.twig:12 + + + specifications.text + Text + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:13 + Part-DB1\templates\Parts\edit\_specifications.html.twig:13 + + + specifications.group + Skupina + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:26 + Part-DB1\templates\Parts\edit\_specifications.html.twig:26 + + + specification.create + Nový parametr + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:31 + Part-DB1\templates\Parts\edit\_specifications.html.twig:31 + + + parameter.delete.confirm + Opravdu chcete tento parametr odstranit? + + + + + Part-DB1\templates\attachment_list.html.twig:3 + Part-DB1\templates\attachment_list.html.twig:3 + + + attachment.list.title + Seznam příloh + + + + + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + + + part_list.loading.caption + Načítání + + + + + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + + + part_list.loading.message + To může chvíli trvat. Pokud tato zpráva nezmizí, zkuste stránku načíst znovu. + + + + + Part-DB1\templates\base.html.twig:68 + Part-DB1\templates\base.html.twig:68 + templates\base.html.twig:246 + + + vendor.base.javascript_hint + Chcete-li používat všechny funkce, aktivujte prosím JavaScript! + + + + + Part-DB1\templates\base.html.twig:73 + Part-DB1\templates\base.html.twig:73 + + + sidebar.big.toggle + Zobrazit/skrýt postranní panel + + + + + Part-DB1\templates\base.html.twig:95 + Part-DB1\templates\base.html.twig:95 + templates\base.html.twig:271 + + + loading.caption + Načítání: + + + + + Part-DB1\templates\base.html.twig:96 + Part-DB1\templates\base.html.twig:96 + templates\base.html.twig:272 + + + loading.message + To může chvíli trvat. Pokud tato zpráva zůstává dlouho, zkuste stránku znovu načíst. + + + + + Part-DB1\templates\base.html.twig:101 + Part-DB1\templates\base.html.twig:101 + templates\base.html.twig:277 + + + loading.bar + Načítání... + + + + + Part-DB1\templates\base.html.twig:112 + Part-DB1\templates\base.html.twig:112 + templates\base.html.twig:288 + + + back_to_top + Zpět na začátek stránky + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:35 + Part-DB1\templates\Form\permissionLayout.html.twig:35 + + + permission.edit.permission + Oprávnění + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:36 + Part-DB1\templates\Form\permissionLayout.html.twig:36 + + + permission.edit.value + Hodnota + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:53 + Part-DB1\templates\Form\permissionLayout.html.twig:53 + + + permission.legend.title + Vysvětlení režimů + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:57 + Part-DB1\templates\Form\permissionLayout.html.twig:57 + + + permission.legend.disallow + Zakázáno + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:61 + Part-DB1\templates\Form\permissionLayout.html.twig:61 + + + permission.legend.allow + Povoleno + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:65 + Part-DB1\templates\Form\permissionLayout.html.twig:65 + + + permission.legend.inherit + Převzít z (nadřazené) skupiny + + + + + Part-DB1\templates\helper.twig:3 + Part-DB1\templates\helper.twig:3 + + + bool.true + Ano + + + + + Part-DB1\templates\helper.twig:5 + Part-DB1\templates\helper.twig:5 + + + bool.false + Ne + + + + + Part-DB1\templates\helper.twig:92 + Part-DB1\templates\helper.twig:87 + + + Yes + Ano + + + + + Part-DB1\templates\helper.twig:94 + Part-DB1\templates\helper.twig:89 + + + No + Ne + + + + + Part-DB1\templates\helper.twig:126 + + + specifications.value + Hodnota + + + + + Part-DB1\templates\homepage.html.twig:7 + Part-DB1\templates\homepage.html.twig:7 + templates\homepage.html.twig:7 + + + version.caption + Verze + + + + + Part-DB1\templates\homepage.html.twig:22 + Part-DB1\templates\homepage.html.twig:22 + templates\homepage.html.twig:19 + + + homepage.license + Informace o licenci + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.caption + Stránka projektu + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.text + Zdrojové kódy, soubory ke stažení, hlášení chyb, seznam úkolů atd. najdete na <a href="%href%" class="link-external" target="_blank">stránce projektu GitHub</a> + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.caption + Nápověda + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.text + Nápovědu a tipy najdete na Wiki na <a href="%href%" class="link-external" target="_blank">stránce GitHub</a> + + + + + Part-DB1\templates\homepage.html.twig:33 + Part-DB1\templates\homepage.html.twig:33 + templates\homepage.html.twig:30 + + + homepage.forum.caption + Fórum + + + + + Part-DB1\templates\homepage.html.twig:45 + Part-DB1\templates\homepage.html.twig:45 + new + + + homepage.last_activity + Poslední aktivita + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:3 + Part-DB1\templates\LabelSystem\dialog.html.twig:6 + + + label_generator.title + Generátor štítků + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:16 + + + label_generator.common + Obecné + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:20 + + + label_generator.advanced + Pokročilé + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:24 + + + label_generator.profiles + Profily + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:58 + + + label_generator.selected_profile + Aktuálně vybraný profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:62 + + + label_generator.edit_profile + Upravit profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:75 + + + label_generator.load_profile + Načíst profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:102 + + + label_generator.download + Stáhnout + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 + + + label_generator.label_btn + Vytvořit štítek + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 + + + label_generator.label_empty + Nový prázdný štítek + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 + + + label_scanner.title + Čtečka štítků + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.title + Nebyla nalezena žádná webová kamera + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.text + Potřebujete webovou kameru a povolení k použití funkce čtečky. Kód čárového kódu můžete zadat ručně níže. + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 + + + label_scanner.source_select + Vybrat zdroj + + + + + Part-DB1\templates\LogSystem\log_list.html.twig:3 + Part-DB1\templates\LogSystem\log_list.html.twig:3 + + + log.list.title + Systémový protokol + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + new + + + log.undo.confirm_title + Opravdu vrátit změnu / vrátit se k časovému razítku? + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + new + + + log.undo.confirm_message + Opravdu chcete vrátit danou změnu / resetovat prvek na dané časové razítko? + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.email_sent_by + Tento e-mail byl automaticky odeslán + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.dont_reply + Na tento e-mail neodpovídejte. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:6 + Part-DB1\templates\mail\pw_reset.html.twig:6 + + + email.hi %name% + Ahoj %name% + + + + + Part-DB1\templates\mail\pw_reset.html.twig:7 + Part-DB1\templates\mail\pw_reset.html.twig:7 + + + email.pw_reset.message + někdo (doufejme, že vy) požádal o obnovení vašeho hesla. Pokud jste tuto žádost nepodali vy, ignorujte tento e-mail. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:9 + Part-DB1\templates\mail\pw_reset.html.twig:9 + + + email.pw_reset.button + Klikněte zde pro obnovení hesla + + + + + Part-DB1\templates\mail\pw_reset.html.twig:11 + Part-DB1\templates\mail\pw_reset.html.twig:11 + + + email.pw_reset.fallback + Pokud vám to nefunguje, přejděte na <a href="%url%">%url%</a> a zadejte následující informace. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:16 + Part-DB1\templates\mail\pw_reset.html.twig:16 + + + email.pw_reset.username + Uživatelské jméno + + + + + Part-DB1\templates\mail\pw_reset.html.twig:19 + Part-DB1\templates\mail\pw_reset.html.twig:19 + + + email.pw_reset.token + Token + + + + + Part-DB1\templates\mail\pw_reset.html.twig:24 + Part-DB1\templates\mail\pw_reset.html.twig:24 + + + email.pw_reset.valid_unit %date% + Token obnovení bude platný do <i>%date%</i>. + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:78 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + + + orderdetail.delete + Odstranit + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + + + pricedetails.edit.min_qty + Minimální množství slevy + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + + + pricedetails.edit.price + Cena + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + + + pricedetails.edit.price_qty + za množství + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + + + pricedetail.create + Přidat cenu + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + templates\Parts\edit_part_info.html.twig:4 + + + part.edit.title + Upravit díl + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + templates\Parts\edit_part_info.html.twig:9 + + + part.edit.card_title + Upravit díl + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + + + part.edit.tab.common + Obecné + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + + + part.edit.tab.manufacturer + Výrobce + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + + + part.edit.tab.advanced + Pokročilé + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + + + part.edit.tab.part_lots + Zásoby + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + + + part.edit.tab.attachments + Přílohy + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + + + part.edit.tab.orderdetails + Informace o nákupu + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.specifications + Parametry + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.comment + Poznámky + + + + + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + templates\Parts\new_part.html.twig:8 + + + part.new.card_title + Přidat nový díl + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + + + part_lot.delete + Odstranit + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + + + part_lot.create + Přidat zásoby + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + + + orderdetail.create + Přidat distributora + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + + + pricedetails.edit.delete.confirm + Opravdu chcete tuto cenu smazat? To nelze vzít zpět. + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 + + + orderdetails.edit.delete.confirm + Opravdu chcete smazat informace o distributorovi? To nelze vzít zpět! + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + templates\Parts\show_part_info.html.twig:4 + templates\Parts\show_part_info.html.twig:9 + + + part.info.title + Detailní informace o dílu + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + + + part.part_lots.label + Zásoby + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 + Part-DB1\templates\Parts\lists\_info_card.html.twig:43 + Part-DB1\templates\_navbar_search.html.twig:31 + Part-DB1\templates\_navbar_search.html.twig:26 + templates\base.html.twig:62 + templates\Parts\show_part_info.html.twig:74 + src\Form\PartType.php:86 + + + comment.label + Poznámky + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + + + part.info.specifications + Parametry + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + templates\Parts\show_part_info.html.twig:82 + + + attachment.labelp + Přílohy + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 + Part-DB1\templates\Parts\info\show_part_info.html.twig:71 + templates\Parts\show_part_info.html.twig:88 + + + vendor.partinfo.shopping_infos + Informace o nákupu + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 + Part-DB1\templates\Parts\info\show_part_info.html.twig:78 + templates\Parts\show_part_info.html.twig:94 + + + vendor.partinfo.history + Historie + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + Part-DB1\templates\Parts\info\show_part_info.html.twig:84 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + templates\base.html.twig:176 + templates\base.html.twig:203 + templates\base.html.twig:217 + templates\base.html.twig:231 + templates\Parts\show_part_info.html.twig:100 + + + tools.label + Nástroje + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 + Part-DB1\templates\Parts\info\show_part_info.html.twig:90 + + + extended_info.label + Rozšířené informace + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + + + attachment.name + Jméno + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + + + attachment.attachment_type + Typ přílohy + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + + + attachment.file_name + Název souboru + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + + + attachment.file_size + Velikost souboru + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 + + + attachment.preview + Náhled + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 + + + attachment.download + Stáhnout + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + new + + + user.creating_user + Uživatel, který vytvořil tento díl + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + + + Unknown + Neznámý + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + new + + + accessDenied + Přístup odepřen + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + new + + + user.last_editing_user + Uživatel, který tento díl upravil jako poslední + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + + + part.isFavorite + Oblíbené + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + + + part.minOrderAmount + Minimální množství + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:46 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:41 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + templates\base.html.twig:70 + templates\Parts\show_part_info.html.twig:24 + src\Form\PartType.php:80 + + + manufacturer.label + Výrobce + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 + Part-DB1\templates\_navbar_search.html.twig:11 + templates\base.html.twig:54 + src\Form\PartType.php:62 + + + name.label + Jméno + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + new + + + part.back_to_info + Zpět na aktuální verzi + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:19 + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:18 + templates\base.html.twig:58 + templates\Parts\show_part_info.html.twig:31 + src\Form\PartType.php:65 + + + description.label + Popis + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:15 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:14 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + templates\base.html.twig:56 + templates\Parts\show_part_info.html.twig:32 + src\Form\PartType.php:74 + + + category.label + Kategorie + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + templates\Parts\show_part_info.html.twig:42 + src\Form\PartType.php:69 + + + instock.label + Skladem + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + templates\Parts\show_part_info.html.twig:44 + src\Form\PartType.php:72 + + + mininstock.label + Minimální skladová zásoba + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:52 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:47 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + templates\base.html.twig:73 + templates\Parts\show_part_info.html.twig:47 + + + footprint.label + Otisk + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 + Part-DB1\templates\Parts\info\_main_infos.html.twig:59 + Part-DB1\templates\Parts\info\_main_infos.html.twig:57 + Part-DB1\templates\Parts\info\_main_infos.html.twig:60 + templates\Parts\show_part_info.html.twig:51 + + + part.avg_price.label + Průměrná cena + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + + + part.supplier.name + Jméno + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + + + part.supplier.partnr + Objednací číslo + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + + + part.order.minamount + Minimální množství + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + + + part.order.price + Cena + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + + + part.order.single_price + Jednotková cena + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + + + edit.caption_short + Upravit + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + + + delete.caption + Smazat + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + Part-DB1\templates\Parts\info\_part_lots.html.twig:6 + + + part_lots.description + Popis + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + + + part_lots.storage_location + Umístění + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + + + part_lots.amount + Množství + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 + Part-DB1\templates\Parts\info\_part_lots.html.twig:22 + + + part_lots.location_unknown + Umístění neznámé + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 + Part-DB1\templates\Parts\info\_part_lots.html.twig:29 + + + part_lots.instock_unknown + Množství neznámé + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 + Part-DB1\templates\Parts\info\_part_lots.html.twig:38 + + + part_lots.expiration_date + Datum vypršení platnosti + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 + Part-DB1\templates\Parts\info\_part_lots.html.twig:46 + + + part_lots.is_expired + Platnost vypršela + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 + Part-DB1\templates\Parts\info\_part_lots.html.twig:53 + + + part_lots.need_refill + Potřebuje doplnit + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:15 + Part-DB1\templates\Parts\info\_picture.html.twig:15 + + + part.info.prev_picture + Předchozí obrázek + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:19 + Part-DB1\templates\Parts\info\_picture.html.twig:19 + + + part.info.next_picture + Další obrázek + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + + + part.mass.tooltip + Hromadné + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + + + part.needs_review.badge + Potřeba revize + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + + + part.favorite.badge + Oblíbené + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + + + part.obsolete.badge + Již není k dispozici + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:10 + + + parameters.extracted_from_description + Automaticky extrahováno z popisu + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:15 + + + parameters.auto_extracted_from_comment + Automaticky extrahované z poznámek + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:6 + Part-DB1\templates\Parts\info\_tools.html.twig:4 + templates\Parts\show_part_info.html.twig:125 + + + part.edit.btn + Upravit díl + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:14 + templates\Parts\show_part_info.html.twig:135 + + + part.clone.btn + Duplikovat díl + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:24 + Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 + templates\Parts\show_part_info.html.twig:143 + + + part.create.btn + Přidat nový díl + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:31 + Part-DB1\templates\Parts\info\_tools.html.twig:29 + + + part.delete.confirm_title + Opravdu chcete tento díl odstranit? + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:32 + Part-DB1\templates\Parts\info\_tools.html.twig:30 + + + part.delete.message + Tento díl a všechny související informace (např. přílohy, informace o ceně atd.) budou odstraněny. Toto nelze vrátit zpět! + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:39 + Part-DB1\templates\Parts\info\_tools.html.twig:37 + + + part.delete + Odstranit díl + + + + + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + + + parts_list.all.title + Všechny díly + + + + + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + + + parts_list.category.title + Díly s kategorií + + + + + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + + + parts_list.footprint.title + Díly s otiskem + + + + + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + + + parts_list.manufacturer.title + Díly s výrobcem + + + + + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + + + parts_list.search.title + Vyhledat díly + + + + + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + + + parts_list.storelocation.title + Díly s místy uložení + + + + + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + + + parts_list.supplier.title + Díly s dodavatelem + + + + + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + + + parts_list.tags.title + Díly se štítkem + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 + Part-DB1\templates\Parts\lists\_info_card.html.twig:17 + + + entity.info.common.tab + Obecné + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 + Part-DB1\templates\Parts\lists\_info_card.html.twig:20 + + + entity.info.statistics.tab + Statistika + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 + + + entity.info.attachments.tab + Attachments + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 + + + entity.info.parameters.tab + Parametry + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 + Part-DB1\templates\Parts\lists\_info_card.html.twig:30 + + + entity.info.name + Jméno + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 + Part-DB1\templates\Parts\lists\_info_card.html.twig:96 + Part-DB1\templates\Parts\lists\_info_card.html.twig:34 + Part-DB1\templates\Parts\lists\_info_card.html.twig:67 + + + entity.info.parent + Nadřazený + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 + Part-DB1\templates\Parts\lists\_info_card.html.twig:46 + + + entity.edit.btn + Upravit + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 + Part-DB1\templates\Parts\lists\_info_card.html.twig:63 + + + entity.info.children_count + Počet podřízených prvků + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + + + tfa.check.title + Potřeba dvoufaktorového ověřování + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:39 + Part-DB1\templates\security\2fa_base_form.html.twig:39 + + + tfa.code.trusted_pc + Jedná se o důvěryhodný počítač (pokud je tato možnost povolena, neprovádějí se na tomto počítači žádné další dvoufaktorové dotazy). + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + + + login.btn + Přihlášení + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:42 + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:40 + + + user.logout + Odhlásit se + + + + + Part-DB1\templates\security\2fa_form.html.twig:6 + Part-DB1\templates\security\2fa_form.html.twig:6 + + + tfa.check.code.label + Kód aplikace Authenticator + + + + + Part-DB1\templates\security\2fa_form.html.twig:10 + Part-DB1\templates\security\2fa_form.html.twig:10 + + + tfa.check.code.help + Zde zadejte šestimístný kód z ověřovací aplikace nebo jeden ze záložních kódů, pokud ověřovací aplikace není k dispozici. + + + + + Part-DB1\templates\security\login.html.twig:3 + Part-DB1\templates\security\login.html.twig:3 + templates\security\login.html.twig:3 + + + login.title + Přihlášení + + + + + Part-DB1\templates\security\login.html.twig:7 + Part-DB1\templates\security\login.html.twig:7 + templates\security\login.html.twig:7 + + + login.card_title + Přihlášení + + + + + Part-DB1\templates\security\login.html.twig:31 + Part-DB1\templates\security\login.html.twig:31 + templates\security\login.html.twig:31 + + + login.username.label + Uživatelské jméno + + + + + Part-DB1\templates\security\login.html.twig:34 + Part-DB1\templates\security\login.html.twig:34 + templates\security\login.html.twig:34 + + + login.username.placeholder + Uživatelské jméno + + + + + Part-DB1\templates\security\login.html.twig:38 + Part-DB1\templates\security\login.html.twig:38 + templates\security\login.html.twig:38 + + + login.password.label + Heslo + + + + + Part-DB1\templates\security\login.html.twig:40 + Part-DB1\templates\security\login.html.twig:40 + templates\security\login.html.twig:40 + + + login.password.placeholder + Heslo + + + + + Part-DB1\templates\security\login.html.twig:50 + Part-DB1\templates\security\login.html.twig:50 + templates\security\login.html.twig:50 + + + login.rememberme + Zapamatovat si (nemělo by se používat na sdílených počítačích) + + + + + Part-DB1\templates\security\login.html.twig:64 + Part-DB1\templates\security\login.html.twig:64 + + + pw_reset.password_forget + Zapomněli jste uživatelské jméno/heslo? + + + + + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + + + pw_reset.new_pw.header.title + Nastavit nové heslo + + + + + Part-DB1\templates\security\pw_reset_request.html.twig:5 + Part-DB1\templates\security\pw_reset_request.html.twig:5 + + + pw_reset.request.header.title + Požádat o nové heslo + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + + + tfa_u2f.http_warning + Na tuto stránku přistupujete pomocí nezabezpečené metody HTTP, takže U2F pravděpodobně nebude fungovat (chybová zpráva Bad Request). Pokud chcete používat bezpečnostní klíče, požádejte správce o nastavení zabezpečené metody HTTPS. + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + + + r_u2f_two_factor.pressbutton + Připojte bezpečnostní klíč a stiskněte jeho tlačítko! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + + + tfa_u2f.add_key.title + Přidání bezpečnostního klíče + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + + + tfa_u2f.explanation + Pomocí bezpečnostního klíče kompatibilního s U2F/FIDO (např. YubiKey nebo NitroKey) lze dosáhnout uživatelsky přívětivého a bezpečného dvoufaktorového ověřování. Bezpečnostní klíče lze zde zaregistrovat a pokud je vyžadováno dvoufaktorové ověření, stačí vložit klíč do USB, nebo zadat přes zařízení prostřednictvím NFC. + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + + + tfa_u2f.add_key.backup_hint + Pro zajištění přístupu i v případě ztráty klíče doporučujeme zaregistrovat druhý klíč jako zálohu a uložit jej na bezpečném místě! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + + + r_u2f_two_factor.name + Zobrazený název klíče (např. Záloha) + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + + + tfa_u2f.add_key.add_button + Přidat bezpečnostní klíč + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + + + tfa_u2f.add_key.back_to_settings + Zpět do nastavení + + + + + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + new + + + statistics.title + Statistiky + + + + + Part-DB1\templates\Statistics\statistics.html.twig:14 + Part-DB1\templates\Statistics\statistics.html.twig:14 + new + + + statistics.parts + Díly + + + + + Part-DB1\templates\Statistics\statistics.html.twig:19 + Part-DB1\templates\Statistics\statistics.html.twig:19 + new + + + statistics.data_structures + Datové struktury + + + + + Part-DB1\templates\Statistics\statistics.html.twig:24 + Part-DB1\templates\Statistics\statistics.html.twig:24 + new + + + statistics.attachments + Přílohy + + + + + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + new + + + statistics.property + Vlastnictví + + + + + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + new + + + statistics.value + Hodnota + + + + + Part-DB1\templates\Statistics\statistics.html.twig:40 + Part-DB1\templates\Statistics\statistics.html.twig:40 + new + + + statistics.distinct_parts_count + Počet jednotlivých dílů + + + + + Part-DB1\templates\Statistics\statistics.html.twig:44 + Part-DB1\templates\Statistics\statistics.html.twig:44 + new + + + statistics.parts_instock_sum + Součet všech zásob dílů + + + + + Part-DB1\templates\Statistics\statistics.html.twig:48 + Part-DB1\templates\Statistics\statistics.html.twig:48 + new + + + statistics.parts_with_price + Počet dílů s informacemi o ceně + + + + + Part-DB1\templates\Statistics\statistics.html.twig:65 + Part-DB1\templates\Statistics\statistics.html.twig:65 + new + + + statistics.categories_count + Počet kategorií + + + + + Part-DB1\templates\Statistics\statistics.html.twig:69 + Part-DB1\templates\Statistics\statistics.html.twig:69 + new + + + statistics.footprints_count + Počet otisků + + + + + Part-DB1\templates\Statistics\statistics.html.twig:73 + Part-DB1\templates\Statistics\statistics.html.twig:73 + new + + + statistics.manufacturers_count + Počet výrobců + + + + + Part-DB1\templates\Statistics\statistics.html.twig:77 + Part-DB1\templates\Statistics\statistics.html.twig:77 + new + + + statistics.storelocations_count + Počet umístění + + + + + Part-DB1\templates\Statistics\statistics.html.twig:81 + Part-DB1\templates\Statistics\statistics.html.twig:81 + new + + + statistics.suppliers_count + Počet dodavatelů + + + + + Part-DB1\templates\Statistics\statistics.html.twig:85 + Part-DB1\templates\Statistics\statistics.html.twig:85 + new + + + statistics.currencies_count + Počet měn + + + + + Part-DB1\templates\Statistics\statistics.html.twig:89 + Part-DB1\templates\Statistics\statistics.html.twig:89 + new + + + statistics.measurement_units_count + Počet měrných jednotek + + + + + Part-DB1\templates\Statistics\statistics.html.twig:93 + Part-DB1\templates\Statistics\statistics.html.twig:93 + new + + + statistics.devices_count + Počet projektů + + + + + Part-DB1\templates\Statistics\statistics.html.twig:110 + Part-DB1\templates\Statistics\statistics.html.twig:110 + new + + + statistics.attachment_types_count + Počet typů příloh + + + + + Part-DB1\templates\Statistics\statistics.html.twig:114 + Part-DB1\templates\Statistics\statistics.html.twig:114 + new + + + statistics.all_attachments_count + Počet všech příloh + + + + + Part-DB1\templates\Statistics\statistics.html.twig:118 + Part-DB1\templates\Statistics\statistics.html.twig:118 + new + + + statistics.user_uploaded_attachments_count + Počet příloh nahraných uživatelem + + + + + Part-DB1\templates\Statistics\statistics.html.twig:122 + Part-DB1\templates\Statistics\statistics.html.twig:122 + new + + + statistics.private_attachments_count + Počet soukromých příloh + + + + + Part-DB1\templates\Statistics\statistics.html.twig:126 + Part-DB1\templates\Statistics\statistics.html.twig:126 + new + + + statistics.external_attachments_count + Počet externích příloh (URL) + + + + + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + + + tfa_backup.codes.title + Záložní kódy + + + + + Part-DB1\templates\Users\backup_codes.html.twig:12 + Part-DB1\templates\Users\backup_codes.html.twig:12 + + + tfa_backup.codes.explanation + Vytiskněte si tyto kódy a uschovejte je na bezpečném místě! + + + + + Part-DB1\templates\Users\backup_codes.html.twig:13 + Part-DB1\templates\Users\backup_codes.html.twig:13 + + + tfa_backup.codes.help + Pokud již nemáte přístup ke svému zařízení s aplikací Authenticator (ztráta smartphonu, ztráta dat atd.), můžete použít jeden z těchto kódů pro přístup ke svému účtu a případně nastavit novou aplikaci Authenticator. Každý z těchto kódů lze použít jednou, použité kódy se doporučuje odstranit. Kdokoli s přístupem k těmto kódům může potenciálně získat přístup k vašemu účtu, proto je uchovávejte na bezpečném místě. + + + + + Part-DB1\templates\Users\backup_codes.html.twig:16 + Part-DB1\templates\Users\backup_codes.html.twig:16 + + + tfa_backup.username + Uživatelské jméno + + + + + Part-DB1\templates\Users\backup_codes.html.twig:29 + Part-DB1\templates\Users\backup_codes.html.twig:29 + + + tfa_backup.codes.page_generated_on + Stránka vygenerovaná dne %date% + + + + + Part-DB1\templates\Users\backup_codes.html.twig:32 + Part-DB1\templates\Users\backup_codes.html.twig:32 + + + tfa_backup.codes.print + Tisk + + + + + Part-DB1\templates\Users\backup_codes.html.twig:35 + Part-DB1\templates\Users\backup_codes.html.twig:35 + + + tfa_backup.codes.copy_clipboard + Zkopírovat do schránky + + + + + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:40 + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:38 + templates\base.html.twig:99 + templates\Users\user_info.html.twig:3 + templates\Users\user_info.html.twig:6 + + + user.info.label + Informace o uživateli + + + + + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + templates\Users\user_info.html.twig:18 + src\Form\UserSettingsType.php:32 + + + user.firstName.label + Jméno + + + + + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + templates\Users\user_info.html.twig:24 + src\Form\UserSettingsType.php:35 + + + user.lastName.label + Příjmení + + + + + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + templates\Users\user_info.html.twig:30 + src\Form\UserSettingsType.php:41 + + + user.email.label + e-mail + + + + + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + templates\Users\user_info.html.twig:37 + src\Form\UserSettingsType.php:38 + + + user.department.label + Oddělení + + + + + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + templates\Users\user_info.html.twig:47 + src\Form\UserSettingsType.php:30 + + + user.username.label + Uživatelské jméno + + + + + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + templates\Users\user_info.html.twig:53 + + + group.label + Skupina + + + + + Part-DB1\templates\Users\user_info.html.twig:67 + Part-DB1\templates\Users\user_info.html.twig:67 + + + user.permissions + Oprávnění + + + + + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:39 + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:37 + templates\base.html.twig:98 + templates\Users\user_settings.html.twig:3 + templates\Users\user_settings.html.twig:6 + + + user.settings.label + Uživatelské nastavení + + + + + Part-DB1\templates\Users\user_settings.html.twig:18 + Part-DB1\templates\Users\user_settings.html.twig:18 + templates\Users\user_settings.html.twig:14 + + + user_settings.data.label + Osobní údaje + + + + + Part-DB1\templates\Users\user_settings.html.twig:22 + Part-DB1\templates\Users\user_settings.html.twig:22 + templates\Users\user_settings.html.twig:18 + + + user_settings.configuration.label + Konfigurace + + + + + Part-DB1\templates\Users\user_settings.html.twig:55 + Part-DB1\templates\Users\user_settings.html.twig:55 + templates\Users\user_settings.html.twig:48 + + + user.settings.change_pw + Změnit heslo + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + + + user.settings.2fa_settings + Dvoufaktorové ověřování + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + + + tfa.settings.google.tab + Authenticator app + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + + + tfa.settings.bakup.tab + Záložní kódy + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + + + tfa.settings.u2f.tab + Bezpečnostní klíče (U2F) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + + + tfa.settings.trustedDevices.tab + Důvěryhodná zařízení + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_title + Opravdu chcete aplikaci Authenticator zakázat? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_message + Pokud aplikaci Authenticator deaktivujete, všechny záložní kódy budou smazány, takže je možná budete muset vytisknout znovu.<br> +Upozorňujeme také, že bez dvoufaktorového ověřování není váš účet tak dobře chráněn před útočníky! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + + + tfa_google.disabled_message + Aplikace Authenticator deaktivována! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + + + tfa_google.step.download + Stáhněte si aplikaci Authenticator (např. <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> nebo <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp">FreeOTP Authenticator</a>). + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + + + tfa_google.step.scan + Naskenujte přiložený QR kód pomocí aplikace nebo zadejte údaje ručně. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + + + tfa_google.step.input_code + Vygenerovaný kód zadejte do níže uvedeného pole a potvrďte. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + + + tfa_google.step.download_backup + Vytiskněte si záložní kódy a uložte je na bezpečném místě. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + + + tfa_google.manual_setup + Ruční nastavení + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + + + tfa_google.manual_setup.type + Typ + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + + + tfa_google.manual_setup.username + Uživatelské jméno + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + + + tfa_google.manual_setup.secret + Tajné + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + + + tfa_google.manual_setup.digit_count + Počet číslic + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + + + tfa_google.enabled_message + Aplikace Authenticator povolena + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + + + tfa_backup.disabled + Záložní kódy jsou zakázány. Nastavte aplikaci Authenticator pro povolení záložních kódů. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + + + tfa_backup.explanation + Tyto záložní kódy můžete použít k přístupu k účtu i v případě ztráty zařízení s aplikací Authenticator. Kódy si vytiskněte a uschovejte na bezpečném místě. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_title + Opravdu resetovat kódy? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_message + Tím se odstraní všechny předchozí kódy a vygeneruje se sada nových kódů. Tuto akci nelze vrátit zpět. Nezapomeňte si nové kódy vytisknout a uložit na bezpečném místě! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + + + tfa_backup.enabled + Záložní kódy povoleny + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + + + tfa_backup.show_codes + Zobrazit záložní kódy + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + + + tfa_u2f.table_caption + Registrované bezpečnostní klíče + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + + + tfa_u2f.delete_u2f.confirm_title + Opravdu odstranit tento bezpečnostní klíč? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + + + tfa_u2f.delete_u2f.confirm_message + Pokud tento klíč odstraníte, nebude již možné se pomocí tohoto klíče přihlásit. Pokud nezůstanou žádné bezpečnostní klíče, dvoufaktorové ověřování bude zakázáno. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + + + tfa_u2f.keys.name + Název klíče + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + + + tfa_u2f.keys.added_date + Datum registrace + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + + + tfa_u2f.key_delete + Smazat klíč + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + + + tfa_u2f.no_keys_registered + Zatím nebyly zaregistrovány žádné klíče. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + + + tfa_u2f.add_new_key + Registrace nového bezpečnostního klíče + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + + + tfa_trustedDevices.explanation + Při kontrole druhého faktoru lze aktuální počítač označit jako důvěryhodný, takže na tomto počítači již není třeba provádět žádné další dvoufaktorové kontroly. +Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodný, můžete zde obnovit stav <i>všech </i>počítačů. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + + + tfa_trustedDevices.invalidate.confirm_title + Opravdu odebrat všechny důvěryhodné počítače? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + + + tfa_trustedDevices.invalidate.confirm_message + Ve všech počítačích bude nutné znovu provést dvoufaktorové ověřování. Ujistěte se, že máte po ruce dvoufaktorové zařízení. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + + + tfa_trustedDevices.invalidate.btn + Reset důvěryhodných zařízení + + + + + Part-DB1\templates\_navbar.html.twig:4 + Part-DB1\templates\_navbar.html.twig:4 + templates\base.html.twig:29 + + + sidebar.toggle + Přepnutí postranního panelu + + + + + Part-DB1\templates\_navbar.html.twig:22 + + + navbar.scanner.link + Čtečka štítků + + + + + Part-DB1\templates\_navbar.html.twig:38 + Part-DB1\templates\_navbar.html.twig:36 + templates\base.html.twig:97 + + + user.loggedin.label + Přihlášen jako + + + + + Part-DB1\templates\_navbar.html.twig:44 + Part-DB1\templates\_navbar.html.twig:42 + templates\base.html.twig:103 + + + user.login + Příhlásit + + + + + Part-DB1\templates\_navbar.html.twig:50 + Part-DB1\templates\_navbar.html.twig:48 + + + ui.toggle_darkmode + Tmavý režim + + + + + Part-DB1\templates\_navbar.html.twig:54 + Part-DB1\src\Form\UserSettingsType.php:97 + Part-DB1\templates\_navbar.html.twig:52 + Part-DB1\src\Form\UserSettingsType.php:97 + templates\base.html.twig:106 + src\Form\UserSettingsType.php:44 + + + user.language_select + Jazyk + + + + + Part-DB1\templates\_navbar_search.html.twig:4 + Part-DB1\templates\_navbar_search.html.twig:4 + templates\base.html.twig:49 + + + search.options.label + Možnosti hledání + + + + + Part-DB1\templates\_navbar_search.html.twig:23 + + + tags.label + Štítky + + + + + Part-DB1\templates\_navbar_search.html.twig:27 + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + templates\base.html.twig:60 + templates\Parts\show_part_info.html.twig:36 + src\Form\PartType.php:77 + + + storelocation.label + Umístění + + + + + Part-DB1\templates\_navbar_search.html.twig:36 + Part-DB1\templates\_navbar_search.html.twig:31 + templates\base.html.twig:65 + + + ordernumber.label.short + Číslo dílu dodavatele + + + + + Part-DB1\templates\_navbar_search.html.twig:40 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + Part-DB1\templates\_navbar_search.html.twig:35 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + templates\base.html.twig:67 + + + supplier.label + Dodavatel + + + + + Part-DB1\templates\_navbar_search.html.twig:57 + Part-DB1\templates\_navbar_search.html.twig:52 + templates\base.html.twig:75 + + + search.deactivateBarcode + Deaktivovat čárový kód + + + + + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_navbar_search.html.twig:56 + templates\base.html.twig:77 + + + search.regexmatching + RegEx. shoda + + + + + Part-DB1\templates\_navbar_search.html.twig:68 + Part-DB1\templates\_navbar_search.html.twig:62 + + + search.submit + Jdi! + + + + + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + templates\base.html.twig:175 + templates\base.html.twig:189 + templates\base.html.twig:202 + templates\base.html.twig:230 + + + project.labelp + Projekty + + + + + Part-DB1\templates\_sidebar.html.twig:2 + Part-DB1\templates\_sidebar.html.twig:2 + templates\base.html.twig:165 + templates\base.html.twig:192 + templates\base.html.twig:220 + + + actions + Akce + + + + + Part-DB1\templates\_sidebar.html.twig:6 + Part-DB1\templates\_sidebar.html.twig:6 + templates\base.html.twig:169 + templates\base.html.twig:196 + templates\base.html.twig:224 + + + datasource + Zdroj dat + + + + + Part-DB1\templates\_sidebar.html.twig:10 + Part-DB1\templates\_sidebar.html.twig:10 + templates\base.html.twig:173 + templates\base.html.twig:200 + templates\base.html.twig:228 + + + manufacturer.labelp + Výrobce + + + + + Part-DB1\templates\_sidebar.html.twig:11 + Part-DB1\templates\_sidebar.html.twig:11 + templates\base.html.twig:174 + templates\base.html.twig:201 + templates\base.html.twig:229 + + + supplier.labelp + Dodavatelé + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:293 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:181 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:243 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:268 + + + attachment.download_failed + Stažení externí přílohy se nezdařilo. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 + + + entity.edit_flash + Změny byly úspěšně uloženy. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 + + + entity.edit_flash.invalid + Nelze uložit změnit. Zkontrolujte prosím své zadání! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 + + + entity.created_flash + Vytvořený prvek. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 + + + entity.created_flash.invalid + Nepodařilo se vytvořit prvek. Zkontrolujte prosím zadání! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 + src\Controller\BaseAdminController.php:154 + + + attachment_type.deleted + Prvek smazán! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 + Part-DB1\src\Controller\UserController.php:109 + Part-DB1\src\Controller\UserSettingsController.php:159 + Part-DB1\src\Controller\UserSettingsController.php:193 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:354 + Part-DB1\src\Controller\UserController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:150 + Part-DB1\src\Controller\UserSettingsController.php:182 + + + csfr_invalid + Token CSFR je neplatný. Pokud tato zpráva přetrvává, načtěte prosím tuto stránku znovu nebo kontaktujte správce. + + + + + Part-DB1\src\Controller\LabelController.php:125 + + + label_generator.no_entities_found + Nebyly nalezeny žádné entity odpovídající zadání. + + + + + Part-DB1\src\Controller\LogController.php:149 + Part-DB1\src\Controller\LogController.php:154 + new + + + log.undo.target_not_found + Cílový prvek nebyl v DB nalezen! + + + + + Part-DB1\src\Controller\LogController.php:156 + Part-DB1\src\Controller\LogController.php:160 + new + + + log.undo.revert_success + Úspěšně vráceno na časové razítko. + + + + + Part-DB1\src\Controller\LogController.php:176 + Part-DB1\src\Controller\LogController.php:180 + new + + + log.undo.element_undelete_success + Prvek byl úspěšně odstraněn. + + + + + Part-DB1\src\Controller\LogController.php:178 + Part-DB1\src\Controller\LogController.php:182 + new + + + log.undo.element_element_already_undeleted + Prvek byl již smazán! + + + + + Part-DB1\src\Controller\LogController.php:185 + Part-DB1\src\Controller\LogController.php:189 + new + + + log.undo.element_delete_success + Prvek byl úspěšně odstraněn. + + + + + Part-DB1\src\Controller\LogController.php:187 + Part-DB1\src\Controller\LogController.php:191 + new + + + log.undo.element.element_already_delted + Prvek byl již smazán! + + + + + Part-DB1\src\Controller\LogController.php:194 + Part-DB1\src\Controller\LogController.php:198 + new + + + log.undo.element_change_undone + Změna úspěšně vrácena! + + + + + Part-DB1\src\Controller\LogController.php:196 + Part-DB1\src\Controller\LogController.php:200 + new + + + log.undo.do_undelete_before + Před zrušením této změny musíte prvek smazat! + + + + + Part-DB1\src\Controller\LogController.php:199 + Part-DB1\src\Controller\LogController.php:203 + new + + + log.undo.log_type_invalid + Tento záznam v protokolu nelze vrátit zpět! + + + + + Part-DB1\src\Controller\PartController.php:182 + Part-DB1\src\Controller\PartController.php:182 + src\Controller\PartController.php:80 + + + part.edited_flash + Uložené změny! + + + + + Part-DB1\src\Controller\PartController.php:186 + Part-DB1\src\Controller\PartController.php:186 + + + part.edited_flash.invalid + Chyba při ukládání: Zkontrolujte prosím své zadání! + + + + + Part-DB1\src\Controller\PartController.php:216 + Part-DB1\src\Controller\PartController.php:219 + + + part.deleted + Díl úspěšně vymazán. + + + + + Part-DB1\src\Controller\PartController.php:302 + Part-DB1\src\Controller\PartController.php:277 + Part-DB1\src\Controller\PartController.php:317 + src\Controller\PartController.php:113 + src\Controller\PartController.php:142 + + + part.created_flash + Díl vytvořen! + + + + + Part-DB1\src\Controller\PartController.php:308 + Part-DB1\src\Controller\PartController.php:283 + + + part.created_flash.invalid + Chyba při vytváření: Zkontrolujte prosím své zadání! + + + + + Part-DB1\src\Controller\ScanController.php:68 + Part-DB1\src\Controller\ScanController.php:90 + + + scan.qr_not_found + Pro daný čárový kód nebyl nalezen žádný prvek. + + + + + Part-DB1\src\Controller\ScanController.php:71 + + + scan.format_unknown + Neznámý formát! + + + + + Part-DB1\src\Controller\ScanController.php:86 + + + scan.qr_success + Nalezený prvek. + + + + + Part-DB1\src\Controller\SecurityController.php:114 + Part-DB1\src\Controller\SecurityController.php:109 + + + pw_reset.user_or_email + Uživatelské jméno / e-mail + + + + + Part-DB1\src\Controller\SecurityController.php:131 + Part-DB1\src\Controller\SecurityController.php:126 + + + pw_reset.request.success + Žádost o obnovení byla úspěšná! Zkontrolujte prosím své e-maily, kde najdete další pokyny. + + + + + Part-DB1\src\Controller\SecurityController.php:162 + Part-DB1\src\Controller\SecurityController.php:160 + + + pw_reset.username + Uživatelské jméno + + + + + Part-DB1\src\Controller\SecurityController.php:165 + Part-DB1\src\Controller\SecurityController.php:163 + + + pw_reset.token + Token + + + + + Part-DB1\src\Controller\SecurityController.php:194 + Part-DB1\src\Controller\SecurityController.php:192 + + + pw_reset.new_pw.error + Uživatelské jméno nebo token je neplatný! Zkontrolujte prosím své zadání. + + + + + Part-DB1\src\Controller\SecurityController.php:196 + Part-DB1\src\Controller\SecurityController.php:194 + + + pw_reset.new_pw.success + Heslo bylo úspěšně obnoveno. Nyní se můžete přihlásit pomocí nového hesla. + + + + + Part-DB1\src\Controller\UserController.php:107 + Part-DB1\src\Controller\UserController.php:99 + + + user.edit.reset_success + Všechny metody dvoufaktorového ověřování byly úspěšně zakázány. + + + + + Part-DB1\src\Controller\UserSettingsController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:92 + + + tfa_backup.no_codes_enabled + Nejsou povoleny žádné zálohovací kódy! + + + + + Part-DB1\src\Controller\UserSettingsController.php:138 + Part-DB1\src\Controller\UserSettingsController.php:132 + + + tfa_u2f.u2f_delete.not_existing + Žádný bezpečnostní klíč s tímto ID neexistuje. + + + + + Part-DB1\src\Controller\UserSettingsController.php:145 + Part-DB1\src\Controller\UserSettingsController.php:139 + + + tfa_u2f.u2f_delete.access_denied + Bezpečnostní klíče jiných uživatelů nelze odstranit! + + + + + Part-DB1\src\Controller\UserSettingsController.php:153 + Part-DB1\src\Controller\UserSettingsController.php:147 + + + tfa.u2f.u2f_delete.success + Bezpečnostní klíč byl úspěšně odstraněn. + + + + + Part-DB1\src\Controller\UserSettingsController.php:188 + Part-DB1\src\Controller\UserSettingsController.php:180 + + + tfa_trustedDevice.invalidate.success + Důvěryhodná zařízení byla úspěšně resetována. + + + + + Part-DB1\src\Controller\UserSettingsController.php:235 + Part-DB1\src\Controller\UserSettingsController.php:226 + src\Controller\UserController.php:98 + + + user.settings.saved_flash + Nastavení uloženo! + + + + + Part-DB1\src\Controller\UserSettingsController.php:297 + Part-DB1\src\Controller\UserSettingsController.php:288 + src\Controller\UserController.php:130 + + + user.settings.pw_changed_flash + Heslo změněno! + + + + + Part-DB1\src\Controller\UserSettingsController.php:317 + Part-DB1\src\Controller\UserSettingsController.php:306 + + + user.settings.2fa.google.activated + Aplikace Authenticator byla úspěšně aktivována. + + + + + Part-DB1\src\Controller\UserSettingsController.php:328 + Part-DB1\src\Controller\UserSettingsController.php:315 + + + user.settings.2fa.google.disabled + Aplikace Authenticator byla úspěšně deaktivována. + + + + + Part-DB1\src\Controller\UserSettingsController.php:346 + Part-DB1\src\Controller\UserSettingsController.php:332 + + + user.settings.2fa.backup_codes.regenerated + Nové záložní kódy byly úspěšně vygenerovány. + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + + + attachment.table.filename + Název souboru + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + + + attachment.table.filesize + Velikost souboru + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:245 + Part-DB1\src\DataTables\PartsDataTable.php:252 + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:193 + Part-DB1\src\DataTables\PartsDataTable.php:200 + + + true + pravda + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:246 + Part-DB1\src\DataTables\PartsDataTable.php:253 + Part-DB1\src\Form\Type\SIUnitType.php:139 + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:201 + Part-DB1\src\Form\Type\SIUnitType.php:139 + + + false + nepravda + + + + + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 + + + log.target_deleted + smazáno + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 + new + + + log.undo.undelete + Odstranit prvek + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 + new + + + log.undo.undo + Vrátit změnu + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 + new + + + log.undo.revert + Vrátit prvek na toto časové razítko + + + + + Part-DB1\src\DataTables\LogDataTable.php:173 + Part-DB1\src\DataTables\LogDataTable.php:161 + + + log.id + ID + + + + + Part-DB1\src\DataTables\LogDataTable.php:178 + Part-DB1\src\DataTables\LogDataTable.php:166 + + + log.timestamp + Časové razítko + + + + + Part-DB1\src\DataTables\LogDataTable.php:183 + Part-DB1\src\DataTables\LogDataTable.php:171 + + + log.type + Událost + + + + + Part-DB1\src\DataTables\LogDataTable.php:191 + Part-DB1\src\DataTables\LogDataTable.php:179 + + + log.level + Úroveň + + + + + Part-DB1\src\DataTables\LogDataTable.php:200 + Part-DB1\src\DataTables\LogDataTable.php:188 + + + log.user + Uživatel + + + + + Part-DB1\src\DataTables\LogDataTable.php:213 + Part-DB1\src\DataTables\LogDataTable.php:201 + + + log.target_type + Typ cíle + + + + + Part-DB1\src\DataTables\LogDataTable.php:226 + Part-DB1\src\DataTables\LogDataTable.php:214 + + + log.target + Cíl + + + + + Part-DB1\src\DataTables\LogDataTable.php:231 + Part-DB1\src\DataTables\LogDataTable.php:218 + new + + + log.extra + Extra + + + + + Part-DB1\src\DataTables\PartsDataTable.php:168 + Part-DB1\src\DataTables\PartsDataTable.php:116 + + + part.table.name + Název + + + + + Part-DB1\src\DataTables\PartsDataTable.php:178 + Part-DB1\src\DataTables\PartsDataTable.php:126 + + + part.table.id + ID + + + + + Part-DB1\src\DataTables\PartsDataTable.php:182 + Part-DB1\src\DataTables\PartsDataTable.php:130 + + + part.table.description + Popis + + + + + Part-DB1\src\DataTables\PartsDataTable.php:185 + Part-DB1\src\DataTables\PartsDataTable.php:133 + + + part.table.category + Kategorie + + + + + Part-DB1\src\DataTables\PartsDataTable.php:190 + Part-DB1\src\DataTables\PartsDataTable.php:138 + + + part.table.footprint + Otisk + + + + + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:142 + + + part.table.manufacturer + Výrobce + + + + + Part-DB1\src\DataTables\PartsDataTable.php:197 + Part-DB1\src\DataTables\PartsDataTable.php:145 + + + part.table.storeLocations + Umístění + + + + + Part-DB1\src\DataTables\PartsDataTable.php:216 + Part-DB1\src\DataTables\PartsDataTable.php:164 + + + part.table.amount + Množství + + + + + Part-DB1\src\DataTables\PartsDataTable.php:224 + Part-DB1\src\DataTables\PartsDataTable.php:172 + + + part.table.minamount + Min. množství + + + + + Part-DB1\src\DataTables\PartsDataTable.php:232 + Part-DB1\src\DataTables\PartsDataTable.php:180 + + + part.table.partUnit + Měrné jednotky + + + + + Part-DB1\src\DataTables\PartsDataTable.php:236 + Part-DB1\src\DataTables\PartsDataTable.php:184 + + + part.table.addedDate + Vytvořeno + + + + + Part-DB1\src\DataTables\PartsDataTable.php:240 + Part-DB1\src\DataTables\PartsDataTable.php:188 + + + part.table.lastModified + Naposledy upraveno + + + + + Part-DB1\src\DataTables\PartsDataTable.php:244 + Part-DB1\src\DataTables\PartsDataTable.php:192 + + + part.table.needsReview + Potřeba revize + + + + + Part-DB1\src\DataTables\PartsDataTable.php:251 + Part-DB1\src\DataTables\PartsDataTable.php:199 + + + part.table.favorite + Oblíbené + + + + + Part-DB1\src\DataTables\PartsDataTable.php:258 + Part-DB1\src\DataTables\PartsDataTable.php:206 + + + part.table.manufacturingStatus + Stav + + + + + Part-DB1\src\DataTables\PartsDataTable.php:260 + Part-DB1\src\DataTables\PartsDataTable.php:262 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:208 + Part-DB1\src\DataTables\PartsDataTable.php:210 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.unknown + Neznámý + + + + + Part-DB1\src\DataTables\PartsDataTable.php:263 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:211 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.announced + Oznámeno + + + + + Part-DB1\src\DataTables\PartsDataTable.php:264 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.active + Aktivní + + + + + Part-DB1\src\DataTables\PartsDataTable.php:265 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:213 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.nrfnd + Nedoporučuje se pro nové návrhy + + + + + Part-DB1\src\DataTables\PartsDataTable.php:266 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:214 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.eol + Ukončeno + + + + + Part-DB1\src\DataTables\PartsDataTable.php:267 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:215 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.discontinued + Přerušeno + + + + + Part-DB1\src\DataTables\PartsDataTable.php:271 + Part-DB1\src\DataTables\PartsDataTable.php:219 + + + part.table.mpn + MPN + + + + + Part-DB1\src\DataTables\PartsDataTable.php:275 + Part-DB1\src\DataTables\PartsDataTable.php:223 + + + part.table.mass + Hmotnost + + + + + Part-DB1\src\DataTables\PartsDataTable.php:279 + Part-DB1\src\DataTables\PartsDataTable.php:227 + + + part.table.tags + Štítky + + + + + Part-DB1\src\DataTables\PartsDataTable.php:283 + Part-DB1\src\DataTables\PartsDataTable.php:231 + + + part.table.attachments + Přílohy + + + + + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 + Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 + + + flash.login_successful + Přihlášení bylo úspěšné + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + JSON + JSON + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + XML + XML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + CSV + CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + YAML + YAML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:124 + Part-DB1\src\Form\AdminPages\ImportType.php:124 + + + import.abort_on_validation.help + Pokud je tato možnost aktivována, celý proces importu se přeruší, pokud jsou zjištěna neplatná data. Není-li tato možnost vybrána, neplatná data jsou ignorována a importér se pokusí importovat ostatní prvky. + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:86 + Part-DB1\src\Form\AdminPages\ImportType.php:86 + src\Form\ImportType.php:70 + + + import.csv_separator + CSV oddělovač + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:93 + Part-DB1\src\Form\AdminPages\ImportType.php:93 + src\Form\ImportType.php:72 + + + parent.label + Nadřazený prvek + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:101 + Part-DB1\src\Form\AdminPages\ImportType.php:101 + src\Form\ImportType.php:75 + + + import.file + Soubor + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:111 + Part-DB1\src\Form\AdminPages\ImportType.php:111 + src\Form\ImportType.php:78 + + + import.preserve_children + Zachování podřízených prvků při importu + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:120 + Part-DB1\src\Form\AdminPages\ImportType.php:120 + src\Form\ImportType.php:80 + + + import.abort_on_validation + Přerušit při neplatných datech + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:132 + Part-DB1\src\Form\AdminPages\ImportType.php:132 + src\Form\ImportType.php:85 + + + import.btn + Import + + + + + Part-DB1\src\Form\AttachmentFormType.php:113 + Part-DB1\src\Form\AttachmentFormType.php:109 + + + attachment.edit.secure_file.help + K příloze označené jako soukromá mají přístup pouze ověření uživatelé s příslušným oprávněním. Pokud je tato funkce aktivována, negenerují se náhledy a přístup k souboru je méně výkonný. + + + + + Part-DB1\src\Form\AttachmentFormType.php:127 + Part-DB1\src\Form\AttachmentFormType.php:123 + + + attachment.edit.url.help + Zde můžete zadat adresu URL externího souboru nebo zadat klíčové slovo, které se používá k hledání ve vestavěných zdrojích (např. otisky). + + + + + Part-DB1\src\Form\AttachmentFormType.php:82 + Part-DB1\src\Form\AttachmentFormType.php:79 + + + attachment.edit.name + Název + + + + + Part-DB1\src\Form\AttachmentFormType.php:85 + Part-DB1\src\Form\AttachmentFormType.php:82 + + + attachment.edit.attachment_type + Typ přílohy + + + + + Part-DB1\src\Form\AttachmentFormType.php:94 + Part-DB1\src\Form\AttachmentFormType.php:91 + + + attachment.edit.show_in_table + Zobrazit v tabulce + + + + + Part-DB1\src\Form\AttachmentFormType.php:105 + Part-DB1\src\Form\AttachmentFormType.php:102 + + + attachment.edit.secure_file + Soukromá příloha + + + + + Part-DB1\src\Form\AttachmentFormType.php:119 + Part-DB1\src\Form\AttachmentFormType.php:115 + + + attachment.edit.url + URL + + + + + Part-DB1\src\Form\AttachmentFormType.php:133 + Part-DB1\src\Form\AttachmentFormType.php:129 + + + attachment.edit.download_url + Stáhnout externí soubor + + + + + Part-DB1\src\Form\AttachmentFormType.php:146 + Part-DB1\src\Form\AttachmentFormType.php:142 + + + attachment.edit.file + Nahrát soubor + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:86 + + + part.label + Díl + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:87 + + + part_lot.label + Inventář + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.none + Žádné + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.qr + QR kód (doporučeno) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code128 + Kód 128 (doporučeno) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code39 + Kód 39 (doporučeno) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code93 + Kód 39 + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.datamatrix + Datamatrix + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label_options.lines_mode.html + Zástupné symboly + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label.options.lines_mode.twig + Twig + + + + + Part-DB1\src\Form\LabelOptionsType.php:126 + + + label_options.lines_mode.help + Pokud zde vyberete Twig, bude pole obsahu interpretováno jako Twig šablona. Viz <a href="https://twig.symfony.com/doc/3.x/templates.html">dokumentace Twig</a> a <a href="https://docs.part-db.de/usage/labels.html#twig-mode">Wiki</a>, kde najdete další informace. + + + + + Part-DB1\src\Form\LabelOptionsType.php:47 + + + label_options.page_size.label + Velikost štítku + + + + + Part-DB1\src\Form\LabelOptionsType.php:66 + + + label_options.supported_elements.label + Typ cíle + + + + + Part-DB1\src\Form\LabelOptionsType.php:75 + + + label_options.barcode_type.label + Čárový kód + + + + + Part-DB1\src\Form\LabelOptionsType.php:102 + + + label_profile.lines.label + Obsah + + + + + Part-DB1\src\Form\LabelOptionsType.php:111 + + + label_options.additional_css.label + Další styly (CSS) + + + + + Part-DB1\src\Form\LabelOptionsType.php:120 + + + label_options.lines_mode.label + Režim parseru + + + + + Part-DB1\src\Form\LabelOptionsType.php:51 + + + label_options.width.placeholder + Šířka + + + + + Part-DB1\src\Form\LabelOptionsType.php:60 + + + label_options.height.placeholder + Výška + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 + + + label_generator.target_id.range_hint + Zde můžete zadat více ID (např. 1,2,3) a/nebo rozsah (1-3), abyste mohli generovat štítky pro více prvků najednou. + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 + + + label_generator.target_id.label + Cílové ID + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 + + + label_generator.update + Aktualizovat + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 + + + scan_dialog.input + Zadání + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 + + + scan_dialog.submit + Odeslat + + + + + Part-DB1\src\Form\ParameterType.php:41 + + + parameters.name.placeholder + např. Stejnosměrný proudový zisk + + + + + Part-DB1\src\Form\ParameterType.php:50 + + + parameters.symbol.placeholder + např. h_{FE} + + + + + Part-DB1\src\Form\ParameterType.php:60 + + + parameters.text.placeholder + např. Testovací podmínky + + + + + Part-DB1\src\Form\ParameterType.php:71 + + + parameters.max.placeholder + např. 350 + + + + + Part-DB1\src\Form\ParameterType.php:82 + + + parameters.min.placeholder + např. 100 + + + + + Part-DB1\src\Form\ParameterType.php:93 + + + parameters.typical.placeholder + např. 200 + + + + + Part-DB1\src\Form\ParameterType.php:103 + + + parameters.unit.placeholder + např. V + + + + + Part-DB1\src\Form\ParameterType.php:114 + + + parameter.group.placeholder + např. Technické specifikace + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:72 + Part-DB1\src\Form\Part\OrderdetailType.php:75 + + + orderdetails.edit.supplierpartnr + Číslo dílu dodavatele + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:81 + Part-DB1\src\Form\Part\OrderdetailType.php:84 + + + orderdetails.edit.supplier + Dodavatel + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:87 + Part-DB1\src\Form\Part\OrderdetailType.php:90 + + + orderdetails.edit.url + Odkaz na nabídku + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:93 + Part-DB1\src\Form\Part\OrderdetailType.php:96 + + + orderdetails.edit.obsolete + Již není k dispozici + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:75 + Part-DB1\src\Form\Part\OrderdetailType.php:78 + + + orderdetails.edit.supplierpartnr.placeholder + např. BC 547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:101 + Part-DB1\src\Form\Part\PartBaseType.php:99 + + + part.edit.name + Název + + + + + Part-DB1\src\Form\Part\PartBaseType.php:109 + Part-DB1\src\Form\Part\PartBaseType.php:107 + + + part.edit.description + Popis + + + + + Part-DB1\src\Form\Part\PartBaseType.php:120 + Part-DB1\src\Form\Part\PartBaseType.php:118 + + + part.edit.mininstock + Minimální zásoba + + + + + Part-DB1\src\Form\Part\PartBaseType.php:129 + Part-DB1\src\Form\Part\PartBaseType.php:127 + + + part.edit.category + Kategorie + + + + + Part-DB1\src\Form\Part\PartBaseType.php:135 + Part-DB1\src\Form\Part\PartBaseType.php:133 + + + part.edit.footprint + Otisk + + + + + Part-DB1\src\Form\Part\PartBaseType.php:142 + Part-DB1\src\Form\Part\PartBaseType.php:140 + + + part.edit.tags + Štítky + + + + + Part-DB1\src\Form\Part\PartBaseType.php:154 + Part-DB1\src\Form\Part\PartBaseType.php:152 + + + part.edit.manufacturer.label + Výrobce + + + + + Part-DB1\src\Form\Part\PartBaseType.php:161 + Part-DB1\src\Form\Part\PartBaseType.php:159 + + + part.edit.manufacturer_url.label + Odkaz na stránku produktu + + + + + Part-DB1\src\Form\Part\PartBaseType.php:167 + Part-DB1\src\Form\Part\PartBaseType.php:165 + + + part.edit.mpn + Číslo dílu výrobce + + + + + Part-DB1\src\Form\Part\PartBaseType.php:173 + Part-DB1\src\Form\Part\PartBaseType.php:171 + + + part.edit.manufacturing_status + Stav výroby + + + + + Part-DB1\src\Form\Part\PartBaseType.php:181 + Part-DB1\src\Form\Part\PartBaseType.php:179 + + + part.edit.needs_review + Potřeba revize + + + + + Part-DB1\src\Form\Part\PartBaseType.php:189 + Part-DB1\src\Form\Part\PartBaseType.php:187 + + + part.edit.is_favorite + Oblíbené + + + + + Part-DB1\src\Form\Part\PartBaseType.php:197 + Part-DB1\src\Form\Part\PartBaseType.php:195 + + + part.edit.mass + Hmotnost + + + + + Part-DB1\src\Form\Part\PartBaseType.php:203 + Part-DB1\src\Form\Part\PartBaseType.php:201 + + + part.edit.partUnit + Měrná jednotka + + + + + Part-DB1\src\Form\Part\PartBaseType.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:210 + + + part.edit.comment + Poznámky + + + + + Part-DB1\src\Form\Part\PartBaseType.php:250 + Part-DB1\src\Form\Part\PartBaseType.php:246 + + + part.edit.master_attachment + Náhled + + + + + Part-DB1\src\Form\Part\PartBaseType.php:295 + Part-DB1\src\Form\Part\PartBaseType.php:276 + src\Form\PartType.php:91 + + + part.edit.save + Uložit změny + + + + + Part-DB1\src\Form\Part\PartBaseType.php:296 + Part-DB1\src\Form\Part\PartBaseType.php:277 + src\Form\PartType.php:92 + + + part.edit.reset + Zrušit změny + + + + + Part-DB1\src\Form\Part\PartBaseType.php:105 + Part-DB1\src\Form\Part\PartBaseType.php:103 + + + part.edit.name.placeholder + např. BC547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:115 + Part-DB1\src\Form\Part\PartBaseType.php:113 + + + part.edit.description.placeholder + např. NPN 45V, 0,1A, 0,5W + + + + + Part-DB1\src\Form\Part\PartBaseType.php:123 + Part-DB1\src\Form\Part\PartBaseType.php:121 + + + part.editmininstock.placeholder + např. 1 + + + + + Part-DB1\src\Form\Part\PartLotType.php:69 + Part-DB1\src\Form\Part\PartLotType.php:69 + + + part_lot.edit.description + Popis + + + + + Part-DB1\src\Form\Part\PartLotType.php:78 + Part-DB1\src\Form\Part\PartLotType.php:78 + + + part_lot.edit.location + Umístění + + + + + Part-DB1\src\Form\Part\PartLotType.php:89 + Part-DB1\src\Form\Part\PartLotType.php:89 + + + part_lot.edit.amount + Množství + + + + + Part-DB1\src\Form\Part\PartLotType.php:98 + Part-DB1\src\Form\Part\PartLotType.php:97 + + + part_lot.edit.instock_unknown + Množství neznámé + + + + + Part-DB1\src\Form\Part\PartLotType.php:109 + Part-DB1\src\Form\Part\PartLotType.php:108 + + + part_lot.edit.needs_refill + Potřebuje doplnit + + + + + Part-DB1\src\Form\Part\PartLotType.php:120 + Part-DB1\src\Form\Part\PartLotType.php:119 + + + part_lot.edit.expiration_date + Datum vypršení platnosti + + + + + Part-DB1\src\Form\Part\PartLotType.php:128 + Part-DB1\src\Form\Part\PartLotType.php:125 + + + part_lot.edit.comment + Poznámky + + + + + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + + + perm.group.other + Různé + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + + + tfa_google.enable + Povolit aplikaci Authenticator + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + + + tfa_google.disable + Deaktivovat aplikaci Authenticator + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + + + google_confirmation + Potvrzovací kód + + + + + Part-DB1\src\Form\UserSettingsType.php:108 + Part-DB1\src\Form\UserSettingsType.php:108 + src\Form\UserSettingsType.php:46 + + + user.timezone.label + Časové pásmo + + + + + Part-DB1\src\Form\UserSettingsType.php:133 + Part-DB1\src\Form\UserSettingsType.php:132 + + + user.currency.label + Preferovaná měna + + + + + Part-DB1\src\Form\UserSettingsType.php:140 + Part-DB1\src\Form\UserSettingsType.php:139 + src\Form\UserSettingsType.php:53 + + + save + Použít změny + + + + + Part-DB1\src\Form\UserSettingsType.php:141 + Part-DB1\src\Form\UserSettingsType.php:140 + src\Form\UserSettingsType.php:54 + + + reset + Zrušit změny + + + + + Part-DB1\src\Form\UserSettingsType.php:104 + Part-DB1\src\Form\UserSettingsType.php:104 + src\Form\UserSettingsType.php:45 + + + user_settings.language.placeholder + Jazyk serveru + + + + + Part-DB1\src\Form\UserSettingsType.php:115 + Part-DB1\src\Form\UserSettingsType.php:115 + src\Form\UserSettingsType.php:48 + + + user_settings.timezone.placeholder + Časové pásmo serveru + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + + + attachment.label + Příloha + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + + + attachment_type.label + Typ přílohy + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + + + project.label + Projekt + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + + + measurement_unit.label + Měrná jednotka + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + + + currency.label + Měna + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + + + orderdetail.label + Detail objednávky + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + + + pricedetail.label + Detail ceny + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + + + user.label + Uživatel + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 + + + parameter.label + Parametr + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 + + + label_profile.label + Profil štítku + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 + new + + + log.element_deleted.old_name.unknown + Neznámý + + + + + Part-DB1\src\Services\MarkdownParser.php:73 + Part-DB1\src\Services\MarkdownParser.php:73 + + + markdown.loading + Načítání markdown. Pokud tato zpráva nezmizí, zkuste stránku načíst znovu. + + + + + Part-DB1\src\Services\PasswordResetManager.php:98 + Part-DB1\src\Services\PasswordResetManager.php:98 + + + pw_reset.email.subject + Obnovení hesla k účtu Part-DB + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + + + tree.tools.tools + Nástroje + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 + src\Services\ToolsTreeBuilder.php:74 + + + tree.tools.edit + Upravit + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + src\Services\ToolsTreeBuilder.php:81 + + + tree.tools.show + Zobrazit + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + + + tree.tools.system + Systém + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 + + + tree.tools.tools.label_dialog + Generátor štítků + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 + + + tree.tools.tools.label_scanner + Čtečka štítků + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 + src\Services\ToolsTreeBuilder.php:62 + + + tree.tools.edit.attachment_types + Typy příloh + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 + src\Services\ToolsTreeBuilder.php:64 + + + tree.tools.edit.categories + Kategorie + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 + src\Services\ToolsTreeBuilder.php:66 + + + tree.tools.edit.projects + Projekty + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 + src\Services\ToolsTreeBuilder.php:68 + + + tree.tools.edit.suppliers + Dodavatelé + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 + src\Services\ToolsTreeBuilder.php:70 + + + tree.tools.edit.manufacturer + Výrobce + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 + + + tree.tools.edit.storelocation + Umístění + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 + + + tree.tools.edit.footprint + Otisky + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 + + + tree.tools.edit.currency + Měny + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 + + + tree.tools.edit.measurement_unit + Měrné jednotky + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.edit.label_profile + Profily štítků + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 + + + tree.tools.edit.part + Nový díl + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + src\Services\ToolsTreeBuilder.php:77 + + + tree.tools.show.all_parts + Zobrazit všechny díly + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.show.all_attachments + Přílohy + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 + new + + + tree.tools.show.statistics + Statistiky + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 + + + tree.tools.system.users + Uživatelé + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 + + + tree.tools.system.groups + Skupiny + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 + new + + + tree.tools.system.event_log + Protokol událostí + + + + + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + src\Services\TreeBuilder.php:124 + + + entity.tree.new + Nový prvek + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 + obsolete + + + attachment.external_file + Externí soubor + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + obsolete + + + attachment.edit + Upravit + + + + + Part-DB1\templates\_navbar.html.twig:27 + templates\base.html.twig:88 + obsolete + + + barcode.scan + Skenovat čárový kód + + + + + Part-DB1\src\Form\UserSettingsType.php:119 + src\Form\UserSettingsType.php:49 + obsolete + + + user.theme.label + Téma + + + + + Part-DB1\src\Form\UserSettingsType.php:129 + src\Form\UserSettingsType.php:50 + obsolete + + + user_settings.theme.placeholder + Serverové téma + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 + new + obsolete + + + log.user_login.ip + IP + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:169 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:207 + new + obsolete + + + log.undo_mode.undo + Změna zrušena + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:171 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:209 + new + obsolete + + + log.undo_mode.revert + Prvek vrácen + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 + new + obsolete + + + log.element_created.original_instock + Staré zásoby + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 + new + obsolete + + + log.element_deleted.old_name + Starý název + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 + new + obsolete + + + log.element_edited.changed_fields + Změněná pole + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 + new + obsolete + + + log.instock_changed.comment + Komentář + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 + new + obsolete + + + log.collection_deleted.deleted + Odstraněný prvek: + + + + + templates\base.html.twig:81 + obsolete + obsolete + + + go.exclamation + Jdi! + + + + + templates\base.html.twig:109 + obsolete + obsolete + + + language.english + English + + + + + templates\base.html.twig:112 + obsolete + obsolete + + + language.german + German + + + + + obsolete + obsolete + + + flash.password_change_needed + Nutná změna hesla! + + + + + obsolete + obsolete + + + attachment.table.type + Typ přílohy + + + + + obsolete + obsolete + + + attachment.table.element + Související prvek + + + + + obsolete + obsolete + + + attachment.edit.isPicture + Obrázek? + + + + + obsolete + obsolete + + + attachment.edit.is3DModel + 3D model? + + + + + obsolete + obsolete + + + attachment.edit.isBuiltin + Vestavěný? + + + + + obsolete + obsolete + + + category.edit.default_comment.placeholder + např. užitečné pro přepínání + + + + + obsolete + obsolete + + + tfa_backup.regenerate_codes + Generování nových záložních kódů + + + + + obsolete + obsolete + + + validator.noneofitschild.self + Prvek nemůže být svým vlastním rodičem. + + + + + obsolete + obsolete + + + validator.noneofitschild.children + Rodič nemůže být jedním ze svých potomků. + + + + + obsolete + obsolete + + + validator.part_lot.location_full.no_increasment + Místo je obsazeno. Množství nelze navýšit (nová hodnota musí být menší než {{ old_amount }}). + + + + + obsolete + obsolete + + + validator.part_lot.location_full + Úložiště bylo označeno jako plné, takže do něj nelze přidat nový díl. + + + + + obsolete + obsolete + + + validator.part_lot.only_existing + Úložiště bylo označeno jako "pouze existující", takže do něj nelze přidat novou část. + + + + + obsolete + obsolete + + + validator.part_lot.single_part + Úložiště bylo označeno jako "jeden díl", takže do něj nelze přidat nový díl. + + + + + obsolete + obsolete + + + m_status.active.help + Tento díl je v současné době a v dohledné budoucnosti ve výrobě. + + + + + obsolete + obsolete + + + m_status.announced.help + Díl byl oznámen, ale zatím není k dispozici. + + + + + obsolete + obsolete + + + m_status.discontinued.help + Tento díl se přestal vyrábět a již se nevyrábí. + + + + + obsolete + obsolete + + + m_status.eol.help + Výrobek dosáhl konce své životnosti a jeho výroba bude brzy ukončena. + + + + + obsolete + obsolete + + + m_status.nrfnd.help + Tento díl se v současné době vyrábí, ale pro nové konstrukce se nedoporučuje. + + + + + obsolete + obsolete + + + m_status.unknown.help + Stav výroby dílu není znám. + + + + + obsolete + obsolete + + + flash.success + Úspěšné + + + + + obsolete + obsolete + + + flash.error + Chyba + + + + + obsolete + obsolete + + + flash.warning + Upozornění + + + + + obsolete + obsolete + + + flash.notice + Oznámení + + + + + obsolete + obsolete + + + flash.info + Info + + + + + obsolete + obsolete + + + validator.noLockout + Oprávnění "změnit oprávnění" nemůžete sami odebrat, abyste se náhodou nezablokovali. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter + Povolené přípony souborů + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.help + Můžete zadat čárkou oddělený seznam přípon souborů nebo typů mime, které musí mít nahraný soubor přiřazený k tomuto typu přílohy. Chcete-li povolit všechny podporované obrazové soubory, můžete použít příkaz image/*. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.placeholder + např. .txt, application/pdf, image/* + + + + + src\Form\PartType.php:63 + obsolete + obsolete + + + part.name.placeholder + např. BC547 + + + + + obsolete + obsolete + + + entity.edit.not_selectable + Nelze vybrat + + + + + obsolete + obsolete + + + entity.edit.not_selectable.help + Pokud je tato možnost aktivována, nelze tento prvek přiřadit k vlastnosti dílu. Užitečné, pokud se tento prvek používá pouze pro seskupování. + + + + + obsolete + obsolete + + + bbcode.hint + Zde můžete použít kód BBC (např. [b]Bold[/b]). + + + + + obsolete + obsolete + + + entity.create + Vytvořit prvek + + + + + obsolete + obsolete + + + entity.edit.save + Uložit + + + + + obsolete + obsolete + + + category.edit.disable_footprints + Zakázat otisky + + + + + obsolete + obsolete + + + category.edit.disable_footprints.help + Pokud je tato možnost aktivována, je vlastnost otisku zakázána pro všechny díly s touto kategorií. + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers + Zakázat výrobce + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers.help + Pokud je tato možnost aktivována, je vlastnost výrobce zakázána pro všechny díly s touto kategorií. + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets + Zakázat automatické odkazy na katalogové listy + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets.help + Pokud je tato možnost aktivována, nevytvářejí se pro díly s touto kategorií žádné automatické odkazy na datové listy. + + + + + obsolete + obsolete + + + category.edit.disable_properties + Zakázat vlastnosti + + + + + obsolete + obsolete + + + category.edit.disable_properties.help + Pokud je tato možnost aktivována, vlastnosti dílu jsou pro díly s touto kategorií zakázány. + + + + + obsolete + obsolete + + + category.edit.partname_hint + Nápověda k názvu dílu + + + + + obsolete + obsolete + + + category.edit.partname_hint.placeholder + např. 100nF + + + + + obsolete + obsolete + + + category.edit.partname_regex + Filtr názvů + + + + + obsolete + obsolete + + + category.edit.default_description + Výchozí popis + + + + + obsolete + obsolete + + + category.edit.default_description.placeholder + Např. kondenzátor, 10 mm x 10 mm, SMD + + + + + obsolete + obsolete + + + category.edit.default_comment + Výchozí poznámky + + + + + obsolete + obsolete + + + company.edit.address + Addresa + + + + + obsolete + obsolete + + + company.edit.address.placeholder + např. Ulice 314 +Město + + + + + obsolete + obsolete + + + company.edit.phone_number + Telefonní číslo + + + + + obsolete + obsolete + + + company.edit.phone_number.placeholder + +420 123 456 789 + + + + + obsolete + obsolete + + + company.edit.fax_number + Číslo faxu + + + + + obsolete + obsolete + + + company.edit.email + e-mail + + + + + obsolete + obsolete + + + company.edit.email.placeholder + např. contact@foo.bar + + + + + obsolete + obsolete + + + company.edit.website + Webové stránky + + + + + obsolete + obsolete + + + company.edit.website.placeholder + https://www.foo.bar + + + + + obsolete + obsolete + + + company.edit.auto_product_url + Produkt URL + + + + + obsolete + obsolete + + + company.edit.auto_product_url.help + Toto pole slouží k určení odkazu na díl na stránce společnosti. %PARTNUMBER% bude nahrazeno číslem objednávky. + + + + + obsolete + obsolete + + + company.edit.auto_product_url.placeholder + https://foo.bar/product/%PARTNUMBER% + + + + + obsolete + obsolete + + + currency.edit.iso_code + Kód ISO + + + + + obsolete + obsolete + + + currency.edit.exchange_rate + Směnný kurz + + + + + obsolete + obsolete + + + footprint.edit.3d_model + 3D model + + + + + obsolete + obsolete + + + mass_creation.lines + Zadání + + + + + obsolete + obsolete + + + mass_creation.lines.placeholder + Element 1 + Element 1.1 + Element 1.1.1 + Element 1.2 +Element 2 +Element 3 + + + + + obsolete + obsolete + + + entity.mass_creation.btn + Vytvořit + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer + Je celé číslo + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer.help + Pokud je tato možnost aktivována, budou všechny hodnoty s touto jednotkou zaokrouhleny na celá čísla. + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix + Použití předpony SI + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix.help + Pokud je tato možnost aktivována, hodnoty se zobrazují s předponami SI (např. 1,2 kg místo 1200 g). + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol + Symbol jednotky + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol.placeholder + např. m + + + + + obsolete + obsolete + + + storelocation.edit.is_full.label + Úložiště plné + + + + + obsolete + obsolete + + + storelocation.edit.is_full.help + Pokud je tato možnost vybrána, není možné přidávat nové díly do tohoto umístění skladu ani zvyšovat množství stávajících dílů. + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.label + Omezení na stávající díly + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.help + Pokud je tato možnost aktivována, není možné přidávat nové díly do tohoto umístění, ale množství stávajících dílů lze navýšit. + + + + + obsolete + obsolete + + + storelocation.only_single_part.label + Pouze jeden díl + + + + + obsolete + obsolete + + + storelocation.only_single_part.help + Pokud je tato možnost aktivována, lze k tomuto úložišti přiřadit pouze jeden díl (s každým množstvím). Užitečné pro malé krabičky SMD nebo podavače. + + + + + obsolete + obsolete + + + storelocation.storage_type.label + Typ úložiště + + + + + obsolete + obsolete + + + storelocation.storage_type.help + Zde můžete vybrat měrnou jednotku, kterou musí díl mít, aby mohl být přiřazen k tomuto úložišti. + + + + + obsolete + obsolete + + + supplier.edit.default_currency + Výchozí měna + + + + + obsolete + obsolete + + + supplier.shipping_costs.label + Náklady na dopravu + + + + + obsolete + obsolete + + + user.username.placeholder + např. j.doe + + + + + obsolete + obsolete + + + user.firstName.placeholder + např. John + + + + + obsolete + obsolete + + + user.lastName.placeholder + např. Doe + + + + + obsolete + obsolete + + + user.email.placeholder + j.doe@ecorp.com + + + + + obsolete + obsolete + + + user.department.placeholder + např. Vývoj + + + + + obsolete + obsolete + + + user.settings.pw_new.label + Nové heslo + + + + + obsolete + obsolete + + + user.settings.pw_confirm.label + Potvrdit nové heslo + + + + + obsolete + obsolete + + + user.edit.needs_pw_change + Uživatel musí změnit heslo + + + + + obsolete + obsolete + + + user.edit.user_disabled + Uživatel zakázán (přihlášení není možné) + + + + + obsolete + obsolete + + + user.create + Vytvořit uživatele + + + + + obsolete + obsolete + + + user.edit.save + Uložit + + + + + obsolete + obsolete + + + entity.edit.reset + Zrušit změny + + + + + templates\Parts\show_part_info.html.twig:166 + obsolete + obsolete + + + part.withdraw.btn + Stáhnout + + + + + templates\Parts\show_part_info.html.twig:171 + obsolete + obsolete + + + part.withdraw.comment: + Komentář/účel + + + + + templates\Parts\show_part_info.html.twig:189 + obsolete + obsolete + + + part.add.caption + Přidání dílů + + + + + templates\Parts\show_part_info.html.twig:194 + obsolete + obsolete + + + part.add.btn + Přidat + + + + + templates\Parts\show_part_info.html.twig:199 + obsolete + obsolete + + + part.add.comment + Komentář/účel + + + + + templates\AdminPages\CompanyAdminBase.html.twig:15 + obsolete + obsolete + + + admin.comment + Poznámky + + + + + src\Form\PartType.php:83 + obsolete + obsolete + + + manufacturer_url.label + Odkaz na výrobce + + + + + src\Form\PartType.php:66 + obsolete + obsolete + + + part.description.placeholder + např. NPN 45V 0,1A 0,5W + + + + + src\Form\PartType.php:69 + obsolete + obsolete + + + part.instock.placeholder + např. 10 + + + + + src\Form\PartType.php:72 + obsolete + obsolete + + + part.mininstock.placeholder + např. 5 + + + + + obsolete + obsolete + + + part.order.price_per + Cena za + + + + + obsolete + obsolete + + + part.withdraw.caption + Odebrání dílů + + + + + obsolete + obsolete + + + datatable.datatable.lengthMenu + _MENU_ + + + + + obsolete + obsolete + + + perm.group.parts + Díly + + + + + obsolete + obsolete + + + perm.group.structures + Datové struktury + + + + + obsolete + obsolete + + + perm.group.system + Systém + + + + + obsolete + obsolete + + + perm.parts + Díly + + + + + obsolete + obsolete + + + perm.read + Zobrazit + + + + + obsolete + obsolete + + + perm.edit + Upravit + + + + + obsolete + obsolete + + + perm.create + Vytvořit + + + + + obsolete + obsolete + + + perm.part.move + Změnit kategorii + + + + + obsolete + obsolete + + + perm.delete + Smazat + + + + + obsolete + obsolete + + + perm.part.search + Hledat + + + + + obsolete + obsolete + + + perm.part.all_parts + Seznam všech dílů + + + + + obsolete + obsolete + + + perm.part.no_price_parts + Seznam dílů bez informací o ceně + + + + + obsolete + obsolete + + + perm.part.obsolete_parts + Seznam zastaralých dílů + + + + + obsolete + obsolete + + + perm.part.unknown_instock_parts + Zobrazit díly s neznámým skladem + + + + + obsolete + obsolete + + + perm.part.change_favorite + Změna stavu oblíbených položek + + + + + obsolete + obsolete + + + perm.part.show_favorite + Seznam oblíbených dílů + + + + + obsolete + obsolete + + + perm.part.show_last_edit_parts + Zobrazit naposledy upravené/přidané díly + + + + + obsolete + obsolete + + + perm.part.show_users + Zobrazit posledního upravujícího uživatele + + + + + obsolete + obsolete + + + perm.part.show_history + Zobrazit historii + + + + + obsolete + obsolete + + + perm.part.name + Název + + + + + obsolete + obsolete + + + perm.part.description + Popis + + + + + obsolete + obsolete + + + perm.part.instock + Skladem + + + + + obsolete + obsolete + + + perm.part.mininstock + Minimální stav zásob + + + + + obsolete + obsolete + + + perm.part.comment + Poznámky + + + + + obsolete + obsolete + + + perm.part.storelocation + Místo skladování + + + + + obsolete + obsolete + + + perm.part.manufacturer + Výrobce + + + + + obsolete + obsolete + + + perm.part.orderdetails + Informace o objednávce + + + + + obsolete + obsolete + + + perm.part.prices + Ceny + + + + + obsolete + obsolete + + + perm.part.attachments + Přílohy souborů + + + + + obsolete + obsolete + + + perm.part.order + Objednávky + + + + + obsolete + obsolete + + + perm.storelocations + Umístění + + + + + obsolete + obsolete + + + perm.move + Přesun + + + + + obsolete + obsolete + + + perm.list_parts + Seznam dílů + + + + + obsolete + obsolete + + + perm.part.footprints + Otisky + + + + + obsolete + obsolete + + + perm.part.categories + Kategorie + + + + + obsolete + obsolete + + + perm.part.supplier + Dodavatelé + + + + + obsolete + obsolete + + + perm.part.manufacturers + Výrobce + + + + + obsolete + obsolete + + + perm.projects + Projekty + + + + + obsolete + obsolete + + + perm.part.attachment_types + Typy příloh + + + + + obsolete + obsolete + + + perm.tools.import + Import + + + + + obsolete + obsolete + + + perm.tools.labels + Štítky + + + + + obsolete + obsolete + + + perm.tools.calculator + Kalkulačka odporu + + + + + obsolete + obsolete + + + perm.tools.footprints + Otisky + + + + + obsolete + obsolete + + + perm.tools.ic_logos + Loga IC + + + + + obsolete + obsolete + + + perm.tools.statistics + Statistiky + + + + + obsolete + obsolete + + + perm.edit_permissions + Oprávnění k úpravám + + + + + obsolete + obsolete + + + perm.users.edit_user_name + Upravit uživatelské jméno + + + + + obsolete + obsolete + + + perm.users.edit_change_group + Změna skupiny + + + + + obsolete + obsolete + + + perm.users.edit_infos + Upravit informace + + + + + obsolete + obsolete + + + perm.users.edit_permissions + Upravit oprávnění + + + + + obsolete + obsolete + + + perm.users.set_password + Nastavit heslo + + + + + obsolete + obsolete + + + perm.users.change_user_settings + Změna uživatelských nastavení + + + + + obsolete + obsolete + + + perm.database.see_status + Zobrazit stav + + + + + obsolete + obsolete + + + perm.database.update_db + Aktualizace databáze + + + + + obsolete + obsolete + + + perm.database.read_db_settings + Čtení parametrů databáze + + + + + obsolete + obsolete + + + perm.database.write_db_settings + Úprava parametrů databáze + + + + + obsolete + obsolete + + + perm.config.read_config + Čtení konfigurace + + + + + obsolete + obsolete + + + perm.config.edit_config + Úprava konfigurace + + + + + obsolete + obsolete + + + perm.config.server_info + Informace o serveru + + + + + obsolete + obsolete + + + perm.config.use_debug + Použití ladicích nástrojů + + + + + obsolete + obsolete + + + perm.show_logs + Zobrazit protokoly + + + + + obsolete + obsolete + + + perm.delete_logs + Odstranit protokoly + + + + + obsolete + obsolete + + + perm.self.edit_infos + Upravit informace + + + + + obsolete + obsolete + + + perm.self.edit_username + Upravit uživatelské jméno + + + + + obsolete + obsolete + + + perm.self.show_permissions + Zobrazit oprávnění + + + + + obsolete + obsolete + + + perm.self.show_logs + Zobrazit vlastní záznamy v protokolu + + + + + obsolete + obsolete + + + perm.self.create_labels + Vytvořit štítky + + + + + obsolete + obsolete + + + perm.self.edit_options + Upravit možnosti + + + + + obsolete + obsolete + + + perm.self.delete_profiles + Odstranit profily + + + + + obsolete + obsolete + + + perm.self.edit_profiles + Upravit profily + + + + + obsolete + obsolete + + + perm.part.tools + Nástroje + + + + + obsolete + obsolete + + + perm.groups + Skupiny + + + + + obsolete + obsolete + + + perm.users + Uživatelé + + + + + obsolete + obsolete + + + perm.database + Databáze + + + + + obsolete + obsolete + + + perm.config + Konfigurace + + + + + obsolete + obsolete + + + perm.system + Systém + + + + + obsolete + obsolete + + + perm.self + Vlastní uživatel + + + + + obsolete + obsolete + + + perm.labels + Štítky + + + + + obsolete + obsolete + + + perm.part.category + Kategorie + + + + + obsolete + obsolete + + + perm.part.minamount + Min. množství + + + + + obsolete + obsolete + + + perm.part.footprint + Otisk + + + + + obsolete + obsolete + + + perm.part.mpn + MPN + + + + + obsolete + obsolete + + + perm.part.status + Stav výroby + + + + + obsolete + obsolete + + + perm.part.tags + Štítky + + + + + obsolete + obsolete + + + perm.part.unit + Jednotka dílu + + + + + obsolete + obsolete + + + perm.part.mass + Hmotnost + + + + + obsolete + obsolete + + + perm.part.lots + Množství dílů + + + + + obsolete + obsolete + + + perm.show_users + Zobrazit posledního upravujícího uživatele + + + + + obsolete + obsolete + + + perm.currencies + Měny + + + + + obsolete + obsolete + + + perm.measurement_units + Měrná jednotka + + + + + obsolete + obsolete + + + user.settings.pw_old.label + Původní heslo + + + + + obsolete + obsolete + + + pw_reset.submit + Obnovit heslo + + + + + obsolete + obsolete + + + u2f_two_factor + Bezpečnostní klíč (U2F) + + + + + obsolete + obsolete + + + google + Google + + + + + tfa.provider.webauthn_two_factor_provider + Bezpečnostní klíč + + + + + obsolete + obsolete + + + tfa.provider.google + Authenticator app + + + + + obsolete + obsolete + + + Login successful + Přihlášení bylo úspěšné + + + + + obsolete + obsolete + + + log.type.exception + Neošetřená výjimka (zastaralé) + + + + + obsolete + obsolete + + + log.type.user_login + Přihlášení uživatele + + + + + obsolete + obsolete + + + log.type.user_logout + Odhlášení uživatele + + + + + obsolete + obsolete + + + log.type.unknown + Neznámý + + + + + obsolete + obsolete + + + log.type.element_created + Prvek vytvořen + + + + + obsolete + obsolete + + + log.type.element_edited + Prvek upraven + + + + + obsolete + obsolete + + + log.type.element_deleted + Prvek smazán + + + + + obsolete + obsolete + + + log.type.database_updated + Databáze aktualizována + + + + + obsolete + + + perm.revert_elements + Vrátit prvek + + + + + obsolete + + + perm.show_history + Zobrazit historii + + + + + obsolete + + + perm.tools.lastActivity + Zobrazit poslední aktivitu + + + + + obsolete + + + perm.tools.timeTravel + Zobrazit staré verze prvků (cestování v čase) + + + + + obsolete + + + tfa_u2f.key_added_successful + Bezpečnostní klíč byl úspěšně přidán. + + + + + obsolete + + + Username + Uživatelské jméno + + + + + obsolete + + + log.type.security.google_disabled + Aplikace Authenticator je vypnutá + + + + + obsolete + + + log.type.security.u2f_removed + Bezpečnostní klíč odstraněn + + + + + obsolete + + + log.type.security.u2f_added + Bezpečnostní klíč přidán + + + + + obsolete + + + log.type.security.backup_keys_reset + Přegenerování záložních klíčů + + + + + obsolete + + + log.type.security.google_enabled + Aplikace Authenticator je zapnutá + + + + + obsolete + + + log.type.security.password_changed + Heslo bylo změněno + + + + + obsolete + + + log.type.security.trusted_device_reset + Důvěryhodná zařízení resetována + + + + + obsolete + + + log.type.collection_element_deleted + Vymazaný prvek sbírky + + + + + obsolete + + + log.type.security.password_reset + Obnovení hesla + + + + + obsolete + + + log.type.security.2fa_admin_reset + Obnovení dvoufaktorového nastavení správcem + + + + + obsolete + + + log.type.user_not_allowed + Neoprávněný pokus o přístup + + + + + obsolete + + + log.database_updated.success + Úspěšné + + + + + obsolete + + + label_options.barcode_type.2D + 2D + + + + + obsolete + + + label_options.barcode_type.1D + 1D + + + + + obsolete + + + perm.part.parameters + Parametry + + + + + obsolete + + + perm.attachment_show_private + Zobrazit soukromé přílohy + + + + + obsolete + + + perm.tools.label_scanner + Čtečka štítků + + + + + obsolete + + + perm.self.read_profiles + Přečíst profily + + + + + obsolete + + + perm.self.create_profiles + Vytvořit profily + + + + + obsolete + + + perm.labels.use_twig + Použití režimu Twig + + + + + label_profile.showInDropdown + Zobrazit v rychlém výběru + + + + + group.edit.enforce_2fa + Vynucení dvoufaktorového ověřování (2FA) + + + + + group.edit.enforce_2fa.help + Pokud je tato možnost povolena, musí každý přímý člen této skupiny nakonfigurovat alespoň jeden druhý faktor pro ověření. Doporučeno pro skupiny správců s velkým počtem oprávnění. + + + + + selectpicker.empty + Nic není vybráno + + + + + selectpicker.nothing_selected + Nic není vybráno + + + + + entity.delete.must_not_contain_parts + Element "%PATH%" stále obsahuje díly! Abyste mohli tento prvek odstranit, musíte díly přesunout. + + + + + entity.delete.must_not_contain_attachments + Typ přílohy stále obsahuje přílohy. Změňte jejich typ, abyste mohli tento typ přílohy odstranit. + + + + + entity.delete.must_not_contain_prices + Měna stále obsahuje údaje o ceně. Abyste mohli tento prvek odstranit, musíte změnit jejich měnu. + + + + + entity.delete.must_not_contain_users + Uživatelé stále používají tuto skupinu! Změňte jejich skupinu, abyste ji mohli smazat. + + + + + part.table.edit + Upravit + + + + + part.table.edit.title + Upravit díl + + + + + part_list.action.action.title + Zvolte akci + + + + + part_list.action.action.group.favorite + Oblíbený stav + + + + + part_list.action.action.favorite + Oblíbený + + + + + part_list.action.action.unfavorite + Neoblíbený + + + + + part_list.action.action.group.change_field + Změnit pole + + + + + part_list.action.action.change_category + Změnit kategorii + + + + + part_list.action.action.change_footprint + Změnit otisk + + + + + part_list.action.action.change_manufacturer + Změnit výrobce + + + + + part_list.action.action.change_unit + Změna jednotky dílu + + + + + part_list.action.action.delete + Smazat + + + + + part_list.action.submit + Odeslat + + + + + part_list.action.part_count + %count% dílů vybráno! + + + + + company.edit.quick.website + Otevřít webové stránky + + + + + company.edit.quick.email + Odeslat e-mail + + + + + company.edit.quick.phone + Telefonní hovor + + + + + company.edit.quick.fax + Odeslat fax + + + + + company.fax_number.placeholder + např. +420 123 456 789 + + + + + part.edit.save_and_clone + Uložit a duplikovat + + + + + validator.file_ext_not_allowed + Přípona souboru není pro tento typ přílohy povolena. + + + + + tools.reel_calc.title + Kalkulačka SMD kotoučů + + + + + tools.reel_calc.inner_dia + Vnitřní průměr + + + + + tools.reel_calc.outer_dia + Vnější průměr + + + + + tools.reel_calc.tape_thick + Tloušťka pásky + + + + + tools.reel_calc.part_distance + Vzdálenost mezi díly + + + + + tools.reel_calc.update + Aktualizace + + + + + tools.reel_calc.parts_per_meter + Díly na metr + + + + + tools.reel_calc.result_length + Délka pásky + + + + + tools.reel_calc.result_amount + Přibližný počet dílů + + + + + tools.reel_calc.outer_greater_inner_error + Chyba: Vnější průměr musí být větší než vnitřní průměr! + + + + + tools.reel_calc.missing_values.error + Vyplňte všechny hodnoty! + + + + + tools.reel_calc.load_preset + Načíst předvolbu + + + + + tools.reel_calc.explanation + Tato kalkulačka vám umožní odhadnout, kolik dílů zbývá na kotouči SMD. Změřte zaznamenané rozměry na cívce (nebo použijte některou z přednastavených hodnot) a kliknutím na tlačítko "Aktualizovat" získáte výsledek. + + + + + perm.tools.reel_calculator + Kalkulačka SMD kotoučů + + + + + tree.tools.tools.reel_calculator + Kalkulačka SMD kotoučů + + + + + user.pw_change_needed.flash + Vaše heslo je třeba změnit! Nastavte prosím nové heslo. + + + + + tree.root_node.text + Kořenový uzel + + + + + part_list.action.select_null + Prázdný prvek + + + + + part_list.action.delete-title + Opravdu chcete tyto díly odstranit? + + + + + part_list.action.delete-message + Tyto díly a všechny související informace (např. přílohy, informace o ceně atd.) budou odstraněny. Toto nelze vrátit zpět! + + + + + part.table.actions.success + Akce byly úspěšně dokončeny. + + + + + attachment.edit.delete.confirm + Opravdu chcete tuto přílohu smazat? + + + + + filter.text_constraint.value.operator.EQ + Is + + + + + filter.text_constraint.value.operator.NEQ + Není + + + + + filter.text_constraint.value.operator.STARTS + Začíná na + + + + + filter.text_constraint.value.operator.CONTAINS + Obsahuje + + + + + filter.text_constraint.value.operator.ENDS + Končí + + + + + filter.text_constraint.value.operator.LIKE + LIKE vzor + + + + + filter.text_constraint.value.operator.REGEX + Regulární výraz + + + + + filter.number_constraint.value.operator.BETWEEN + Mezi + + + + + filter.number_constraint.AND + a + + + + + filter.entity_constraint.operator.EQ + Je (kromě podřízených) + + + + + filter.entity_constraint.operator.NEQ + Není (s výjimkou podřízených) + + + + + filter.entity_constraint.operator.INCLUDING_CHILDREN + Je (včetně podřízených) + + + + + filter.entity_constraint.operator.EXCLUDING_CHILDREN + Není (s výjimkou podřízených) + + + + + part.filter.dbId + Databáze ID + + + + + filter.tags_constraint.operator.ANY + Kterákoli ze značek + + + + + filter.tags_constraint.operator.ALL + Všechny značky + + + + + filter.tags_constraint.operator.NONE + Žádná ze značek + + + + + part.filter.lot_count + Počet inventářů + + + + + part.filter.attachments_count + Počet příloh + + + + + part.filter.orderdetails_count + Počet údajů k objednávce + + + + + part.filter.lotExpirationDate + Datum ukončení platnosti inventáře + + + + + part.filter.lotNeedsRefill + Jakýkoliv inventář potřebuje doplnit + + + + + part.filter.lotUnknwonAmount + Jakýkoliv inventář má neznámé množství + + + + + part.filter.attachmentName + Název přílohy + + + + + filter.choice_constraint.operator.ANY + Kterýkoli z + + + + + filter.choice_constraint.operator.NONE + Žádný z + + + + + part.filter.amount_sum + Celkové množství + + + + + filter.submit + Aktualizovat + + + + + filter.discard + Zrušit změny + + + + + filter.clear_filters + Vymazat všechny filtry + + + + + filter.title + Filtr + + + + + filter.parameter_value_constraint.operator.= + Typ. Hodnota = + + + + + filter.parameter_value_constraint.operator.!= + Typ. Hodnota != + + + + + filter.parameter_value_constraint.operator.< + Typ. Hodnota < + + + + + filter.parameter_value_constraint.operator.> + Typ. Hodnota > + + + + + filter.parameter_value_constraint.operator.<= + Typ. Hodnota <= + + + + + filter.parameter_value_constraint.operator.>= + Typ. Hodnota >= + + + + + filter.parameter_value_constraint.operator.BETWEEN + Typ. Hodnota je mezi + + + + + filter.parameter_value_constraint.operator.IN_RANGE + V rozsahu hodnot + + + + + filter.parameter_value_constraint.operator.NOT_IN_RANGE + Není v rozsahu hodnot + + + + + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE + Větší než rozsah hodnot + + + + + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE + Větší rovná se než rozsah hodnot + + + + + filter.parameter_value_constraint.operator.LESS_THAN_RANGE + Méně než rozsah hodnot + + + + + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE + Méně rovné než rozsah hodnot + + + + + filter.parameter_value_constraint.operator.RANGE_IN_RANGE + Rozsah je zcela v rozsahu hodnot + + + + + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE + Rozsah protíná rozsah hodnot + + + + + filter.text_constraint.value + Žádná nastavená hodnota + + + + + filter.number_constraint.value1 + Žádná nastavená hodnota + + + + + filter.number_constraint.value2 + Maximální hodnota + + + + + filter.datetime_constraint.value1 + Není nastaven žádný datum + + + + + filter.datetime_constraint.value2 + Maximální datum + + + + + filter.constraint.add + Přidat filtr + + + + + part.filter.parameters_count + Počet parametrů + + + + + part.filter.lotDescription + Popis inventáře + + + + + parts_list.search.searching_for + Hledání dílů pomocí klíčového slova <b>%keyword%</b> + + + + + parts_list.search_options.caption + Povolené možnosti hledání + + + + + attachment.table.element_type + Přidružený typ prvku + + + + + log.level.debug + Ladění + + + + + log.level.info + Info + + + + + log.level.notice + Oznámení + + + + + log.level.warning + Varování + + + + + log.level.error + Chyba + + + + + log.level.critical + Kritické + + + + + log.level.alert + Upozornění + + + + + log.level.emergency + Nouzové + + + + + log.type.security + Událost související s bezpečností + + + + + log.type.instock_changed + [LEGACY] Instock changed + + + + + log.target_id + ID cílového prvku + + + + + entity.info.parts_count_recursive + Počet dílů s tímto prvkem nebo jeho souvisejícími prvky + + + + + tools.server_infos.title + Informace o serveru + + + + + permission.preset.read_only + Pouze pro čtení + + + + + permission.preset.read_only.desc + Povolit pouze operace čtení dat + + + + + permission.preset.all_inherit + Převzít vše + + + + + permission.preset.all_inherit.desc + Nastavení všech oprávnění na Převzít + + + + + permission.preset.all_forbid + Zakázat všechny + + + + + permission.preset.all_forbid.desc + Nastavit všechna oprávnění na hodnotu Zakázat + + + + + permission.preset.all_allow + Povolit všechny + + + + + permission.preset.all_allow.desc + Nastavit všechny oprávnění na možnost povolit + + + + + perm.server_infos + Informace o serveru + + + + + permission.preset.editor + Editor + + + + + permission.preset.editor.desc + Umožňuje měnit díly a datové struktury + + + + + permission.preset.admin + Admin + + + + + permission.preset.admin.desc + Povolení administrativních úkonů + + + + + permission.preset.button + Použít předvolbu + + + + + perm.attachments.show_private + Zobrazit soukromé přílohy + + + + + perm.attachments.list_attachments + Zobrazit seznam všech příloh + + + + + user.edit.permission_success + Předvolba oprávnění byla úspěšně použita. Zkontrolujte, zda nová oprávnění vyhovují vašim potřebám. + + + + + perm.group.data + Data + + + + + part_list.action.action.group.needs_review + Potřeba revize + + + + + part_list.action.action.set_needs_review + Nastavit stav "Potřebuje kontrolu" + + + + + part_list.action.action.unset_needs_review + Zrušit stav "Potřebuje kontrolu" + + + + + part.edit.ipn + Interní číslo dílu (IPN) + + + + + part.ipn.not_defined + Není definováno + + + + + part.table.ipn + IPN + + + + + currency.edit.update_rate + Získat směnný kurz + + + + + currency.edit.exchange_rate_update.unsupported_currency + Měna není podporována zdrojem směnných kurzů. Zkontrolujte konfiguraci zdroje směnných kurzů. + + + + + currency.edit.exchange_rate_update.generic_error + Nelze načíst směnný kurz. Zkontrolujte konfiguraci zdroje směnných kurzů. + + + + + currency.edit.exchange_rate_updated.success + Úspěšně načtený směnný kurz. + + + + + project.bom.quantity + Množství BOM. + + + + + project.bom.mountnames + Názvy sestav + + + + + project.bom.name + Název + + + + + project.bom.comment + Poznámky + + + + + project.bom.part + Díl + + + + + project.bom.add_entry + Přidat položku + + + + + part_list.action.group.projects + Projekty + + + + + part_list.action.projects.add_to_project + Přidat díl do projektu + + + + + project.bom.delete.confirm + Opravdu chcete tuto položku BOM odstranit? + + + + + project.add_parts_to_project + Přidání dílů do BOM projektu + + + + + part.info.add_part_to_project + Přidat tento díl do projektu + + + + + project_bom_entry.label + Položka BOM + + + + + project.edit.status + Stav projektu + + + + + project.status.draft + Návrh + + + + + project.status.planning + Plánování + + + + + project.status.in_production + Ve výrobě + + + + + project.status.finished + Dokončeno + + + + + project.status.archived + Archivováno + + + + + part.new_build_part.error.build_part_already_exists + Projekt již má stavební díl! + + + + + project.edit.associated_build_part + Přidružené sestavy dílu + + + + + project.edit.associated_build_part.add + Přidat díl sestavy + + + + + project.edit.associated_build.hint + Tento díl představuje sestavu tohoto projektu, která jsou někde uložena. + + + + + part.info.projectBuildPart.hint + Tento díl představuje sestavy následujícího projektu a je s ním spojena. + + + + + part.is_build_part + Je součástí projektu + + + + + project.info.title + Informace o projektu + + + + + project.info.bom_entries_count + Položky BOM + + + + + project.info.sub_projects_count + Podprojekty + + + + + project.info.bom_add_parts + Přidat položku BOM + + + + + project.info.info.label + Info + + + + + project.info.sub_projects.label + Podprojekty + + + + + project.bom.price + Cena + + + + + part.info.withdraw_modal.title.withdraw + Odebrání dílů z inventáře + + + + + part.info.withdraw_modal.title.add + Přidání dílů do inventáře + + + + + part.info.withdraw_modal.title.move + Přesun dílů z inventáře do jiného + + + + + part.info.withdraw_modal.amount + Množství + + + + + part.info.withdraw_modal.move_to + Přesun do + + + + + part.info.withdraw_modal.comment + Komentář + + + + + part.info.withdraw_modal.comment.hint + Zde můžete zadat komentář, ve kterém popíšete, proč tuto operaci provádíte (např. k čemu díly potřebujete). Tato informace se uloží do protokolu. + + + + + modal.close + Zavřít + + + + + modal.submit + Odeslat + + + + + part.withdraw.success + Úspěšně přidané/přesunuté/odebrané díly. + + + + + perm.parts_stock + Zásoba dílů + + + + + perm.parts_stock.withdraw + Vyskladnění dílů ze skladu + + + + + perm.parts_stock.add + Přidání dílů na sklad + + + + + perm.parts_stock.move + Přesun dílů mezi inventáři + + + + + user.permissions_schema_updated + Schéma oprávnění vašeho uživatele bylo aktualizováno na nejnovější verzi. + + + + + log.type.part_stock_changed + Změna skladové zásoby dílu + + + + + log.part_stock_changed.withdraw + Odebrání zásob + + + + + log.part_stock_changed.add + Přidání zásob + + + + + log.part_stock_changed.move + Přesun zásob + + + + + log.part_stock_changed.comment + Komentář + + + + + log.part_stock_changed.change + Změna + + + + + log.part_stock_changed.move_target + Přesun cíle + + + + + tools.builtin_footprints_viewer.title + Vestavěná galerie obrázků + + + + + tools.builtin_footprints_viewer.hint + V této galerii jsou uvedeny všechny dostupné vestavěné obrázky otisků. Pokud je chcete použít v příloze, zadejte název (nebo klíčové slovo) do pole cesta k příloze a vyberte obrázek z rozbalovacího seznamu. + + + + + tools.ic_logos.title + Loga IC + + + + + part_list.action.group.labels + Štítky + + + + + part_list.action.projects.generate_label + Generování štítků (pro díly) + + + + + part_list.action.projects.generate_label_lot + Generování štítků (pro inventáře dílů) + + + + + part_list.action.generate_label.empty + Prázdný štítek + + + + + project.info.builds.label + Sestavit + + + + + project.builds.build_not_possible + Sestavení není možné: Díly nejsou skladem + + + + + project.builds.following_bom_entries_miss_instock + Následující díly nejsou dostatečně skladem, aby bylo možné tento projekt alespoň jednou sestavit: + + + + + project.builds.stocked + k dispozici + + + + + project.builds.needed + potřebné + + + + + project.builds.build_possible + Sestavení možné + + + + + project.builds.number_of_builds_possible + Máte dostatek zásob na sestavení <b>%max_builds%</b> sestavení tohoto projektu. + + + + + project.builds.check_project_status + Aktuální stav projektu je <b>"%project_status%"</b>. Měli byste zkontrolovat, zda chcete projekt s tímto stavem skutečně sestavit! + + + + + project.builds.following_bom_entries_miss_instock_n + Nemáte na skladě dostatek dílů pro sestavení tohoto projektu %number_of_builds% times. Následující díly chybí na skladě: + + + + + project.build.flash.invalid_input + Nelze sestavit projekt. Zkontrolujte zadání! + + + + + project.build.required_qty + Požadované množství + + + + + project.build.btn_build + Sestavit + + + + + project.build.help + Zvolte, z jakých zásob (a v jakém množství) mají být odebrány komponenty potřebné pro sestavení. Zaškrtněte políčko u každé položky BOM, když jste komponenty odebrali, nebo pomocí horního zaškrtávacího políčka zaškrtněte všechna políčka najednou. + + + + + project.build.buildsPartLot.new_lot + Vytvořit nový inventář + + + + + project.build.add_builds_to_builds_part + Přidání sestavení do části sestavení projektu + + + + + project.build.builds_part_lot + Cílový inventář + + + + + project.builds.number_of_builds + Množství sestavy + + + + + project.builds.no_stocked_builds + Počet skladovaných sestav + + + + + user.change_avatar.label + Změna profilového obrázku + + + + + user_settings.change_avatar.label + Změna profilového obrázku + + + + + user_settings.remove_avatar.label + Odstranit profilový obrázek + + + + + part.edit.name.category_hint + Nápověda z kategorie + + + + + category.edit.partname_regex.placeholder + např. "/Kondenzátor \d+ nF/i" + + + + + category.edit.partname_regex.help + Regulární výraz kompatibilní s PCRE, kterému musí název dílu odpovídat. + + + + + entity.select.add_hint + Použijte -> pro vytvoření vnořených struktur, např. "Node 1->Node 1.1". + + + + + entity.select.group.new_not_added_to_DB + Nový (zatím nebyl přidán do DB) + + + + + part.edit.save_and_new + Uložit a přidat nový prázdný díl + + + + + homepage.first_steps.title + První kroky + + + + + homepage.first_steps.introduction + Vaše databáze je stále prázdná. Možná byste si měli přečíst <a href="%url%">dokumentaci</a> nebo začít vytvářet následující datové struktury: + + + + + homepage.first_steps.create_part + Nebo můžete přímo <a href="%url%">přidat nový díl</a>. + + + + + homepage.first_steps.hide_hint + Toto pole se skryje, jakmile vytvoříte první díl. + + + + + homepage.forum.text + Pro dotazy týkající se Part-DB použijte <a href="%href%" class="link-external" target="_blank">diskusní fórum</a> + + + + + log.element_edited.changed_fields.category + Kategorie + + + + + log.element_edited.changed_fields.footprint + Otisk + + + + + log.element_edited.changed_fields.manufacturer + Výrobce + + + + + log.element_edited.changed_fields.value_typical + typ. hodnota + + + + + log.element_edited.changed_fields.pw_reset_expires + Obnovení hesla + + + + + log.element_edited.changed_fields.comment + Poznámky + + + + + log.element_edited.changed_fields.supplierpartnr + Číslo dílu dodavatele + + + + + log.element_edited.changed_fields.supplier_product_url + Odkaz na nabídku + + + + + log.element_edited.changed_fields.price + Cena + + + + + log.element_edited.changed_fields.min_discount_quantity + Minimální výše slevy + + + + + log.element_edited.changed_fields.original_filename + Původní název souboru + + + + + log.element_edited.changed_fields.path + Cesta k souboru + + + + + log.element_edited.changed_fields.description + Popis + + + + + log.element_edited.changed_fields.manufacturing_status + Stav výroby + + + + + log.element_edited.changed_fields.options.barcode_type + Typ čárového kódu + + + + + log.element_edited.changed_fields.status + Stav + + + + + log.element_edited.changed_fields.quantity + Množství BOM + + + + + log.element_edited.changed_fields.mountnames + Názvy sestav + + + + + log.element_edited.changed_fields.name + Název + + + + + log.element_edited.changed_fields.part + Díl + + + + + log.element_edited.changed_fields.price_currency + Měna ceny + + + + + log.element_edited.changed_fields.partname_hint + Nápověda k názvu dílu + + + + + log.element_edited.changed_fields.partname_regex + Filtr názvu + + + + + log.element_edited.changed_fields.disable_footprints + Zakázat otisky + + + + + log.element_edited.changed_fields.disable_manufacturers + Zakázat výrobce + + + + + log.element_edited.changed_fields.disable_autodatasheets + Zakázat automatické odkazy na katalogové listy + + + + + log.element_edited.changed_fields.disable_properties + Zakázat vlastnosti + + + + + log.element_edited.changed_fields.default_description + Výchozí popis + + + + + log.element_edited.changed_fields.default_comment + Výchozí poznámky + + + + + log.element_edited.changed_fields.filetype_filter + Povolené přípony souborů + + + + + log.element_edited.changed_fields.not_selectable + Nevybráno + + + + + log.element_edited.changed_fields.parent + Nadřazený prvek + + + + + log.element_edited.changed_fields.shipping_costs + Náklady na dopravu + + + + + log.element_edited.changed_fields.default_currency + Výchozí měna + + + + + log.element_edited.changed_fields.address + Adresa + + + + + log.element_edited.changed_fields.phone_number + Telefonní číslo + + + + + log.element_edited.changed_fields.fax_number + Číslo faxu + + + + + log.element_edited.changed_fields.email_address + e-mail + + + + + log.element_edited.changed_fields.website + Webové stránky + + + + + log.element_edited.changed_fields.auto_product_url + Produkt URL + + + + + log.element_edited.changed_fields.is_full + Umístění plné + + + + + log.element_edited.changed_fields.limit_to_existing_parts + Omezení na stávající díly + + + + + log.element_edited.changed_fields.only_single_part + Pouze jeden díl + + + + + log.element_edited.changed_fields.storage_type + Typ úložiště + + + + + log.element_edited.changed_fields.footprint_3d + 3D model + + + + + log.element_edited.changed_fields.master_picture_attachment + Náhled + + + + + log.element_edited.changed_fields.exchange_rate + Směnný kurz + + + + + log.element_edited.changed_fields.iso_code + Směnný kurz + + + + + log.element_edited.changed_fields.unit + Symbol jednotky + + + + + log.element_edited.changed_fields.is_integer + Je celé číslo + + + + + log.element_edited.changed_fields.use_si_prefix + Použít předponu SI + + + + + log.element_edited.changed_fields.options.width + Šířka + + + + + log.element_edited.changed_fields.options.height + Výška + + + + + log.element_edited.changed_fields.options.supported_element + Typ cíle + + + + + log.element_edited.changed_fields.options.additional_css + Další styly (CSS) + + + + + log.element_edited.changed_fields.options.lines + Obsah + + + + + log.element_edited.changed_fields.permissions.data + Oprávnění + + + + + log.element_edited.changed_fields.disabled + Zakázano + + + + + log.element_edited.changed_fields.theme + Téma + + + + + log.element_edited.changed_fields.timezone + Časové pásmo + + + + + log.element_edited.changed_fields.language + Jazyk + + + + + log.element_edited.changed_fields.email + e-mail + + + + + log.element_edited.changed_fields.department + Oddělení + + + + + log.element_edited.changed_fields.last_name + Příjmení + + + + + log.element_edited.changed_fields.first_name + Jméno + + + + + log.element_edited.changed_fields.group + Skupina + + + + + log.element_edited.changed_fields.currency + Preferovaná měna + + + + + log.element_edited.changed_fields.enforce2FA + Vynucení 2FA + + + + + log.element_edited.changed_fields.symbol + Symbol + + + + + log.element_edited.changed_fields.value_min + Min. hodnota + + + + + log.element_edited.changed_fields.value_max + Max. hodnota + + + + + log.element_edited.changed_fields.value_text + Hodnota textu + + + + + log.element_edited.changed_fields.show_in_table + Zobrazit v tabulce + + + + + log.element_edited.changed_fields.attachment_type + Zobrazit v tabulce + + + + + log.element_edited.changed_fields.needs_review + Potřeba revize + + + + + log.element_edited.changed_fields.tags + Štítky + + + + + log.element_edited.changed_fields.mass + Hmotnost + + + + + log.element_edited.changed_fields.ipn + IPN + + + + + log.element_edited.changed_fields.favorite + Oblíbené + + + + + log.element_edited.changed_fields.minamount + Minimální zásoba + + + + + log.element_edited.changed_fields.manufacturer_product_url + Odkaz na stránku produktu + + + + + log.element_edited.changed_fields.manufacturer_product_number + MPN + + + + + log.element_edited.changed_fields.partUnit + Měrná jednotka + + + + + log.element_edited.changed_fields.expiration_date + Datum vypršení platnosti + + + + + log.element_edited.changed_fields.amount + Množství + + + + + log.element_edited.changed_fields.storage_location + Umístění + + + + + attachment.max_file_size + Maximální velikost souboru + + + + + user.saml_user + SSO / SAML uživatel + + + + + user.saml_user.pw_change_hint + Uživatel používá jednotné přihlášení (SSO). Heslo a nastavení 2FA zde nelze změnit. Nakonfigurujte je raději u svého centrálního zdroje SSO! + + + + + login.sso_saml_login + Jednotné přihlášení (SSO) + + + + + login.local_login_hint + Níže uvedený formulář je určen pouze pro přihlášení pomocí místního uživatele. Pokud se chcete přihlásit prostřednictvím jednotného přihlášení, stiskněte tlačítko výše. + + + + + part_list.action.action.export + Export dílů + + + + + part_list.action.export_json + Export jako JSON + + + + + part_list.action.export_csv + Export jako CSV + + + + + part_list.action.export_yaml + Export jako YAML + + + + + part_list.action.export_xml + Export jako XML + + + + + parts.import.title + Import dílů + + + + + parts.import.errors.title + Porušení při dovozu + + + + + parts.import.flash.error + Chyby při importu. Příčinou jsou pravděpodobně některá neplatná data. + + + + + parts.import.format.auto + Automaticky (na základě přípony souboru) + + + + + parts.import.flash.error.unknown_format + Z daného souboru se nepodařilo určit formát! + + + + + parts.import.flash.error.invalid_file + Soubor je neplatný. Zkontrolujte, zda jste vybrali správný formát! + + + + + parts.import.part_category.label + Přepsání kategorie + + + + + parts.import.part_category.help + Pokud zde vyberete hodnotu, budou do této kategorie přiřazeny všechny importované díly. Bez ohledu na to, co bylo nastaveno v datech. + + + + + import.create_unknown_datastructures + Vytvořit neznámé datové struktury + + + + + import.create_unknown_datastructures.help + Pokud je tato možnost vybrána, budou automaticky vytvořeny datové struktury (jako jsou kategorie, otisky atd.), které v databázi ještě neexistují. Není-li tato možnost vybrána, budou použity pouze existující datové struktury, a pokud nebude nalezena žádná odpovídající datová struktura, nebude dílu přiřazeno nic. + + + + + import.path_delimiter + Oddělovač cesty + + + + + import.path_delimiter.help + Oddělovač používaný k označení různých úrovní v datových strukturách, jako je kategorie, otisk atd. + + + + + parts.import.help_documentation + Další informace o formátu souboru najdete v <a href="%link%">dokumentaci</a>. + + + + + parts.import.help + Pomocí tohoto nástroje můžete importovat díly z existujících souborů. Díly budou zapsány přímo do databáze, proto před nahráním souboru sem zkontrolujte, zda je jeho obsah správný. + + + + + parts.import.flash.success + Import dílu úspěšný! + + + + + parts.import.errors.imported_entities + Dovážené díly + + + + + perm.import + Import dat + + + + + parts.import.part_needs_review.label + Označit všechny importované díly jako "Potřeba zkontrolovat". + + + + + parts.import.part_needs_review.help + Pokud je tato možnost vybrána, budou všechny díly označeny jako "Potřeba revize" bez ohledu na to, co bylo nastaveno v údajích. + + + + + project.bom_import.flash.success + Import %count% položek BOM proběhl úspěšně. + + + + + project.bom_import.type + Typ + + + + + project.bom_import.type.kicad_pcbnew + KiCAD Pcbnew BOM (CSV soubor) + + + + + project.bom_import.clear_existing_bom + Vymazání stávajících položek BOM před importem + + + + + project.bom_import.clear_existing_bom.help + Výběrem této možnosti odstraníte všechny existující položky BOM v projektu a přepíšete je importovaným souborem BOM! + + + + + project.bom_import.flash.invalid_file + Soubor se nepodařilo importovat. Zkontrolujte, zda jste vybrali správný typ souboru. Chybové hlášení: Zprávy: %zprávy% + + + + + project.bom_import.flash.invalid_entries + Chyba ověření! Zkontrolujte prosím svá data! + + + + + project.import_bom + Import BOM do projektu + + + + + project.edit.bom.import_bom + Import BOM + + + + + measurement_unit.new + Nová měrná jednotka + + + + + measurement_unit.edit + Upravit měrnou jednotku + + + + + user.aboutMe.label + O mně + + + + + storelocation.owner.label + Majitel + + + + + storelocation.part_owner_must_match.label + Vlastník se musí shodovat s vlastníkem umístění + + + + + part_lot.owner + Majitel + + + + + part_lot.owner.help + Pouze vlastník může z tohoto skladu odebírat nebo přidávat díly. + + + + + log.element_edited.changed_fields.owner + Majitel + + + + + log.element_edited.changed_fields.instock_unknown + Množství neznámé + + + + + log.element_edited.changed_fields.needs_refill + Potřebné doplnění + + + + + part.withdraw.access_denied + Není povoleno provést požadovanou akci. Zkontrolujte prosím svá oprávnění a vlastníka souvisejících prvků. + + + + + part.info.amount.less_than_desired + Méně než je požadováno + + + + + log.cli_user + Uživatel CLI + + + + + log.element_edited.changed_fields.part_owner_must_match + Vlastník dílu se musí shodovat s vlastníkem umístění + + + + + part.filter.lessThanDesired + Méně než požadované množství (celkové množství < min. množství) + + + + + part.filter.lotOwner + Vlastník + + + + + user.show_email_on_profile.label + Zobrazit e-mail na veřejné stránce profilu + + + + + log.details.title + Podrobnosti záznamu + + + + + log.user_login.login_from_ip + Přihlášení z IP adresy + + + + + log.user_login.ip_anonymize_hint + Pokud poslední číslice IP adresy chybí, je povolen režim GDPR, ve kterém jsou IP adresy anynomizovány. + + + + + log.user_not_allowed.unauthorized_access_attempt_to + Neoprávněný pokus o přístup na stránku + + + + + log.user_not_allowed.hint + Žádost byla zablokována. Neměla by být vyžadována žádná akce. + + + + + log.no_comment + Bez komentáře + + + + + log.element_changed.field + Pole + + + + + log.element_changed.data_before + Údaje před změnou + + + + + error_table.error + Během vašeho požadavku došlo k chybě. + + + + + part.table.invalid_regex + Nesprávný regulární výraz (regex) + + + + + log.element_changed.data_after + Údaje po změně + + + + + log.element_changed.diff + Rozdíl + + + + + log.undo.undo.short + Zrušit + + + + + log.undo.revert.short + Návrat k tomuto časovému razítku + + + + + log.view_version + Zobrazit verzi + + + + + log.undo.undelete.short + Odstranit + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + Majitel + + + + + log.element_edited.changed_fields.parent_id + Nadřazený + + + + + log.details.delete_entry + Odstranit záznam protokolu + + + + + log.delete.message.title + Opravdu chcete odstranit záznam protokolu? + + + + + log.delete.message + Pokud se jedná o položku historie prvků, dojde k přerušení historie prvků! To může vést k neočekávaným výsledkům při použití funkce cestování v čase. + + + + + log.collection_deleted.on_collection + o sbírce + + + + + log.element_edited.changed_fields.attachments + Přílohy + + + + + tfa_u2f.add_key.registration_error + Při registraci bezpečnostního klíče došlo k chybě. Zkuste to znovu nebo použijte jiný bezpečnostní klíč! + + + + + log.target_type.none + Žádné + + + + + ui.darkmode.light + Světlý + + + + + ui.darkmode.dark + Tmavý + + + + + ui.darkmode.auto + Automaticky (podle nastavení systému) + + + + + label_generator.no_lines_given + Není uveden žádný textový obsah! Popisky zůstanou prázdné. + + + + + user.password_strength.very_weak + Velmi slabé + + + + + user.password_strength.weak + Slabé + + + + + user.password_strength.medium + Střední + + + + + user.password_strength.strong + Silné + + + + + user.password_strength.very_strong + Velmi silné + + + + + perm.users.impersonate + Vydávat se za jiné uživatele + + + + + user.impersonated_by.label + Vydává se za + + + + + user.stop_impersonation + Zastavit vydávání se za někoho jiného + + + + + user.impersonate.btn + Vydávat se za + + + + + user.impersonate.confirm.title + Opravdu se chcete vydávat za tohoto uživatele? + + + + + user.impersonate.confirm.message + Tato skutečnost bude zaznamenána. Měli byste to dělat pouze z dobrého důvodu. + +Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakázaným přístupem. Pokud se o to pokusíte, zobrazí se zpráva "Přístup odepřen". + + + + + log.type.security.user_impersonated + Vydávaný uživatel + + + + + info_providers.providers_list.title + Poskytovatelé informací + + + + + info_providers.providers_list.active + Aktivní + + + + + info_providers.providers_list.disabled + Zakázané + + + + + info_providers.capabilities.basic + Základní + + + + + info_providers.capabilities.footprint + Otisk + + + + + info_providers.capabilities.picture + Obrázek + + + + + info_providers.capabilities.datasheet + Datové listy + + + + + info_providers.capabilities.price + Ceny + + + + + part.info_provider_reference.badge + Zdroj informací použitý k vytvoření tohoto dílu. + + + + + part.info_provider_reference + Vytvořeno zdrojem informací + + + + + oauth_client.connect.btn + Připojení OAuth + + + + + info_providers.table.provider.label + Poskytovatel + + + + + info_providers.search.keyword + Klíčové slovo + + + + + info_providers.search.submit + Hledat + + + + + info_providers.search.providers.help + Vyberte zdroje, ve kterých se má vyhledávat. + + + + + info_providers.search.providers + Zdroje + + + + + info_providers.search.info_providers_list + Zobrazit všechny dostupné zdroje informací + + + + + info_providers.search.title + Vytvořit díly ze zdroje informací + + + + + oauth_client.flash.connection_successful + Úspěšné připojení k aplikaci OAuth! + + + + + perm.part.info_providers + Poskytovatelé informací + + + + + perm.part.info_providers.create_parts + Vytvořit díly ze zdroje informací + + + + + entity.edit.alternative_names.label + Alternativní názvy + + + + + entity.edit.alternative_names.help + Zde uvedené alternativní názvy se používají k vyhledání tohoto prvku na základě výsledků poskytovatelů informací. + + + + + info_providers.form.help_prefix + Zdroj + + + + + update_manager.new_version_available.title + K dispozici je nová verze + + + + + update_manager.new_version_available.text + K dispozici je nová verze Part-DB. Podívejte se na ni zde + + + + + update_manager.new_version_available.only_administrators_can_see + Tuto zprávu mohou vidět pouze správci. + + + + + perm.system.show_available_updates + Zobrazit dostupné aktualizace Part-DB + + + + + user.settings.api_tokens + API tokeny + + + + + user.settings.api_tokens.description + Pomocí tokenu API mohou jiné aplikace přistupovat k Part-DB s vašimi uživatelskými právy a provádět různé akce pomocí rozhraní Part-DB REST API. Pokud zde token API odstraníte, aplikace, která token používá, již nebude moci vaším jménem přistupovat k Part-DB. + + + + + api_tokens.name + Název + + + + + api_tokens.access_level + Úroveň přístupu + + + + + api_tokens.expiration_date + Datum vypršení platnosti + + + + + api_tokens.added_date + Přidáno v + + + + + api_tokens.last_time_used + Naposledy použité + + + + + datetime.never + Nikdy + + + + + api_token.valid + Platný + + + + + api_token.expired + Vypršela platnost + + + + + user.settings.show_api_documentation + Zobrazit dokumentaci API + + + + + api_token.create_new + Vytvořit nový token API + + + + + api_token.level.read_only + Pouze pro čtení + + + + + api_token.level.edit + Upravit + + + + + api_token.level.admin + Admin + + + + + api_token.level.full + Úplný + + + + + api_tokens.access_level.help + Můžete omezit, k čemu má token API přístup. Přístup je vždy omezen oprávněním uživatele. + + + + + api_tokens.expiration_date.help + Po tomto datu již není token použitelný. Pokud token nemá nikdy vypršet, ponechte prázdné pole. + + + + + api_tokens.your_token_is + Token API je + + + + + api_tokens.please_save_it + Prosím, uložte si ji. Nebudete ji moci znovu vidět! + + + + + api_tokens.create_new.back_to_user_settings + Zpět na uživatelská nastavení + + + + + project.build.dont_check_quantity + Nekontrolovat množství + + + + + project.build.dont_check_quantity.help + Pokud je tato možnost vybrána, použijí se zadaná stažená množství bez ohledu na to, zda je k sestavení projektu skutečně zapotřebí více nebo méně dílů. + + + + + part_list.action.invert_selection + Inverzní výběr + + + + + perm.api + API + + + + + perm.api.access_api + Přístup k API + + + + + perm.api.manage_tokens + Správa tokenů API + + + + + user.settings.api_tokens.delete.title + Opravdu chcete tento token API odstranit? + + + + + user.settings.api_tokens.delete + Smazat + + + + + user.settings.api_tokens.delete.message + Aplikace, která používá tento token API, již nebude mít přístup k Part-DB. Tuto akci nelze vzít zpět! + + + + + api_tokens.deleted + Token API byl úspěšně smazán! + + + + + user.settings.api_tokens.no_api_tokens_yet + Zatím nejsou nakonfigurovány žádné tokeny API. + + + + + api_token.ends_with + Končí + + + + + entity.select.creating_new_entities_not_allowed + Není dovoleno vytvářet nové entity tohoto typu! Vyberte si prosím již existující subjekt. + + + + + scan_dialog.mode + Typ čárového kódu + + + + + scan_dialog.mode.auto + Automatická detekce + + + + + scan_dialog.mode.ipn + Čárový kód IPN + + + + + scan_dialog.mode.internal + Čárový kód Part-DB + + + + + part_association.label + Spojení dílu + + + + + part.edit.tab.associations + Související díly + + + + + part_association.edit.other_part + Související díl + + + + + part_association.edit.type + Typ vztahu + + + + + part_association.edit.comment + Poznámky + + + + + part_association.edit.type.help + Zde můžete vybrat, jak vybraný díl souvisí s tímto dílem. + + + + + part_association.table.from_this_part + Přidružení tohoto dílu k ostatním + + + + + part_association.table.from + Z + + + + + part_association.table.type + Vztah + + + + + part_association.table.to + Do + + + + + part_association.type.compatible + Je kompatibilní s + + + + + part_association.table.to_this_part + Přidružení k tomuto dílu od ostatních + + + + + part_association.type.other + Ostatní (vlastní hodnota) + + + + + part_association.type.supersedes + Nahrazuje + + + + + part_association.edit.other_type + Vlastní typ + + + + + part_association.edit.delete.confirm + Opravdu chcete toto přidružení smazat? To nelze vrátit zpět. + + + + + part_lot.edit.advanced + Rozbalit pokročilé možnosti + + + + + part_lot.edit.vendor_barcode + Čárový kód dodavatele + + + + + part_lot.edit.vendor_barcode.help + Pokud již tento inventář má čárový kód (např. vložený prodejcem), můžete zde zadat jeho obsah, abyste jej mohli snadno naskenovat. + + + + + scan_dialog.mode.vendor + Čárový kód prodejce (nakonfigurovaný v inventáři) + + + + + project.bom.instockAmount + Množství zásob + + + + + collection_type.new_element.tooltip + Tento prvek byl nově vytvořen a dosud není uložen v databázi. + + + + + part.merge.title + Sloučit díl + + + + + part.merge.title.into + na + + + + + part.merge.confirm.title + Opravdu chcete sloučit <b>%other%</b> do <b>%target%</b>? + + + + + part.merge.confirm.message + <b>%other%</b> bude odstraněn a díl bude uložen se zobrazenými informacemi. + + + + + part.info.merge_modal.title + Sloučení dílů + + + + + part.info.merge_modal.other_part + Jiné díly + + + + + part.info.merge_modal.other_into_this + Sloučení jiný díl do tohoto (smazání jiného dílu, ponechání tohoto) + + + + + part.info.merge_modal.this_into_other + Sloučit tento díl do jiného (tento díl smazat, jiný ponechat) + + + + + part.info.merge_btn + Sloučit díl + + + + + part.update_part_from_info_provider.btn + Aktualizovat díl ze zdroje informací + + + + + info_providers.update_part.title + Aktualizace stávajícího dílu ze zdroje informací + + + + + part.merge.flash.please_review + Data zatím nebyla uložena. Zkontrolujte změny a kliknutím na tlačítko uložit nová data zachovejte. + + + + + user.edit.flash.permissions_fixed + Chyběla oprávnění vyžadovaná jinými oprávněními. To bylo opraveno. Zkontrolujte prosím, zda jsou oprávnění v souladu s vaším záměrem. + + + + + permission.legend.dependency_note + Vezměte prosím na vědomí, že některé operace povolení jsou na sobě závislé. Pokud se setkáte s varováním, že chybějící oprávnění byla opravena a oprávnění bylo znovu nastaveno na povolit, musíte nastavit i závislou operaci na zakázat. Závislé operace lze obvykle nalézt napravo od operace. + + + + + log.part_stock_changed.timestamp + Časové razítko + + + + + part.info.withdraw_modal.timestamp + Časové razítko akce + + + + + part.info.withdraw_modal.timestamp.hint + Toto pole umožňuje zadat skutečné datum, kdy byla skladová operace skutečně provedena, a ne pouze kdy byla zaznamenána. Tato hodnota je uložena v extra poli záznamu protokolu. + + + + + part.info.withdraw_modal.delete_lot_if_empty + Vymazat tento inventář, až se vyprázdní + + + + + info_providers.search.error.client_exception + Při komunikaci se zdrojem informací došlo k chybě. Zkontrolujte konfiguraci tohoto zdroje a pokud možno obnovte tokeny OAuth. + + + + + eda_info.reference_prefix.placeholder + např. R + + + + + eda_info.reference_prefix + Referenční předpona + + + + + eda_info.kicad_section.title + KiCad specifické nastavení + + + + + eda_info.value + Hodnota + + + + + eda_info.value.placeholder + např. 100n + + + + + eda_info.exclude_from_bom + Vyloučit díl z BOM + + + + + eda_info.exclude_from_board + Vyloučit díl z desky plošných spojů + + + + + eda_info.exclude_from_sim + Vyloučit díl ze simulace + + + + + eda_info.kicad_symbol + Symbol schématu KiCad + + + + + eda_info.kicad_symbol.placeholder + např. Transistor_BJT:BC547 + + + + + eda_info.kicad_footprint + KiCad otisk + + + + + eda_info.kicad_footprint.placeholder + např. Package_TO_SOT_THT:TO-92 + + + + + part.edit.tab.eda + Informace EDA + + + + + api.api_endpoints.title + Koncové body API + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + KiCad API root URL + + + + + eda_info.visibility + Viditelné + + + + + eda_info.visibility.help + Ve výchozím nastavení je viditelnost pro software EDA určena automaticky. Pomocí tohoto zaškrtávacího políčka můžete vynutit, aby byl díl viditelný nebo neviditelný. + + + + + part.withdraw.zero_amount + Pokusili jste se vybrat/přidat nulové množství! Nebyla provedena žádná akce. + + + + + login.flash.access_denied_please_login + Přístup odepřen! Pro pokračování se prosím přihlaste. + + + + + attachment.upload_multiple_files + Nahrát soubory + + + + + entity.mass_creation_flash + Úspěšně vytvořeno %COUNT% entit. + + + + diff --git a/translations/messages.da.xlf b/translations/messages.da.xlf new file mode 100644 index 00000000..d7258986 --- /dev/null +++ b/translations/messages.da.xlf @@ -0,0 +1,12200 @@ + + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + + + attachment_type.caption + Bilags datatyper + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 + new + + + attachment_type.edit + Ret bilags filtype + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 + new + + + attachment_type.new + Ny filtype + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + templates\AdminPages\CategoryAdmin.html.twig:4 + templates\base.html.twig:163 + templates\base.html.twig:170 + templates\base.html.twig:197 + templates\base.html.twig:225 + + + category.labelp + Kategorier + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:11 + templates\AdminPages\CategoryAdmin.html.twig:8 + + + admin.options + Optioner + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + templates\AdminPages\CategoryAdmin.html.twig:9 + + + admin.advanced + Advanceret + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 + new + + + category.edit + Ret kategori + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 + new + + + category.new + Ny kategori + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + + + currency.caption + Valuta + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + + + currency.iso_code.caption + ISO kode + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + + + currency.symbol.caption + Valutaenhed + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 + new + + + currency.edit + Ret valuta + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 + new + + + currency.new + Ny valuta + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + templates\AdminPages\DeviceAdmin.html.twig:4 + + + project.caption + Projekt + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 + new + + + project.edit + Ret projekt + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 + new + + + project.new + Nyt projekt + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:67 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + templates\AdminPages\EntityAdminBase.html.twig:9 + templates\base.html.twig:80 + templates\base.html.twig:179 + templates\base.html.twig:206 + templates\base.html.twig:237 + + + search.placeholder + Søg + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + templates\AdminPages\EntityAdminBase.html.twig:13 + templates\base.html.twig:166 + templates\base.html.twig:193 + templates\base.html.twig:221 + + + expandAll + Udfold alle + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + templates\AdminPages\EntityAdminBase.html.twig:17 + templates\base.html.twig:167 + templates\base.html.twig:194 + templates\base.html.twig:222 + + + reduceAll + Sammenfold alle + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + + + part.info.timetravel_hint + Det er hvordan delen fromstod før %timestamp%. <i>Venligst bemærk at dette er en eksperimentel funktion. Så derfor kan info være ukorrekt.</i> + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + templates\AdminPages\EntityAdminBase.html.twig:42 + + + standard.label + Egenskaber + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + templates\AdminPages\EntityAdminBase.html.twig:43 + + + infos.label + Info + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + new + + + history.label + Historik + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + templates\AdminPages\EntityAdminBase.html.twig:45 + + + export.label + Eksport + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + templates\AdminPages\EntityAdminBase.html.twig:47 + + + import_export.label + Import + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + + + mass_creation.label + Masseoprettelse + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + templates\AdminPages\EntityAdminBase.html.twig:59 + + + admin.common + Fælles + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + + + admin.attachments + Bilag + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 + + + admin.parameters + Parametre + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 + templates\AdminPages\EntityAdminBase.html.twig:142 + + + export_all.label + Eksportér alle elementer + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 + + + mass_creation.help + Hver linje fortolkes og oprettes som et navn til et nyt element. Ved at indrykke tekst kan du lave strukturer + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + templates\AdminPages\EntityAdminBase.html.twig:35 + + + edit.caption + Ret element "%name" + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + templates\AdminPages\EntityAdminBase.html.twig:37 + + + new.caption + Nyt element + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + templates\base.html.twig:172 + templates\base.html.twig:199 + templates\base.html.twig:227 + + + footprint.labelp + Footprint + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 + new + + + footprint.edit + Ret footprint + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 + new + + + footprint.new + Nyt footprint + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + + + group.edit.caption + Grupper + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + + + user.edit.permissions + Rettigheder + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 + new + + + group.edit + Ret gruppe + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 + new + + + group.new + Ny gruppe + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 + + + label_profile.caption + Labelprofiler + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 + + + label_profile.advanced + Advanceret + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 + + + label_profile.comment + Notater + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 + new + + + label_profile.edit + Ret labelprofil + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 + new + + + label_profile.new + Ny labelprofil + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + templates\AdminPages\ManufacturerAdmin.html.twig:4 + + + manufacturer.caption + Fabrikant + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 + new + + + manufacturer.edit + Ret fabrikanter + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 + new + + + manufacturer.new + Ny fabrikant + + + + + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + + + measurement_unit.caption + Måleenhed + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 + Part-DB1\templates\_sidebar.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:8 + templates\base.html.twig:171 + templates\base.html.twig:198 + templates\base.html.twig:226 + + + storelocation.labelp + Lagerlokationer + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 + new + + + storelocation.edit + Ret lagerlokation + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 + new + + + storelocation.new + Ny lagerlokation + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + templates\AdminPages\SupplierAdmin.html.twig:4 + + + supplier.caption + Leverandører + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 + new + + + supplier.edit + Ret leverandør + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 + new + + + supplier.new + Ny leverandør + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + + + user.edit.caption + Brugere + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + + + user.edit.configuration + Opsætning + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + + + user.edit.password + Password + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + + + user.edit.tfa.caption + To-faktor godkendelse + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + + + user.edit.tfa.google_active + Godkendelses-app aktiv + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + + + tfa_backup.remaining_tokens + Antal resterende backupkoder + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + + + tfa_backup.generation_date + Oprettelsesdato for backupkoder + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + + + user.edit.tfa.disabled + Metode ikke aktiveret + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + + + user.edit.tfa.u2f_keys_count + Aktive sikkerhedsnøgler + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_title + Ønsker du at fortsætte? + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_message + Dette vil deaktiver <b>alle aktive to-faktor godkendelses metoder af brugere</b> og slette <b>backupkoderne</b>! +<br> +Brugen skal sætte all to-faktor godkendelsesmetoder op igen og printe nye backupkoder! <br><br> +<b>Gør kun dette hvis du er helt sikker på identiten af brugeren (som søger hjælp), eller kan kontoen blive kompromiteret af en som ønsker at angrive systemet!</b> + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + + + user.edit.tfa.disable_tfa.btn + Deaktivér all to-faktor godkendelsesmetoder + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 + new + + + user.edit + Ret bruger + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 + new + + + user.new + Ny bruger + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:63 + + + attachment.delete + Slet + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:41 + Part-DB1\templates\Parts\edit\_attachments.html.twig:38 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:35 + Part-DB1\src\DataTables\AttachmentDataTable.php:159 + Part-DB1\templates\Parts\edit\_attachments.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:159 + + + attachment.external + Ekstern + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:49 + Part-DB1\templates\Parts\edit\_attachments.html.twig:47 + Part-DB1\templates\AdminPages\_attachments.html.twig:47 + Part-DB1\templates\Parts\edit\_attachments.html.twig:45 + + + attachment.preview.alt + Billede af bilag + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:52 + Part-DB1\templates\Parts\edit\_attachments.html.twig:50 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + Part-DB1\templates\AdminPages\_attachments.html.twig:50 + Part-DB1\templates\Parts\edit\_attachments.html.twig:48 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:45 + + + attachment.view + Vis + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:58 + Part-DB1\templates\Parts\edit\_attachments.html.twig:56 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:43 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + Part-DB1\templates\AdminPages\_attachments.html.twig:56 + Part-DB1\templates\Parts\edit\_attachments.html.twig:54 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + + + attachment.file_not_found + Fil ikke fundet + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:66 + Part-DB1\templates\Parts\edit\_attachments.html.twig:64 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:48 + Part-DB1\templates\Parts\edit\_attachments.html.twig:62 + + + attachment.secure + Privat bilag + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:79 + Part-DB1\templates\Parts\edit\_attachments.html.twig:77 + Part-DB1\templates\AdminPages\_attachments.html.twig:77 + Part-DB1\templates\Parts\edit\_attachments.html.twig:75 + + + attachment.create + Tilføj bilag + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:84 + Part-DB1\templates\Parts\edit\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + Part-DB1\templates\AdminPages\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_attachments.html.twig:80 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + + + part_lot.edit.delete.confirm + Ønsker du virkeligt at slette dette lager? Du kan ikke fortryde det senere! + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + templates\AdminPages\_delete_form.html.twig:2 + + + entity.delete.confirm_title + Ønsker du virkeligt at slette %name%? + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + templates\AdminPages\_delete_form.html.twig:3 + + + entity.delete.message + Dette kan ikke fortrydes! + +Underelementer vil blive flyttet opad. + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + templates\AdminPages\_delete_form.html.twig:9 + + + entity.delete + Slet element + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:45 + Part-DB1\src\Form\Part\PartBaseType.php:286 + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:43 + Part-DB1\src\Form\Part\PartBaseType.php:267 + new + + + edit.log_comment + Ret kommentar + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + templates\AdminPages\_delete_form.html.twig:12 + + + entity.delete.recursive + Slet rekursivt (alle underelementer) + + + + + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 + + + entity.duplicate + Kopier element + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + templates\AdminPages\_export_form.html.twig:4 + src\Form\ImportType.php:67 + + + export.format + Filformat + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + templates\AdminPages\_export_form.html.twig:16 + + + export.level + Detaljegrad + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + templates\AdminPages\_export_form.html.twig:19 + + + export.level.simple + Simpel + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + templates\AdminPages\_export_form.html.twig:20 + + + export.level.extended + Udvidet + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + templates\AdminPages\_export_form.html.twig:21 + + + export.level.full + Fuldstændig + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + templates\AdminPages\_export_form.html.twig:31 + + + export.include_children + medtag underelementer ved eksport + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + templates\AdminPages\_export_form.html.twig:39 + + + export.btn + Eksport + + + + + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + templates\AdminPages\EntityAdminBase.html.twig:94 + templates\Parts\edit_part_info.html.twig:12 + templates\Parts\show_part_info.html.twig:11 + + + id.label + ID + + + + + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:77 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:77 + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:59 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:60 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:53 + templates\AdminPages\EntityAdminBase.html.twig:101 + templates\Parts\show_part_info.html.twig:248 + + + createdAt + Oprettet på + + + + + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:73 + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:49 + templates\AdminPages\EntityAdminBase.html.twig:114 + templates\Parts\show_part_info.html.twig:263 + + + lastModified + Sidst rettet + + + + + Part-DB1\templates\AdminPages\_info.html.twig:38 + Part-DB1\templates\AdminPages\_info.html.twig:38 + + + entity.info.parts_count + antal dele med dette element + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:6 + Part-DB1\templates\helper.twig:125 + Part-DB1\templates\Parts\edit\_specifications.html.twig:6 + + + specifications.property + Parameter + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:7 + Part-DB1\templates\Parts\edit\_specifications.html.twig:7 + + + specifications.symbol + Symbol + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:8 + Part-DB1\templates\Parts\edit\_specifications.html.twig:8 + + + specifications.value_min + Min. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:9 + Part-DB1\templates\Parts\edit\_specifications.html.twig:9 + + + specifications.value_typ + Typ. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:10 + Part-DB1\templates\Parts\edit\_specifications.html.twig:10 + + + specifications.value_max + Max. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:11 + Part-DB1\templates\Parts\edit\_specifications.html.twig:11 + + + specifications.unit + Enhed + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:12 + Part-DB1\templates\Parts\edit\_specifications.html.twig:12 + + + specifications.text + Tekst + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:13 + Part-DB1\templates\Parts\edit\_specifications.html.twig:13 + + + specifications.group + Gruppe + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:26 + Part-DB1\templates\Parts\edit\_specifications.html.twig:26 + + + specification.create + Ny parameter + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:31 + Part-DB1\templates\Parts\edit\_specifications.html.twig:31 + + + parameter.delete.confirm + Ønsker du at slette denne parameter? + + + + + Part-DB1\templates\attachment_list.html.twig:3 + Part-DB1\templates\attachment_list.html.twig:3 + + + attachment.list.title + Bilagsliste + + + + + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + + + part_list.loading.caption + Henter + + + + + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + + + part_list.loading.message + Dette kan tage et øjeblik. Hvis denne meddelelse ikke forsvinder, prøv at genindlæse siden. + + + + + Part-DB1\templates\base.html.twig:68 + Part-DB1\templates\base.html.twig:68 + templates\base.html.twig:246 + + + vendor.base.javascript_hint + Sørg for at aktivere alle Javascriptfunktioner! + + + + + Part-DB1\templates\base.html.twig:73 + Part-DB1\templates\base.html.twig:73 + + + sidebar.big.toggle + Vis/skjul sidepanel + + + + + Part-DB1\templates\base.html.twig:95 + Part-DB1\templates\base.html.twig:95 + templates\base.html.twig:271 + + + loading.caption + Henter: + + + + + Part-DB1\templates\base.html.twig:96 + Part-DB1\templates\base.html.twig:96 + templates\base.html.twig:272 + + + loading.message + Dette kan taget noget tid. Hvis denne meddelelse bliver stående i lang tid, forsøg da at genindlæse siden. + + + + + Part-DB1\templates\base.html.twig:101 + Part-DB1\templates\base.html.twig:101 + templates\base.html.twig:277 + + + loading.bar + Henter... + + + + + Part-DB1\templates\base.html.twig:112 + Part-DB1\templates\base.html.twig:112 + templates\base.html.twig:288 + + + back_to_top + Tilbage til toppen af siden + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:35 + Part-DB1\templates\Form\permissionLayout.html.twig:35 + + + permission.edit.permission + Rettigheder + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:36 + Part-DB1\templates\Form\permissionLayout.html.twig:36 + + + permission.edit.value + Værdi + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:53 + Part-DB1\templates\Form\permissionLayout.html.twig:53 + + + permission.legend.title + Forklaring til tilstande + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:57 + Part-DB1\templates\Form\permissionLayout.html.twig:57 + + + permission.legend.disallow + Ej tilladt + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:61 + Part-DB1\templates\Form\permissionLayout.html.twig:61 + + + permission.legend.allow + Tilladt + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:65 + Part-DB1\templates\Form\permissionLayout.html.twig:65 + + + permission.legend.inherit + Nedarv fra (overordnet) gruppe + + + + + Part-DB1\templates\helper.twig:3 + Part-DB1\templates\helper.twig:3 + + + bool.true + Sand + + + + + Part-DB1\templates\helper.twig:5 + Part-DB1\templates\helper.twig:5 + + + bool.false + Falsk + + + + + Part-DB1\templates\helper.twig:92 + Part-DB1\templates\helper.twig:87 + + + Yes + Ja + + + + + Part-DB1\templates\helper.twig:94 + Part-DB1\templates\helper.twig:89 + + + No + Nej + + + + + Part-DB1\templates\helper.twig:126 + + + specifications.value + Værdi + + + + + Part-DB1\templates\homepage.html.twig:7 + Part-DB1\templates\homepage.html.twig:7 + templates\homepage.html.twig:7 + + + version.caption + Version + + + + + Part-DB1\templates\homepage.html.twig:22 + Part-DB1\templates\homepage.html.twig:22 + templates\homepage.html.twig:19 + + + homepage.license + Licensinformation + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.caption + Projektoversigt + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.text + Kilde, downloads, fejlrapporter, to-do-list etc. kan findes på <a href="%href%" class="link-external" target="_blank">GitHub project page</a> + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.caption + Hjælp + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.text + Hjælp og tips kan findes på Wiki <a href="%href%" class="link-external" target="_blank">GitHub siden</a> + + + + + Part-DB1\templates\homepage.html.twig:33 + Part-DB1\templates\homepage.html.twig:33 + templates\homepage.html.twig:30 + + + homepage.forum.caption + Forum + + + + + Part-DB1\templates\homepage.html.twig:45 + Part-DB1\templates\homepage.html.twig:45 + new + + + homepage.last_activity + Sidste aktivitet + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:3 + Part-DB1\templates\LabelSystem\dialog.html.twig:6 + + + label_generator.title + Labelgenerator + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:16 + + + label_generator.common + Fælles + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:20 + + + label_generator.advanced + Avanceret + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:24 + + + label_generator.profiles + Profiler + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:58 + + + label_generator.selected_profile + Valgte profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:62 + + + label_generator.edit_profile + Ret profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:75 + + + label_generator.load_profile + Hent profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:102 + + + label_generator.download + Hent + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 + + + label_generator.label_btn + Opret label + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 + + + label_generator.label_empty + Ny tom label + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 + + + label_scanner.title + Label scanner + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.title + Intet webcam fundet + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.text + Du skal bruge et webcam og give lov til at bruge det som scanner. Du kan indtaste stregkoden manuelt nedenfor. + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 + + + label_scanner.source_select + Vælg kilde + + + + + Part-DB1\templates\LogSystem\log_list.html.twig:3 + Part-DB1\templates\LogSystem\log_list.html.twig:3 + + + log.list.title + Systemlog + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + new + + + log.undo.confirm_title + Er du sikker på at du vil fortryde ændringerne / gå tilbage til forrige version? + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + new + + + log.undo.confirm_message + Er du sikker på at du vil fortryde ændringen / og gå tilbage til en tidligere version? + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.email_sent_by + Denne e-mail er afsendt automatisk af + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.dont_reply + Venligt undlad at svare på denne e-mail. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:6 + Part-DB1\templates\mail\pw_reset.html.twig:6 + + + email.hi %name% + Hej %name% + + + + + Part-DB1\templates\mail\pw_reset.html.twig:7 + Part-DB1\templates\mail\pw_reset.html.twig:7 + + + email.pw_reset.message + En eller anden (forhåbentlig dig) har anmodet om at nulstille det gemte password. Hvis du ikke har anmodet om dette, venligst ignorér denne e-mail. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:9 + Part-DB1\templates\mail\pw_reset.html.twig:9 + + + email.pw_reset.button + Klik her for at nulstille password + + + + + Part-DB1\templates\mail\pw_reset.html.twig:11 + Part-DB1\templates\mail\pw_reset.html.twig:11 + + + email.pw_reset.fallback + Hvis dette ikke virker, gå til <a href="%url%">%url%</a> og indtast følgende information + + + + + Part-DB1\templates\mail\pw_reset.html.twig:16 + Part-DB1\templates\mail\pw_reset.html.twig:16 + + + email.pw_reset.username + Brugernavn + + + + + Part-DB1\templates\mail\pw_reset.html.twig:19 + Part-DB1\templates\mail\pw_reset.html.twig:19 + + + email.pw_reset.token + Token + + + + + Part-DB1\templates\mail\pw_reset.html.twig:24 + Part-DB1\templates\mail\pw_reset.html.twig:24 + + + email.pw_reset.valid_unit %date% + Nulstillingstoken'en vil være gyldig indtil <i>%date%</i>. + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:78 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + + + orderdetail.delete + Slet + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + + + pricedetails.edit.min_qty + Minimum rabat-antal + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + + + pricedetails.edit.price + Pris + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + + + pricedetails.edit.price_qty + for mængde + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + + + pricedetail.create + Anfør pris + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + templates\Parts\edit_part_info.html.twig:4 + + + part.edit.title + Rediger komponent %name% + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + templates\Parts\edit_part_info.html.twig:9 + + + part.edit.card_title + Rediger del + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + + + part.edit.tab.common + Fælles + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + + + part.edit.tab.manufacturer + Fabrikant + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + + + part.edit.tab.advanced + Advanceret + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + + + part.edit.tab.part_lots + Lagerbestand + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + + + part.edit.tab.attachments + Vedhæftede filer + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + + + part.edit.tab.orderdetails + indkøbsinformation + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.specifications + Paremetre + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.comment + Noter + + + + + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + templates\Parts\new_part.html.twig:8 + + + part.new.card_title + Opret ny del + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + + + part_lot.delete + Slet + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + + + part_lot.create + Opret beholdning + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + + + orderdetail.create + tilføj distributør + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + + + pricedetails.edit.delete.confirm + Er du sikker på, at du vil slette denne pris? Dette kan ikke fortrydes! + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 + + + orderdetails.edit.delete.confirm + Er du sikker på, at du vil slette denne leverandør? Dette kan ikke fortrydes! + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + templates\Parts\show_part_info.html.twig:4 + templates\Parts\show_part_info.html.twig:9 + + + part.info.title + Detaljerede oplysninger vedr. + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + + + part.part_lots.label + Lagerbestand + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 + Part-DB1\templates\Parts\lists\_info_card.html.twig:43 + Part-DB1\templates\_navbar_search.html.twig:31 + Part-DB1\templates\_navbar_search.html.twig:26 + templates\base.html.twig:62 + templates\Parts\show_part_info.html.twig:74 + src\Form\PartType.php:86 + + + comment.label + Noter + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + + + part.info.specifications + Paremeter + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + templates\Parts\show_part_info.html.twig:82 + + + attachment.labelp + Vedhæftede filer + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 + Part-DB1\templates\Parts\info\show_part_info.html.twig:71 + templates\Parts\show_part_info.html.twig:88 + + + vendor.partinfo.shopping_infos + Indkøbsinformation + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 + Part-DB1\templates\Parts\info\show_part_info.html.twig:78 + templates\Parts\show_part_info.html.twig:94 + + + vendor.partinfo.history + Historik + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + Part-DB1\templates\Parts\info\show_part_info.html.twig:84 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + templates\base.html.twig:176 + templates\base.html.twig:203 + templates\base.html.twig:217 + templates\base.html.twig:231 + templates\Parts\show_part_info.html.twig:100 + + + tools.label + Værktøjer + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 + Part-DB1\templates\Parts\info\show_part_info.html.twig:90 + + + extended_info.label + Yderligere Informationen + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + + + attachment.name + Navn + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + + + attachment.attachment_type + Vedhæft sikkerhedstype + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + + + attachment.file_name + vedhæft fil_navn + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + + + attachment.file_size + vedhæftet fil_størrelse + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 + + + attachment.preview + Forhåndsvisningbillede + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 + + + attachment.download + Vedhæftet fil + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + new + + + user.creating_user + Hvem oprettede denne del + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + + + Unknown + Ukendt + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + new + + + accessDenied + Adgang nægtet + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + new + + + user.last_editing_user + Bruger som rettede denne del + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + + + part.isFavorite + Favorit + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + + + part.minOrderAmount + Minimum ordrestrørrelse + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:46 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:41 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + templates\base.html.twig:70 + templates\Parts\show_part_info.html.twig:24 + src\Form\PartType.php:80 + + + manufacturer.label + Fabrikant + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 + Part-DB1\templates\_navbar_search.html.twig:11 + templates\base.html.twig:54 + src\Form\PartType.php:62 + + + name.label + Navn + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + new + + + part.back_to_info + Tilbage til forrige version + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:19 + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:18 + templates\base.html.twig:58 + templates\Parts\show_part_info.html.twig:31 + src\Form\PartType.php:65 + + + description.label + Beskrivelse + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:15 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:14 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + templates\base.html.twig:56 + templates\Parts\show_part_info.html.twig:32 + src\Form\PartType.php:74 + + + category.label + Kategori + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + templates\Parts\show_part_info.html.twig:42 + src\Form\PartType.php:69 + + + instock.label + På lager + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + templates\Parts\show_part_info.html.twig:44 + src\Form\PartType.php:72 + + + mininstock.label + Minimumbestand + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:52 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:47 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + templates\base.html.twig:73 + templates\Parts\show_part_info.html.twig:47 + + + footprint.label + Footprint + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 + Part-DB1\templates\Parts\info\_main_infos.html.twig:59 + Part-DB1\templates\Parts\info\_main_infos.html.twig:57 + Part-DB1\templates\Parts\info\_main_infos.html.twig:60 + templates\Parts\show_part_info.html.twig:51 + + + part.avg_price.label + Gennemsnitspris + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + + + part.supplier.name + Navn + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + + + part.supplier.partnr + Bestillingsnummer. + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + + + part.order.minamount + Mindsteantal + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + + + part.order.price + Pris + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + + + part.order.single_price + Enhedspris + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + + + edit.caption_short + Ret + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + + + delete.caption + Slet + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + Part-DB1\templates\Parts\info\_part_lots.html.twig:6 + + + part_lots.description + Beskrivelse + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + + + part_lots.storage_location + Lagerlokation + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + + + part_lots.amount + Mængde + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 + Part-DB1\templates\Parts\info\_part_lots.html.twig:22 + + + part_lots.location_unknown + Ukendt lagerlokation + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 + Part-DB1\templates\Parts\info\_part_lots.html.twig:29 + + + part_lots.instock_unknown + Ukendt mængde + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 + Part-DB1\templates\Parts\info\_part_lots.html.twig:38 + + + part_lots.expiration_date + Udløbsdato + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 + Part-DB1\templates\Parts\info\_part_lots.html.twig:46 + + + part_lots.is_expired + Udløbet + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 + Part-DB1\templates\Parts\info\_part_lots.html.twig:53 + + + part_lots.need_refill + Skal fyldes op + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:15 + Part-DB1\templates\Parts\info\_picture.html.twig:15 + + + part.info.prev_picture + Forrige billede + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:19 + Part-DB1\templates\Parts\info\_picture.html.twig:19 + + + part.info.next_picture + Næste billede + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + + + part.mass.tooltip + Vægt + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + + + part.needs_review.badge + Gennemgang nødvendig + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + + + part.favorite.badge + Favorit + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + + + part.obsolete.badge + Ikke længere tilgængelig + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:10 + + + parameters.extracted_from_description + Automatisk udtrukket fra beskrivelse + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:15 + + + parameters.auto_extracted_from_comment + Automatisk udtrukket fra noter + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:6 + Part-DB1\templates\Parts\info\_tools.html.twig:4 + templates\Parts\show_part_info.html.twig:125 + + + part.edit.btn + Rediger komponent + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:14 + templates\Parts\show_part_info.html.twig:135 + + + part.clone.btn + Kopier komponent + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:24 + Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 + templates\Parts\show_part_info.html.twig:143 + + + part.create.btn + Opret ny komponent + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:31 + Part-DB1\templates\Parts\info\_tools.html.twig:29 + + + part.delete.confirm_title + Vil du virkelig slette denne komponent + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:32 + Part-DB1\templates\Parts\info\_tools.html.twig:30 + + + part.delete.message + Komponenten og alle dens relaterede oplysninger (bilag, priser osv. ) slettes. Dette kan ikke fortrydes! + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:39 + Part-DB1\templates\Parts\info\_tools.html.twig:37 + + + part.delete + Slet komponent + + + + + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + + + parts_list.all.title + Alle komponenter + + + + + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + + + parts_list.category.title + Komponent med kategori + + + + + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + + + parts_list.footprint.title + Komponent med footprint + + + + + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + + + parts_list.manufacturer.title + Komponenter med fabrikanter + + + + + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + + + parts_list.search.title + Søg komponenter + + + + + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + + + parts_list.storelocation.title + Komponenter med lagerlokationer + + + + + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + + + parts_list.supplier.title + Komponenter med leverandører + + + + + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + + + parts_list.tags.title + Komponenter med tag + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 + Part-DB1\templates\Parts\lists\_info_card.html.twig:17 + + + entity.info.common.tab + Fælles + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 + Part-DB1\templates\Parts\lists\_info_card.html.twig:20 + + + entity.info.statistics.tab + Statistik + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 + + + entity.info.attachments.tab + Vedhæftede filer + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 + + + entity.info.parameters.tab + Parametre + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 + Part-DB1\templates\Parts\lists\_info_card.html.twig:30 + + + entity.info.name + Navn + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 + Part-DB1\templates\Parts\lists\_info_card.html.twig:96 + Part-DB1\templates\Parts\lists\_info_card.html.twig:34 + Part-DB1\templates\Parts\lists\_info_card.html.twig:67 + + + entity.info.parent + Overordnet element + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 + Part-DB1\templates\Parts\lists\_info_card.html.twig:46 + + + entity.edit.btn + Redigere + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 + Part-DB1\templates\Parts\lists\_info_card.html.twig:63 + + + entity.info.children_count + Antal af underelementer + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + + + tfa.check.title + To-faktor godkendelse påkrævet + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:39 + Part-DB1\templates\security\2fa_base_form.html.twig:39 + + + tfa.code.trusted_pc + Dette er en pålidelig computer (hvis dette er aktiveret, udføres der ikke yderligere to-faktorforespørgsler på denne computer) + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + + + login.btn + Login + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:42 + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:40 + + + user.logout + Log ud + + + + + Part-DB1\templates\security\2fa_form.html.twig:6 + Part-DB1\templates\security\2fa_form.html.twig:6 + + + tfa.check.code.label + Godkendelses app kode + + + + + Part-DB1\templates\security\2fa_form.html.twig:10 + Part-DB1\templates\security\2fa_form.html.twig:10 + + + tfa.check.code.help + Indtast den 6-cifrede kode fra din godkendelsesapp her eller en af dine backupkoder, hvis godkendelses app'en ikke er tilgændelig. + + + + + Part-DB1\templates\security\login.html.twig:3 + Part-DB1\templates\security\login.html.twig:3 + templates\security\login.html.twig:3 + + + login.title + Login + + + + + Part-DB1\templates\security\login.html.twig:7 + Part-DB1\templates\security\login.html.twig:7 + templates\security\login.html.twig:7 + + + login.card_title + Login + + + + + Part-DB1\templates\security\login.html.twig:31 + Part-DB1\templates\security\login.html.twig:31 + templates\security\login.html.twig:31 + + + login.username.label + Brugernavn + + + + + Part-DB1\templates\security\login.html.twig:34 + Part-DB1\templates\security\login.html.twig:34 + templates\security\login.html.twig:34 + + + login.username.placeholder + Brugernavn + + + + + Part-DB1\templates\security\login.html.twig:38 + Part-DB1\templates\security\login.html.twig:38 + templates\security\login.html.twig:38 + + + login.password.label + Password + + + + + Part-DB1\templates\security\login.html.twig:40 + Part-DB1\templates\security\login.html.twig:40 + templates\security\login.html.twig:40 + + + login.password.placeholder + Password + + + + + Part-DB1\templates\security\login.html.twig:50 + Part-DB1\templates\security\login.html.twig:50 + templates\security\login.html.twig:50 + + + login.rememberme + Forbliv logget ind (anbefales ikke på delte computere) + + + + + Part-DB1\templates\security\login.html.twig:64 + Part-DB1\templates\security\login.html.twig:64 + + + pw_reset.password_forget + Glemt brugernavn/password? + + + + + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + + + pw_reset.new_pw.header.title + Indstil ny adgangskode + + + + + Part-DB1\templates\security\pw_reset_request.html.twig:5 + Part-DB1\templates\security\pw_reset_request.html.twig:5 + + + pw_reset.request.header.title + Anmod om nyt password + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + + + tfa_u2f.http_warning + Du tilgår denne side ved hjælp af den usikre HTTP-metode, så U2F vil højst sandsynligt ikke fungere (Bad Request-fejlmeddelelse). Bed en administrator om at konfigurere den sikre HTTPS-metode, hvis du vil bruge sikkerhedsnøgler. + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + + + r_u2f_two_factor.pressbutton + Indsæt venligst sikkerhedsnøglen og tryk på knappen + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + + + tfa_u2f.add_key.title + Tilføj sikkerhedsnøgle + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + + + tfa_u2f.explanation + Brug af en U2F/FIDO-kompatibel sikkerhedsnøgle (f.eks. YubiKey eller NitroKey) kan øge brugervenligheden og sikre en sikker to-faktor-godkendelse. Sikkerhedsnøglerne kan registreres her. Hvis to-faktor verifikation er påkrævet, skal nøglen kun tilsluttes via USB eller sættes op mod enheden via NFC. + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + + + tfa_u2f.add_key.backup_hint + For at sikre adgang, selvom nøglen går tabt, anbefales det at registrere en anden nøgle som backup og opbevare den et sikkert sted! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + + + r_u2f_two_factor.name + Vist nøglenavn (f.eks. backup) + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + + + tfa_u2f.add_key.add_button + Tilføj sikkerhedsnøgle + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + + + tfa_u2f.add_key.back_to_settings + Tilbage til indstillinger + + + + + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + new + + + statistics.title + Statistikker + + + + + Part-DB1\templates\Statistics\statistics.html.twig:14 + Part-DB1\templates\Statistics\statistics.html.twig:14 + new + + + statistics.parts + Komponenter + + + + + Part-DB1\templates\Statistics\statistics.html.twig:19 + Part-DB1\templates\Statistics\statistics.html.twig:19 + new + + + statistics.data_structures + Datastrukturer + + + + + Part-DB1\templates\Statistics\statistics.html.twig:24 + Part-DB1\templates\Statistics\statistics.html.twig:24 + new + + + statistics.attachments + Bilag + + + + + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + new + + + statistics.property + Egenskab + + + + + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + new + + + statistics.value + Værdi + + + + + Part-DB1\templates\Statistics\statistics.html.twig:40 + Part-DB1\templates\Statistics\statistics.html.twig:40 + new + + + statistics.distinct_parts_count + Antal forskellige komponenter + + + + + Part-DB1\templates\Statistics\statistics.html.twig:44 + Part-DB1\templates\Statistics\statistics.html.twig:44 + new + + + statistics.parts_instock_sum + Komponenter på lager i alt + + + + + Part-DB1\templates\Statistics\statistics.html.twig:48 + Part-DB1\templates\Statistics\statistics.html.twig:48 + new + + + statistics.parts_with_price + Komponenter med prisinformantion + + + + + Part-DB1\templates\Statistics\statistics.html.twig:65 + Part-DB1\templates\Statistics\statistics.html.twig:65 + new + + + statistics.categories_count + Antal kategorier + + + + + Part-DB1\templates\Statistics\statistics.html.twig:69 + Part-DB1\templates\Statistics\statistics.html.twig:69 + new + + + statistics.footprints_count + Antal footprints + + + + + Part-DB1\templates\Statistics\statistics.html.twig:73 + Part-DB1\templates\Statistics\statistics.html.twig:73 + new + + + statistics.manufacturers_count + Antal fabrikanter + + + + + Part-DB1\templates\Statistics\statistics.html.twig:77 + Part-DB1\templates\Statistics\statistics.html.twig:77 + new + + + statistics.storelocations_count + Antal lagerlokationer + + + + + Part-DB1\templates\Statistics\statistics.html.twig:81 + Part-DB1\templates\Statistics\statistics.html.twig:81 + new + + + statistics.suppliers_count + Antal leverandører + + + + + Part-DB1\templates\Statistics\statistics.html.twig:85 + Part-DB1\templates\Statistics\statistics.html.twig:85 + new + + + statistics.currencies_count + Antal valutaer + + + + + Part-DB1\templates\Statistics\statistics.html.twig:89 + Part-DB1\templates\Statistics\statistics.html.twig:89 + new + + + statistics.measurement_units_count + Antal måleenheder + + + + + Part-DB1\templates\Statistics\statistics.html.twig:93 + Part-DB1\templates\Statistics\statistics.html.twig:93 + new + + + statistics.devices_count + Antal projekter + + + + + Part-DB1\templates\Statistics\statistics.html.twig:110 + Part-DB1\templates\Statistics\statistics.html.twig:110 + new + + + statistics.attachment_types_count + Antal af bilagstyper + + + + + Part-DB1\templates\Statistics\statistics.html.twig:114 + Part-DB1\templates\Statistics\statistics.html.twig:114 + new + + + statistics.all_attachments_count + Antal bilag i alt + + + + + Part-DB1\templates\Statistics\statistics.html.twig:118 + Part-DB1\templates\Statistics\statistics.html.twig:118 + new + + + statistics.user_uploaded_attachments_count + Antal uploadede bilag + + + + + Part-DB1\templates\Statistics\statistics.html.twig:122 + Part-DB1\templates\Statistics\statistics.html.twig:122 + new + + + statistics.private_attachments_count + Antal private bilag + + + + + Part-DB1\templates\Statistics\statistics.html.twig:126 + Part-DB1\templates\Statistics\statistics.html.twig:126 + new + + + statistics.external_attachments_count + Antallet af alle eksterne bilag (URL) + + + + + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + + + tfa_backup.codes.title + Backupkoder + + + + + Part-DB1\templates\Users\backup_codes.html.twig:12 + Part-DB1\templates\Users\backup_codes.html.twig:12 + + + tfa_backup.codes.explanation + Udskriv disse koder og opbevar dem et sikkert sted! + + + + + Part-DB1\templates\Users\backup_codes.html.twig:13 + Part-DB1\templates\Users\backup_codes.html.twig:13 + + + tfa_backup.codes.help + Hvis du ikke længere har adgang til din enhed med Godkendelses-appen (smartphone tabt, datatab osv.), kan du bruge en af ​​disse koder til at få adgang til din konto og eventuelt igen forbinde til godkendelses-app. Hver af disse koder kan bruges én gang; det er tilrådeligt at slette brugte koder. Alle med adgang til disse koder kan potentielt få adgang til din konto, så opbevar dem et sikkert sted. + + + + + Part-DB1\templates\Users\backup_codes.html.twig:16 + Part-DB1\templates\Users\backup_codes.html.twig:16 + + + tfa_backup.username + Brugernavn + + + + + Part-DB1\templates\Users\backup_codes.html.twig:29 + Part-DB1\templates\Users\backup_codes.html.twig:29 + + + tfa_backup.codes.page_generated_on + Side genereret den %date% + + + + + Part-DB1\templates\Users\backup_codes.html.twig:32 + Part-DB1\templates\Users\backup_codes.html.twig:32 + + + tfa_backup.codes.print + Udskriv + + + + + Part-DB1\templates\Users\backup_codes.html.twig:35 + Part-DB1\templates\Users\backup_codes.html.twig:35 + + + tfa_backup.codes.copy_clipboard + Kopier til udklipsholder + + + + + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:40 + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:38 + templates\base.html.twig:99 + templates\Users\user_info.html.twig:3 + templates\Users\user_info.html.twig:6 + + + user.info.label + Brugerinformation + + + + + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + templates\Users\user_info.html.twig:18 + src\Form\UserSettingsType.php:32 + + + user.firstName.label + Fornavn + + + + + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + templates\Users\user_info.html.twig:24 + src\Form\UserSettingsType.php:35 + + + user.lastName.label + Efternavn + + + + + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + templates\Users\user_info.html.twig:30 + src\Form\UserSettingsType.php:41 + + + user.email.label + E-mail + + + + + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + templates\Users\user_info.html.twig:37 + src\Form\UserSettingsType.php:38 + + + user.department.label + Afdeling + + + + + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + templates\Users\user_info.html.twig:47 + src\Form\UserSettingsType.php:30 + + + user.username.label + Brugernavn + + + + + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + templates\Users\user_info.html.twig:53 + + + group.label + Gruppe + + + + + Part-DB1\templates\Users\user_info.html.twig:67 + Part-DB1\templates\Users\user_info.html.twig:67 + + + user.permissions + Tilladelser + + + + + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:39 + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:37 + templates\base.html.twig:98 + templates\Users\user_settings.html.twig:3 + templates\Users\user_settings.html.twig:6 + + + user.settings.label + Brugerindstillinger + + + + + Part-DB1\templates\Users\user_settings.html.twig:18 + Part-DB1\templates\Users\user_settings.html.twig:18 + templates\Users\user_settings.html.twig:14 + + + user_settings.data.label + Personlige data + + + + + Part-DB1\templates\Users\user_settings.html.twig:22 + Part-DB1\templates\Users\user_settings.html.twig:22 + templates\Users\user_settings.html.twig:18 + + + user_settings.configuration.label + Konfiguration + + + + + Part-DB1\templates\Users\user_settings.html.twig:55 + Part-DB1\templates\Users\user_settings.html.twig:55 + templates\Users\user_settings.html.twig:48 + + + user.settings.change_pw + Ændre password + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + + + user.settings.2fa_settings + To-faktor godkendelse + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + + + tfa.settings.google.tab + Godkendelses-app + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + + + tfa.settings.bakup.tab + Backup-koder + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + + + tfa.settings.u2f.tab + Sikkerhedsnøgler (U2F) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + + + tfa.settings.trustedDevices.tab + Pålidelige enheder + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_title + Er du sikker på, at du vil deaktivere godkendelses-appen? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_message + Hvis du deaktiverer godkendelses-apen slettes alle backupkoder. Så du skal muligvis genudskrive dem.<br> +Bemærk også, at uden to-faktor-godkendelse er din konto ikke længere så godt beskyttet mod misbrug! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + + + tfa_google.disabled_message + Godkendelses-app er deaktiveret + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + + + tfa_google.step.download + Download en godkendelses-app (f.eks. <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android. apps. authenticator2">Google Authenticator</a> eller <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=org .fedorahosted .freeotp">FreeOTP Authenticator</a>) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + + + tfa_google.step.scan + Scan QR-koden nedenfor med appen eller indtast data manuelt + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + + + tfa_google.step.input_code + Indtast den genererede kode i feltet nedenfor og bekræft + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + + + tfa_google.step.download_backup + Udskriv dine backupkoder og gem dem et sikkert sted + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + + + tfa_google.manual_setup + Manuel opsætning + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + + + tfa_google.manual_setup.type + Type + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + + + tfa_google.manual_setup.username + Brugernavn + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + + + tfa_google.manual_setup.secret + Secret + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + + + tfa_google.manual_setup.digit_count + Antal ciffre + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + + + tfa_google.enabled_message + Godkendelses-app aktiv + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + + + tfa_backup.disabled + Backup-koder deaktiveret. Konfigurer godkendelses-appen for at aktivere backupkoder. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + + + tfa_backup.explanation + Disse backupkoder giver dig adgang til din konto, selvom du mister enheden med godkendelse-appen. Udskriv koderne og opbevar dem et sikkert sted. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_title + Vil du virkelig nulstille koder? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_message + Dette vil slette alle tidligere koder og generere et sæt nye koder. Dette kan ikke fortrydes. Husk at udskrive de nye koder og gem dem et sikkert sted! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + + + tfa_backup.enabled + Backupkoder aktiveret + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + + + tfa_backup.show_codes + Vis backupkoder + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + + + tfa_u2f.table_caption + Gemte backupkoder + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + + + tfa_u2f.delete_u2f.confirm_title + Ønsker du virkelig at slette denne backupkode? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + + + tfa_u2f.delete_u2f.confirm_message + Hvis du fjerner denne nøgle, vil du ikke længere kunne logge på med den. Hvis der ikke er nogen sikkerhedsnøgler tilbage, er to-faktor-godkendelse deaktiveret. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + + + tfa_u2f.keys.name + Nøglenavn + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + + + tfa_u2f.keys.added_date + Registreringsdato + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + + + tfa_u2f.key_delete + Slet nøgle + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + + + tfa_u2f.no_keys_registered + Ingen gemte nøgler + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + + + tfa_u2f.add_new_key + Registrer ny sikkerhedsnøgle + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + + + tfa_trustedDevices.explanation + Når du tjekker to-faktor, kan den aktuelle computer markeres som troværdig og så er to-faktor-tjek er ikke længere nødvendig på denne computer. Hvis du udførte dette ved en fejl, eller hvis en computer ikke længere er troværdig. Så kan du nulstille status for <i>alle </i>computere her. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + + + tfa_trustedDevices.invalidate.confirm_title + Vil du virkelig fjerne alle pålidelige computere? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + + + tfa_trustedDevices.invalidate.confirm_message + Du skal opsætte to-faktor-godkendelse igen på alle computere. Sørg for, at du har din to-faktor-enhed ved hånden. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + + + tfa_trustedDevices.invalidate.btn + Fjern alle pålidelige enheder + + + + + Part-DB1\templates\_navbar.html.twig:4 + Part-DB1\templates\_navbar.html.twig:4 + templates\base.html.twig:29 + + + sidebar.toggle + Aktivér/de-aktiver sidebjælke + + + + + Part-DB1\templates\_navbar.html.twig:22 + + + navbar.scanner.link + Scanner + + + + + Part-DB1\templates\_navbar.html.twig:38 + Part-DB1\templates\_navbar.html.twig:36 + templates\base.html.twig:97 + + + user.loggedin.label + Logget ind som + + + + + Part-DB1\templates\_navbar.html.twig:44 + Part-DB1\templates\_navbar.html.twig:42 + templates\base.html.twig:103 + + + user.login + Log ind + + + + + Part-DB1\templates\_navbar.html.twig:50 + Part-DB1\templates\_navbar.html.twig:48 + + + ui.toggle_darkmode + Darkmode + + + + + Part-DB1\templates\_navbar.html.twig:54 + Part-DB1\src\Form\UserSettingsType.php:97 + Part-DB1\templates\_navbar.html.twig:52 + Part-DB1\src\Form\UserSettingsType.php:97 + templates\base.html.twig:106 + src\Form\UserSettingsType.php:44 + + + user.language_select + Sprog + + + + + Part-DB1\templates\_navbar_search.html.twig:4 + Part-DB1\templates\_navbar_search.html.twig:4 + templates\base.html.twig:49 + + + search.options.label + Søgemuligheder + + + + + Part-DB1\templates\_navbar_search.html.twig:23 + + + tags.label + Tags + + + + + Part-DB1\templates\_navbar_search.html.twig:27 + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + templates\base.html.twig:60 + templates\Parts\show_part_info.html.twig:36 + src\Form\PartType.php:77 + + + storelocation.label + Lagerlokation + + + + + Part-DB1\templates\_navbar_search.html.twig:36 + Part-DB1\templates\_navbar_search.html.twig:31 + templates\base.html.twig:65 + + + ordernumber.label.short + Bestillingsnummer + + + + + Part-DB1\templates\_navbar_search.html.twig:40 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + Part-DB1\templates\_navbar_search.html.twig:35 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + templates\base.html.twig:67 + + + supplier.label + Leverandør + + + + + Part-DB1\templates\_navbar_search.html.twig:57 + Part-DB1\templates\_navbar_search.html.twig:52 + templates\base.html.twig:75 + + + search.deactivateBarcode + Deakt. stregkode + + + + + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_navbar_search.html.twig:56 + templates\base.html.twig:77 + + + search.regexmatching + Reg. Ex. matching + + + + + Part-DB1\templates\_navbar_search.html.twig:68 + Part-DB1\templates\_navbar_search.html.twig:62 + + + search.submit + Kom nu! + + + + + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + templates\base.html.twig:175 + templates\base.html.twig:189 + templates\base.html.twig:202 + templates\base.html.twig:230 + + + project.labelp + Projekter + + + + + Part-DB1\templates\_sidebar.html.twig:2 + Part-DB1\templates\_sidebar.html.twig:2 + templates\base.html.twig:165 + templates\base.html.twig:192 + templates\base.html.twig:220 + + + actions + Handlinger + + + + + Part-DB1\templates\_sidebar.html.twig:6 + Part-DB1\templates\_sidebar.html.twig:6 + templates\base.html.twig:169 + templates\base.html.twig:196 + templates\base.html.twig:224 + + + datasource + Datakilde + + + + + Part-DB1\templates\_sidebar.html.twig:10 + Part-DB1\templates\_sidebar.html.twig:10 + templates\base.html.twig:173 + templates\base.html.twig:200 + templates\base.html.twig:228 + + + manufacturer.labelp + Fabrikant + + + + + Part-DB1\templates\_sidebar.html.twig:11 + Part-DB1\templates\_sidebar.html.twig:11 + templates\base.html.twig:174 + templates\base.html.twig:201 + templates\base.html.twig:229 + + + supplier.labelp + Leverandører + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:293 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:181 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:243 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:268 + + + attachment.download_failed + Download af eksterne data fejlet! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 + + + entity.edit_flash + Ændringer gemt med succes. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 + + + entity.edit_flash.invalid + Handlinger + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 + + + entity.created_flash + Element oprettet med succes! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 + + + entity.created_flash.invalid + Elementet kunne ikke oprettes! Tjek dit input data + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 + src\Controller\BaseAdminController.php:154 + + + attachment_type.deleted + Element slettet! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 + Part-DB1\src\Controller\UserController.php:109 + Part-DB1\src\Controller\UserSettingsController.php:159 + Part-DB1\src\Controller\UserSettingsController.php:193 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:354 + Part-DB1\src\Controller\UserController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:150 + Part-DB1\src\Controller\UserSettingsController.php:182 + + + csfr_invalid + CSRF-token er ugyldig! Genindlæs denne side, eller kontakt en administrator, hvis problemet fortsætter! + + + + + Part-DB1\src\Controller\LabelController.php:125 + + + label_generator.no_entities_found + ingen elementer fundet. + + + + + Part-DB1\src\Controller\LogController.php:149 + Part-DB1\src\Controller\LogController.php:154 + new + + + log.undo.target_not_found + Elementet blev ikke fundet i databasen! + + + + + Part-DB1\src\Controller\LogController.php:156 + Part-DB1\src\Controller\LogController.php:160 + new + + + log.undo.revert_success + Komponent rettet tilbage til tidligere version + + + + + Part-DB1\src\Controller\LogController.php:176 + Part-DB1\src\Controller\LogController.php:180 + new + + + log.undo.element_undelete_success + Komponent gendannet med succes. + + + + + Part-DB1\src\Controller\LogController.php:178 + Part-DB1\src\Controller\LogController.php:182 + new + + + log.undo.element_element_already_undeleted + Komponent er allerede gendannet! + + + + + Part-DB1\src\Controller\LogController.php:185 + Part-DB1\src\Controller\LogController.php:189 + new + + + log.undo.element_delete_success + Komponent slettet med succes + + + + + Part-DB1\src\Controller\LogController.php:187 + Part-DB1\src\Controller\LogController.php:191 + new + + + log.undo.element.element_already_delted + Komponent er allerede blevet slettet + + + + + Part-DB1\src\Controller\LogController.php:194 + Part-DB1\src\Controller\LogController.php:198 + new + + + log.undo.element_change_undone + Annullering af ændringerne gennemført + + + + + Part-DB1\src\Controller\LogController.php:196 + Part-DB1\src\Controller\LogController.php:200 + new + + + log.undo.do_undelete_before + Du skal først gendanne elementet, før du kan fortryde denne ændring! + + + + + Part-DB1\src\Controller\LogController.php:199 + Part-DB1\src\Controller\LogController.php:203 + new + + + log.undo.log_type_invalid + Denne logtype kan ikke fortrydes! + + + + + Part-DB1\src\Controller\PartController.php:182 + Part-DB1\src\Controller\PartController.php:182 + src\Controller\PartController.php:80 + + + part.edited_flash + Ændringer gemt! + + + + + Part-DB1\src\Controller\PartController.php:186 + Part-DB1\src\Controller\PartController.php:186 + + + part.edited_flash.invalid + Fejl ved lagring: Tjek dine indtastninger! + + + + + Part-DB1\src\Controller\PartController.php:216 + Part-DB1\src\Controller\PartController.php:219 + + + part.deleted + Komponent slettet. + + + + + Part-DB1\src\Controller\PartController.php:302 + Part-DB1\src\Controller\PartController.php:277 + Part-DB1\src\Controller\PartController.php:317 + src\Controller\PartController.php:113 + src\Controller\PartController.php:142 + + + part.created_flash + Komponenter oprettet med succes! + + + + + Part-DB1\src\Controller\PartController.php:308 + Part-DB1\src\Controller\PartController.php:283 + + + part.created_flash.invalid + Fejl ved oprettelse: Tjek dine indtastninger! + + + + + Part-DB1\src\Controller\ScanController.php:68 + Part-DB1\src\Controller\ScanController.php:90 + + + scan.qr_not_found + Ingen element fundet + + + + + Part-DB1\src\Controller\ScanController.php:71 + + + scan.format_unknown + Ukendt format! + + + + + Part-DB1\src\Controller\ScanController.php:86 + + + scan.qr_success + Element fundet. + + + + + Part-DB1\src\Controller\SecurityController.php:114 + Part-DB1\src\Controller\SecurityController.php:109 + + + pw_reset.user_or_email + Brugernavn / e-mail + + + + + Part-DB1\src\Controller\SecurityController.php:131 + Part-DB1\src\Controller\SecurityController.php:126 + + + pw_reset.request.success + Anmodning om password lykkedes! Tjek din e-mail for mere information. + + + + + Part-DB1\src\Controller\SecurityController.php:162 + Part-DB1\src\Controller\SecurityController.php:160 + + + pw_reset.username + Brugernavn + + + + + Part-DB1\src\Controller\SecurityController.php:165 + Part-DB1\src\Controller\SecurityController.php:163 + + + pw_reset.token + Token + + + + + Part-DB1\src\Controller\SecurityController.php:194 + Part-DB1\src\Controller\SecurityController.php:192 + + + pw_reset.new_pw.error + Brugernavn eller token er ugyldigt! Tjek dine indtastninger. + + + + + Part-DB1\src\Controller\SecurityController.php:196 + Part-DB1\src\Controller\SecurityController.php:194 + + + pw_reset.new_pw.success + Password blev nulstillet. Du kan nu logge ind med det nye password. + + + + + Part-DB1\src\Controller\UserController.php:107 + Part-DB1\src\Controller\UserController.php:99 + + + user.edit.reset_success + Alle to-faktor-godkendelsesmetoder er blevet deaktiveret. + + + + + Part-DB1\src\Controller\UserSettingsController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:92 + + + tfa_backup.no_codes_enabled + Ingen backup-koder er aktiveret! + + + + + Part-DB1\src\Controller\UserSettingsController.php:138 + Part-DB1\src\Controller\UserSettingsController.php:132 + + + tfa_u2f.u2f_delete.not_existing + Der er ingen sikkerhedsnøgle med dette ID! + + + + + Part-DB1\src\Controller\UserSettingsController.php:145 + Part-DB1\src\Controller\UserSettingsController.php:139 + + + tfa_u2f.u2f_delete.access_denied + Du kan kun slette dine egne sikkerhedsnøgler! + + + + + Part-DB1\src\Controller\UserSettingsController.php:153 + Part-DB1\src\Controller\UserSettingsController.php:147 + + + tfa.u2f.u2f_delete.success + Sikkerhedsnøglen blev fjernet. + + + + + Part-DB1\src\Controller\UserSettingsController.php:188 + Part-DB1\src\Controller\UserSettingsController.php:180 + + + tfa_trustedDevice.invalidate.success + Pålidelige enheder blev slettet. + + + + + Part-DB1\src\Controller\UserSettingsController.php:235 + Part-DB1\src\Controller\UserSettingsController.php:226 + src\Controller\UserController.php:98 + + + user.settings.saved_flash + Indstillinger gemt! + + + + + Part-DB1\src\Controller\UserSettingsController.php:297 + Part-DB1\src\Controller\UserSettingsController.php:288 + src\Controller\UserController.php:130 + + + user.settings.pw_changed_flash + Password ændret! + + + + + Part-DB1\src\Controller\UserSettingsController.php:317 + Part-DB1\src\Controller\UserSettingsController.php:306 + + + user.settings.2fa.google.activated + Godkendelses-app'en blev aktiveret. + + + + + Part-DB1\src\Controller\UserSettingsController.php:328 + Part-DB1\src\Controller\UserSettingsController.php:315 + + + user.settings.2fa.google.disabled + Godkendelses-app'en er deaktiveret + + + + + Part-DB1\src\Controller\UserSettingsController.php:346 + Part-DB1\src\Controller\UserSettingsController.php:332 + + + user.settings.2fa.backup_codes.regenerated + Nye backupkoder blev oprettet. + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + + + attachment.table.filename + Filnavn + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + + + attachment.table.filesize + filstørrelse + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:245 + Part-DB1\src\DataTables\PartsDataTable.php:252 + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:193 + Part-DB1\src\DataTables\PartsDataTable.php:200 + + + true + Sand + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:246 + Part-DB1\src\DataTables\PartsDataTable.php:253 + Part-DB1\src\Form\Type\SIUnitType.php:139 + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:201 + Part-DB1\src\Form\Type\SIUnitType.php:139 + + + false + Falsk + + + + + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 + + + log.target_deleted + Slettet + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 + new + + + log.undo.undelete + Gendan element + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 + new + + + log.undo.undo + Fortryd ændring + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 + new + + + log.undo.revert + Nulstil element til nuværende status! + + + + + Part-DB1\src\DataTables\LogDataTable.php:173 + Part-DB1\src\DataTables\LogDataTable.php:161 + + + log.id + ID + + + + + Part-DB1\src\DataTables\LogDataTable.php:178 + Part-DB1\src\DataTables\LogDataTable.php:166 + + + log.timestamp + Tidsstempel + + + + + Part-DB1\src\DataTables\LogDataTable.php:183 + Part-DB1\src\DataTables\LogDataTable.php:171 + + + log.type + Begivenhed + + + + + Part-DB1\src\DataTables\LogDataTable.php:191 + Part-DB1\src\DataTables\LogDataTable.php:179 + + + log.level + Niveau + + + + + Part-DB1\src\DataTables\LogDataTable.php:200 + Part-DB1\src\DataTables\LogDataTable.php:188 + + + log.user + Bruger + + + + + Part-DB1\src\DataTables\LogDataTable.php:213 + Part-DB1\src\DataTables\LogDataTable.php:201 + + + log.target_type + Måltype + + + + + Part-DB1\src\DataTables\LogDataTable.php:226 + Part-DB1\src\DataTables\LogDataTable.php:214 + + + log.target + Mål + + + + + Part-DB1\src\DataTables\LogDataTable.php:231 + Part-DB1\src\DataTables\LogDataTable.php:218 + new + + + log.extra + Ekstra + + + + + Part-DB1\src\DataTables\PartsDataTable.php:168 + Part-DB1\src\DataTables\PartsDataTable.php:116 + + + part.table.name + Navn + + + + + Part-DB1\src\DataTables\PartsDataTable.php:178 + Part-DB1\src\DataTables\PartsDataTable.php:126 + + + part.table.id + ID + + + + + Part-DB1\src\DataTables\PartsDataTable.php:182 + Part-DB1\src\DataTables\PartsDataTable.php:130 + + + part.table.description + Beskrivelse + + + + + Part-DB1\src\DataTables\PartsDataTable.php:185 + Part-DB1\src\DataTables\PartsDataTable.php:133 + + + part.table.category + Kategori + + + + + Part-DB1\src\DataTables\PartsDataTable.php:190 + Part-DB1\src\DataTables\PartsDataTable.php:138 + + + part.table.footprint + Footprint + + + + + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:142 + + + part.table.manufacturer + Fabrikant + + + + + Part-DB1\src\DataTables\PartsDataTable.php:197 + Part-DB1\src\DataTables\PartsDataTable.php:145 + + + part.table.storeLocations + Lagerlokationer + + + + + Part-DB1\src\DataTables\PartsDataTable.php:216 + Part-DB1\src\DataTables\PartsDataTable.php:164 + + + part.table.amount + Antal + + + + + Part-DB1\src\DataTables\PartsDataTable.php:224 + Part-DB1\src\DataTables\PartsDataTable.php:172 + + + part.table.minamount + Min. beholdning + + + + + Part-DB1\src\DataTables\PartsDataTable.php:232 + Part-DB1\src\DataTables\PartsDataTable.php:180 + + + part.table.partUnit + Måleenhed + + + + + Part-DB1\src\DataTables\PartsDataTable.php:236 + Part-DB1\src\DataTables\PartsDataTable.php:184 + + + part.table.addedDate + Tilføjet + + + + + Part-DB1\src\DataTables\PartsDataTable.php:240 + Part-DB1\src\DataTables\PartsDataTable.php:188 + + + part.table.lastModified + Sidst redigeret + + + + + Part-DB1\src\DataTables\PartsDataTable.php:244 + Part-DB1\src\DataTables\PartsDataTable.php:192 + + + part.table.needsReview + Gennemgang nødvendig + + + + + Part-DB1\src\DataTables\PartsDataTable.php:251 + Part-DB1\src\DataTables\PartsDataTable.php:199 + + + part.table.favorite + Favorit + + + + + Part-DB1\src\DataTables\PartsDataTable.php:258 + Part-DB1\src\DataTables\PartsDataTable.php:206 + + + part.table.manufacturingStatus + Status + + + + + Part-DB1\src\DataTables\PartsDataTable.php:260 + Part-DB1\src\DataTables\PartsDataTable.php:262 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:208 + Part-DB1\src\DataTables\PartsDataTable.php:210 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.unknown + Ukendt + + + + + Part-DB1\src\DataTables\PartsDataTable.php:263 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:211 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.announced + Meddelt + + + + + Part-DB1\src\DataTables\PartsDataTable.php:264 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.active + Aktiv + + + + + Part-DB1\src\DataTables\PartsDataTable.php:265 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:213 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.nrfnd + Anbefales ikke til nye designs + + + + + Part-DB1\src\DataTables\PartsDataTable.php:266 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:214 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.eol + End of life + + + + + Part-DB1\src\DataTables\PartsDataTable.php:267 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:215 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.discontinued + Discontinued + + + + + Part-DB1\src\DataTables\PartsDataTable.php:271 + Part-DB1\src\DataTables\PartsDataTable.php:219 + + + part.table.mpn + MPN + + + + + Part-DB1\src\DataTables\PartsDataTable.php:275 + Part-DB1\src\DataTables\PartsDataTable.php:223 + + + part.table.mass + Vægt + + + + + Part-DB1\src\DataTables\PartsDataTable.php:279 + Part-DB1\src\DataTables\PartsDataTable.php:227 + + + part.table.tags + Tags + + + + + Part-DB1\src\DataTables\PartsDataTable.php:283 + Part-DB1\src\DataTables\PartsDataTable.php:231 + + + part.table.attachments + Bilag + + + + + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 + Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 + + + flash.login_successful + Login lykkedes + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + JSON + JSON + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + XML + XML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + CSV + CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + YAML + YAML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:124 + Part-DB1\src\Form\AdminPages\ImportType.php:124 + + + import.abort_on_validation.help + Hvis denne indstilling er aktiveret, vil hele processen blive afbrudt, hvis der registreres ugyldige data. Hvis denne indstilling ikke er aktiv, ignoreres ugyldige poster, og de andre poster forsøges importeret. + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:86 + Part-DB1\src\Form\AdminPages\ImportType.php:86 + src\Form\ImportType.php:70 + + + import.csv_separator + CSV-separator + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:93 + Part-DB1\src\Form\AdminPages\ImportType.php:93 + src\Form\ImportType.php:72 + + + parent.label + Overordnet element + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:101 + Part-DB1\src\Form\AdminPages\ImportType.php:101 + src\Form\ImportType.php:75 + + + import.file + Fil + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:111 + Part-DB1\src\Form\AdminPages\ImportType.php:111 + src\Form\ImportType.php:78 + + + import.preserve_children + Importer også underelementer + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:120 + Part-DB1\src\Form\AdminPages\ImportType.php:120 + src\Form\ImportType.php:80 + + + import.abort_on_validation + Annuller ved ugyldige data + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:132 + Part-DB1\src\Form\AdminPages\ImportType.php:132 + src\Form\ImportType.php:85 + + + import.btn + Importer + + + + + Part-DB1\src\Form\AttachmentFormType.php:113 + Part-DB1\src\Form\AttachmentFormType.php:109 + + + attachment.edit.secure_file.help + En vedhæftet fil, der er markeret som privat, kan kun tilgås af autoriseret bruger som har de relevante tilladelser. Når denne indstilling er aktiv, genereres der ikke miniaturebilleder, og adgangen til filen er langsommere. + + + + + Part-DB1\src\Form\AttachmentFormType.php:127 + Part-DB1\src\Form\AttachmentFormType.php:123 + + + attachment.edit.url.help + Her kan du enten indtaste en URL til en ekstern fil, eller du kan søge efter de indbyggede ressourcer ved at indtaste et nøgleord (f.eks. footprint). + + + + + Part-DB1\src\Form\AttachmentFormType.php:82 + Part-DB1\src\Form\AttachmentFormType.php:79 + + + attachment.edit.name + Navn + + + + + Part-DB1\src\Form\AttachmentFormType.php:85 + Part-DB1\src\Form\AttachmentFormType.php:82 + + + attachment.edit.attachment_type + Bilagstype + + + + + Part-DB1\src\Form\AttachmentFormType.php:94 + Part-DB1\src\Form\AttachmentFormType.php:91 + + + attachment.edit.show_in_table + Vis i tabel + + + + + Part-DB1\src\Form\AttachmentFormType.php:105 + Part-DB1\src\Form\AttachmentFormType.php:102 + + + attachment.edit.secure_file + Privat bilag + + + + + Part-DB1\src\Form\AttachmentFormType.php:119 + Part-DB1\src\Form\AttachmentFormType.php:115 + + + attachment.edit.url + URL + + + + + Part-DB1\src\Form\AttachmentFormType.php:133 + Part-DB1\src\Form\AttachmentFormType.php:129 + + + attachment.edit.download_url + Download eksternt data + + + + + Part-DB1\src\Form\AttachmentFormType.php:146 + Part-DB1\src\Form\AttachmentFormType.php:142 + + + attachment.edit.file + Upload fil + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:86 + + + part.label + Komponent + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:87 + + + part_lot.label + Komponentbeholdning + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.none + Ingen + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.qr + QR-kode (anbefalet) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code128 + Kode 128 (anbefalet) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code39 + Kode 39 (anbefalet) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code93 + Kode 93 + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.datamatrix + Datamatrix + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label_options.lines_mode.html + HTML + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label.options.lines_mode.twig + Twig + + + + + Part-DB1\src\Form\LabelOptionsType.php:126 + + + label_options.lines_mode.help + Hvis du vælger Twig her, vil indholdsfeltet blive fortolket som en Twig-skabelon. Yderligere hjælp er tilgængelig i <a href="https://twig.symfony.com/doc/3.x/templates.html">Twig Dokumentation</a> og <a href="https://docs.part-db.dk/usage/labels.html#twig-mode">Wiki</a>. + + + + + Part-DB1\src\Form\LabelOptionsType.php:47 + + + label_options.page_size.label + Størrelse + + + + + Part-DB1\src\Form\LabelOptionsType.php:66 + + + label_options.supported_elements.label + Elementtype + + + + + Part-DB1\src\Form\LabelOptionsType.php:75 + + + label_options.barcode_type.label + Stregkodetype + + + + + Part-DB1\src\Form\LabelOptionsType.php:102 + + + label_profile.lines.label + Indhold + + + + + Part-DB1\src\Form\LabelOptionsType.php:111 + + + label_options.additional_css.label + Yderligere CSS + + + + + Part-DB1\src\Form\LabelOptionsType.php:120 + + + label_options.lines_mode.label + Parser mode + + + + + Part-DB1\src\Form\LabelOptionsType.php:51 + + + label_options.width.placeholder + Bredde + + + + + Part-DB1\src\Form\LabelOptionsType.php:60 + + + label_options.height.placeholder + Højde + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 + + + label_generator.target_id.range_hint + Du kan her angive flere ID'er (f.eks. 1, 2, 3) og/eller et interval her for at generere stregkoder for flere elementer på én gang. + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 + + + label_generator.target_id.label + Element ID'ere + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 + + + label_generator.update + Opdatering + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 + + + scan_dialog.input + Input + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 + + + scan_dialog.submit + Afsend + + + + + Part-DB1\src\Form\ParameterType.php:41 + + + parameters.name.placeholder + F.eks. DC Current Gain + + + + + Part-DB1\src\Form\ParameterType.php:50 + + + parameters.symbol.placeholder + F.eks. h_[FE] + + + + + Part-DB1\src\Form\ParameterType.php:60 + + + parameters.text.placeholder + f.eks. Test specifikationer + + + + + Part-DB1\src\Form\ParameterType.php:71 + + + parameters.max.placeholder + f.eks. 350 + + + + + Part-DB1\src\Form\ParameterType.php:82 + + + parameters.min.placeholder + f.eks. 100 + + + + + Part-DB1\src\Form\ParameterType.php:93 + + + parameters.typical.placeholder + f.eks. 200 + + + + + Part-DB1\src\Form\ParameterType.php:103 + + + parameters.unit.placeholder + f.eks. V + + + + + Part-DB1\src\Form\ParameterType.php:114 + + + parameter.group.placeholder + f.eks. tekniske specifikationer + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:72 + Part-DB1\src\Form\Part\OrderdetailType.php:75 + + + orderdetails.edit.supplierpartnr + Leverandør varenummer + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:81 + Part-DB1\src\Form\Part\OrderdetailType.php:84 + + + orderdetails.edit.supplier + Leverandør + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:87 + Part-DB1\src\Form\Part\OrderdetailType.php:90 + + + orderdetails.edit.url + Link til tilbud + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:93 + Part-DB1\src\Form\Part\OrderdetailType.php:96 + + + orderdetails.edit.obsolete + Ikke længere tilgængelig + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:75 + Part-DB1\src\Form\Part\OrderdetailType.php:78 + + + orderdetails.edit.supplierpartnr.placeholder + f.eks. BC 547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:101 + Part-DB1\src\Form\Part\PartBaseType.php:99 + + + part.edit.name + Navn + + + + + Part-DB1\src\Form\Part\PartBaseType.php:109 + Part-DB1\src\Form\Part\PartBaseType.php:107 + + + part.edit.description + Beskrivelse + + + + + Part-DB1\src\Form\Part\PartBaseType.php:120 + Part-DB1\src\Form\Part\PartBaseType.php:118 + + + part.edit.mininstock + Minimumsmængde + + + + + Part-DB1\src\Form\Part\PartBaseType.php:129 + Part-DB1\src\Form\Part\PartBaseType.php:127 + + + part.edit.category + Kategori + + + + + Part-DB1\src\Form\Part\PartBaseType.php:135 + Part-DB1\src\Form\Part\PartBaseType.php:133 + + + part.edit.footprint + Footprint + + + + + Part-DB1\src\Form\Part\PartBaseType.php:142 + Part-DB1\src\Form\Part\PartBaseType.php:140 + + + part.edit.tags + Tags + + + + + Part-DB1\src\Form\Part\PartBaseType.php:154 + Part-DB1\src\Form\Part\PartBaseType.php:152 + + + part.edit.manufacturer.label + Fabrikant + + + + + Part-DB1\src\Form\Part\PartBaseType.php:161 + Part-DB1\src\Form\Part\PartBaseType.php:159 + + + part.edit.manufacturer_url.label + Link til produktside + + + + + Part-DB1\src\Form\Part\PartBaseType.php:167 + Part-DB1\src\Form\Part\PartBaseType.php:165 + + + part.edit.mpn + Fabrikant partnummer + + + + + Part-DB1\src\Form\Part\PartBaseType.php:173 + Part-DB1\src\Form\Part\PartBaseType.php:171 + + + part.edit.manufacturing_status + Fabrikantstatus + + + + + Part-DB1\src\Form\Part\PartBaseType.php:181 + Part-DB1\src\Form\Part\PartBaseType.php:179 + + + part.edit.needs_review + Gennemgang nødvendig + + + + + Part-DB1\src\Form\Part\PartBaseType.php:189 + Part-DB1\src\Form\Part\PartBaseType.php:187 + + + part.edit.is_favorite + Favorit + + + + + Part-DB1\src\Form\Part\PartBaseType.php:197 + Part-DB1\src\Form\Part\PartBaseType.php:195 + + + part.edit.mass + Vægt + + + + + Part-DB1\src\Form\Part\PartBaseType.php:203 + Part-DB1\src\Form\Part\PartBaseType.php:201 + + + part.edit.partUnit + Måleenhed + + + + + Part-DB1\src\Form\Part\PartBaseType.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:210 + + + part.edit.comment + Notater + + + + + Part-DB1\src\Form\Part\PartBaseType.php:250 + Part-DB1\src\Form\Part\PartBaseType.php:246 + + + part.edit.master_attachment + Miniaturebillede + + + + + Part-DB1\src\Form\Part\PartBaseType.php:295 + Part-DB1\src\Form\Part\PartBaseType.php:276 + src\Form\PartType.php:91 + + + part.edit.save + Anvend ændringer + + + + + Part-DB1\src\Form\Part\PartBaseType.php:296 + Part-DB1\src\Form\Part\PartBaseType.php:277 + src\Form\PartType.php:92 + + + part.edit.reset + Annuller ændringer + + + + + Part-DB1\src\Form\Part\PartBaseType.php:105 + Part-DB1\src\Form\Part\PartBaseType.php:103 + + + part.edit.name.placeholder + f.eks. BC547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:115 + Part-DB1\src\Form\Part\PartBaseType.php:113 + + + part.edit.description.placeholder + f.eks. NPN 45V, 0,1A 0,5W + + + + + Part-DB1\src\Form\Part\PartBaseType.php:123 + Part-DB1\src\Form\Part\PartBaseType.php:121 + + + part.editmininstock.placeholder + f.eks. 1 + + + + + Part-DB1\src\Form\Part\PartLotType.php:69 + Part-DB1\src\Form\Part\PartLotType.php:69 + + + part_lot.edit.description + Beskrivelse + + + + + Part-DB1\src\Form\Part\PartLotType.php:78 + Part-DB1\src\Form\Part\PartLotType.php:78 + + + part_lot.edit.location + Lagerlokation + + + + + Part-DB1\src\Form\Part\PartLotType.php:89 + Part-DB1\src\Form\Part\PartLotType.php:89 + + + part_lot.edit.amount + Antal + + + + + Part-DB1\src\Form\Part\PartLotType.php:98 + Part-DB1\src\Form\Part\PartLotType.php:97 + + + part_lot.edit.instock_unknown + Ukendt antal + + + + + Part-DB1\src\Form\Part\PartLotType.php:109 + Part-DB1\src\Form\Part\PartLotType.php:108 + + + part_lot.edit.needs_refill + skal fyldes op igen + + + + + Part-DB1\src\Form\Part\PartLotType.php:120 + Part-DB1\src\Form\Part\PartLotType.php:119 + + + part_lot.edit.expiration_date + Udløbsdato + + + + + Part-DB1\src\Form\Part\PartLotType.php:128 + Part-DB1\src\Form\Part\PartLotType.php:125 + + + part_lot.edit.comment + Bemærk + + + + + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + + + perm.group.other + Forskellige + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + + + tfa_google.enable + Aktiver godkendelses-app + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + + + tfa_google.disable + Deaktiver godkendelses-app + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + + + google_confirmation + Verifikationskode + + + + + Part-DB1\src\Form\UserSettingsType.php:108 + Part-DB1\src\Form\UserSettingsType.php:108 + src\Form\UserSettingsType.php:46 + + + user.timezone.label + Tidszone + + + + + Part-DB1\src\Form\UserSettingsType.php:133 + Part-DB1\src\Form\UserSettingsType.php:132 + + + user.currency.label + Foretrukken valuta + + + + + Part-DB1\src\Form\UserSettingsType.php:140 + Part-DB1\src\Form\UserSettingsType.php:139 + src\Form\UserSettingsType.php:53 + + + save + Anvend ændringer + + + + + Part-DB1\src\Form\UserSettingsType.php:141 + Part-DB1\src\Form\UserSettingsType.php:140 + src\Form\UserSettingsType.php:54 + + + reset + Kasser ændringer + + + + + Part-DB1\src\Form\UserSettingsType.php:104 + Part-DB1\src\Form\UserSettingsType.php:104 + src\Form\UserSettingsType.php:45 + + + user_settings.language.placeholder + Serversprog + + + + + Part-DB1\src\Form\UserSettingsType.php:115 + Part-DB1\src\Form\UserSettingsType.php:115 + src\Form\UserSettingsType.php:48 + + + user_settings.timezone.placeholder + Server tidszone + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + + + attachment.label + Bilag + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + + + attachment_type.label + Bilagstype + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + + + project.label + Projekt + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + + + measurement_unit.label + Måleenhed + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + + + currency.label + Valuta + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + + + orderdetail.label + Bestillingsoplysninger + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + + + pricedetail.label + Prisinformation + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + + + user.label + Rediger + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 + + + parameter.label + Parameter + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 + + + label_profile.label + Labelprofil + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 + new + + + log.element_deleted.old_name.unknown + Ukendt + + + + + Part-DB1\src\Services\MarkdownParser.php:73 + Part-DB1\src\Services\MarkdownParser.php:73 + + + markdown.loading + Indlæs Markdown. Hvis dette varer ved i lang tid, så prøv at indlæse hjemmesiden igen! + + + + + Part-DB1\src\Services\PasswordResetManager.php:98 + Part-DB1\src\Services\PasswordResetManager.php:98 + + + pw_reset.email.subject + Password nulstillet for din Part-DB-konto + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + + + tree.tools.tools + Værktøjer + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 + src\Services\ToolsTreeBuilder.php:74 + + + tree.tools.edit + Rediger + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + src\Services\ToolsTreeBuilder.php:81 + + + tree.tools.show + Vis + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + + + tree.tools.system + System + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 + + + tree.tools.tools.label_dialog + Labeldialog + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 + + + tree.tools.tools.label_scanner + Scanner + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 + src\Services\ToolsTreeBuilder.php:62 + + + tree.tools.edit.attachment_types + Bilagstype + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 + src\Services\ToolsTreeBuilder.php:64 + + + tree.tools.edit.categories + Kategorier + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 + src\Services\ToolsTreeBuilder.php:66 + + + tree.tools.edit.projects + Projekter + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 + src\Services\ToolsTreeBuilder.php:68 + + + tree.tools.edit.suppliers + Leverandører + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 + src\Services\ToolsTreeBuilder.php:70 + + + tree.tools.edit.manufacturer + Fabrikanter + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 + + + tree.tools.edit.storelocation + Lagerlokationer + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 + + + tree.tools.edit.footprint + Footprints + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 + + + tree.tools.edit.currency + Valuta + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 + + + tree.tools.edit.measurement_unit + Måleenhed + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.edit.label_profile + Labelprofil + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 + + + tree.tools.edit.part + Ny komponent + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + src\Services\ToolsTreeBuilder.php:77 + + + tree.tools.show.all_parts + Vis alle dele + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.show.all_attachments + Bilag + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 + new + + + tree.tools.show.statistics + Statistik + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 + + + tree.tools.system.users + Bruger + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 + + + tree.tools.system.groups + Grupper + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 + new + + + tree.tools.system.event_log + Hændelseslog + + + + + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + src\Services\TreeBuilder.php:124 + + + entity.tree.new + Nyt element + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 + obsolete + + + attachment.external_file + Ekstern fil + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + obsolete + + + attachment.edit + Rediger + + + + + Part-DB1\templates\_navbar.html.twig:27 + templates\base.html.twig:88 + obsolete + + + barcode.scan + Scan stregkode + + + + + Part-DB1\src\Form\UserSettingsType.php:119 + src\Form\UserSettingsType.php:49 + obsolete + + + user.theme.label + Tema + + + + + Part-DB1\src\Form\UserSettingsType.php:129 + src\Form\UserSettingsType.php:50 + obsolete + + + user_settings.theme.placeholder + Server Tema + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 + new + obsolete + + + log.user_login.ip + IP: + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:169 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:207 + new + obsolete + + + log.undo_mode.undo + Fortryd ændringer + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:171 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:209 + new + obsolete + + + log.undo_mode.revert + Element nulstillet + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 + new + obsolete + + + log.element_created.original_instock + Gammelt lager + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 + new + obsolete + + + log.element_deleted.old_name + Gammelt navn + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 + new + obsolete + + + log.element_edited.changed_fields + Ændrede egenskaber + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 + new + obsolete + + + log.instock_changed.comment + Kommentar + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 + new + obsolete + + + log.collection_deleted.deleted + Slettet element + + + + + templates\base.html.twig:81 + obsolete + obsolete + + + go.exclamation + Kom nu! + + + + + templates\base.html.twig:109 + obsolete + obsolete + + + language.english + Engelsk + + + + + templates\base.html.twig:112 + obsolete + obsolete + + + language.german + Tysk + + + + + obsolete + obsolete + + + flash.password_change_needed + Dit password skal ændres + + + + + obsolete + obsolete + + + attachment.table.type + Bilagstype + + + + + obsolete + obsolete + + + attachment.table.element + forbundet element + + + + + obsolete + obsolete + + + attachment.edit.isPicture + Billede? + + + + + obsolete + obsolete + + + attachment.edit.is3DModel + 3D-model? + + + + + obsolete + obsolete + + + attachment.edit.isBuiltin + Indbygget ressource? + + + + + obsolete + obsolete + + + category.edit.default_comment.placeholder + f.eks. brugbar til strømforsyning + + + + + obsolete + obsolete + + + tfa_backup.regenerate_codes + Generer nye backup-koder + + + + + obsolete + obsolete + + + validator.noneofitschild.self + Et element kan ikke være overordnet for sig selv! + + + + + obsolete + obsolete + + + validator.noneofitschild.children + Et underordnet element kan ikke være det overordnede element! + + + + + obsolete + obsolete + + + validator.part_lot.location_full.no_increasment + Den anvendte lagerlokation er markeret som fuld, derfor kan beholdningen ikke øges. (Nyt lager maksimum {{ old_amount }}) + + + + + obsolete + obsolete + + + validator.part_lot.location_full + Lagerpladsen er fuld, så nye dele kan ikke tilføjes. + + + + + obsolete + obsolete + + + validator.part_lot.only_existing + Opbevaringsstedet er markeret som "kun eksisterende dele", så nye dele kan ikke tilføjes. + + + + + obsolete + obsolete + + + validator.part_lot.single_part + Lagerlokationen er markeret som "Kun én komponent", så en ny komponent kan ikke tilføjes. + + + + + obsolete + obsolete + + + m_status.active.help + Komponenten er i øjeblikket under produktion og vil bliver produceret inden for en overskuelig fremtid. + + + + + obsolete + obsolete + + + m_status.announced.help + Komponenten er blevet annonceret, men er endnu ikke tilgængelig. + + + + + obsolete + obsolete + + + m_status.discontinued.help + Komponenten produceres ikke længere. + + + + + obsolete + obsolete + + + m_status.eol.help + Produktion af ​​komponenten vil snart ophøre. + + + + + obsolete + obsolete + + + m_status.nrfnd.help + Komponenten fremstilles stadig. Dog anbefales den ikke anvendt længere til nye designs. + + + + + obsolete + obsolete + + + m_status.unknown.help + Produktionsstatus er ukendt. + + + + + obsolete + obsolete + + + flash.success + Succes + + + + + obsolete + obsolete + + + flash.error + Fejl + + + + + obsolete + obsolete + + + flash.warning + Advarsel + + + + + obsolete + obsolete + + + flash.notice + Varsel + + + + + obsolete + obsolete + + + flash.info + Info + + + + + obsolete + obsolete + + + validator.noLockout + Du kan ikke fjerne din egen tilladelse til at redigere tilladelser. Dette sikrer dig imod ved et uheld at låse dig ude! + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter + Tilladte filtyper + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.help + Her kan du angive en kommasepareret liste over filtypenavne eller mime-typer, som en uploadet fil af denne type skal have. For at tillade alle understøttede billedfiler kan image/* benyttes. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.placeholder + f.eks. .txt, application/pdf, image/* + + + + + src\Form\PartType.php:63 + obsolete + obsolete + + + part.name.placeholder + f.eks. BC547 + + + + + obsolete + obsolete + + + entity.edit.not_selectable + Kan ikke vælges + + + + + obsolete + obsolete + + + entity.edit.not_selectable.help + Hvis denne mulighed er aktiveret, kan dette element ikke tildeles som en egenskab til nogen komponent. Nyttigt, for eksempel hvis dette element kun er beregnet til rene sorteringsformål. + + + + + obsolete + obsolete + + + bbcode.hint + BBCode kan bruges her (f.eks. [b]Fed[/b]) + + + + + obsolete + obsolete + + + entity.create + Opret element + + + + + obsolete + obsolete + + + entity.edit.save + Gem + + + + + obsolete + obsolete + + + category.edit.disable_footprints + Deaktiver Footprints + + + + + obsolete + obsolete + + + category.edit.disable_footprints.help + Når denne indstilling er aktiveret, er egenskaben footprint deaktiveret for alle komponenter i denne kategori. + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers + Deaktiver fabrikant + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers.help + Når denne indstilling er aktiveret, er fakbrikantegenskaben deaktiveret for alle komponenter i denne kategori. + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets + Deaktiver automatiske databladlinks + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets.help + Hvis denne mulighed er aktiveret, vil der ikke blive genereret automatiske databladlinks for komponenter med denne kategori. + + + + + obsolete + obsolete + + + category.edit.disable_properties + Deaktiver egenskaber + + + + + obsolete + obsolete + + + category.edit.disable_properties.help + Hvis denne indstilling er aktiveret, deaktiveres komponentegenskaberne for alle komponenter i denne kategori. + + + + + obsolete + obsolete + + + category.edit.partname_hint + Ledetråd for navn + + + + + obsolete + obsolete + + + category.edit.partname_hint.placeholder + f.eks. 100nF + + + + + obsolete + obsolete + + + category.edit.partname_regex + Navnefilter + + + + + obsolete + obsolete + + + category.edit.default_description + Standardbeskrivelse + + + + + obsolete + obsolete + + + category.edit.default_description.placeholder + f.eks. kondensator, 10 mm x 10 mm, SMD + + + + + obsolete + obsolete + + + category.edit.default_comment + Standardkommentar + + + + + obsolete + obsolete + + + company.edit.address + Adresse + + + + + obsolete + obsolete + + + company.edit.address.placeholder + F. eks. Eksempelvej 314 +3140 Eksempelby + + + + + obsolete + obsolete + + + company.edit.phone_number + Telefonnummer + + + + + obsolete + obsolete + + + company.edit.phone_number.placeholder + f. eks. +45 1234 4321 + + + + + obsolete + obsolete + + + company.edit.fax_number + Faxnummer + + + + + obsolete + obsolete + + + company.edit.email + e-mail + + + + + obsolete + obsolete + + + company.edit.email.placeholder + f.eks. kontakt@foo.bar + + + + + obsolete + obsolete + + + company.edit.website + Webside + + + + + obsolete + obsolete + + + company.edit.website.placeholder + https://www.foo.bar + + + + + obsolete + obsolete + + + company.edit.auto_product_url + Produkt URL + + + + + obsolete + obsolete + + + company.edit.auto_product_url.help + Dette felt benyttes til at knytte linke til en fabrikants komponentside Der vil %PARTNUMBER% så blive erstattet med ordrenummeret. + + + + + obsolete + obsolete + + + company.edit.auto_product_url.placeholder + https://foo.bar/product/%PARTNUMBER% + + + + + obsolete + obsolete + + + currency.edit.iso_code + ISO-kode + + + + + obsolete + obsolete + + + currency.edit.exchange_rate + Valutakurs + + + + + obsolete + obsolete + + + footprint.edit.3d_model + 3D-model + + + + + obsolete + obsolete + + + mass_creation.lines + Input + + + + + obsolete + obsolete + + + mass_creation.lines.placeholder + Element 1 + Element 1.1 + Element 1.1.1 + Element 1.2 +Element 2 +Element 3 + + + + + obsolete + obsolete + + + entity.mass_creation.btn + Opret + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer + er heltal + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer.help + Når denne option er aktiveret, vil alle mængder i denne enhed blive afrundet til hele tal. + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix + Benyt SI prefix + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix.help + Når denne option er aktiveret, bruges SI-præfikser, når tallene udlæses (f.eks. 1,2 kg i stedet for 1200 g) + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol + Enhedssymbol + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol.placeholder + f.eks. m + + + + + obsolete + obsolete + + + storelocation.edit.is_full.label + Lagerlokation er fyldt op + + + + + obsolete + obsolete + + + storelocation.edit.is_full.help + Når denne option er aktiveret, er det hverken muligt at tilføje nye komponenter til denne lagerplads eller at øge antallet af eksisterende komponenter. + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.label + Kun eksisterende komponenter + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.help + Når denne option er aktiveret, er det ikke muligt at tilføje nye komponenter til denne lagerplads, men det er muligt at øge antallet af eksisterende komponenter. + + + + + obsolete + obsolete + + + storelocation.only_single_part.label + Kun en komponent + + + + + obsolete + obsolete + + + storelocation.only_single_part.help + Hvis denne option er aktiveret, kan denne lagerplads kun indeholde en enkelt komponent, men i en hvilken som helst mængde. Nyttigt til små SMD-rum eller fødere. + + + + + obsolete + obsolete + + + storelocation.storage_type.label + Lagertype + + + + + obsolete + obsolete + + + storelocation.storage_type.help + Her kan du vælge en måleenhed, som en komponent skal have, så den kan opbevares på denne lagerplads. + + + + + obsolete + obsolete + + + supplier.edit.default_currency + Standardvaluta + + + + + obsolete + obsolete + + + supplier.shipping_costs.label + Forsendelsesomkostninger + + + + + obsolete + obsolete + + + user.username.placeholder + f.eks. j.doe + + + + + obsolete + obsolete + + + user.firstName.placeholder + f.eks. John + + + + + obsolete + obsolete + + + user.lastName.placeholder + f.eks. Doe + + + + + obsolete + obsolete + + + user.email.placeholder + j.doe@ecorp.com + + + + + obsolete + obsolete + + + user.department.placeholder + f.eks. Udvikling + + + + + obsolete + obsolete + + + user.settings.pw_new.label + Nyt pasword + + + + + obsolete + obsolete + + + user.settings.pw_confirm.label + bekræft nyt password + + + + + obsolete + obsolete + + + user.edit.needs_pw_change + Bruger skal ændre password + + + + + obsolete + obsolete + + + user.edit.user_disabled + Bruger deaktiveret (login ikke muligt) + + + + + obsolete + obsolete + + + user.create + Opret bruger + + + + + obsolete + obsolete + + + user.edit.save + Gem + + + + + obsolete + obsolete + + + entity.edit.reset + Fortryd ændringer + + + + + templates\Parts\show_part_info.html.twig:166 + obsolete + obsolete + + + part.withdraw.btn + Fjern + + + + + templates\Parts\show_part_info.html.twig:171 + obsolete + obsolete + + + part.withdraw.comment: + Kommentar/formål + + + + + templates\Parts\show_part_info.html.twig:189 + obsolete + obsolete + + + part.add.caption + Tilføj komponent + + + + + templates\Parts\show_part_info.html.twig:194 + obsolete + obsolete + + + part.add.btn + Tilføj + + + + + templates\Parts\show_part_info.html.twig:199 + obsolete + obsolete + + + part.add.comment + Kommentar/formål + + + + + templates\AdminPages\CompanyAdminBase.html.twig:15 + obsolete + obsolete + + + admin.comment + Noter + + + + + src\Form\PartType.php:83 + obsolete + obsolete + + + manufacturer_url.label + Producentlink + + + + + src\Form\PartType.php:66 + obsolete + obsolete + + + part.description.placeholder + f.eks. NPN 45V 0,1A 0,5W + + + + + src\Form\PartType.php:69 + obsolete + obsolete + + + part.instock.placeholder + f.eks. 10 + + + + + src\Form\PartType.php:72 + obsolete + obsolete + + + part.mininstock.placeholder + f.eks. 12 + + + + + obsolete + obsolete + + + part.order.price_per + Stykpris + + + + + obsolete + obsolete + + + part.withdraw.caption + Fjern komponenter + + + + + obsolete + obsolete + + + datatable.datatable.lengthMenu + _MENU_ + + + + + obsolete + obsolete + + + perm.group.parts + Komponenter + + + + + obsolete + obsolete + + + perm.group.structures + Datastrukturer + + + + + obsolete + obsolete + + + perm.group.system + System + + + + + obsolete + obsolete + + + perm.parts + Generelt + + + + + obsolete + obsolete + + + perm.read + Vis + + + + + obsolete + obsolete + + + perm.edit + Ret + + + + + obsolete + obsolete + + + perm.create + Opret + + + + + obsolete + obsolete + + + perm.part.move + Skift kategori + + + + + obsolete + obsolete + + + perm.delete + Slet + + + + + obsolete + obsolete + + + perm.part.search + Søg + + + + + obsolete + obsolete + + + perm.part.all_parts + Liste over alle komponenter + + + + + obsolete + obsolete + + + perm.part.no_price_parts + Komponenter uden prisinformation + + + + + obsolete + obsolete + + + perm.part.obsolete_parts + Vis udgåede komponenter + + + + + obsolete + obsolete + + + perm.part.unknown_instock_parts + Vis komponenter med ukendt lagerstatus + + + + + obsolete + obsolete + + + perm.part.change_favorite + Ret favoritstatus + + + + + obsolete + obsolete + + + perm.part.show_favorite + Vis favoritkomponenter + + + + + obsolete + obsolete + + + perm.part.show_last_edit_parts + Vis nyligt redigerede/tilføjede komponenter + + + + + obsolete + obsolete + + + perm.part.show_users + Vis den sidste bruger, der redigerede + + + + + obsolete + obsolete + + + perm.part.show_history + Se historik + + + + + obsolete + obsolete + + + perm.part.name + Navn + + + + + obsolete + obsolete + + + perm.part.description + Beskrivelse + + + + + obsolete + obsolete + + + perm.part.instock + På lager + + + + + obsolete + obsolete + + + perm.part.mininstock + mindste lager + + + + + obsolete + obsolete + + + perm.part.comment + Noter + + + + + obsolete + obsolete + + + perm.part.storelocation + Lagerlokation + + + + + obsolete + obsolete + + + perm.part.manufacturer + Fabrikant + + + + + obsolete + obsolete + + + perm.part.orderdetails + Ordreinformation + + + + + obsolete + obsolete + + + perm.part.prices + Pris + + + + + obsolete + obsolete + + + perm.part.attachments + Bilag + + + + + obsolete + obsolete + + + perm.part.order + Ordrer + + + + + obsolete + obsolete + + + perm.storelocations + Lagerlokationer + + + + + obsolete + obsolete + + + perm.move + Flyt + + + + + obsolete + obsolete + + + perm.list_parts + Vis komponentliste + + + + + obsolete + obsolete + + + perm.part.footprints + Footprints + + + + + obsolete + obsolete + + + perm.part.categories + Kategorier + + + + + obsolete + obsolete + + + perm.part.supplier + Leverandører + + + + + obsolete + obsolete + + + perm.part.manufacturers + Fabrikant + + + + + obsolete + obsolete + + + perm.projects + Projekter + + + + + obsolete + obsolete + + + perm.part.attachment_types + bilagstype + + + + + obsolete + obsolete + + + perm.tools.import + Import + + + + + obsolete + obsolete + + + perm.tools.labels + Labels + + + + + obsolete + obsolete + + + perm.tools.calculator + Modstandsberegner + + + + + obsolete + obsolete + + + perm.tools.footprints + Footprints + + + + + obsolete + obsolete + + + perm.tools.ic_logos + IC logoer + + + + + obsolete + obsolete + + + perm.tools.statistics + Statistik + + + + + obsolete + obsolete + + + perm.edit_permissions + Ret tilladelser + + + + + obsolete + obsolete + + + perm.users.edit_user_name + Ret brugernavn + + + + + obsolete + obsolete + + + perm.users.edit_change_group + Skift gruppe + + + + + obsolete + obsolete + + + perm.users.edit_infos + Ret information + + + + + obsolete + obsolete + + + perm.users.edit_permissions + Ret tilladelser + + + + + obsolete + obsolete + + + perm.users.set_password + Skift password + + + + + obsolete + obsolete + + + perm.users.change_user_settings + Ret brugerindstillinger + + + + + obsolete + obsolete + + + perm.database.see_status + Vis status + + + + + obsolete + obsolete + + + perm.database.update_db + Opdater database + + + + + obsolete + obsolete + + + perm.database.read_db_settings + Vis indstillinger + + + + + obsolete + obsolete + + + perm.database.write_db_settings + Ret indstillinger + + + + + obsolete + obsolete + + + perm.config.read_config + Vis konfiguration + + + + + obsolete + obsolete + + + perm.config.edit_config + Ret konfiguration + + + + + obsolete + obsolete + + + perm.config.server_info + Server info + + + + + obsolete + obsolete + + + perm.config.use_debug + Brug fejlfindingsværktøjer + + + + + obsolete + obsolete + + + perm.show_logs + Vis logs + + + + + obsolete + obsolete + + + perm.delete_logs + Slet log + + + + + obsolete + obsolete + + + perm.self.edit_infos + Ret info + + + + + obsolete + obsolete + + + perm.self.edit_username + Ret brugernavn + + + + + obsolete + obsolete + + + perm.self.show_permissions + Vis tilladelser + + + + + obsolete + obsolete + + + perm.self.show_logs + Se log + + + + + obsolete + obsolete + + + perm.self.create_labels + Opret labels + + + + + obsolete + obsolete + + + perm.self.edit_options + Ret indstillinger + + + + + obsolete + obsolete + + + perm.self.delete_profiles + Slet profiler + + + + + obsolete + obsolete + + + perm.self.edit_profiles + Ret profiler + + + + + obsolete + obsolete + + + perm.part.tools + Værktøjer + + + + + obsolete + obsolete + + + perm.groups + Grupper + + + + + obsolete + obsolete + + + perm.users + Brugere + + + + + obsolete + obsolete + + + perm.database + Database + + + + + obsolete + obsolete + + + perm.config + Instilinger + + + + + obsolete + obsolete + + + perm.system + System + + + + + obsolete + obsolete + + + perm.self + Ret din egen bruger + + + + + obsolete + obsolete + + + perm.labels + Labels + + + + + obsolete + obsolete + + + perm.part.category + Kategori + + + + + obsolete + obsolete + + + perm.part.minamount + Mindstebeholdning + + + + + obsolete + obsolete + + + perm.part.footprint + Footprint + + + + + obsolete + obsolete + + + perm.part.mpn + MPN + + + + + obsolete + obsolete + + + perm.part.status + Fremstillingsstatus + + + + + obsolete + obsolete + + + perm.part.tags + Tags + + + + + obsolete + obsolete + + + perm.part.unit + Måleenhed + + + + + obsolete + obsolete + + + perm.part.mass + Vægt + + + + + obsolete + obsolete + + + perm.part.lots + Lagerlokationer + + + + + obsolete + obsolete + + + perm.show_users + Vis den sidste bruger, der redigerede + + + + + obsolete + obsolete + + + perm.currencies + Valuta + + + + + obsolete + obsolete + + + perm.measurement_units + Måleenhed + + + + + obsolete + obsolete + + + user.settings.pw_old.label + Gammelt password + + + + + obsolete + obsolete + + + pw_reset.submit + Nulstil password + + + + + obsolete + obsolete + + + u2f_two_factor + Sikkerhedsnøgle (U2F) + + + + + obsolete + obsolete + + + google + Google + + + + + tfa.provider.webauthn_two_factor_provider + Sikkerhedsnøgle + + + + + obsolete + obsolete + + + tfa.provider.google + Godkendelses-app + + + + + obsolete + obsolete + + + Login successful + Logget ind med succes + + + + + obsolete + obsolete + + + log.type.exception + Ubehandlet undtagelse (udfaset) + + + + + obsolete + obsolete + + + log.type.user_login + Bruger logget ind + + + + + obsolete + obsolete + + + log.type.user_logout + Bruger logget ud + + + + + obsolete + obsolete + + + log.type.unknown + Ukendt + + + + + obsolete + obsolete + + + log.type.element_created + Element oprettet + + + + + obsolete + obsolete + + + log.type.element_edited + Element rettet + + + + + obsolete + obsolete + + + log.type.element_deleted + Element slettet + + + + + obsolete + obsolete + + + log.type.database_updated + Database er opdateret + + + + + obsolete + + + perm.revert_elements + Nulstil element + + + + + obsolete + + + perm.show_history + Vis historik + + + + + obsolete + + + perm.tools.lastActivity + Vis sidste aktivitet + + + + + obsolete + + + perm.tools.timeTravel + Vis tidligere versioner (tidsrejser) + + + + + obsolete + + + tfa_u2f.key_added_successful + Sikkerhedsnøgle tilføjet + + + + + obsolete + + + Username + Brugernavn + + + + + obsolete + + + log.type.security.google_disabled + Godkendelses-app deaktiveret + + + + + obsolete + + + log.type.security.u2f_removed + Sikkerhedsnøgle slettet + + + + + obsolete + + + log.type.security.u2f_added + Sikkerhedsnøgle tilføjet + + + + + obsolete + + + log.type.security.backup_keys_reset + Backupnøgler regenereret + + + + + obsolete + + + log.type.security.google_enabled + Godkendelses-app aktiveret + + + + + obsolete + + + log.type.security.password_changed + Password ændret + + + + + obsolete + + + log.type.security.trusted_device_reset + Godkendte enheder nulstillet + + + + + obsolete + + + log.type.collection_element_deleted + Hovedelement slettet + + + + + obsolete + + + log.type.security.password_reset + Nulstil password + + + + + obsolete + + + log.type.security.2fa_admin_reset + To-faktor-godkendelse nulstillet af administrator + + + + + obsolete + + + log.type.user_not_allowed + Forsøg på uautoriseret adgang + + + + + obsolete + + + log.database_updated.success + Succes + + + + + obsolete + + + label_options.barcode_type.2D + 2D + + + + + obsolete + + + label_options.barcode_type.1D + 1D + + + + + obsolete + + + perm.part.parameters + Parametre + + + + + obsolete + + + perm.attachment_show_private + Vis private bilag + + + + + obsolete + + + perm.tools.label_scanner + Labelscannner + + + + + obsolete + + + perm.self.read_profiles + Vis profiler + + + + + obsolete + + + perm.self.create_profiles + Opret profil + + + + + obsolete + + + perm.labels.use_twig + Brug Twig tilstand + + + + + label_profile.showInDropdown + Vis hurtigvalg i barcode + + + + + group.edit.enforce_2fa + Forlang to-faktor-godkendelse (2FA) + + + + + group.edit.enforce_2fa.help + Hvis denne option er valgt, skal hvert direkte medlem af denne gruppe konfigurere mindst en anden faktor til godkendelse. Anbefales f.eks. til administrative grupper med omfattende autorisationer. + + + + + selectpicker.empty + Ingenting valgt + + + + + selectpicker.nothing_selected + Ingenting valgt + + + + + entity.delete.must_not_contain_parts + Elementet "%PATH%" indeholder stadig komponenter. Rediger komponenterne for at kunne slette dette element. + + + + + entity.delete.must_not_contain_attachments + Filtypen indeholder stadig komponenter. Skift deres filtype for at kunne slette denne filtype. + + + + + entity.delete.must_not_contain_prices + Valuta indeholder stadig komponenter. Skift deres valuta for at kunne slette denne valuta. + + + + + entity.delete.must_not_contain_users + Der er stadigvæk brugere i denne gruppe. Vælg en anden gruppe for disse brugere for at kunne slette denne gruppe. + + + + + part.table.edit + Ret + + + + + part.table.edit.title + Ret komponent + + + + + part_list.action.action.title + Vælg handling + + + + + part_list.action.action.group.favorite + Favorit + + + + + part_list.action.action.favorite + Gør denne til favorit + + + + + part_list.action.action.unfavorite + Fjern favorit + + + + + part_list.action.action.group.change_field + Ret felt + + + + + part_list.action.action.change_category + Skift kategori + + + + + part_list.action.action.change_footprint + Ret footprint + + + + + part_list.action.action.change_manufacturer + Ret fabrikant + + + + + part_list.action.action.change_unit + Ret måleenhed + + + + + part_list.action.action.delete + Slet + + + + + part_list.action.submit + Ok + + + + + part_list.action.part_count + %count% komponenter valgt + + + + + company.edit.quick.website + Åbn webside + + + + + company.edit.quick.email + send e-mail + + + + + company.edit.quick.phone + Ring op + + + + + company.edit.quick.fax + Send fax + + + + + company.fax_number.placeholder + f.eks. +45 1234 5678 + + + + + part.edit.save_and_clone + Gem og dupliker + + + + + validator.file_ext_not_allowed + Filtypenavnet er ikke tilladt for denne bilagstype. + + + + + tools.reel_calc.title + SMD-rulle beregner + + + + + tools.reel_calc.inner_dia + Indre diameter + + + + + tools.reel_calc.outer_dia + Ydre diameter + + + + + tools.reel_calc.tape_thick + Tape tykkelse + + + + + tools.reel_calc.part_distance + Komponentafstand + + + + + tools.reel_calc.update + Opdater + + + + + tools.reel_calc.parts_per_meter + Komponenter per meter + + + + + tools.reel_calc.result_length + Tapelængde + + + + + tools.reel_calc.result_amount + Omtrentligt antal komponenter + + + + + tools.reel_calc.outer_greater_inner_error + Fejl: Den ydre diameter skal være større end den indvendige diameter! + + + + + tools.reel_calc.missing_values.error + Angiv venligst alle værdier! + + + + + tools.reel_calc.load_preset + Indlæs preset + + + + + tools.reel_calc.explanation + Denne kalkulator giver dig mulighed for at estimere hvor mange komponenter der er tilbage på en SMD-rulle. Mål de angivne dimensioner på rullen (eller brug specifikationerne) og tryk på "Opdater". + + + + + perm.tools.reel_calculator + SMD-rulle kalkulator + + + + + tree.tools.tools.reel_calculator + SMD-rullle kalkulator + + + + + user.pw_change_needed.flash + Ændring af password påkrævet! Venligst vælg et nyt password. + + + + + tree.root_node.text + Rod + + + + + part_list.action.select_null + Ingen elementer tilstede! + + + + + part_list.action.delete-title + Vil du virkelig slette disse komponenter? + + + + + part_list.action.delete-message + Disse komponenter og alle tilknyttede oplysninger (bilag, vedhæftede filer, prisoplysninger osv.) slettes. Dette kan ikke fortrydes! + + + + + part.table.actions.success + Handling lykkedes med succes. + + + + + attachment.edit.delete.confirm + Er du sikker på, at du vil slette dette bilag? + + + + + filter.text_constraint.value.operator.EQ + Lig med + + + + + filter.text_constraint.value.operator.NEQ + Forskellig fra + + + + + filter.text_constraint.value.operator.STARTS + Begynder med + + + + + filter.text_constraint.value.operator.CONTAINS + Indeholder + + + + + filter.text_constraint.value.operator.ENDS + Slutter med + + + + + filter.text_constraint.value.operator.LIKE + LIKE udtryk + + + + + filter.text_constraint.value.operator.REGEX + Almindelig udtryk + + + + + filter.number_constraint.value.operator.BETWEEN + Mellem + + + + + filter.number_constraint.AND + og + + + + + filter.entity_constraint.operator.EQ + Lig med (uden underelementer) + + + + + filter.entity_constraint.operator.NEQ + Forskellig fra (uden underelementer) + + + + + filter.entity_constraint.operator.INCLUDING_CHILDREN + Lig med (inklusiv underelementer) + + + + + filter.entity_constraint.operator.EXCLUDING_CHILDREN + Forskellig fra (inklusiv underelementer) + + + + + part.filter.dbId + Database ID + + + + + filter.tags_constraint.operator.ANY + Alle tags + + + + + filter.tags_constraint.operator.ALL + Alle tags + + + + + filter.tags_constraint.operator.NONE + Ingen tags + + + + + part.filter.lot_count + Antal partier + + + + + part.filter.attachments_count + Antal bilag + + + + + part.filter.orderdetails_count + Antal bestillingsinformationer + + + + + part.filter.lotExpirationDate + Udløbsdato for komponentbeholdning + + + + + part.filter.lotNeedsRefill + Lagerbeholdning skal genopfyldes + + + + + part.filter.lotUnknwonAmount + Lagerbestand med ukendt antal + + + + + part.filter.attachmentName + Bilagsnavn + + + + + filter.choice_constraint.operator.ANY + En af de udvalgte + + + + + filter.choice_constraint.operator.NONE + Ingen af de udvalgte + + + + + part.filter.amount_sum + Samlet mængde + + + + + filter.submit + Updatér + + + + + filter.discard + Fortryd ændringer + + + + + filter.clear_filters + Slet alle filtre + + + + + filter.title + Filter + + + + + filter.parameter_value_constraint.operator.= + Typ. Værdi = + + + + + filter.parameter_value_constraint.operator.!= + Typ. Værdi != + + + + + filter.parameter_value_constraint.operator.< + Typ. værdi < + + + + + filter.parameter_value_constraint.operator.> + Typ. værdi > + + + + + filter.parameter_value_constraint.operator.<= + Typ. værdi <= + + + + + filter.parameter_value_constraint.operator.>= + Typ. værdi >= + + + + + filter.parameter_value_constraint.operator.BETWEEN + Typ. værdi imellem + + + + + filter.parameter_value_constraint.operator.IN_RANGE + I værdiområdet + + + + + filter.parameter_value_constraint.operator.NOT_IN_RANGE + Ikke i værdiområdet + + + + + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE + Større end værdiområdet + + + + + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE + Større end eller lig med værdiområdet + + + + + filter.parameter_value_constraint.operator.LESS_THAN_RANGE + Mindre end værdiområdet + + + + + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE + Mindre end lig med værdiområdet + + + + + filter.parameter_value_constraint.operator.RANGE_IN_RANGE + helt indenfor værdiområdet + + + + + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE + Skærer værdiområdet + + + + + filter.text_constraint.value + Ingen værdi angivet + + + + + filter.number_constraint.value1 + Ingen værdi angivet + + + + + filter.number_constraint.value2 + Maksimalværdi + + + + + filter.datetime_constraint.value1 + Ingen dato/tid indstillet + + + + + filter.datetime_constraint.value2 + Maksimal dato/tid + + + + + filter.constraint.add + Tilføj filter + + + + + part.filter.parameters_count + Antal parametre + + + + + part.filter.lotDescription + Beskrivelse af komponentbeholdning + + + + + parts_list.search.searching_for + Søg i dele med søgeordet <b>%søgeord%</b> + + + + + parts_list.search_options.caption + Aktiverede søgemuligheder + + + + + attachment.table.element_type + Aktiverede søgemuligheder + + + + + log.level.debug + Debug + + + + + log.level.info + Info + + + + + log.level.notice + Meddelele + + + + + log.level.warning + Advarsel + + + + + log.level.error + Fejl + + + + + log.level.critical + Kritisk + + + + + log.level.alert + Alarm + + + + + log.level.emergency + Nødsituation + + + + + log.type.security + Sikkerhedsbegivenhed + + + + + log.type.instock_changed + [ALT] Beholdning ændret + + + + + log.target_id + ID for målelementet + + + + + entity.info.parts_count_recursive + Komponenter med dette element eller dets underelementer + + + + + tools.server_infos.title + Serverinfo + + + + + permission.preset.read_only + Read-only + + + + + permission.preset.read_only.desc + Tillad kun read-only for data + + + + + permission.preset.all_inherit + Alle nedarvede + + + + + permission.preset.all_inherit.desc + Indstil alle tilladelser til at kunne arve + + + + + permission.preset.all_forbid + Forbyd alle + + + + + permission.preset.all_forbid.desc + Indstil alle tilladelser til Ikke tilladt + + + + + permission.preset.all_allow + Tillad alle + + + + + permission.preset.all_allow.desc + Indstill alle tilladelser til Tilladt + + + + + perm.server_infos + Serverinfo + + + + + permission.preset.editor + Redaktør + + + + + permission.preset.editor.desc + Tillad at komponenter og datastrukturer kan redigeres + + + + + permission.preset.admin + Admin + + + + + permission.preset.admin.desc + Tillad administrative handlinger + + + + + permission.preset.button + Anvend skabelon + + + + + perm.attachments.show_private + Vis private bilag + + + + + perm.attachments.list_attachments + Se liste over alle bilag + + + + + user.edit.permission_success + Tilladelsesskabelon blev anvendt. Tjek, at de nye tilladelser lever op til dine forventninger. + + + + + perm.group.data + Data + + + + + part_list.action.action.group.needs_review + Gennemsyn nødvendigt + + + + + part_list.action.action.set_needs_review + Sæt status til Gennemgang nødvændig + + + + + part_list.action.action.unset_needs_review + Fjern Gennemganng nødvendig status + + + + + part.edit.ipn + Internt Partnummer (IPN) + + + + + part.ipn.not_defined + Ikke defineret + + + + + part.table.ipn + IPN + + + + + currency.edit.update_rate + Hent valutakurs + + + + + currency.edit.exchange_rate_update.unsupported_currency + Valutaen understøttes ikke af valutakursudbyderen. Tjek venligst konfigurationen af ​​valutakursudbyderne. + + + + + currency.edit.exchange_rate_update.generic_error + Valutakursen kan kke hentes. Tjek venligst konfigurationen af ​​valutakursudbyderne. + + + + + currency.edit.exchange_rate_updated.success + Valutakurs hentet med succes + + + + + project.bom.quantity + BOM mængde + + + + + project.bom.mountnames + Bestykningsnavn + + + + + project.bom.name + Navn + + + + + project.bom.comment + Noter + + + + + project.bom.part + Komponent + + + + + project.bom.add_entry + Tilføj post + + + + + part_list.action.group.projects + Projekter + + + + + part_list.action.projects.add_to_project + Tilføj komponent til projekt + + + + + project.bom.delete.confirm + Vil du virkeligt slette denne stykliste (BOM)? + + + + + project.add_parts_to_project + Tilføj komponenter til stykliste (BOM) + + + + + part.info.add_part_to_project + Føj denne komponent til et projekt + + + + + project_bom_entry.label + Registrering af BOM + + + + + project.edit.status + Projektstatus + + + + + project.status.draft + Kladde + + + + + project.status.planning + Under planlægning + + + + + project.status.in_production + I produktion + + + + + project.status.finished + Ophørt + + + + + project.status.archived + Arkiveret + + + + + part.new_build_part.error.build_part_already_exists + Dette projekt har allerede en linket komponent + + + + + project.edit.associated_build_part + Tilhørende bygge komponent + + + + + project.edit.associated_build_part.add + Tilføj bygge komponent + + + + + project.edit.associated_build.hint + Denne komponent repræsenterer de færdigbyggede forekomster af projektet, der er gemt et sted + + + + + part.info.projectBuildPart.hint + Denne komponent repræsenterer de byggede forekomster af det følgende projekt og er knyttet til det + + + + + part.is_build_part + Er produktkomponent + + + + + project.info.title + Projektinfo + + + + + project.info.bom_entries_count + Styklisteposter + + + + + project.info.sub_projects_count + Underprojekt + + + + + project.info.bom_add_parts + Tilføj nye styklisteposter + + + + + project.info.info.label + Info + + + + + project.info.sub_projects.label + Underprojekter + + + + + project.bom.price + Pris + + + + + part.info.withdraw_modal.title.withdraw + Fjern komponenter fra lot + + + + + part.info.withdraw_modal.title.add + Tilføj komponenter til lot + + + + + part.info.withdraw_modal.title.move + Flyt komponenter til et andet lot + + + + + part.info.withdraw_modal.amount + Mængde + + + + + part.info.withdraw_modal.move_to + Flyt til + + + + + part.info.withdraw_modal.comment + Kommentar + + + + + part.info.withdraw_modal.comment.hint + Du kan indtaste en kommentar her, der beskriver, hvorfor denne handling blev udført (f.eks. hvorfor denne komponent var nødvendig). Disse oplysninger gemmes i loggen. + + + + + modal.close + Luk + + + + + modal.submit + Indsend + + + + + part.withdraw.success + Komponenter blev fjernet/tilføjet/flyttet. + + + + + perm.parts_stock + Komponentbeholdning + + + + + perm.parts_stock.withdraw + Fjern komponenter fra lageret + + + + + perm.parts_stock.add + Tilføj komponenter til lager + + + + + perm.parts_stock.move + Flyt komponenter mellem lagerbeholdninger + + + + + user.permissions_schema_updated + Din brugerkontos tilladelsesliste er blevet opdateret til den seneste version. + + + + + log.type.part_stock_changed + Komponentbeholdning ændret + + + + + log.part_stock_changed.withdraw + Komponenter fjernet + + + + + log.part_stock_changed.add + Komponenter tilføjet til lager + + + + + log.part_stock_changed.move + Komponenter flyttet + + + + + log.part_stock_changed.comment + Kommentar + + + + + log.part_stock_changed.change + Ændring + + + + + log.part_stock_changed.move_target + Flyttet til + + + + + tools.builtin_footprints_viewer.title + Indbyggede Footprint billeder + + + + + tools.builtin_footprints_viewer.hint + Dette galleri viser alle de inkluderede footprint-billeder. Hvis du vil bruge det i et bilag, skal du skrive navnet (eller et nøgleord) i bilagets URL-felt og vælge det ønskede billede fra rullemenuen. + + + + + tools.ic_logos.title + IC logoer + + + + + part_list.action.group.labels + Labels + + + + + part_list.action.projects.generate_label + Opret labels (til komponenter) + + + + + part_list.action.projects.generate_label_lot + Opret labels (til komponentbeholdning) + + + + + part_list.action.generate_label.empty + Tom label + + + + + project.info.builds.label + Byg + + + + + project.builds.build_not_possible + Byg ikke mulig: Der er ikke nok komponenter tilsted for dette byg + + + + + project.builds.following_bom_entries_miss_instock + Følgende komponenter har ikke nok lagerbeholdning til at bygge dette projekt mindst én gang: + + + + + project.builds.stocked + På lager + + + + + project.builds.needed + Nødvendig + + + + + project.builds.build_possible + Der kan laves et byg + + + + + project.builds.number_of_builds_possible + Du har nok komponenter på lager til at bygge <b>%max_builds%</b> kopier af dette projekt. + + + + + project.builds.check_project_status + Den aktuelle projektstatus er <b>"%project_status%"</b>. Du bør tjekke, om du virkelig vil bygge projektet med denne status! + + + + + project.builds.following_bom_entries_miss_instock_n + Der er ikke nok komponenter på lager til at bygge dette projekt %number_of_builds% gange. Der er ikke nok af følgende komponenter på lager. + + + + + project.build.flash.invalid_input + Projektet kan ikke bygges. Tjek venligst dine input + + + + + project.build.required_qty + Nødvendigt antal + + + + + project.build.btn_build + Byg + + + + + project.build.help + Vælg fra hvilke lagre de komponenter, der kræves til bygget skal tages (og i hvilken mængde). Marker afkrydsningsfeltet for hver styklistepost, når du har fjernet komponenterne, eller brug det øverste afkrydsningsfelt til at markere alle felterne på én gang. + + + + + project.build.buildsPartLot.new_lot + Opret ny beholdning + + + + + project.build.add_builds_to_builds_part + Tilføj byg til projekt-byg-dele + + + + + project.build.builds_part_lot + Mål mængde + + + + + project.builds.number_of_builds + Byg antal + + + + + project.builds.no_stocked_builds + Antal gemte byg-instanser + + + + + user.change_avatar.label + Ændr profilbillede + + + + + user_settings.change_avatar.label + Ændre profilbillede + + + + + user_settings.remove_avatar.label + Fjern profilbillede + + + + + part.edit.name.category_hint + tip fra kategori + + + + + category.edit.partname_regex.placeholder + f.eks. "/Kondensator \d+ nF/i" + + + + + category.edit.partname_regex.help + Et PCRE-kompatibelt regulært udtryk, som delnavnet skal opfylde. + + + + + entity.select.add_hint + Brug -> for at oprette under-strukturer, f.eks. "Element 1->Element 1.1" + + + + + entity.select.group.new_not_added_to_DB + Ny (endnu ikke tilføjet til database) + + + + + part.edit.save_and_new + Gem og opret en ny tom komponent + + + + + homepage.first_steps.title + Første skridt + + + + + homepage.first_steps.introduction + Databasen er i øjeblikket tom. Du vil måske læse <a href="%url%">dokumentationen</a> eller begynde at oprette følgende datastrukturer. + + + + + homepage.first_steps.create_part + Eller du kan direkte oprette en <a href="%url%">ny komponent</a>. + + + + + homepage.first_steps.hide_hint + Denne meddelelse vil blive skjult, når du har oprettet den første komponent. + + + + + homepage.forum.text + For spørgsmål om Part-DB, brug <a class="link-external" rel="noopener" target="_blank" href="%href%">diskussionsforummet</a> + + + + + log.element_edited.changed_fields.category + Kategori + + + + + log.element_edited.changed_fields.footprint + Footprint + + + + + log.element_edited.changed_fields.manufacturer + Fabrikant + + + + + log.element_edited.changed_fields.value_typical + typ. værdi + + + + + log.element_edited.changed_fields.pw_reset_expires + Nulstil password + + + + + log.element_edited.changed_fields.comment + Noter + + + + + log.element_edited.changed_fields.supplierpartnr + Leverandør part-nummer + + + + + log.element_edited.changed_fields.supplier_product_url + Produkt URL + + + + + log.element_edited.changed_fields.price + Pris + + + + + log.element_edited.changed_fields.min_discount_quantity + Minimum ordremængde + + + + + log.element_edited.changed_fields.original_filename + Originalt bilagsnavn, filnavn + + + + + log.element_edited.changed_fields.path + Sti + + + + + log.element_edited.changed_fields.description + Beskrivelse + + + + + log.element_edited.changed_fields.manufacturing_status + Fabrikantstatus + + + + + log.element_edited.changed_fields.options.barcode_type + Barcode type + + + + + log.element_edited.changed_fields.status + Status + + + + + log.element_edited.changed_fields.quantity + BOM antal + + + + + log.element_edited.changed_fields.mountnames + Montagenavne + + + + + log.element_edited.changed_fields.name + Navn + + + + + log.element_edited.changed_fields.part + Komponent + + + + + log.element_edited.changed_fields.price_currency + prisens valuta + + + + + log.element_edited.changed_fields.partname_hint + Komponentnavn henvisning + + + + + log.element_edited.changed_fields.partname_regex + Navnefilter + + + + + log.element_edited.changed_fields.disable_footprints + Deaktiver footprints + + + + + log.element_edited.changed_fields.disable_manufacturers + Deaktiver fabrikanter + + + + + log.element_edited.changed_fields.disable_autodatasheets + Deaktiver automatiske databladlinks + + + + + log.element_edited.changed_fields.disable_properties + Deaktiver egenskaber + + + + + log.element_edited.changed_fields.default_description + Standardbeskrivelse + + + + + log.element_edited.changed_fields.default_comment + Standardkommentar + + + + + log.element_edited.changed_fields.filetype_filter + Tilladte bilagstyper + + + + + log.element_edited.changed_fields.not_selectable + Ikke valgt + + + + + log.element_edited.changed_fields.parent + Overordnet element + + + + + log.element_edited.changed_fields.shipping_costs + Forsendelsespris + + + + + log.element_edited.changed_fields.default_currency + Standard valuta + + + + + log.element_edited.changed_fields.address + Adresse + + + + + log.element_edited.changed_fields.phone_number + Telefonnummer + + + + + log.element_edited.changed_fields.fax_number + Fax nummer + + + + + log.element_edited.changed_fields.email_address + e-mail + + + + + log.element_edited.changed_fields.website + Webside + + + + + log.element_edited.changed_fields.auto_product_url + Produkt URL + + + + + log.element_edited.changed_fields.is_full + Lagerplads fuld + + + + + log.element_edited.changed_fields.limit_to_existing_parts + Kun eksisterende komponenter + + + + + log.element_edited.changed_fields.only_single_part + Kun én komponent + + + + + log.element_edited.changed_fields.storage_type + Lagertype + + + + + log.element_edited.changed_fields.footprint_3d + 3D-model + + + + + log.element_edited.changed_fields.master_picture_attachment + Miniaturebillede + + + + + log.element_edited.changed_fields.exchange_rate + Veksel kurs + + + + + log.element_edited.changed_fields.iso_code + ISO-kode + + + + + log.element_edited.changed_fields.unit + Enhedssymbol + + + + + log.element_edited.changed_fields.is_integer + Er heltal + + + + + log.element_edited.changed_fields.use_si_prefix + Benyt SI-præfiks + + + + + log.element_edited.changed_fields.options.width + Bredde + + + + + log.element_edited.changed_fields.options.height + Højde + + + + + log.element_edited.changed_fields.options.supported_element + Elementtype + + + + + log.element_edited.changed_fields.options.additional_css + Yderligere CSS + + + + + log.element_edited.changed_fields.options.lines + Indhold + + + + + log.element_edited.changed_fields.permissions.data + Tilladelser + + + + + log.element_edited.changed_fields.disabled + Deaktiveret + + + + + log.element_edited.changed_fields.theme + Tema + + + + + log.element_edited.changed_fields.timezone + Tidszone + + + + + log.element_edited.changed_fields.language + Sprog + + + + + log.element_edited.changed_fields.email + e-mail + + + + + log.element_edited.changed_fields.department + Afdeling + + + + + log.element_edited.changed_fields.last_name + Fornavn + + + + + log.element_edited.changed_fields.first_name + Efternavn + + + + + log.element_edited.changed_fields.group + Gruppe + + + + + log.element_edited.changed_fields.currency + foretrukken valuta + + + + + log.element_edited.changed_fields.enforce2FA + Fremtving 2FA + + + + + log.element_edited.changed_fields.symbol + Symbol + + + + + log.element_edited.changed_fields.value_min + Minimum værdi + + + + + log.element_edited.changed_fields.value_max + Maksimum værdi + + + + + log.element_edited.changed_fields.value_text + Tekstværdi + + + + + log.element_edited.changed_fields.show_in_table + Vis på tabelform + + + + + log.element_edited.changed_fields.attachment_type + Datatype + + + + + log.element_edited.changed_fields.needs_review + Behøver gennemgang + + + + + log.element_edited.changed_fields.tags + Tags + + + + + log.element_edited.changed_fields.mass + Masse + + + + + log.element_edited.changed_fields.ipn + IPN + + + + + log.element_edited.changed_fields.favorite + Favorit + + + + + log.element_edited.changed_fields.minamount + Minimum lagerbeholdning + + + + + log.element_edited.changed_fields.manufacturer_product_url + Link til produktside + + + + + log.element_edited.changed_fields.manufacturer_product_number + MPN + + + + + log.element_edited.changed_fields.partUnit + Måleenhed + + + + + log.element_edited.changed_fields.expiration_date + Udløbsdato + + + + + log.element_edited.changed_fields.amount + Mængde + + + + + log.element_edited.changed_fields.storage_location + Lagerlokation + + + + + attachment.max_file_size + Maksimum bilagsstørrelse + + + + + user.saml_user + SSO / SAML Bruger + + + + + user.saml_user.pw_change_hint + Du bruger Single Sign-On (SSO) til at logge ind. Du kan derfor ikke konfigurere dit password og to-faktor godkendelse her. Brug i stedet det centrale websted for din SSO-udbyder! + + + + + login.sso_saml_login + Single Sign-On Login (SSO) + + + + + login.local_login_hint + Nedenstående formular kan kun bruges til at logge ind med en lokal bruger. Ønsker du i stedet at logge ind via single sign-on, skal du bruge knappen ovenfor. + + + + + part_list.action.action.export + Eksporter komponenter + + + + + part_list.action.export_json + Eksporter som JSON + + + + + part_list.action.export_csv + Eksporter som CSV + + + + + part_list.action.export_yaml + Esporter som YAML + + + + + part_list.action.export_xml + Eksporter som XML + + + + + parts.import.title + Importer kompenter + + + + + parts.import.errors.title + Problemer ved import + + + + + parts.import.flash.error + Der opstod fejl under eksport. Dette skyldes sandsynligvis fejlbehæftede data. + + + + + parts.import.format.auto + Automatisk (baseret på endelsen på bilagsnavnet) + + + + + parts.import.flash.error.unknown_format + Formatet kunne ikke bestemmes automatisk. Vælg venligst det korrekte format manuelt! + + + + + parts.import.flash.error.invalid_file + Bilaget er beskadiget/forkert formateret. Tjek, at du har valgt det rigtige format. + + + + + parts.import.part_category.label + Gennemtving kategori + + + + + parts.import.part_category.help + Hvis du vælger en kategori her, vil alle importerede komponenter blive tildelt denne kategori, uanset hvad der er i importdataene. + + + + + import.create_unknown_datastructures + Opret ukendte datastrukturer + + + + + import.create_unknown_datastructures.help + Hvis denne mulighed er valgt, oprettes automatisk datastrukturer (f.eks. kategorier, footprints osv.), som endnu ikke findes i databasen. Hvis denne mulighed ikke er valgt, vil kun datastrukturer, der allerede findes i databasen, blive brugt. Og hvis der ikke findes en passende struktur, vil det tilsvarende felt for komponenten stå tomt. + + + + + import.path_delimiter + Stibegrænser + + + + + import.path_delimiter.help + Afgrænseren bruges til at adskille de forskellige niveauer af datastrukturer (såsom kategorier, footprint osv.) i stispecifikationer. + + + + + parts.import.help_documentation + Se <a href="%link%">dokumentationen</a> for mere information om bilagsformatet. + + + + + parts.import.help + Med dette værktøj kan du importere komponenter fra eksisterende bilag. Komponenterne gemmes direkte i databasen (uden mulighed for at kontrollere dem igen på forhånd). Tjek venligst din importfil her, før du uploader! + + + + + parts.import.flash.success + Komponentimport lykkedes + + + + + parts.import.errors.imported_entities + Importerde komponenter + + + + + perm.import + Importer data + + + + + parts.import.part_needs_review.label + Marker alle komponenter som "Gennnemgang nødvendig" + + + + + parts.import.part_needs_review.help + Hvis denne mulighed er valgt, vil alle dele blive markeret som "Kræver gennemgang", uanset hvad der blev angivet i dataene. + + + + + project.bom_import.flash.success + %count% BOM Einträge erfolgreich importiert. +​ +Oversæt med stemmen +44 / 5.000 +Oversættelsesresultater +Oversættelsen +%count% styklisteposter blev importeret. + + + + + project.bom_import.type + Typ + + + + + project.bom_import.type.kicad_pcbnew + KiCAD Pcbnew BOM (CSV fil) + + + + + project.bom_import.clear_existing_bom + let eksisterende styklisteposter før import + + + + + project.bom_import.clear_existing_bom.help + Hvis denne mulighed er valgt, vil alle styklisteposter, der allerede findes i projektet, blive slettet og overskrevet med de importerede styklistedata. + + + + + project.bom_import.flash.invalid_file + Filen kunne ikke importeres. Tjek, at du har valgt den korrekte bilagstype. Fejlmeddelelse: %message% + + + + + project.bom_import.flash.invalid_entries + Valideringsfejl! Tjek venligst den importerede fil! + + + + + project.import_bom + Importer stykliste til projekt + + + + + project.edit.bom.import_bom + Importer BOM + + + + + measurement_unit.new + Ny måleenhed + + + + + measurement_unit.edit + Ret måleenhed + + + + + user.aboutMe.label + Om mig + + + + + storelocation.owner.label + Ejer + + + + + storelocation.part_owner_must_match.label + Komponentejer skal matche lagerplaceringsejer + + + + + part_lot.owner + Ejer + + + + + part_lot.owner.help + Kun ejeren kan fjerne eller tilføje komponenter fra denne beholdning. + + + + + log.element_edited.changed_fields.owner + Ejer + + + + + log.element_edited.changed_fields.instock_unknown + Mængde ukendt + + + + + log.element_edited.changed_fields.needs_refill + Skal genopfyldes + + + + + part.withdraw.access_denied + Du er ikke autoriseret til at udføre den ønskede handling! Tjek venligst dine autorisationer og ejeren af ​​komponentbeholdningen. + + + + + part.info.amount.less_than_desired + Mindre end ønsket + + + + + log.cli_user + CLI bruger + + + + + log.element_edited.changed_fields.part_owner_must_match + Komponentejeren skal svare til lagerstedets ejer! + + + + + part.filter.lessThanDesired + Mindre tilgængelig end ønsket (samlet mængde < minimumsmængde) + + + + + part.filter.lotOwner + Ejer af inventaret + + + + + user.show_email_on_profile.label + Vis e-mail adresse på den offenlige profilside + + + + + log.details.title + Log detaljer + + + + + log.user_login.login_from_ip + Login fra IP-Adresse + + + + + log.user_login.ip_anonymize_hint + Hvis de sidste cifre i IP-adressen mangler, aktiveres databeskyttelsesforordningen mode, hvor IP-adresserne anonymiseres. + + + + + log.user_not_allowed.unauthorized_access_attempt_to + Uautoriseret adgang forsøg på side + + + + + log.user_not_allowed.hint + Anmodningen blev blokeret. Der skulle ikke være behov for yderligere handling. + + + + + log.no_comment + Ingen kommentarer + + + + + log.element_changed.field + Felt + + + + + log.element_changed.data_before + Data før ændring + + + + + error_table.error + Der opstod en fejl under anmodningen. + + + + + part.table.invalid_regex + Ugyldigt regulært udtryk (regex) + + + + + log.element_changed.data_after + Data efter ændring + + + + + log.element_changed.diff + Forskel + + + + + log.undo.undo.short + Fortryd + + + + + log.undo.revert.short + Vend tilbage til version + + + + + log.view_version + Vis version + + + + + log.undo.undelete.short + Gendan + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + Ejer + + + + + log.element_edited.changed_fields.parent_id + Overordnet element + + + + + log.details.delete_entry + Slet logpost + + + + + log.delete.message.title + Vil du virkelig slette denne logpost? + + + + + log.delete.message + Hvis dette er en historikindgang for et element, vil sletning af det resultere i tab af historikdata! Dette kan give uventede resultater, når du bruger tidsrejsefunktionen. + + + + + log.collection_deleted.on_collection + i samling + + + + + log.element_edited.changed_fields.attachments + Bilag + + + + + tfa_u2f.add_key.registration_error + Der opstod en fejl under registrering af sikkerhedsnøgle. Prøv igen, eller brug en anden nøgle! + + + + + log.target_type.none + Ingen + + + + + ui.darkmode.light + Lys + + + + + ui.darkmode.dark + Mørk + + + + + ui.darkmode.auto + Auto (baseret på systemindstillinger) + + + + + label_generator.no_lines_given + Intet tekstindhold angivet! De oprettede etiketter vil være tomme. + + + + + user.password_strength.very_weak + Meget svag + + + + + user.password_strength.weak + Svag + + + + + user.password_strength.medium + Middel + + + + + user.password_strength.strong + Stærk + + + + + user.password_strength.very_strong + Meget stærk + + + + + perm.users.impersonate + Kopier en anden bruger + + + + + user.impersonated_by.label + Kopieret fra bruger + + + + + user.stop_impersonation + Afslut kopiering fra bruger + + + + + user.impersonate.btn + Kopier fra bruger + + + + + user.impersonate.confirm.title + Er du sikker på at du vil kopiere fra denne bruger? + + + + + user.impersonate.confirm.message + Dette vil blive logget. Du bør kun gøre dette med en grund. + +Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver dette, vil du modtage en "Adgang nægtet"-meddelelse. + + + + + log.type.security.user_impersonated + Kopieret bruger + + + + + info_providers.providers_list.title + Kilder til information + + + + + info_providers.providers_list.active + Aktiv + + + + + info_providers.providers_list.disabled + Deaktiveret + + + + + info_providers.capabilities.basic + Basis + + + + + info_providers.capabilities.footprint + Footprint + + + + + info_providers.capabilities.picture + Billede + + + + + info_providers.capabilities.datasheet + Datablade + + + + + info_providers.capabilities.price + Pris + + + + + part.info_provider_reference.badge + Kilden til information, der bruges til at oprette denne komponent + + + + + part.info_provider_reference + Genereret fra informationskilde + + + + + oauth_client.connect.btn + Forbind OAuth + + + + + info_providers.table.provider.label + Kilde + + + + + info_providers.search.keyword + Søgeord + + + + + info_providers.search.submit + Søg + + + + + info_providers.search.providers.help + Vælg de informationskilder, der skal søges i. + + + + + info_providers.search.providers + Kilder + + + + + info_providers.search.info_providers_list + Se alle tilgængelige informationskilder + + + + + info_providers.search.title + Opret komponent vha. informationskilde + + + + + oauth_client.flash.connection_successful + Forbindelsen til OAuth-applikationen er etableret! + + + + + perm.part.info_providers + Informationskilder + + + + + perm.part.info_providers.create_parts + Opret komponenter + + + + + entity.edit.alternative_names.label + Alternativt navn + + + + + entity.edit.alternative_names.help + De alternative navne, der er angivet her, bruges til automatisk at vælge dette element baseret på data returneret fra informationskilder. + + + + + info_providers.form.help_prefix + Kilde + + + + + update_manager.new_version_available.title + Ny version tilgængelig + + + + + update_manager.new_version_available.text + En ny version af Part-DB er tilgængelig. Her finder du mere information + + + + + update_manager.new_version_available.only_administrators_can_see + Kun administratorer kan se denne meddelelse + + + + + perm.system.show_available_updates + Vis tilgængelige Part-DB opdateringer + + + + + user.settings.api_tokens + API token + + + + + user.settings.api_tokens.description + Ved at bruge et API-token kan andre applikationer få adgang til Part-DB med deres brugerrettigheder til at udføre forskellige handlinger ved hjælp af Part-DB REST API. Hvis du sletter et API-token, kan det program, der bruger token'et, ikke længere få adgang til Part-DB på dets vegne. + + + + + api_tokens.name + Navn + + + + + api_tokens.access_level + Adgangsniveau + + + + + api_tokens.expiration_date + Udløbsdato + + + + + api_tokens.added_date + Oprettet den + + + + + api_tokens.last_time_used + Sidste anvendelse + + + + + datetime.never + Aldrig + + + + + api_token.valid + Gyldig + + + + + api_token.expired + Udløbet + + + + + user.settings.show_api_documentation + Vis API dokumentation + + + + + api_token.create_new + Opret nyt API token + + + + + api_token.level.read_only + Read-only + + + + + api_token.level.edit + Ret + + + + + api_token.level.admin + Admin + + + + + api_token.level.full + Fuld + + + + + api_tokens.access_level.help + Dette giver dig mulighed for at begrænse, hvad API-tokenet giver adgang til. Adgang er altid begrænset af brugerens tilladelser. + + + + + api_tokens.expiration_date.help + Efter denne dato vil tokenet ikke længere være brugbart. Hvis dette felt efterlades tomt, vil tokenet aldrig udløbe. + + + + + api_tokens.your_token_is + Dit API token er + + + + + api_tokens.please_save_it + Gem venligst dette. Du vil ikke kunne se det igen! + + + + + api_tokens.create_new.back_to_user_settings + Tilbage til brugerindstillinger + + + + + project.build.dont_check_quantity + Kontrollerer ikke mængder + + + + + project.build.dont_check_quantity.help + Hvis denne mulighed er valgt, vil de valgte mængder blive fjernet fra lageret, uanset om der er flere eller færre komponenter, end der reelt er nødvendige for at bygge projektet. + + + + + part_list.action.invert_selection + Inverter valg + + + + + perm.api + API + + + + + perm.api.access_api + API adgang + + + + + perm.api.manage_tokens + Administrer API-tokens + + + + + user.settings.api_tokens.delete.title + Er du sikker på, at du vil slette dette API-token? + + + + + user.settings.api_tokens.delete + Slet + + + + + user.settings.api_tokens.delete.message + Den applikation, der bruger dette token, vil ikke længere have adgang til Part-DB. Dette kan ikke fortrydes! + + + + + api_tokens.deleted + API-token blev fjernet! + + + + + user.settings.api_tokens.no_api_tokens_yet + Ingen API-tokens er blevet oprettet endnu. + + + + + api_token.ends_with + Ender med + + + + + entity.select.creating_new_entities_not_allowed + Du er ikke autoriseret til at oprette nye elementer af denne type! Vælg venligst et givet element. + + + + + scan_dialog.mode + Barcode type + + + + + scan_dialog.mode.auto + Automatisk genkendelse + + + + + scan_dialog.mode.ipn + IPN barcode + + + + + scan_dialog.mode.internal + Part-DB barcode + + + + + part_association.label + Komponentforbindelse + + + + + part.edit.tab.associations + Forbundne komponenter + + + + + part_association.edit.other_part + Forbunden komponent + + + + + part_association.edit.type + type relation + + + + + part_association.edit.comment + Noter + + + + + part_association.edit.type.help + Her kan du vælge hvilken type forbindelse komponenterne har. + + + + + part_association.table.from_this_part + Links fra denne komponent til andre + + + + + part_association.table.from + Fra + + + + + part_association.table.type + Relation + + + + + part_association.table.to + Til + + + + + part_association.type.compatible + Er kompatibel med + + + + + part_association.table.to_this_part + Links til denne komponent fra andre + + + + + part_association.type.other + Andet (egen værdi) + + + + + part_association.type.supersedes + Erstatter + + + + + part_association.edit.other_type + brugerdefineret type + + + + + part_association.edit.delete.confirm + Er du sikker på, at du vil slette denne genvej? Dette kan ikke fortrydes. + + + + + part_lot.edit.advanced + Vis avancerede muligheder + + + + + part_lot.edit.vendor_barcode + Leverandør barcode + + + + + part_lot.edit.vendor_barcode.help + Hvis denne beholdning allerede har en stregkode (f.eks. påført af leverandøren), kan du indtaste stregkodens indhold her, så du kan finde denne beholdning ved at scanne stregkoden. + + + + + scan_dialog.mode.vendor + Leverandørstregkode (konfigureret i komponentbeholdning) + + + + + project.bom.instockAmount + Lagerantal + + + + + collection_type.new_element.tooltip + Dette element er nyoprettet og er endnu ikke gemt i databasen. + + + + + part.merge.title + Sammenflæt komponent + + + + + part.merge.title.into + sammen til + + + + + part.merge.confirm.title + Er du sikker på at du vil sammenflætte <b>%other%</b> til <b>%target%</b>? + + + + + part.merge.confirm.message + <b>%other%</b> vil blive slettet, og komponenten vil blive gemt med den viste informaton. + + + + + part.info.merge_modal.title + Sammenflæt kompontenter + + + + + part.info.merge_modal.other_part + Andet komponent + + + + + part.info.merge_modal.other_into_this + Sammenflet anden komponent ind i denne (slet anden komponent, behold denne) + + + + + part.info.merge_modal.this_into_other + Flet denne komponent til en anden (slet denne komponent, behold en anden) + + + + + part.info.merge_btn + Sammenflæt komponent + + + + + part.update_part_from_info_provider.btn + Opdater komponent fra informationskilden + + + + + info_providers.update_part.title + Opdater komponent fra informationskilden + + + + + part.merge.flash.please_review + Data er endnu ikke blevet gemt. Gennemgå ændringerne, og klik på Gem for at gemme dataene. + + + + + user.edit.flash.permissions_fixed + De nødvendige tilladelser til andre tilladelser manglede. Dette er blevet rettet. Tjek venligst, om tilladelserne svarer til dine krav. + + + + + permission.legend.dependency_note + Bemærk venligst, at nogle autorisationsoperationer afhænger af hinanden. Hvis du modtager en advarsel om, at manglende tilladelser er blevet rettet, og en tilladelse er blevet sat tilbage til tilladt, skal du også indstille den afhængige handling til forbudt. Afhængighederne er normalt placeret til højre for en operation. + + + + + log.part_stock_changed.timestamp + Tidspunkt + + + + + part.info.withdraw_modal.timestamp + Handlingstidspunkt + + + + + part.info.withdraw_modal.timestamp.hint + Dette felt giver dig mulighed for at angive den faktiske dato, da lageroperationen rent faktisk blev udført, ikke kun datoen, hvor den blev logget. Denne værdi gemmes i det ekstra felt i logposten. + + + + + part.info.withdraw_modal.delete_lot_if_empty + Slet denne beholdning, når den bliver tom under drift + + + + + info_providers.search.error.client_exception + Der opstod en fejl under kommunikationen med informationsudbyderen. Gennemgå konfigurationen for denne udbyder, og forny OAuth-token'erne, hvis det er muligt. + + + + + eda_info.reference_prefix.placeholder + f.eks. R + + + + + eda_info.reference_prefix + Referencepræfiks + + + + + eda_info.kicad_section.title + KiCad specifikke indstillinger + + + + + eda_info.value + Værdi + + + + + eda_info.value.placeholder + f.eks. 100n + + + + + eda_info.exclude_from_bom + Udelad komponent fra stykliste (BOM) + + + + + eda_info.exclude_from_board + Udelad komponent fra PCB/Print + + + + + eda_info.exclude_from_sim + Udelad komponent fra simulering + + + + + eda_info.kicad_symbol + KiCad diagramsymbol + + + + + eda_info.kicad_symbol.placeholder + f.eks. transistor_BJT:BC547 + + + + + eda_info.kicad_footprint + KiCad footprint + + + + + eda_info.kicad_footprint.placeholder + f.eks. Package_TO_SOT_THT:TO-92 + + + + + part.edit.tab.eda + EDA information + + + + + api.api_endpoints.title + API endpoint + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + KiCad API root URL + + + + + eda_info.visibility + Gennemtving synlighed + + + + + eda_info.visibility.help + Som standard bestemmes synlighed automatisk i EDA-softwaren. Ved at bruge dette afkrydsningsfelt kan du tvinge komponenten til at være synlig eller usynlig. + + + + + part.withdraw.zero_amount + Du forsøgte at fjerne/tilføje en mængde sat til nul! Der blev ikke foretaget nogen handling. + + + + diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf index aad972d7..24e140c8 100644 --- a/translations/messages.de.xlf +++ b/translations/messages.de.xlf @@ -1,7 +1,7 @@ - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 @@ -12,7 +12,7 @@ Dateitypen für Anhänge - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 new @@ -22,7 +22,7 @@ Bearbeite Dateityp - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 new @@ -32,7 +32,7 @@ Neuer Dateityp - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 Part-DB1\templates\_sidebar.html.twig:22 @@ -51,7 +51,7 @@ Kategorien - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 @@ -64,7 +64,7 @@ Optionen - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 @@ -77,7 +77,7 @@ Erweitert - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 new @@ -87,7 +87,7 @@ Bearbeite Kategorie - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 new @@ -97,7 +97,7 @@ Neue Kategorie - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 @@ -107,7 +107,7 @@ Währung - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 @@ -117,7 +117,7 @@ ISO Code - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 @@ -127,7 +127,7 @@ Währungssymbol - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 new @@ -137,7 +137,7 @@ Bearbeite Währung - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 new @@ -147,7 +147,7 @@ Neue Währung - + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 @@ -158,7 +158,7 @@ Projekte - + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 new @@ -168,7 +168,7 @@ Bearbeite Projekt - + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 new @@ -178,7 +178,7 @@ Neues Projekt - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 Part-DB1\templates\_navbar_search.html.twig:67 @@ -201,7 +201,7 @@ Suche - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 Part-DB1\templates\_sidebar.html.twig:3 @@ -217,7 +217,7 @@ Alle ausklappen - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 Part-DB1\templates\_sidebar.html.twig:4 @@ -233,7 +233,7 @@ Alle einklappen - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 Part-DB1\templates\Parts\info\_sidebar.html.twig:4 @@ -245,7 +245,7 @@ Dies ist wie das Bauteil bevor dem %timestamp% aussah. <i>Beachten Sie, dass dieses Feature experimentell ist und die angezeigten Infos daher nicht unbedingt korrekt sind.</i> - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 @@ -256,7 +256,7 @@ Eigenschaften - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 @@ -267,7 +267,7 @@ Informationen - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 @@ -278,7 +278,7 @@ Historie - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 @@ -289,7 +289,7 @@ Exportieren - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 @@ -300,7 +300,7 @@ Import / Export - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 @@ -310,7 +310,7 @@ Masseneingabe - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 @@ -321,7 +321,7 @@ Allgemein - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 @@ -331,7 +331,7 @@ Dateianhänge - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 @@ -340,7 +340,7 @@ Parameter - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 @@ -351,7 +351,7 @@ Alles exportieren - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 @@ -361,7 +361,7 @@ Jede Zeile wird als Name für ein neues Element interpretiert und angelegt. - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 @@ -372,7 +372,7 @@ Bearbeite Element "%name" - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 @@ -383,7 +383,7 @@ Neues Element - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 Part-DB1\templates\_sidebar.html.twig:9 @@ -398,7 +398,7 @@ Footprints - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 new @@ -408,7 +408,7 @@ Bearbeite Footprint - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 new @@ -418,7 +418,7 @@ Neuer Footprint - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 @@ -428,7 +428,7 @@ Gruppen - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 @@ -440,7 +440,7 @@ Berechtigungen - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 new @@ -450,7 +450,7 @@ Bearbeite Gruppe - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 new @@ -460,7 +460,7 @@ Neue Gruppe - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 @@ -469,7 +469,7 @@ Labelprofile - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 @@ -478,7 +478,7 @@ Erweitert - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 @@ -487,7 +487,7 @@ Notizen - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 new @@ -497,7 +497,7 @@ Bearbeite Labelprofil - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 new @@ -507,7 +507,7 @@ Neues Labelprofil - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 @@ -518,7 +518,7 @@ Hersteller - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 new @@ -528,7 +528,7 @@ Bearbeite Hersteller - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 new @@ -538,7 +538,7 @@ Neuer Hersteller - + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 @@ -548,7 +548,7 @@ Maßeinheit - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 Part-DB1\templates\_sidebar.html.twig:8 @@ -563,7 +563,7 @@ Lagerorte - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 new @@ -573,7 +573,7 @@ Bearbeite Lagerort - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 new @@ -583,7 +583,7 @@ Neuer Lagerort - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 @@ -594,7 +594,7 @@ Lieferanten - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 new @@ -604,7 +604,7 @@ Bearbeite Lieferant - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 new @@ -614,7 +614,7 @@ Neuer Lieferant - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 @@ -624,7 +624,7 @@ Benutzer - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 @@ -634,7 +634,7 @@ Konfiguration - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 @@ -644,7 +644,7 @@ Passwort - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 @@ -654,7 +654,7 @@ Zwei-Faktor-Authentifizierung - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 @@ -664,7 +664,7 @@ Authentifizierungsapp aktiv - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 Part-DB1\templates\Users\backup_codes.html.twig:15 @@ -678,7 +678,7 @@ Verbleibende Backupcodes - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 Part-DB1\templates\Users\backup_codes.html.twig:17 @@ -692,7 +692,7 @@ Erzeugungsdatum der Backupcodes - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 @@ -704,7 +704,7 @@ Methode deaktiviert - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 @@ -714,7 +714,7 @@ Aktive Sicherheitsschlüssel - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 @@ -724,7 +724,7 @@ Wirklich fortfahren? - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 @@ -736,7 +736,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs <b>Führen sie dies nur durch, wenn Sie über die Identität des (um Hilfe suchenden) Benutzers absolut sicher sind, da ansonsten eine Kompromittierung des Accounts durch einen Angreifer erfolgen könnte!</b> - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 @@ -746,7 +746,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Alle Zwei-Faktor-Authentifizierungsmethoden deaktivieren - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 new @@ -756,7 +756,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Bearbeite Benutzer - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 new @@ -766,7 +766,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Neuer Benutzer - + Part-DB1\templates\AdminPages\_attachments.html.twig:4 Part-DB1\templates\Parts\edit\_attachments.html.twig:4 @@ -779,21 +779,13 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Löschen - - - Part-DB1\templates\AdminPages\_attachments.html.twig:41 - Part-DB1\templates\Parts\edit\_attachments.html.twig:38 - Part-DB1\templates\Parts\info\_attachments_info.html.twig:35 - Part-DB1\src\DataTables\AttachmentDataTable.php:159 - Part-DB1\templates\Parts\edit\_attachments.html.twig:38 - Part-DB1\src\DataTables\AttachmentDataTable.php:159 - + - attachment.external - Extern + attachment.external_only + Nur Extern - + Part-DB1\templates\AdminPages\_attachments.html.twig:49 Part-DB1\templates\Parts\edit\_attachments.html.twig:47 @@ -805,7 +797,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Thumbnail des Dateianhanges - + Part-DB1\templates\AdminPages\_attachments.html.twig:52 Part-DB1\templates\Parts\edit\_attachments.html.twig:50 @@ -815,11 +807,11 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Part-DB1\templates\Parts\info\_attachments_info.html.twig:45 - attachment.view - Anzeigen + attachment.view_local + Lokale Datei anzeigen - + Part-DB1\templates\AdminPages\_attachments.html.twig:58 Part-DB1\templates\Parts\edit\_attachments.html.twig:56 @@ -835,7 +827,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Datei nicht gefunden - + Part-DB1\templates\AdminPages\_attachments.html.twig:66 Part-DB1\templates\Parts\edit\_attachments.html.twig:64 @@ -847,7 +839,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Privat - + Part-DB1\templates\AdminPages\_attachments.html.twig:79 Part-DB1\templates\Parts\edit\_attachments.html.twig:77 @@ -859,7 +851,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Dateianhang hinzufügen - + Part-DB1\templates\AdminPages\_attachments.html.twig:84 Part-DB1\templates\Parts\edit\_attachments.html.twig:82 @@ -873,7 +865,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Möchten Sie diesen Bestand wirklich löschen? Dies kann nicht rückgängig gemacht werden! - + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 Part-DB1\templates\AdminPages\_delete_form.html.twig:2 @@ -884,7 +876,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Wollen sie das Element %name% wirklich löschen? - + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 Part-DB1\templates\AdminPages\_delete_form.html.twig:3 @@ -897,7 +889,7 @@ Der Benutzer wird alle Zwei-Faktor-Authentifizierungmethoden neu einrichten müs Subelemente werden beim Löschen nach oben verschoben. - + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 Part-DB1\templates\AdminPages\_delete_form.html.twig:11 @@ -908,7 +900,7 @@ Subelemente werden beim Löschen nach oben verschoben. Element löschen - + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 Part-DB1\templates\Parts\info\_tools.html.twig:45 @@ -923,7 +915,7 @@ Subelemente werden beim Löschen nach oben verschoben. Änderungskommentar - + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 Part-DB1\templates\AdminPages\_delete_form.html.twig:24 @@ -934,7 +926,7 @@ Subelemente werden beim Löschen nach oben verschoben. Rekursiv (alle Unterelemente) löschen - + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 @@ -943,7 +935,7 @@ Subelemente werden beim Löschen nach oben verschoben. Element duplizieren - + Part-DB1\templates\AdminPages\_export_form.html.twig:4 Part-DB1\src\Form\AdminPages\ImportType.php:76 @@ -957,7 +949,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dateiformat - + Part-DB1\templates\AdminPages\_export_form.html.twig:16 Part-DB1\templates\AdminPages\_export_form.html.twig:16 @@ -968,7 +960,7 @@ Subelemente werden beim Löschen nach oben verschoben. Ausführlichkeit - + Part-DB1\templates\AdminPages\_export_form.html.twig:19 Part-DB1\templates\AdminPages\_export_form.html.twig:19 @@ -979,7 +971,7 @@ Subelemente werden beim Löschen nach oben verschoben. Einfach - + Part-DB1\templates\AdminPages\_export_form.html.twig:20 Part-DB1\templates\AdminPages\_export_form.html.twig:20 @@ -990,7 +982,7 @@ Subelemente werden beim Löschen nach oben verschoben. Erweitert - + Part-DB1\templates\AdminPages\_export_form.html.twig:21 Part-DB1\templates\AdminPages\_export_form.html.twig:21 @@ -1001,7 +993,7 @@ Subelemente werden beim Löschen nach oben verschoben. Vollständig - + Part-DB1\templates\AdminPages\_export_form.html.twig:31 Part-DB1\templates\AdminPages\_export_form.html.twig:31 @@ -1012,7 +1004,7 @@ Subelemente werden beim Löschen nach oben verschoben. Unterelemente auch exportieren - + Part-DB1\templates\AdminPages\_export_form.html.twig:39 Part-DB1\templates\AdminPages\_export_form.html.twig:39 @@ -1023,7 +1015,7 @@ Subelemente werden beim Löschen nach oben verschoben. Exportieren - + Part-DB1\templates\AdminPages\_info.html.twig:4 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 @@ -1042,7 +1034,7 @@ Subelemente werden beim Löschen nach oben verschoben. ID - + Part-DB1\templates\AdminPages\_info.html.twig:11 Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 @@ -1066,7 +1058,7 @@ Subelemente werden beim Löschen nach oben verschoben. Erstellt am - + Part-DB1\templates\AdminPages\_info.html.twig:25 Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 @@ -1084,7 +1076,7 @@ Subelemente werden beim Löschen nach oben verschoben. Zuletzt bearbeitet - + Part-DB1\templates\AdminPages\_info.html.twig:38 Part-DB1\templates\AdminPages\_info.html.twig:38 @@ -1094,7 +1086,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile mit diesem Element - + Part-DB1\templates\AdminPages\_parameters.html.twig:6 Part-DB1\templates\helper.twig:125 @@ -1105,7 +1097,7 @@ Subelemente werden beim Löschen nach oben verschoben. Parameter - + Part-DB1\templates\AdminPages\_parameters.html.twig:7 Part-DB1\templates\Parts\edit\_specifications.html.twig:7 @@ -1115,7 +1107,7 @@ Subelemente werden beim Löschen nach oben verschoben. Symbol - + Part-DB1\templates\AdminPages\_parameters.html.twig:8 Part-DB1\templates\Parts\edit\_specifications.html.twig:8 @@ -1125,7 +1117,7 @@ Subelemente werden beim Löschen nach oben verschoben. Min. - + Part-DB1\templates\AdminPages\_parameters.html.twig:9 Part-DB1\templates\Parts\edit\_specifications.html.twig:9 @@ -1135,7 +1127,7 @@ Subelemente werden beim Löschen nach oben verschoben. Typ. - + Part-DB1\templates\AdminPages\_parameters.html.twig:10 Part-DB1\templates\Parts\edit\_specifications.html.twig:10 @@ -1145,7 +1137,7 @@ Subelemente werden beim Löschen nach oben verschoben. Max. - + Part-DB1\templates\AdminPages\_parameters.html.twig:11 Part-DB1\templates\Parts\edit\_specifications.html.twig:11 @@ -1155,7 +1147,7 @@ Subelemente werden beim Löschen nach oben verschoben. Einheit - + Part-DB1\templates\AdminPages\_parameters.html.twig:12 Part-DB1\templates\Parts\edit\_specifications.html.twig:12 @@ -1165,7 +1157,7 @@ Subelemente werden beim Löschen nach oben verschoben. Text - + Part-DB1\templates\AdminPages\_parameters.html.twig:13 Part-DB1\templates\Parts\edit\_specifications.html.twig:13 @@ -1175,7 +1167,7 @@ Subelemente werden beim Löschen nach oben verschoben. Sektion - + Part-DB1\templates\AdminPages\_parameters.html.twig:26 Part-DB1\templates\Parts\edit\_specifications.html.twig:26 @@ -1185,7 +1177,7 @@ Subelemente werden beim Löschen nach oben verschoben. Neuer Parameter - + Part-DB1\templates\AdminPages\_parameters.html.twig:31 Part-DB1\templates\Parts\edit\_specifications.html.twig:31 @@ -1195,7 +1187,7 @@ Subelemente werden beim Löschen nach oben verschoben. Möchten Sie den Parameter wirklich löschen? - + Part-DB1\templates\attachment_list.html.twig:3 Part-DB1\templates\attachment_list.html.twig:3 @@ -1205,7 +1197,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dateianhänge - + Part-DB1\templates\attachment_list.html.twig:10 Part-DB1\templates\LogSystem\_log_table.html.twig:8 @@ -1219,7 +1211,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lade - + Part-DB1\templates\attachment_list.html.twig:11 Part-DB1\templates\LogSystem\_log_table.html.twig:9 @@ -1233,7 +1225,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dies kann einen Moment dauern. Wenn diese Nachricht längere Zeit bestehen bleibt, versuchen sie die Seite erneut zu laden. - + Part-DB1\templates\base.html.twig:68 Part-DB1\templates\base.html.twig:68 @@ -1244,7 +1236,7 @@ Subelemente werden beim Löschen nach oben verschoben. Aktivieren Sie Javascript um alle Features zu nutzen! - + Part-DB1\templates\base.html.twig:73 Part-DB1\templates\base.html.twig:73 @@ -1254,7 +1246,7 @@ Subelemente werden beim Löschen nach oben verschoben. Seitenleiste ein/ausblenden - + Part-DB1\templates\base.html.twig:95 Part-DB1\templates\base.html.twig:95 @@ -1265,7 +1257,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lade: - + Part-DB1\templates\base.html.twig:96 Part-DB1\templates\base.html.twig:96 @@ -1276,7 +1268,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dies kann einen Moment dauern. Sollte diese Nachricht bestehen bleiben, dann laden sie die Seite erneut. - + Part-DB1\templates\base.html.twig:101 Part-DB1\templates\base.html.twig:101 @@ -1287,7 +1279,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lade... - + Part-DB1\templates\base.html.twig:112 Part-DB1\templates\base.html.twig:112 @@ -1298,7 +1290,7 @@ Subelemente werden beim Löschen nach oben verschoben. Zurück zum Seitenbeginn - + Part-DB1\templates\Form\permissionLayout.html.twig:35 Part-DB1\templates\Form\permissionLayout.html.twig:35 @@ -1308,7 +1300,7 @@ Subelemente werden beim Löschen nach oben verschoben. Berechtigung - + Part-DB1\templates\Form\permissionLayout.html.twig:36 Part-DB1\templates\Form\permissionLayout.html.twig:36 @@ -1318,7 +1310,7 @@ Subelemente werden beim Löschen nach oben verschoben. Wert - + Part-DB1\templates\Form\permissionLayout.html.twig:53 Part-DB1\templates\Form\permissionLayout.html.twig:53 @@ -1328,7 +1320,7 @@ Subelemente werden beim Löschen nach oben verschoben. Erläuterung der Zustände - + Part-DB1\templates\Form\permissionLayout.html.twig:57 Part-DB1\templates\Form\permissionLayout.html.twig:57 @@ -1338,7 +1330,7 @@ Subelemente werden beim Löschen nach oben verschoben. Verboten - + Part-DB1\templates\Form\permissionLayout.html.twig:61 Part-DB1\templates\Form\permissionLayout.html.twig:61 @@ -1348,7 +1340,7 @@ Subelemente werden beim Löschen nach oben verschoben. Erlaubt - + Part-DB1\templates\Form\permissionLayout.html.twig:65 Part-DB1\templates\Form\permissionLayout.html.twig:65 @@ -1358,7 +1350,7 @@ Subelemente werden beim Löschen nach oben verschoben. Erbe von (übergeordneter) Gruppe - + Part-DB1\templates\helper.twig:3 Part-DB1\templates\helper.twig:3 @@ -1368,7 +1360,7 @@ Subelemente werden beim Löschen nach oben verschoben. Ja - + Part-DB1\templates\helper.twig:5 Part-DB1\templates\helper.twig:5 @@ -1378,7 +1370,7 @@ Subelemente werden beim Löschen nach oben verschoben. Nein - + Part-DB1\templates\helper.twig:92 Part-DB1\templates\helper.twig:87 @@ -1388,7 +1380,7 @@ Subelemente werden beim Löschen nach oben verschoben. Ja - + Part-DB1\templates\helper.twig:94 Part-DB1\templates\helper.twig:89 @@ -1398,7 +1390,7 @@ Subelemente werden beim Löschen nach oben verschoben. Nein - + Part-DB1\templates\helper.twig:126 @@ -1407,7 +1399,7 @@ Subelemente werden beim Löschen nach oben verschoben. Wert - + Part-DB1\templates\homepage.html.twig:7 Part-DB1\templates\homepage.html.twig:7 @@ -1418,7 +1410,7 @@ Subelemente werden beim Löschen nach oben verschoben. Version - + Part-DB1\templates\homepage.html.twig:22 Part-DB1\templates\homepage.html.twig:22 @@ -1429,7 +1421,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lizenzinformation - + Part-DB1\templates\homepage.html.twig:31 Part-DB1\templates\homepage.html.twig:31 @@ -1440,7 +1432,7 @@ Subelemente werden beim Löschen nach oben verschoben. Projektseite - + Part-DB1\templates\homepage.html.twig:31 Part-DB1\templates\homepage.html.twig:31 @@ -1451,7 +1443,7 @@ Subelemente werden beim Löschen nach oben verschoben. Quellcode, Downloads, Bugreports, ToDo-Liste usw. gibts auf der <a class="link-external" target="_blank" href="%href%">GitHub Projektseite</a> - + Part-DB1\templates\homepage.html.twig:32 Part-DB1\templates\homepage.html.twig:32 @@ -1462,7 +1454,7 @@ Subelemente werden beim Löschen nach oben verschoben. Hilfe - + Part-DB1\templates\homepage.html.twig:32 Part-DB1\templates\homepage.html.twig:32 @@ -1473,7 +1465,7 @@ Subelemente werden beim Löschen nach oben verschoben. Hilfe und Tipps finden sie im <a class="link-external" rel="noopener" target="_blank" href="%href%">Wiki</a> der GitHub Seite. - + Part-DB1\templates\homepage.html.twig:33 Part-DB1\templates\homepage.html.twig:33 @@ -1484,29 +1476,7 @@ Subelemente werden beim Löschen nach oben verschoben. Forum - - - Part-DB1\templates\homepage.html.twig:36 - Part-DB1\templates\homepage.html.twig:36 - templates\homepage.html.twig:33 - - - homepage.basedOn - Basierend auf dem originale Part-DB von - - - - - Part-DB1\templates\homepage.html.twig:39 - Part-DB1\templates\homepage.html.twig:39 - templates\homepage.html.twig:36 - - - homepage.others - und anderen - - - + Part-DB1\templates\homepage.html.twig:45 Part-DB1\templates\homepage.html.twig:45 @@ -1517,7 +1487,7 @@ Subelemente werden beim Löschen nach oben verschoben. Letzte Aktivitäten - + Part-DB1\templates\LabelSystem\dialog.html.twig:3 Part-DB1\templates\LabelSystem\dialog.html.twig:6 @@ -1527,7 +1497,7 @@ Subelemente werden beim Löschen nach oben verschoben. Labelgenerator - + Part-DB1\templates\LabelSystem\dialog.html.twig:16 @@ -1536,7 +1506,7 @@ Subelemente werden beim Löschen nach oben verschoben. Allgemein - + Part-DB1\templates\LabelSystem\dialog.html.twig:20 @@ -1545,7 +1515,7 @@ Subelemente werden beim Löschen nach oben verschoben. Erweitert - + Part-DB1\templates\LabelSystem\dialog.html.twig:24 @@ -1554,7 +1524,7 @@ Subelemente werden beim Löschen nach oben verschoben. Profil - + Part-DB1\templates\LabelSystem\dialog.html.twig:58 @@ -1563,7 +1533,7 @@ Subelemente werden beim Löschen nach oben verschoben. Ausgewähltes Profil - + Part-DB1\templates\LabelSystem\dialog.html.twig:62 @@ -1572,7 +1542,7 @@ Subelemente werden beim Löschen nach oben verschoben. Profil ändern - + Part-DB1\templates\LabelSystem\dialog.html.twig:75 @@ -1581,7 +1551,7 @@ Subelemente werden beim Löschen nach oben verschoben. Profil laden - + Part-DB1\templates\LabelSystem\dialog.html.twig:102 @@ -1590,7 +1560,7 @@ Subelemente werden beim Löschen nach oben verschoben. Download - + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 @@ -1600,7 +1570,7 @@ Subelemente werden beim Löschen nach oben verschoben. Label erzeugen - + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 @@ -1609,7 +1579,7 @@ Subelemente werden beim Löschen nach oben verschoben. Leeres Label - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 @@ -1618,7 +1588,7 @@ Subelemente werden beim Löschen nach oben verschoben. Scanner - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 @@ -1627,7 +1597,7 @@ Subelemente werden beim Löschen nach oben verschoben. Keine Kamera gefunden - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 @@ -1636,7 +1606,7 @@ Subelemente werden beim Löschen nach oben verschoben. Sie müssen eine Kamera anschließen und die Berechtigung erteilen, um den Scanner nutzen zu können. Sie können unten den Barcode manuell eingeben. - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 @@ -1645,7 +1615,7 @@ Subelemente werden beim Löschen nach oben verschoben. Kamera auswählen - + Part-DB1\templates\LogSystem\log_list.html.twig:3 Part-DB1\templates\LogSystem\log_list.html.twig:3 @@ -1655,7 +1625,7 @@ Subelemente werden beim Löschen nach oben verschoben. Systemlog - + Part-DB1\templates\LogSystem\_log_table.html.twig:1 Part-DB1\templates\LogSystem\_log_table.html.twig:1 @@ -1666,7 +1636,7 @@ Subelemente werden beim Löschen nach oben verschoben. Änderung wirklich rückgängig machen / Element wirklich zurücksetzen? - + Part-DB1\templates\LogSystem\_log_table.html.twig:2 Part-DB1\templates\LogSystem\_log_table.html.twig:2 @@ -1677,7 +1647,7 @@ Subelemente werden beim Löschen nach oben verschoben. Wollen Sie wirklich die gegebene Änderung rückgängig machen / Das Element auf einen alten Versionsstand zurücksetzen? - + Part-DB1\templates\mail\base.html.twig:24 Part-DB1\templates\mail\base.html.twig:24 @@ -1687,7 +1657,7 @@ Subelemente werden beim Löschen nach oben verschoben. Diese Email wurde automatisch erstellt von - + Part-DB1\templates\mail\base.html.twig:24 Part-DB1\templates\mail\base.html.twig:24 @@ -1697,7 +1667,7 @@ Subelemente werden beim Löschen nach oben verschoben. Antworten Sie nicht auf diese Email. - + Part-DB1\templates\mail\pw_reset.html.twig:6 Part-DB1\templates\mail\pw_reset.html.twig:6 @@ -1707,7 +1677,7 @@ Subelemente werden beim Löschen nach oben verschoben. Hallo %name% - + Part-DB1\templates\mail\pw_reset.html.twig:7 Part-DB1\templates\mail\pw_reset.html.twig:7 @@ -1717,7 +1687,7 @@ Subelemente werden beim Löschen nach oben verschoben. jemand (hoffentlich Sie) hat ein Reset ihres Passwortes angefordert. Wenn diese Anfrage nicht von Ihnen stammt, ignorieren sie diese Email. - + Part-DB1\templates\mail\pw_reset.html.twig:9 Part-DB1\templates\mail\pw_reset.html.twig:9 @@ -1727,7 +1697,7 @@ Subelemente werden beim Löschen nach oben verschoben. Passwort zurücksetzen - + Part-DB1\templates\mail\pw_reset.html.twig:11 Part-DB1\templates\mail\pw_reset.html.twig:11 @@ -1737,7 +1707,7 @@ Subelemente werden beim Löschen nach oben verschoben. Wenn dies nicht funktioniert, rufen Sie <a href="%url%">%url%</a> auf und geben Sie die folgenden Daten ein - + Part-DB1\templates\mail\pw_reset.html.twig:16 Part-DB1\templates\mail\pw_reset.html.twig:16 @@ -1747,7 +1717,7 @@ Subelemente werden beim Löschen nach oben verschoben. Benutzername - + Part-DB1\templates\mail\pw_reset.html.twig:19 Part-DB1\templates\mail\pw_reset.html.twig:19 @@ -1757,7 +1727,7 @@ Subelemente werden beim Löschen nach oben verschoben. Token - + Part-DB1\templates\mail\pw_reset.html.twig:24 Part-DB1\templates\mail\pw_reset.html.twig:24 @@ -1767,7 +1737,7 @@ Subelemente werden beim Löschen nach oben verschoben. Das Reset Token ist gültig bis <i>%date%</i> - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 @@ -1779,7 +1749,7 @@ Subelemente werden beim Löschen nach oben verschoben. Löschen - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 @@ -1789,7 +1759,7 @@ Subelemente werden beim Löschen nach oben verschoben. Mindestbestellmenge - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 @@ -1799,7 +1769,7 @@ Subelemente werden beim Löschen nach oben verschoben. Preis - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 @@ -1809,7 +1779,7 @@ Subelemente werden beim Löschen nach oben verschoben. für Menge - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 @@ -1819,7 +1789,7 @@ Subelemente werden beim Löschen nach oben verschoben. Preis hinzufügen - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 @@ -1830,7 +1800,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bearbeite Bauteil %name% - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 @@ -1841,7 +1811,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bearbeite Bauteileinformationen von - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 @@ -1851,7 +1821,7 @@ Subelemente werden beim Löschen nach oben verschoben. Allgemein - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 @@ -1861,7 +1831,7 @@ Subelemente werden beim Löschen nach oben verschoben. Hersteller - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 @@ -1871,7 +1841,7 @@ Subelemente werden beim Löschen nach oben verschoben. Erweiterte Optionen - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 @@ -1881,7 +1851,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lagerbestände - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 @@ -1891,7 +1861,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dateianhänge - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 @@ -1901,7 +1871,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bestellinformationen - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 @@ -1910,7 +1880,7 @@ Subelemente werden beim Löschen nach oben verschoben. Parameter - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 @@ -1920,7 +1890,7 @@ Subelemente werden beim Löschen nach oben verschoben. Notizen - + Part-DB1\templates\Parts\edit\new_part.html.twig:8 Part-DB1\templates\Parts\edit\new_part.html.twig:8 @@ -1931,7 +1901,7 @@ Subelemente werden beim Löschen nach oben verschoben. Neues Bauteil erstellen - + Part-DB1\templates\Parts\edit\_lots.html.twig:5 Part-DB1\templates\Parts\edit\_lots.html.twig:5 @@ -1941,7 +1911,7 @@ Subelemente werden beim Löschen nach oben verschoben. Löschen - + Part-DB1\templates\Parts\edit\_lots.html.twig:28 Part-DB1\templates\Parts\edit\_lots.html.twig:28 @@ -1951,7 +1921,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bestand anlegen - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 @@ -1961,7 +1931,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lieferant hinzufügen - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 @@ -1971,7 +1941,7 @@ Subelemente werden beim Löschen nach oben verschoben. Möchten Sie diesen Preis wirklich löschen? Das kann nicht rückgängig gemacht werden! - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 @@ -1981,7 +1951,7 @@ Subelemente werden beim Löschen nach oben verschoben. Möchten Sie diesen Lieferanten wirklich löschen? Dies kann nicht rückgängig gemacht werden! - + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 Part-DB1\templates\Parts\info\show_part_info.html.twig:19 @@ -1995,7 +1965,7 @@ Subelemente werden beim Löschen nach oben verschoben. Detailinfo für - + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 Part-DB1\templates\Parts\info\show_part_info.html.twig:47 @@ -2005,7 +1975,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lagerbestände - + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 Part-DB1\templates\Parts\lists\_info_card.html.twig:43 @@ -2020,7 +1990,7 @@ Subelemente werden beim Löschen nach oben verschoben. Notizen - + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 @@ -2029,7 +1999,7 @@ Subelemente werden beim Löschen nach oben verschoben. Parameter - + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 Part-DB1\templates\Parts\info\show_part_info.html.twig:64 @@ -2040,7 +2010,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dateianhänge - + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 Part-DB1\templates\Parts\info\show_part_info.html.twig:71 @@ -2051,7 +2021,7 @@ Subelemente werden beim Löschen nach oben verschoben. Einkaufsinformationen - + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 Part-DB1\templates\Parts\info\show_part_info.html.twig:78 @@ -2062,7 +2032,7 @@ Subelemente werden beim Löschen nach oben verschoben. Historie - + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 Part-DB1\templates\_sidebar.html.twig:54 @@ -2081,7 +2051,7 @@ Subelemente werden beim Löschen nach oben verschoben. Tools - + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 Part-DB1\templates\Parts\info\show_part_info.html.twig:90 @@ -2091,7 +2061,7 @@ Subelemente werden beim Löschen nach oben verschoben. Erweiterte Informationen - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 @@ -2101,7 +2071,7 @@ Subelemente werden beim Löschen nach oben verschoben. Name - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 @@ -2111,7 +2081,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anhangstyp - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 @@ -2121,7 +2091,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dateiname - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 @@ -2131,7 +2101,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dateigröße - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 @@ -2140,17 +2110,17 @@ Subelemente werden beim Löschen nach oben verschoben. Vorschaubild - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 - attachment.download - Herunterladen + attachment.download_local + Lokale Datei downloaden - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 @@ -2161,7 +2131,7 @@ Subelemente werden beim Löschen nach oben verschoben. Nutzer der dieses Bauteil erstellte - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 @@ -2175,7 +2145,7 @@ Subelemente werden beim Löschen nach oben verschoben. Unbekannt - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 @@ -2188,7 +2158,7 @@ Subelemente werden beim Löschen nach oben verschoben. Zugriff verboten - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 @@ -2199,7 +2169,7 @@ Subelemente werden beim Löschen nach oben verschoben. Nutzer der dieses Bauteil zu Letzt bearbeitete - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 @@ -2209,7 +2179,7 @@ Subelemente werden beim Löschen nach oben verschoben. Favorit - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 @@ -2219,7 +2189,7 @@ Subelemente werden beim Löschen nach oben verschoben. Mindestbestellmenge - + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 Part-DB1\templates\_navbar_search.html.twig:46 @@ -2236,7 +2206,7 @@ Subelemente werden beim Löschen nach oben verschoben. Hersteller - + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 Part-DB1\templates\_navbar_search.html.twig:11 @@ -2248,7 +2218,7 @@ Subelemente werden beim Löschen nach oben verschoben. Name - + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 Part-DB1\templates\Parts\info\_main_infos.html.twig:27 @@ -2259,7 +2229,7 @@ Subelemente werden beim Löschen nach oben verschoben. Zurück zum aktuellen Versionsstand - + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 Part-DB1\templates\_navbar_search.html.twig:19 @@ -2274,7 +2244,7 @@ Subelemente werden beim Löschen nach oben verschoben. Beschreibung - + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 Part-DB1\templates\_navbar_search.html.twig:15 @@ -2291,7 +2261,7 @@ Subelemente werden beim Löschen nach oben verschoben. Kategorie - + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 Part-DB1\templates\Parts\info\_main_infos.html.twig:39 @@ -2303,7 +2273,7 @@ Subelemente werden beim Löschen nach oben verschoben. Im Lager - + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 Part-DB1\templates\Parts\info\_main_infos.html.twig:41 @@ -2315,7 +2285,7 @@ Subelemente werden beim Löschen nach oben verschoben. Mindestbestand - + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 Part-DB1\templates\_navbar_search.html.twig:52 @@ -2331,7 +2301,7 @@ Subelemente werden beim Löschen nach oben verschoben. Footprint - + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 Part-DB1\templates\Parts\info\_main_infos.html.twig:59 @@ -2344,7 +2314,7 @@ Subelemente werden beim Löschen nach oben verschoben. Durchschnittspreis - + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 Part-DB1\templates\Parts\info\_order_infos.html.twig:5 @@ -2354,7 +2324,7 @@ Subelemente werden beim Löschen nach oben verschoben. Name - + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 Part-DB1\templates\Parts\info\_order_infos.html.twig:6 @@ -2364,7 +2334,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bestellnr. - + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 Part-DB1\templates\Parts\info\_order_infos.html.twig:28 @@ -2374,7 +2344,7 @@ Subelemente werden beim Löschen nach oben verschoben. Mindestanzahl - + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 Part-DB1\templates\Parts\info\_order_infos.html.twig:29 @@ -2384,7 +2354,7 @@ Subelemente werden beim Löschen nach oben verschoben. Preis - + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 Part-DB1\templates\Parts\info\_order_infos.html.twig:31 @@ -2394,7 +2364,7 @@ Subelemente werden beim Löschen nach oben verschoben. Stückpreis - + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 Part-DB1\templates\Parts\info\_order_infos.html.twig:71 @@ -2404,7 +2374,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bearbeiten - + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 Part-DB1\templates\Parts\info\_order_infos.html.twig:72 @@ -2414,7 +2384,7 @@ Subelemente werden beim Löschen nach oben verschoben. Löschen - + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 Part-DB1\templates\Parts\info\_part_lots.html.twig:6 @@ -2424,7 +2394,7 @@ Subelemente werden beim Löschen nach oben verschoben. Beschreibung - + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 Part-DB1\templates\Parts\info\_part_lots.html.twig:7 @@ -2434,7 +2404,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lagerort - + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 Part-DB1\templates\Parts\info\_part_lots.html.twig:8 @@ -2444,7 +2414,7 @@ Subelemente werden beim Löschen nach oben verschoben. Menge - + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 Part-DB1\templates\Parts\info\_part_lots.html.twig:22 @@ -2454,7 +2424,7 @@ Subelemente werden beim Löschen nach oben verschoben. Lagerort unbekannt - + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 Part-DB1\templates\Parts\info\_part_lots.html.twig:29 @@ -2464,7 +2434,7 @@ Subelemente werden beim Löschen nach oben verschoben. Menge unbekannt - + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 Part-DB1\templates\Parts\info\_part_lots.html.twig:38 @@ -2474,7 +2444,7 @@ Subelemente werden beim Löschen nach oben verschoben. Ablaufdatum - + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 Part-DB1\templates\Parts\info\_part_lots.html.twig:46 @@ -2484,7 +2454,7 @@ Subelemente werden beim Löschen nach oben verschoben. Abgelaufen - + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 Part-DB1\templates\Parts\info\_part_lots.html.twig:53 @@ -2494,7 +2464,7 @@ Subelemente werden beim Löschen nach oben verschoben. Muss aufgefüllt werden - + Part-DB1\templates\Parts\info\_picture.html.twig:15 Part-DB1\templates\Parts\info\_picture.html.twig:15 @@ -2504,7 +2474,7 @@ Subelemente werden beim Löschen nach oben verschoben. Vorheriges Bild - + Part-DB1\templates\Parts\info\_picture.html.twig:19 Part-DB1\templates\Parts\info\_picture.html.twig:19 @@ -2514,7 +2484,7 @@ Subelemente werden beim Löschen nach oben verschoben. Nächstes Bild - + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 Part-DB1\templates\Parts\info\_sidebar.html.twig:21 @@ -2524,7 +2494,7 @@ Subelemente werden beim Löschen nach oben verschoben. Gewicht - + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 Part-DB1\templates\Parts\info\_sidebar.html.twig:30 @@ -2534,7 +2504,7 @@ Subelemente werden beim Löschen nach oben verschoben. Review benötigt - + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 Part-DB1\templates\Parts\info\_sidebar.html.twig:39 @@ -2544,7 +2514,7 @@ Subelemente werden beim Löschen nach oben verschoben. Favorit - + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 Part-DB1\templates\Parts\info\_sidebar.html.twig:47 @@ -2554,7 +2524,7 @@ Subelemente werden beim Löschen nach oben verschoben. Nicht mehr lieferbar - + Part-DB1\templates\Parts\info\_specifications.html.twig:10 @@ -2563,7 +2533,7 @@ Subelemente werden beim Löschen nach oben verschoben. Automatisch aus Beschreibung extrahiert - + Part-DB1\templates\Parts\info\_specifications.html.twig:15 @@ -2572,7 +2542,7 @@ Subelemente werden beim Löschen nach oben verschoben. Automatisch aus Notizen extrahiert - + Part-DB1\templates\Parts\info\_tools.html.twig:6 Part-DB1\templates\Parts\info\_tools.html.twig:4 @@ -2583,7 +2553,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteil bearbeiten - + Part-DB1\templates\Parts\info\_tools.html.twig:16 Part-DB1\templates\Parts\info\_tools.html.twig:14 @@ -2594,7 +2564,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteil kopieren - + Part-DB1\templates\Parts\info\_tools.html.twig:24 Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 @@ -2605,7 +2575,7 @@ Subelemente werden beim Löschen nach oben verschoben. Neues Bauteil anlegen - + Part-DB1\templates\Parts\info\_tools.html.twig:31 Part-DB1\templates\Parts\info\_tools.html.twig:29 @@ -2615,7 +2585,7 @@ Subelemente werden beim Löschen nach oben verschoben. Möchten Sie dieses Bauteil wirklich löschen? - + Part-DB1\templates\Parts\info\_tools.html.twig:32 Part-DB1\templates\Parts\info\_tools.html.twig:30 @@ -2625,7 +2595,7 @@ Subelemente werden beim Löschen nach oben verschoben. Das Bauteil und alle zugehörigen Informationen (Bestände, Dateianhänge, etc.) werden gelöscht. Dies kann nicht rückgängig gemacht werden. - + Part-DB1\templates\Parts\info\_tools.html.twig:39 Part-DB1\templates\Parts\info\_tools.html.twig:37 @@ -2635,7 +2605,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteil löschen - + Part-DB1\templates\Parts\lists\all_list.html.twig:4 Part-DB1\templates\Parts\lists\all_list.html.twig:4 @@ -2645,7 +2615,7 @@ Subelemente werden beim Löschen nach oben verschoben. Alle Bauteile - + Part-DB1\templates\Parts\lists\category_list.html.twig:4 Part-DB1\templates\Parts\lists\category_list.html.twig:4 @@ -2655,7 +2625,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile mit Kategorie - + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 @@ -2665,7 +2635,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile mit Footprint - + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 @@ -2675,7 +2645,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile mit Hersteller - + Part-DB1\templates\Parts\lists\search_list.html.twig:4 Part-DB1\templates\Parts\lists\search_list.html.twig:4 @@ -2685,7 +2655,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteilesuche - + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 @@ -2695,7 +2665,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile mit Lagerort - + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 @@ -2705,7 +2675,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile mit Lieferant - + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 Part-DB1\templates\Parts\lists\tags_list.html.twig:4 @@ -2715,7 +2685,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile mit Tag - + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 Part-DB1\templates\Parts\lists\_info_card.html.twig:17 @@ -2725,7 +2695,7 @@ Subelemente werden beim Löschen nach oben verschoben. Allgemein - + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 Part-DB1\templates\Parts\lists\_info_card.html.twig:20 @@ -2735,7 +2705,7 @@ Subelemente werden beim Löschen nach oben verschoben. Statistik - + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 @@ -2744,7 +2714,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dateianhänge - + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 @@ -2753,7 +2723,7 @@ Subelemente werden beim Löschen nach oben verschoben. Parameter - + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 Part-DB1\templates\Parts\lists\_info_card.html.twig:30 @@ -2763,7 +2733,7 @@ Subelemente werden beim Löschen nach oben verschoben. Name - + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 Part-DB1\templates\Parts\lists\_info_card.html.twig:96 @@ -2775,7 +2745,7 @@ Subelemente werden beim Löschen nach oben verschoben. Übergeordnetes Element - + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 Part-DB1\templates\Parts\lists\_info_card.html.twig:46 @@ -2785,7 +2755,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bearbeiten - + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 Part-DB1\templates\Parts\lists\_info_card.html.twig:63 @@ -2795,7 +2765,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl an Unterelementen - + Part-DB1\templates\security\2fa_base_form.html.twig:3 Part-DB1\templates\security\2fa_base_form.html.twig:5 @@ -2807,7 +2777,7 @@ Subelemente werden beim Löschen nach oben verschoben. Zwei-Faktor-Authentifizierung benötigt - + Part-DB1\templates\security\2fa_base_form.html.twig:39 Part-DB1\templates\security\2fa_base_form.html.twig:39 @@ -2817,7 +2787,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dies ist ein vertrauenswürdiger Computer (wenn dies aktiviert ist, werden auf diesem Computer keine weiteren Zwei-Faktor-Abfragen durchgeführt) - + Part-DB1\templates\security\2fa_base_form.html.twig:52 Part-DB1\templates\security\login.html.twig:58 @@ -2829,7 +2799,7 @@ Subelemente werden beim Löschen nach oben verschoben. Login - + Part-DB1\templates\security\2fa_base_form.html.twig:53 Part-DB1\templates\security\U2F\u2f_login.html.twig:13 @@ -2843,7 +2813,7 @@ Subelemente werden beim Löschen nach oben verschoben. Ausloggen - + Part-DB1\templates\security\2fa_form.html.twig:6 Part-DB1\templates\security\2fa_form.html.twig:6 @@ -2853,7 +2823,7 @@ Subelemente werden beim Löschen nach oben verschoben. Authenticator App Code - + Part-DB1\templates\security\2fa_form.html.twig:10 Part-DB1\templates\security\2fa_form.html.twig:10 @@ -2863,7 +2833,7 @@ Subelemente werden beim Löschen nach oben verschoben. Geben Sie hier den 6-stelligen Code aus ihrer Authenticator App ein oder einen ihrer Backupcodes, wenn der Authenticator nicht verfügbar ist. - + Part-DB1\templates\security\login.html.twig:3 Part-DB1\templates\security\login.html.twig:3 @@ -2874,7 +2844,7 @@ Subelemente werden beim Löschen nach oben verschoben. Login - + Part-DB1\templates\security\login.html.twig:7 Part-DB1\templates\security\login.html.twig:7 @@ -2885,7 +2855,7 @@ Subelemente werden beim Löschen nach oben verschoben. Login - + Part-DB1\templates\security\login.html.twig:31 Part-DB1\templates\security\login.html.twig:31 @@ -2896,7 +2866,7 @@ Subelemente werden beim Löschen nach oben verschoben. Benutzername - + Part-DB1\templates\security\login.html.twig:34 Part-DB1\templates\security\login.html.twig:34 @@ -2907,7 +2877,7 @@ Subelemente werden beim Löschen nach oben verschoben. Benutzername - + Part-DB1\templates\security\login.html.twig:38 Part-DB1\templates\security\login.html.twig:38 @@ -2918,7 +2888,7 @@ Subelemente werden beim Löschen nach oben verschoben. Passwort - + Part-DB1\templates\security\login.html.twig:40 Part-DB1\templates\security\login.html.twig:40 @@ -2929,7 +2899,7 @@ Subelemente werden beim Löschen nach oben verschoben. Passwort - + Part-DB1\templates\security\login.html.twig:50 Part-DB1\templates\security\login.html.twig:50 @@ -2940,7 +2910,7 @@ Subelemente werden beim Löschen nach oben verschoben. Eingeloggt bleiben (nicht empfohlen auf geteilten Computern) - + Part-DB1\templates\security\login.html.twig:64 Part-DB1\templates\security\login.html.twig:64 @@ -2950,7 +2920,7 @@ Subelemente werden beim Löschen nach oben verschoben. Nutzername/Passwort vergessen? - + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 @@ -2960,7 +2930,7 @@ Subelemente werden beim Löschen nach oben verschoben. Neues Passwort setzen - + Part-DB1\templates\security\pw_reset_request.html.twig:5 Part-DB1\templates\security\pw_reset_request.html.twig:5 @@ -2970,7 +2940,7 @@ Subelemente werden beim Löschen nach oben verschoben. Neues Passwort anfordern - + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 Part-DB1\templates\security\U2F\u2f_register.html.twig:10 @@ -2982,7 +2952,7 @@ Subelemente werden beim Löschen nach oben verschoben. Sie greifen auf diese Seite über das unsichere HTTP-Verfahren zu, daher wird U2F sehr wahrscheinlich nicht funktionieren (Fehlermeldung Bad Request). Bitten Sie einen Administrator, das sichere HTTPS Verfahren einzurichten, wenn Sie Sicherheitsschlüssel benutzen möchten. - + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 Part-DB1\templates\security\U2F\u2f_register.html.twig:22 @@ -2994,7 +2964,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bitte Sicherheitsschlüssel einstecken und Button drücken! - + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 Part-DB1\templates\security\U2F\u2f_register.html.twig:3 @@ -3004,7 +2974,7 @@ Subelemente werden beim Löschen nach oben verschoben. Sicherheitsschlüssel hinzufügen - + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 Part-DB1\templates\Users\_2fa_settings.html.twig:111 @@ -3016,7 +2986,7 @@ Subelemente werden beim Löschen nach oben verschoben. Mithilfe eines U2F/FIDO kompatiblem Sicherheitsschlüssel (z.B. YubiKey oder NitroKey) kann eine benutzerfreundliche und sichere Zwei-Faktor-Authentifizierung ermöglicht. Die Sicherheitsschlüssel können hier registriert werden, und wird eine Zwei-Faktor-Überprüfung benötigt, so muss der Schlüssel nur per USB angesteckt oder per NFC gegen das Gerät getippt werden. - + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 Part-DB1\templates\security\U2F\u2f_register.html.twig:7 @@ -3026,7 +2996,7 @@ Subelemente werden beim Löschen nach oben verschoben. Um den Zugang auch bei Verlust des Schlüssels zu gewährleisten, ist es empfehlenswert einen zweiten Schlüssel als Backup zu registrieren und diesen an einem sicherem Ort zu lagern! - + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 Part-DB1\templates\security\U2F\u2f_register.html.twig:16 @@ -3036,7 +3006,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzeigename des Schlüssels (z.B. Backup) - + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 Part-DB1\templates\security\U2F\u2f_register.html.twig:19 @@ -3046,7 +3016,7 @@ Subelemente werden beim Löschen nach oben verschoben. Schlüssel hinzufügen - + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 Part-DB1\templates\security\U2F\u2f_register.html.twig:27 @@ -3056,7 +3026,7 @@ Subelemente werden beim Löschen nach oben verschoben. Zurück zu den Einstellungen - + Part-DB1\templates\Statistics\statistics.html.twig:5 Part-DB1\templates\Statistics\statistics.html.twig:8 @@ -3069,7 +3039,7 @@ Subelemente werden beim Löschen nach oben verschoben. Statistik - + Part-DB1\templates\Statistics\statistics.html.twig:14 Part-DB1\templates\Statistics\statistics.html.twig:14 @@ -3080,7 +3050,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile - + Part-DB1\templates\Statistics\statistics.html.twig:19 Part-DB1\templates\Statistics\statistics.html.twig:19 @@ -3091,7 +3061,7 @@ Subelemente werden beim Löschen nach oben verschoben. Datenstrukturen - + Part-DB1\templates\Statistics\statistics.html.twig:24 Part-DB1\templates\Statistics\statistics.html.twig:24 @@ -3102,7 +3072,7 @@ Subelemente werden beim Löschen nach oben verschoben. Dateianhänge - + Part-DB1\templates\Statistics\statistics.html.twig:34 Part-DB1\templates\Statistics\statistics.html.twig:59 @@ -3117,7 +3087,7 @@ Subelemente werden beim Löschen nach oben verschoben. Eigenschaft - + Part-DB1\templates\Statistics\statistics.html.twig:35 Part-DB1\templates\Statistics\statistics.html.twig:60 @@ -3132,7 +3102,7 @@ Subelemente werden beim Löschen nach oben verschoben. Wert - + Part-DB1\templates\Statistics\statistics.html.twig:40 Part-DB1\templates\Statistics\statistics.html.twig:40 @@ -3143,7 +3113,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl verschiedener Bauteile - + Part-DB1\templates\Statistics\statistics.html.twig:44 Part-DB1\templates\Statistics\statistics.html.twig:44 @@ -3154,7 +3124,7 @@ Subelemente werden beim Löschen nach oben verschoben. Summe aller vorhanden Bauteilebestände - + Part-DB1\templates\Statistics\statistics.html.twig:48 Part-DB1\templates\Statistics\statistics.html.twig:48 @@ -3165,7 +3135,7 @@ Subelemente werden beim Löschen nach oben verschoben. Bauteile mit Preisinformationen - + Part-DB1\templates\Statistics\statistics.html.twig:65 Part-DB1\templates\Statistics\statistics.html.twig:65 @@ -3176,7 +3146,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Kategorien - + Part-DB1\templates\Statistics\statistics.html.twig:69 Part-DB1\templates\Statistics\statistics.html.twig:69 @@ -3187,7 +3157,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Footprints - + Part-DB1\templates\Statistics\statistics.html.twig:73 Part-DB1\templates\Statistics\statistics.html.twig:73 @@ -3198,7 +3168,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Hersteller - + Part-DB1\templates\Statistics\statistics.html.twig:77 Part-DB1\templates\Statistics\statistics.html.twig:77 @@ -3209,7 +3179,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Lagerorte - + Part-DB1\templates\Statistics\statistics.html.twig:81 Part-DB1\templates\Statistics\statistics.html.twig:81 @@ -3220,7 +3190,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Lieferanten - + Part-DB1\templates\Statistics\statistics.html.twig:85 Part-DB1\templates\Statistics\statistics.html.twig:85 @@ -3231,7 +3201,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Währungen - + Part-DB1\templates\Statistics\statistics.html.twig:89 Part-DB1\templates\Statistics\statistics.html.twig:89 @@ -3242,7 +3212,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Maßeinheiten - + Part-DB1\templates\Statistics\statistics.html.twig:93 Part-DB1\templates\Statistics\statistics.html.twig:93 @@ -3253,7 +3223,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Baugruppen - + Part-DB1\templates\Statistics\statistics.html.twig:110 Part-DB1\templates\Statistics\statistics.html.twig:110 @@ -3264,7 +3234,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl Anhangstypen - + Part-DB1\templates\Statistics\statistics.html.twig:114 Part-DB1\templates\Statistics\statistics.html.twig:114 @@ -3275,7 +3245,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl aller Dateianhänge - + Part-DB1\templates\Statistics\statistics.html.twig:118 Part-DB1\templates\Statistics\statistics.html.twig:118 @@ -3286,7 +3256,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl aller vom Nutzer hochgeladener Anhänge - + Part-DB1\templates\Statistics\statistics.html.twig:122 Part-DB1\templates\Statistics\statistics.html.twig:122 @@ -3297,7 +3267,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl aller privaten Anhänge - + Part-DB1\templates\Statistics\statistics.html.twig:126 Part-DB1\templates\Statistics\statistics.html.twig:126 @@ -3308,7 +3278,7 @@ Subelemente werden beim Löschen nach oben verschoben. Anzahl aller externen Anhänge (URL) - + Part-DB1\templates\Users\backup_codes.html.twig:3 Part-DB1\templates\Users\backup_codes.html.twig:9 @@ -3320,7 +3290,7 @@ Subelemente werden beim Löschen nach oben verschoben. Backupcodes - + Part-DB1\templates\Users\backup_codes.html.twig:12 Part-DB1\templates\Users\backup_codes.html.twig:12 @@ -3330,7 +3300,7 @@ Subelemente werden beim Löschen nach oben verschoben. Drucken Sie diese Codes aus und bewahren Sie sie an einem sicherem Ort auf! - + Part-DB1\templates\Users\backup_codes.html.twig:13 Part-DB1\templates\Users\backup_codes.html.twig:13 @@ -3340,7 +3310,7 @@ Subelemente werden beim Löschen nach oben verschoben. Wenn Sie keinen Zugriff auf ihr Gerät mit der Authenticator App mehr haben sollten (Smartphone verloren, Datenverlust, etc.) können Sie einen dieser Codes benutzen, um Zugriff auf ihren Account zu erhalten und evtl. eine neue Authenticator App einzurichten. Jeder dieser Codes lässt sich einmal einsetzen, es empfiehlt sich benutzte Codes zu streichen. Jeder mit Zugriff auf diese Codes kann potentiell auf ihren Account zugreifen, daher bewahren Sie sie an einem sicheren Ort auf. - + Part-DB1\templates\Users\backup_codes.html.twig:16 Part-DB1\templates\Users\backup_codes.html.twig:16 @@ -3350,7 +3320,7 @@ Subelemente werden beim Löschen nach oben verschoben. Benutzername - + Part-DB1\templates\Users\backup_codes.html.twig:29 Part-DB1\templates\Users\backup_codes.html.twig:29 @@ -3360,7 +3330,7 @@ Subelemente werden beim Löschen nach oben verschoben. Codes abgerufen am %date% - + Part-DB1\templates\Users\backup_codes.html.twig:32 Part-DB1\templates\Users\backup_codes.html.twig:32 @@ -3370,7 +3340,7 @@ Subelemente werden beim Löschen nach oben verschoben. Drucken - + Part-DB1\templates\Users\backup_codes.html.twig:35 Part-DB1\templates\Users\backup_codes.html.twig:35 @@ -3380,7 +3350,7 @@ Subelemente werden beim Löschen nach oben verschoben. In die Zwischenablage kopieren - + Part-DB1\templates\Users\user_info.html.twig:3 Part-DB1\templates\Users\user_info.html.twig:6 @@ -3397,7 +3367,7 @@ Subelemente werden beim Löschen nach oben verschoben. Benutzerinformationen - + Part-DB1\templates\Users\user_info.html.twig:18 Part-DB1\src\Form\UserSettingsType.php:77 @@ -3411,7 +3381,7 @@ Subelemente werden beim Löschen nach oben verschoben. Vorname - + Part-DB1\templates\Users\user_info.html.twig:24 Part-DB1\src\Form\UserSettingsType.php:82 @@ -3425,7 +3395,7 @@ Subelemente werden beim Löschen nach oben verschoben. Nachname - + Part-DB1\templates\Users\user_info.html.twig:30 Part-DB1\src\Form\UserSettingsType.php:92 @@ -3439,7 +3409,7 @@ Subelemente werden beim Löschen nach oben verschoben. Email - + Part-DB1\templates\Users\user_info.html.twig:37 Part-DB1\src\Form\UserSettingsType.php:87 @@ -3453,7 +3423,7 @@ Subelemente werden beim Löschen nach oben verschoben. Abteilung - + Part-DB1\templates\Users\user_info.html.twig:47 Part-DB1\src\Form\UserSettingsType.php:73 @@ -3467,7 +3437,7 @@ Subelemente werden beim Löschen nach oben verschoben. Benutzername - + Part-DB1\templates\Users\user_info.html.twig:53 Part-DB1\src\Services\ElementTypeNameGenerator.php:93 @@ -3480,7 +3450,7 @@ Subelemente werden beim Löschen nach oben verschoben. Group - + Part-DB1\templates\Users\user_info.html.twig:67 Part-DB1\templates\Users\user_info.html.twig:67 @@ -3490,7 +3460,7 @@ Subelemente werden beim Löschen nach oben verschoben. Berechtigungen - + Part-DB1\templates\Users\user_settings.html.twig:3 Part-DB1\templates\Users\user_settings.html.twig:6 @@ -3507,7 +3477,7 @@ Subelemente werden beim Löschen nach oben verschoben. Benutzereinstellungen - + Part-DB1\templates\Users\user_settings.html.twig:18 Part-DB1\templates\Users\user_settings.html.twig:18 @@ -3518,7 +3488,7 @@ Subelemente werden beim Löschen nach oben verschoben. Persönliche Daten - + Part-DB1\templates\Users\user_settings.html.twig:22 Part-DB1\templates\Users\user_settings.html.twig:22 @@ -3529,7 +3499,7 @@ Subelemente werden beim Löschen nach oben verschoben. Konfiguration - + Part-DB1\templates\Users\user_settings.html.twig:55 Part-DB1\templates\Users\user_settings.html.twig:55 @@ -3540,7 +3510,7 @@ Subelemente werden beim Löschen nach oben verschoben. Passwort ändern - + Part-DB1\templates\Users\_2fa_settings.html.twig:6 Part-DB1\templates\Users\_2fa_settings.html.twig:6 @@ -3550,7 +3520,7 @@ Subelemente werden beim Löschen nach oben verschoben. Zwei-Faktor-Authentifizierung - + Part-DB1\templates\Users\_2fa_settings.html.twig:13 Part-DB1\templates\Users\_2fa_settings.html.twig:13 @@ -3560,7 +3530,7 @@ Subelemente werden beim Löschen nach oben verschoben. Authenticator App - + Part-DB1\templates\Users\_2fa_settings.html.twig:17 Part-DB1\templates\Users\_2fa_settings.html.twig:17 @@ -3570,7 +3540,7 @@ Subelemente werden beim Löschen nach oben verschoben. Backupcodes - + Part-DB1\templates\Users\_2fa_settings.html.twig:21 Part-DB1\templates\Users\_2fa_settings.html.twig:21 @@ -3580,7 +3550,7 @@ Subelemente werden beim Löschen nach oben verschoben. Sicherheitsschlüssel (U2F) - + Part-DB1\templates\Users\_2fa_settings.html.twig:25 Part-DB1\templates\Users\_2fa_settings.html.twig:25 @@ -3590,7 +3560,7 @@ Subelemente werden beim Löschen nach oben verschoben. Vertrauenswürdige Geräte - + Part-DB1\templates\Users\_2fa_settings.html.twig:33 Part-DB1\templates\Users\_2fa_settings.html.twig:33 @@ -3600,7 +3570,7 @@ Subelemente werden beim Löschen nach oben verschoben. Möchten Sie die Authenticator App wirklich deaktivieren? - + Part-DB1\templates\Users\_2fa_settings.html.twig:33 Part-DB1\templates\Users\_2fa_settings.html.twig:33 @@ -3611,7 +3581,7 @@ Subelemente werden beim Löschen nach oben verschoben. Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nicht mehr so gut gegen Angreifer geschützt ist! - + Part-DB1\templates\Users\_2fa_settings.html.twig:39 Part-DB1\templates\Users\_2fa_settings.html.twig:39 @@ -3621,7 +3591,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Authenticator App deaktiviert - + Part-DB1\templates\Users\_2fa_settings.html.twig:48 Part-DB1\templates\Users\_2fa_settings.html.twig:48 @@ -3631,7 +3601,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Laden Sie eine Authenticator App herunter (z.B. <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> oder <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp">FreeOTP Authenticator</a>) - + Part-DB1\templates\Users\_2fa_settings.html.twig:49 Part-DB1\templates\Users\_2fa_settings.html.twig:49 @@ -3641,7 +3611,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Scannen Sie den nebenstehenden QR-Code mit der App oder geben Sie die Daten manuell ein - + Part-DB1\templates\Users\_2fa_settings.html.twig:50 Part-DB1\templates\Users\_2fa_settings.html.twig:50 @@ -3651,7 +3621,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Geben Sie den erzeugten Code in das untere Feld ein und bestätigen Sie - + Part-DB1\templates\Users\_2fa_settings.html.twig:51 Part-DB1\templates\Users\_2fa_settings.html.twig:51 @@ -3661,7 +3631,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Drucken Sie ihre Backupcodes aus und lagern sie an einem sicheren Ort - + Part-DB1\templates\Users\_2fa_settings.html.twig:58 Part-DB1\templates\Users\_2fa_settings.html.twig:58 @@ -3671,7 +3641,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Manuelle Einrichtung - + Part-DB1\templates\Users\_2fa_settings.html.twig:62 Part-DB1\templates\Users\_2fa_settings.html.twig:62 @@ -3681,7 +3651,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Typ - + Part-DB1\templates\Users\_2fa_settings.html.twig:63 Part-DB1\templates\Users\_2fa_settings.html.twig:63 @@ -3691,7 +3661,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Benutzername - + Part-DB1\templates\Users\_2fa_settings.html.twig:64 Part-DB1\templates\Users\_2fa_settings.html.twig:64 @@ -3701,7 +3671,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Secret - + Part-DB1\templates\Users\_2fa_settings.html.twig:65 Part-DB1\templates\Users\_2fa_settings.html.twig:65 @@ -3711,7 +3681,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Anzahl Stellen - + Part-DB1\templates\Users\_2fa_settings.html.twig:74 Part-DB1\templates\Users\_2fa_settings.html.twig:74 @@ -3721,7 +3691,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Authenticator App aktiv - + Part-DB1\templates\Users\_2fa_settings.html.twig:83 Part-DB1\templates\Users\_2fa_settings.html.twig:83 @@ -3731,7 +3701,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Backupcodes deaktiviert. Authenticator App einrichten, um Backupcodes zu aktivieren. - + Part-DB1\templates\Users\_2fa_settings.html.twig:84 Part-DB1\templates\Users\_2fa_settings.html.twig:92 @@ -3743,7 +3713,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Mithilfe dieser Backupcodes können Sie auf ihren Account zugreifen, selbst wenn Sie das Gerät mit der Authenticator App verlieren sollten. Drucken Sie die Codes aus und bewahren Sie sie an einem sicherem Ort auf. - + Part-DB1\templates\Users\_2fa_settings.html.twig:88 Part-DB1\templates\Users\_2fa_settings.html.twig:88 @@ -3753,7 +3723,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Codes wirklich zurücksetzen? - + Part-DB1\templates\Users\_2fa_settings.html.twig:88 Part-DB1\templates\Users\_2fa_settings.html.twig:88 @@ -3763,7 +3733,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Dies wird alle bisherigen Codes löschen und einen Satz neuer Codes generieren. Dies lässt sich nicht rückgängig machen. Denken Sie daran die neuen Codes auszudrucken und an einem sicheren Ort zu hinterlegen! - + Part-DB1\templates\Users\_2fa_settings.html.twig:91 Part-DB1\templates\Users\_2fa_settings.html.twig:91 @@ -3773,7 +3743,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Backupcodes aktiviert - + Part-DB1\templates\Users\_2fa_settings.html.twig:99 Part-DB1\templates\Users\_2fa_settings.html.twig:99 @@ -3783,7 +3753,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Backupcodes anzeigen - + Part-DB1\templates\Users\_2fa_settings.html.twig:114 Part-DB1\templates\Users\_2fa_settings.html.twig:114 @@ -3793,7 +3763,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Registrierte Sicherheitsschlüssel - + Part-DB1\templates\Users\_2fa_settings.html.twig:115 Part-DB1\templates\Users\_2fa_settings.html.twig:115 @@ -3803,7 +3773,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Diesen Sicherheitsschlüssel wirklich entfernen? - + Part-DB1\templates\Users\_2fa_settings.html.twig:116 Part-DB1\templates\Users\_2fa_settings.html.twig:116 @@ -3813,7 +3783,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Wenn Sie diesen Schlüssel entfernen, dann wird kein Login mehr mit diesem möglich sein. Wenn keine Sicherheitsschlüssel verleiben, wird die Zwei-Faktor-Authentifizierung deaktiviert. - + Part-DB1\templates\Users\_2fa_settings.html.twig:123 Part-DB1\templates\Users\_2fa_settings.html.twig:123 @@ -3823,7 +3793,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Name des Schlüssels - + Part-DB1\templates\Users\_2fa_settings.html.twig:124 Part-DB1\templates\Users\_2fa_settings.html.twig:124 @@ -3833,7 +3803,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Datum der Registrierung - + Part-DB1\templates\Users\_2fa_settings.html.twig:134 Part-DB1\templates\Users\_2fa_settings.html.twig:134 @@ -3843,7 +3813,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Schlüssel löschen - + Part-DB1\templates\Users\_2fa_settings.html.twig:141 Part-DB1\templates\Users\_2fa_settings.html.twig:141 @@ -3853,7 +3823,7 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Keine Sicherheitsschlüssel registriert - + Part-DB1\templates\Users\_2fa_settings.html.twig:144 Part-DB1\templates\Users\_2fa_settings.html.twig:144 @@ -3863,18 +3833,18 @@ Beachten Sie außerdem, dass ihr Account ohne Zwei-Faktor-Authentifizierung nich Neuen Sicherheitsschlüssel registrieren - + Part-DB1\templates\Users\_2fa_settings.html.twig:148 Part-DB1\templates\Users\_2fa_settings.html.twig:148 tfa_trustedDevices.explanation - Bei der Überprüfung des zweiten Faktors, kann der aktuelle Computer als vertrauenswürdig gekennzeichnet werden, daher es werden keine Zwei-Faktor-Überprüfungen mehr an diesem Computer benötigt. + Bei der Überprüfung des zweiten Faktors, kann der aktuelle Computer als vertrauenswürdig gekennzeichnet werden, daher werden keine Zwei-Faktor-Überprüfungen mehr an diesem Computer benötigt. Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertrauenswürdig ist, können Sie hier den Status <i>aller </i>Computer zurücksetzen. - + Part-DB1\templates\Users\_2fa_settings.html.twig:149 Part-DB1\templates\Users\_2fa_settings.html.twig:149 @@ -3884,7 +3854,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wirklich alle vertrauenswürdigen Computer entfernen? - + Part-DB1\templates\Users\_2fa_settings.html.twig:150 Part-DB1\templates\Users\_2fa_settings.html.twig:150 @@ -3894,7 +3864,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sie werden auf allen Rechnern erneut eine Zwei-Faktor-Authentifizierung durchführen müssen. Achten Sie darauf, dass Sie ihr Zwei-Faktor-Gerät zur Hand haben. - + Part-DB1\templates\Users\_2fa_settings.html.twig:154 Part-DB1\templates\Users\_2fa_settings.html.twig:154 @@ -3904,7 +3874,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Alle vertrauenswürdigen Geräte entfernen - + Part-DB1\templates\_navbar.html.twig:4 Part-DB1\templates\_navbar.html.twig:4 @@ -3915,7 +3885,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sidebar umschalten - + Part-DB1\templates\_navbar.html.twig:22 @@ -3924,7 +3894,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Scanner - + Part-DB1\templates\_navbar.html.twig:38 Part-DB1\templates\_navbar.html.twig:36 @@ -3935,7 +3905,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Eingeloggt als - + Part-DB1\templates\_navbar.html.twig:44 Part-DB1\templates\_navbar.html.twig:42 @@ -3946,7 +3916,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Einloggen - + Part-DB1\templates\_navbar.html.twig:50 Part-DB1\templates\_navbar.html.twig:48 @@ -3956,7 +3926,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Darkmode - + Part-DB1\templates\_navbar.html.twig:54 Part-DB1\src\Form\UserSettingsType.php:97 @@ -3970,7 +3940,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sprache - + Part-DB1\templates\_navbar_search.html.twig:4 Part-DB1\templates\_navbar_search.html.twig:4 @@ -3981,7 +3951,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Suchoptionen - + Part-DB1\templates\_navbar_search.html.twig:23 @@ -3990,7 +3960,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Tags - + Part-DB1\templates\_navbar_search.html.twig:27 Part-DB1\src\Form\LabelOptionsType.php:68 @@ -4005,7 +3975,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lagerort - + Part-DB1\templates\_navbar_search.html.twig:36 Part-DB1\templates\_navbar_search.html.twig:31 @@ -4016,7 +3986,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bestellnr. - + Part-DB1\templates\_navbar_search.html.twig:40 Part-DB1\src\Services\ElementTypeNameGenerator.php:89 @@ -4029,7 +3999,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lieferant - + Part-DB1\templates\_navbar_search.html.twig:57 Part-DB1\templates\_navbar_search.html.twig:52 @@ -4040,7 +4010,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Deakt. Barcode - + Part-DB1\templates\_navbar_search.html.twig:61 Part-DB1\templates\_navbar_search.html.twig:56 @@ -4051,7 +4021,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Reg.Ex. Matching - + Part-DB1\templates\_navbar_search.html.twig:68 Part-DB1\templates\_navbar_search.html.twig:62 @@ -4061,7 +4031,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Los! - + Part-DB1\templates\_sidebar.html.twig:37 Part-DB1\templates\_sidebar.html.twig:12 @@ -4077,7 +4047,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Projekte - + Part-DB1\templates\_sidebar.html.twig:2 Part-DB1\templates\_sidebar.html.twig:2 @@ -4090,7 +4060,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Aktionen - + Part-DB1\templates\_sidebar.html.twig:6 Part-DB1\templates\_sidebar.html.twig:6 @@ -4103,7 +4073,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Datenquelle - + Part-DB1\templates\_sidebar.html.twig:10 Part-DB1\templates\_sidebar.html.twig:10 @@ -4116,7 +4086,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Hersteller - + Part-DB1\templates\_sidebar.html.twig:11 Part-DB1\templates\_sidebar.html.twig:11 @@ -4129,7 +4099,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lieferanten - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 @@ -4145,7 +4115,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Download der externen Datei fehlgeschlagen! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 @@ -4155,7 +4125,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderungen erfolgreich gespeichert. - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 @@ -4165,7 +4135,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderungen konnten nicht gespeichert werden! Prüfen Sie ihre Eingaben! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 @@ -4175,7 +4145,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Element erfolgreich angelegt! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 @@ -4185,7 +4155,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Element konnte nicht angelegt werden! Prüfen Sie ihre Eingaben! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 @@ -4196,7 +4166,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Element gelöscht! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 Part-DB1\src\Controller\UserController.php:109 @@ -4212,7 +4182,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr CSFR-Token ungültig! Laden Sie diese Seite erneut oder kontaktieren Sie einen Administrator, wenn das Problem bestehen bleibt! - + Part-DB1\src\Controller\LabelController.php:125 @@ -4221,7 +4191,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Keine Elemente gefunden - + Part-DB1\src\Controller\LogController.php:149 Part-DB1\src\Controller\LogController.php:154 @@ -4232,7 +4202,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Zielelement nicht in Datenbank gefunden! - + Part-DB1\src\Controller\LogController.php:156 Part-DB1\src\Controller\LogController.php:160 @@ -4243,7 +4213,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteil erfolgreich zurückgesetzt. - + Part-DB1\src\Controller\LogController.php:176 Part-DB1\src\Controller\LogController.php:180 @@ -4254,7 +4224,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteil erfolgreich wiederhergestellt. - + Part-DB1\src\Controller\LogController.php:178 Part-DB1\src\Controller\LogController.php:182 @@ -4265,7 +4235,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteile wurde bereits wiederhergestellt! - + Part-DB1\src\Controller\LogController.php:185 Part-DB1\src\Controller\LogController.php:189 @@ -4276,7 +4246,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteil erfolgreich gelöscht. - + Part-DB1\src\Controller\LogController.php:187 Part-DB1\src\Controller\LogController.php:191 @@ -4287,7 +4257,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteil wurde bereits gelöscht - + Part-DB1\src\Controller\LogController.php:194 Part-DB1\src\Controller\LogController.php:198 @@ -4298,7 +4268,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderung erfolgreich rückgängig gemacht. - + Part-DB1\src\Controller\LogController.php:196 Part-DB1\src\Controller\LogController.php:200 @@ -4309,7 +4279,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sie müssen das Element zuerst wiederherstellen bevor sie diese Änderung rückgängig machen können! - + Part-DB1\src\Controller\LogController.php:199 Part-DB1\src\Controller\LogController.php:203 @@ -4320,7 +4290,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Dieser Logtyp kann nicht rückgängig gemacht werden! - + Part-DB1\src\Controller\PartController.php:182 Part-DB1\src\Controller\PartController.php:182 @@ -4331,7 +4301,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderungen gespeichert! - + Part-DB1\src\Controller\PartController.php:186 Part-DB1\src\Controller\PartController.php:186 @@ -4341,7 +4311,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Fehler beim Speichern: Überprüfen Sie ihre Eingaben! - + Part-DB1\src\Controller\PartController.php:216 Part-DB1\src\Controller\PartController.php:219 @@ -4351,7 +4321,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteil erfolgreich gelöscht. - + Part-DB1\src\Controller\PartController.php:302 Part-DB1\src\Controller\PartController.php:277 @@ -4364,7 +4334,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteile erfolgreich angelegt! - + Part-DB1\src\Controller\PartController.php:308 Part-DB1\src\Controller\PartController.php:283 @@ -4374,7 +4344,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Fehler beim Anlegen: Überprüfen Sie ihre Eingaben! - + Part-DB1\src\Controller\ScanController.php:68 Part-DB1\src\Controller\ScanController.php:90 @@ -4384,7 +4354,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Kein Element gefunden - + Part-DB1\src\Controller\ScanController.php:71 @@ -4393,7 +4363,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Format unbekannt - + Part-DB1\src\Controller\ScanController.php:86 @@ -4402,7 +4372,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Element gefunden - + Part-DB1\src\Controller\SecurityController.php:114 Part-DB1\src\Controller\SecurityController.php:109 @@ -4412,7 +4382,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Benutzername / Email - + Part-DB1\src\Controller\SecurityController.php:131 Part-DB1\src\Controller\SecurityController.php:126 @@ -4422,7 +4392,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Passwort Anfrage erfolgreich! Überprüfen Sie Ihre Emails für weitere Informationen. - + Part-DB1\src\Controller\SecurityController.php:162 Part-DB1\src\Controller\SecurityController.php:160 @@ -4432,7 +4402,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Benutzername - + Part-DB1\src\Controller\SecurityController.php:165 Part-DB1\src\Controller\SecurityController.php:163 @@ -4442,7 +4412,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Token - + Part-DB1\src\Controller\SecurityController.php:194 Part-DB1\src\Controller\SecurityController.php:192 @@ -4452,7 +4422,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Benutzername oder Token ungültig! Überprüfen Sie ihre Eingaben. - + Part-DB1\src\Controller\SecurityController.php:196 Part-DB1\src\Controller\SecurityController.php:194 @@ -4462,7 +4432,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Passwort wurde erfolgreich zurückgesetzt. Sie können sich nun mit dem neuen Passwort einloggen. - + Part-DB1\src\Controller\UserController.php:107 Part-DB1\src\Controller\UserController.php:99 @@ -4472,7 +4442,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Alle Zwei-Faktor-Authentisierungsmethoden wurden erfolgreich deaktiviert. - + Part-DB1\src\Controller\UserSettingsController.php:101 Part-DB1\src\Controller\UserSettingsController.php:92 @@ -4482,7 +4452,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Es sind keine Backupcodes aktiviert! - + Part-DB1\src\Controller\UserSettingsController.php:138 Part-DB1\src\Controller\UserSettingsController.php:132 @@ -4492,7 +4462,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Es existiert kein Sicherheitsschlüssel mit dieser ID! - + Part-DB1\src\Controller\UserSettingsController.php:145 Part-DB1\src\Controller\UserSettingsController.php:139 @@ -4502,7 +4472,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sie können nur ihre eigenen Sicherheitsschlüssel löschen! - + Part-DB1\src\Controller\UserSettingsController.php:153 Part-DB1\src\Controller\UserSettingsController.php:147 @@ -4512,7 +4482,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sicherheitsschlüssel erfolgreich entfernt. - + Part-DB1\src\Controller\UserSettingsController.php:188 Part-DB1\src\Controller\UserSettingsController.php:180 @@ -4522,7 +4492,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Vertrauenswürdige Geräte erfolgreich zurückgesetzt. - + Part-DB1\src\Controller\UserSettingsController.php:235 Part-DB1\src\Controller\UserSettingsController.php:226 @@ -4533,7 +4503,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Einstellungen gespeichert! - + Part-DB1\src\Controller\UserSettingsController.php:297 Part-DB1\src\Controller\UserSettingsController.php:288 @@ -4544,7 +4514,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Passwort geändert! - + Part-DB1\src\Controller\UserSettingsController.php:317 Part-DB1\src\Controller\UserSettingsController.php:306 @@ -4554,7 +4524,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Authenticator App erfolgreich aktiviert. - + Part-DB1\src\Controller\UserSettingsController.php:328 Part-DB1\src\Controller\UserSettingsController.php:315 @@ -4564,7 +4534,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Authenticator App erfolgreich deaktiviert. - + Part-DB1\src\Controller\UserSettingsController.php:346 Part-DB1\src\Controller\UserSettingsController.php:332 @@ -4574,7 +4544,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Neue Backupcodes erfolgreich erzeugt. - + Part-DB1\src\DataTables\AttachmentDataTable.php:148 Part-DB1\src\DataTables\AttachmentDataTable.php:148 @@ -4584,7 +4554,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Dateiname - + Part-DB1\src\DataTables\AttachmentDataTable.php:153 Part-DB1\src\DataTables\AttachmentDataTable.php:153 @@ -4594,7 +4564,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Dateigröße - + Part-DB1\src\DataTables\AttachmentDataTable.php:183 Part-DB1\src\DataTables\AttachmentDataTable.php:191 @@ -4614,7 +4584,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr wahr - + Part-DB1\src\DataTables\AttachmentDataTable.php:184 Part-DB1\src\DataTables\AttachmentDataTable.php:192 @@ -4636,7 +4606,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr falsch - + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 @@ -4646,7 +4616,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr gelöscht - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 @@ -4654,10 +4624,10 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr log.undo.undelete - Bauteil wiederherstellen + Element wiederherstellen - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 @@ -4668,7 +4638,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderung rückgängig machen - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 @@ -4679,7 +4649,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Element auf Stand dieses Zeitpunktes zurücksetzen! - + Part-DB1\src\DataTables\LogDataTable.php:173 Part-DB1\src\DataTables\LogDataTable.php:161 @@ -4689,7 +4659,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr ID - + Part-DB1\src\DataTables\LogDataTable.php:178 Part-DB1\src\DataTables\LogDataTable.php:166 @@ -4699,7 +4669,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Zeitstempel - + Part-DB1\src\DataTables\LogDataTable.php:183 Part-DB1\src\DataTables\LogDataTable.php:171 @@ -4709,7 +4679,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Ereignis - + Part-DB1\src\DataTables\LogDataTable.php:191 Part-DB1\src\DataTables\LogDataTable.php:179 @@ -4719,7 +4689,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Level - + Part-DB1\src\DataTables\LogDataTable.php:200 Part-DB1\src\DataTables\LogDataTable.php:188 @@ -4729,7 +4699,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Benutzer - + Part-DB1\src\DataTables\LogDataTable.php:213 Part-DB1\src\DataTables\LogDataTable.php:201 @@ -4739,7 +4709,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Zieltyp - + Part-DB1\src\DataTables\LogDataTable.php:226 Part-DB1\src\DataTables\LogDataTable.php:214 @@ -4749,7 +4719,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Ziel - + Part-DB1\src\DataTables\LogDataTable.php:231 Part-DB1\src\DataTables\LogDataTable.php:218 @@ -4760,7 +4730,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Extra - + Part-DB1\src\DataTables\PartsDataTable.php:168 Part-DB1\src\DataTables\PartsDataTable.php:116 @@ -4770,7 +4740,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Name - + Part-DB1\src\DataTables\PartsDataTable.php:178 Part-DB1\src\DataTables\PartsDataTable.php:126 @@ -4780,7 +4750,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr ID - + Part-DB1\src\DataTables\PartsDataTable.php:182 Part-DB1\src\DataTables\PartsDataTable.php:130 @@ -4790,7 +4760,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Beschreibung - + Part-DB1\src\DataTables\PartsDataTable.php:185 Part-DB1\src\DataTables\PartsDataTable.php:133 @@ -4800,7 +4770,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Kategorie - + Part-DB1\src\DataTables\PartsDataTable.php:190 Part-DB1\src\DataTables\PartsDataTable.php:138 @@ -4810,7 +4780,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Footprint - + Part-DB1\src\DataTables\PartsDataTable.php:194 Part-DB1\src\DataTables\PartsDataTable.php:142 @@ -4820,7 +4790,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Hersteller - + Part-DB1\src\DataTables\PartsDataTable.php:197 Part-DB1\src\DataTables\PartsDataTable.php:145 @@ -4830,7 +4800,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lagerorte - + Part-DB1\src\DataTables\PartsDataTable.php:216 Part-DB1\src\DataTables\PartsDataTable.php:164 @@ -4840,7 +4810,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Menge - + Part-DB1\src\DataTables\PartsDataTable.php:224 Part-DB1\src\DataTables\PartsDataTable.php:172 @@ -4850,7 +4820,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Min. Menge - + Part-DB1\src\DataTables\PartsDataTable.php:232 Part-DB1\src\DataTables\PartsDataTable.php:180 @@ -4860,7 +4830,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Maßeinheit - + Part-DB1\src\DataTables\PartsDataTable.php:236 Part-DB1\src\DataTables\PartsDataTable.php:184 @@ -4870,7 +4840,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Hinzugefügt - + Part-DB1\src\DataTables\PartsDataTable.php:240 Part-DB1\src\DataTables\PartsDataTable.php:188 @@ -4880,7 +4850,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Zuletzt bearbeitet - + Part-DB1\src\DataTables\PartsDataTable.php:244 Part-DB1\src\DataTables\PartsDataTable.php:192 @@ -4890,7 +4860,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Review benötigt - + Part-DB1\src\DataTables\PartsDataTable.php:251 Part-DB1\src\DataTables\PartsDataTable.php:199 @@ -4900,7 +4870,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Favorit - + Part-DB1\src\DataTables\PartsDataTable.php:258 Part-DB1\src\DataTables\PartsDataTable.php:206 @@ -4910,7 +4880,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Status - + Part-DB1\src\DataTables\PartsDataTable.php:260 Part-DB1\src\DataTables\PartsDataTable.php:262 @@ -4924,7 +4894,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Unbekannt - + Part-DB1\src\DataTables\PartsDataTable.php:263 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4936,7 +4906,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Angekündigt - + Part-DB1\src\DataTables\PartsDataTable.php:264 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4948,7 +4918,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Aktiv - + Part-DB1\src\DataTables\PartsDataTable.php:265 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4960,7 +4930,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Not recommended for new designs - + Part-DB1\src\DataTables\PartsDataTable.php:266 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4972,7 +4942,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr End of life - + Part-DB1\src\DataTables\PartsDataTable.php:267 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4984,7 +4954,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Discontinued - + Part-DB1\src\DataTables\PartsDataTable.php:271 Part-DB1\src\DataTables\PartsDataTable.php:219 @@ -4994,7 +4964,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr MPN - + Part-DB1\src\DataTables\PartsDataTable.php:275 Part-DB1\src\DataTables\PartsDataTable.php:223 @@ -5004,7 +4974,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Gewicht - + Part-DB1\src\DataTables\PartsDataTable.php:279 Part-DB1\src\DataTables\PartsDataTable.php:227 @@ -5014,7 +4984,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Tags - + Part-DB1\src\DataTables\PartsDataTable.php:283 Part-DB1\src\DataTables\PartsDataTable.php:231 @@ -5024,7 +4994,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Dateianhänge - + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 @@ -5034,7 +5004,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Login erfolgreich. - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5045,7 +5015,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr JSON - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5056,7 +5026,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr XML - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5067,7 +5037,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr CSV - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5078,7 +5048,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr YAML - + Part-DB1\src\Form\AdminPages\ImportType.php:124 Part-DB1\src\Form\AdminPages\ImportType.php:124 @@ -5088,7 +5058,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wenn diese Option aktiviert ist, wird beim Erkennen ungültiger Daten der gesamte Vorgang abgebrochen. Ist diese Option nicht aktiv, werden ungültige Einträge ignoriert und versucht, die anderen Einträge zu importieren. - + Part-DB1\src\Form\AdminPages\ImportType.php:86 Part-DB1\src\Form\AdminPages\ImportType.php:86 @@ -5099,7 +5069,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr CSV Trennzeichen - + Part-DB1\src\Form\AdminPages\ImportType.php:93 Part-DB1\src\Form\AdminPages\ImportType.php:93 @@ -5110,7 +5080,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Übergeordnetes Element - + Part-DB1\src\Form\AdminPages\ImportType.php:101 Part-DB1\src\Form\AdminPages\ImportType.php:101 @@ -5121,7 +5091,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Datei - + Part-DB1\src\Form\AdminPages\ImportType.php:111 Part-DB1\src\Form\AdminPages\ImportType.php:111 @@ -5132,7 +5102,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Importiere auch Unterelemente - + Part-DB1\src\Form\AdminPages\ImportType.php:120 Part-DB1\src\Form\AdminPages\ImportType.php:120 @@ -5143,7 +5113,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Breche bei Invaliden Daten ab - + Part-DB1\src\Form\AdminPages\ImportType.php:132 Part-DB1\src\Form\AdminPages\ImportType.php:132 @@ -5154,7 +5124,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Importieren - + Part-DB1\src\Form\AttachmentFormType.php:113 Part-DB1\src\Form\AttachmentFormType.php:109 @@ -5164,7 +5134,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Auf ein Anhang der als privat gekennzeichnet ist, kann nur durch einen angemeldeten Benutzer zugegriffen werden, der die entsprechende Berechtigung besitzt. Wenn diese Option aktiv ist, werden keine Thumbnails erzeugt, und der Zugriff auf die Datei ist langsamer. - + Part-DB1\src\Form\AttachmentFormType.php:127 Part-DB1\src\Form\AttachmentFormType.php:123 @@ -5174,7 +5144,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Hier kann entweder eine URL zu einer externen Datei eingetragen werden, oder es wird durch Eingabe eines Stichwortes in den eingebauten Ressourcen gesucht (z.B. Footprints). - + Part-DB1\src\Form\AttachmentFormType.php:82 Part-DB1\src\Form\AttachmentFormType.php:79 @@ -5184,7 +5154,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Name - + Part-DB1\src\Form\AttachmentFormType.php:85 Part-DB1\src\Form\AttachmentFormType.php:82 @@ -5194,7 +5164,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Anhangstyp - + Part-DB1\src\Form\AttachmentFormType.php:94 Part-DB1\src\Form\AttachmentFormType.php:91 @@ -5204,7 +5174,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Zeige in Tabelle - + Part-DB1\src\Form\AttachmentFormType.php:105 Part-DB1\src\Form\AttachmentFormType.php:102 @@ -5214,7 +5184,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Privater Anhang - + Part-DB1\src\Form\AttachmentFormType.php:119 Part-DB1\src\Form\AttachmentFormType.php:115 @@ -5224,7 +5194,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr URL - + Part-DB1\src\Form\AttachmentFormType.php:133 Part-DB1\src\Form\AttachmentFormType.php:129 @@ -5234,7 +5204,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Downloade externe Datei - + Part-DB1\src\Form\AttachmentFormType.php:146 Part-DB1\src\Form\AttachmentFormType.php:142 @@ -5244,7 +5214,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Datei hochladen - + Part-DB1\src\Form\LabelOptionsType.php:68 Part-DB1\src\Services\ElementTypeNameGenerator.php:86 @@ -5254,7 +5224,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteil - + Part-DB1\src\Form\LabelOptionsType.php:68 Part-DB1\src\Services\ElementTypeNameGenerator.php:87 @@ -5264,7 +5234,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteilebestand - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5273,7 +5243,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Keiner - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5282,7 +5252,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr QR-Code (empfohlen) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5291,7 +5261,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Code 128 (empfohlen) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5300,7 +5270,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Code 39 (empfohlen) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5309,7 +5279,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Code 93 - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5318,7 +5288,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Datamatrix - + Part-DB1\src\Form\LabelOptionsType.php:122 @@ -5327,7 +5297,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr HTML - + Part-DB1\src\Form\LabelOptionsType.php:122 @@ -5336,7 +5306,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Twig - + Part-DB1\src\Form\LabelOptionsType.php:126 @@ -5345,7 +5315,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wenn Sie hier Twig auswählen, wird das Contentfeld als Twig-Template interpretiert. Weitere Hilfe gibt es in der <a href="https://twig.symfony.com/doc/3.x/templates.html">Twig Dokumentation</a> und dem <a href="https://docs.part-db.de/usage/labels.html#twig-mode">Wiki</a>. - + Part-DB1\src\Form\LabelOptionsType.php:47 @@ -5354,7 +5324,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Größe - + Part-DB1\src\Form\LabelOptionsType.php:66 @@ -5363,7 +5333,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Elementtyp - + Part-DB1\src\Form\LabelOptionsType.php:75 @@ -5372,7 +5342,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Barcodetyp - + Part-DB1\src\Form\LabelOptionsType.php:102 @@ -5381,7 +5351,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Inhalt - + Part-DB1\src\Form\LabelOptionsType.php:111 @@ -5390,7 +5360,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Zusätzliches CSS - + Part-DB1\src\Form\LabelOptionsType.php:120 @@ -5399,7 +5369,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Parser Modus - + Part-DB1\src\Form\LabelOptionsType.php:51 @@ -5408,7 +5378,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Breite - + Part-DB1\src\Form\LabelOptionsType.php:60 @@ -5417,7 +5387,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Höhe - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 @@ -5426,7 +5396,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sie können hier mehrere IDs (z.B. 1, 2, 3) und/oder einen Bereich angeben, um Barcodes für mehrere Elemente auf einmal zu erzeugen. - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 @@ -5435,7 +5405,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Element IDs - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 @@ -5444,7 +5414,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Update - + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 @@ -5453,7 +5423,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Input - + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 @@ -5462,7 +5432,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Absenden - + Part-DB1\src\Form\ParameterType.php:41 @@ -5471,7 +5441,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. DC Current Gain - + Part-DB1\src\Form\ParameterType.php:50 @@ -5480,7 +5450,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. h_{FE} - + Part-DB1\src\Form\ParameterType.php:60 @@ -5489,7 +5459,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. Test Specifications - + Part-DB1\src\Form\ParameterType.php:71 @@ -5498,7 +5468,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. 350 - + Part-DB1\src\Form\ParameterType.php:82 @@ -5507,7 +5477,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. 100 - + Part-DB1\src\Form\ParameterType.php:93 @@ -5516,7 +5486,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. 200 - + Part-DB1\src\Form\ParameterType.php:103 @@ -5525,7 +5495,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. V - + Part-DB1\src\Form\ParameterType.php:114 @@ -5534,7 +5504,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. Technische Spezifikationen - + Part-DB1\src\Form\Part\OrderdetailType.php:72 Part-DB1\src\Form\Part\OrderdetailType.php:75 @@ -5544,7 +5514,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bestellnummer - + Part-DB1\src\Form\Part\OrderdetailType.php:81 Part-DB1\src\Form\Part\OrderdetailType.php:84 @@ -5554,7 +5524,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lieferant - + Part-DB1\src\Form\Part\OrderdetailType.php:87 Part-DB1\src\Form\Part\OrderdetailType.php:90 @@ -5564,7 +5534,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Link zum Angebot - + Part-DB1\src\Form\Part\OrderdetailType.php:93 Part-DB1\src\Form\Part\OrderdetailType.php:96 @@ -5574,7 +5544,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Nicht mehr lieferbar - + Part-DB1\src\Form\Part\OrderdetailType.php:75 Part-DB1\src\Form\Part\OrderdetailType.php:78 @@ -5584,7 +5554,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. BC 547C - + Part-DB1\src\Form\Part\PartBaseType.php:101 Part-DB1\src\Form\Part\PartBaseType.php:99 @@ -5594,7 +5564,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Name - + Part-DB1\src\Form\Part\PartBaseType.php:109 Part-DB1\src\Form\Part\PartBaseType.php:107 @@ -5604,7 +5574,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Beschreibung - + Part-DB1\src\Form\Part\PartBaseType.php:120 Part-DB1\src\Form\Part\PartBaseType.php:118 @@ -5614,7 +5584,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Mindestbestand - + Part-DB1\src\Form\Part\PartBaseType.php:129 Part-DB1\src\Form\Part\PartBaseType.php:127 @@ -5624,7 +5594,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Kategorie - + Part-DB1\src\Form\Part\PartBaseType.php:135 Part-DB1\src\Form\Part\PartBaseType.php:133 @@ -5634,7 +5604,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Footprint - + Part-DB1\src\Form\Part\PartBaseType.php:142 Part-DB1\src\Form\Part\PartBaseType.php:140 @@ -5644,7 +5614,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Tags - + Part-DB1\src\Form\Part\PartBaseType.php:154 Part-DB1\src\Form\Part\PartBaseType.php:152 @@ -5654,7 +5624,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Hersteller - + Part-DB1\src\Form\Part\PartBaseType.php:161 Part-DB1\src\Form\Part\PartBaseType.php:159 @@ -5664,7 +5634,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Link zur Produktseite - + Part-DB1\src\Form\Part\PartBaseType.php:167 Part-DB1\src\Form\Part\PartBaseType.php:165 @@ -5674,7 +5644,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bauteilenummer des Herstellers - + Part-DB1\src\Form\Part\PartBaseType.php:173 Part-DB1\src\Form\Part\PartBaseType.php:171 @@ -5684,7 +5654,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Herstellungsstatus - + Part-DB1\src\Form\Part\PartBaseType.php:181 Part-DB1\src\Form\Part\PartBaseType.php:179 @@ -5694,7 +5664,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Review benötigt - + Part-DB1\src\Form\Part\PartBaseType.php:189 Part-DB1\src\Form\Part\PartBaseType.php:187 @@ -5704,7 +5674,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Favorit - + Part-DB1\src\Form\Part\PartBaseType.php:197 Part-DB1\src\Form\Part\PartBaseType.php:195 @@ -5714,7 +5684,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Gewicht - + Part-DB1\src\Form\Part\PartBaseType.php:203 Part-DB1\src\Form\Part\PartBaseType.php:201 @@ -5724,7 +5694,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Maßeinheit - + Part-DB1\src\Form\Part\PartBaseType.php:212 Part-DB1\src\Form\Part\PartBaseType.php:210 @@ -5734,7 +5704,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Notizen - + Part-DB1\src\Form\Part\PartBaseType.php:250 Part-DB1\src\Form\Part\PartBaseType.php:246 @@ -5744,7 +5714,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Vorschaubild - + Part-DB1\src\Form\Part\PartBaseType.php:295 Part-DB1\src\Form\Part\PartBaseType.php:276 @@ -5755,7 +5725,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderungen übernehmen - + Part-DB1\src\Form\Part\PartBaseType.php:296 Part-DB1\src\Form\Part\PartBaseType.php:277 @@ -5766,7 +5736,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderungen verwerfen - + Part-DB1\src\Form\Part\PartBaseType.php:105 Part-DB1\src\Form\Part\PartBaseType.php:103 @@ -5776,7 +5746,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. BC547 - + Part-DB1\src\Form\Part\PartBaseType.php:115 Part-DB1\src\Form\Part\PartBaseType.php:113 @@ -5786,7 +5756,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. NPN 45V 0,1A 0,5W - + Part-DB1\src\Form\Part\PartBaseType.php:123 Part-DB1\src\Form\Part\PartBaseType.php:121 @@ -5796,7 +5766,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. 1 - + Part-DB1\src\Form\Part\PartLotType.php:69 Part-DB1\src\Form\Part\PartLotType.php:69 @@ -5806,7 +5776,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Beschreibung - + Part-DB1\src\Form\Part\PartLotType.php:78 Part-DB1\src\Form\Part\PartLotType.php:78 @@ -5816,7 +5786,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lagerort - + Part-DB1\src\Form\Part\PartLotType.php:89 Part-DB1\src\Form\Part\PartLotType.php:89 @@ -5826,7 +5796,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Menge - + Part-DB1\src\Form\Part\PartLotType.php:98 Part-DB1\src\Form\Part\PartLotType.php:97 @@ -5836,7 +5806,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Menge unbekannt - + Part-DB1\src\Form\Part\PartLotType.php:109 Part-DB1\src\Form\Part\PartLotType.php:108 @@ -5846,7 +5816,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Muss aufgefüllt werden - + Part-DB1\src\Form\Part\PartLotType.php:120 Part-DB1\src\Form\Part\PartLotType.php:119 @@ -5856,7 +5826,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Ablaufdatum - + Part-DB1\src\Form\Part\PartLotType.php:128 Part-DB1\src\Form\Part\PartLotType.php:125 @@ -5866,7 +5836,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Notiz - + Part-DB1\src\Form\Permissions\PermissionsType.php:99 Part-DB1\src\Form\Permissions\PermissionsType.php:99 @@ -5876,7 +5846,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Verschiedene - + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 Part-DB1\src\Form\TFAGoogleSettingsType.php:97 @@ -5886,7 +5856,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Authenticator App aktivieren - + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 Part-DB1\src\Form\TFAGoogleSettingsType.php:101 @@ -5896,7 +5866,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Authenticator App deaktivieren - + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 Part-DB1\src\Form\TFAGoogleSettingsType.php:74 @@ -5906,7 +5876,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bestätigungscode - + Part-DB1\src\Form\UserSettingsType.php:108 Part-DB1\src\Form\UserSettingsType.php:108 @@ -5917,7 +5887,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Zeitzone - + Part-DB1\src\Form\UserSettingsType.php:133 Part-DB1\src\Form\UserSettingsType.php:132 @@ -5927,7 +5897,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bevorzugte Währung - + Part-DB1\src\Form\UserSettingsType.php:140 Part-DB1\src\Form\UserSettingsType.php:139 @@ -5938,7 +5908,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderungen übernehmen - + Part-DB1\src\Form\UserSettingsType.php:141 Part-DB1\src\Form\UserSettingsType.php:140 @@ -5949,7 +5919,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderungen verwerfen - + Part-DB1\src\Form\UserSettingsType.php:104 Part-DB1\src\Form\UserSettingsType.php:104 @@ -5960,7 +5930,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Serverweite Sprache - + Part-DB1\src\Form\UserSettingsType.php:115 Part-DB1\src\Form\UserSettingsType.php:115 @@ -5971,7 +5941,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Serverweite Zeitzone - + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 Part-DB1\src\Services\ElementTypeNameGenerator.php:79 @@ -5981,7 +5951,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Dateianhang - + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 Part-DB1\src\Services\ElementTypeNameGenerator.php:81 @@ -5991,7 +5961,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Anhangstyp - + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 Part-DB1\src\Services\ElementTypeNameGenerator.php:82 @@ -6001,7 +5971,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Projekt - + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 Part-DB1\src\Services\ElementTypeNameGenerator.php:85 @@ -6011,7 +5981,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Maßeinheit - + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 Part-DB1\src\Services\ElementTypeNameGenerator.php:90 @@ -6021,7 +5991,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Währung - + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 Part-DB1\src\Services\ElementTypeNameGenerator.php:91 @@ -6031,7 +6001,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bestellinformation - + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 Part-DB1\src\Services\ElementTypeNameGenerator.php:92 @@ -6041,7 +6011,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Preisinformation - + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 Part-DB1\src\Services\ElementTypeNameGenerator.php:94 @@ -6051,7 +6021,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Benutzer - + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 @@ -6060,7 +6030,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Parameter - + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 @@ -6069,7 +6039,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Labelprofil - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 @@ -6080,7 +6050,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Unbekannt - + Part-DB1\src\Services\MarkdownParser.php:73 Part-DB1\src\Services\MarkdownParser.php:73 @@ -6090,7 +6060,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lade Markdown. Wenn diese längere Zeit bestehen bleibt, versuchen sie die Website erneut zu laden! - + Part-DB1\src\Services\PasswordResetManager.php:98 Part-DB1\src\Services\PasswordResetManager.php:98 @@ -6100,7 +6070,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Passwort Reset für Ihren Part-DB Account - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 @@ -6109,7 +6079,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Tools - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 @@ -6120,7 +6090,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bearbeiten - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 @@ -6131,7 +6101,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Zeige - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 @@ -6141,7 +6111,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr System - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 @@ -6150,7 +6120,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Labeldialog - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 @@ -6159,7 +6129,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Labelscanner - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 @@ -6170,7 +6140,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Dateitypen - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 @@ -6181,7 +6151,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Kategorien - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 @@ -6192,7 +6162,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Projekte - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 @@ -6203,7 +6173,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lieferanten - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 @@ -6214,7 +6184,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Hersteller - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 @@ -6224,7 +6194,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Lagerorte - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 @@ -6234,7 +6204,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Footprints - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 @@ -6244,7 +6214,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Währungen - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 @@ -6254,7 +6224,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Maßeinheiten - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 @@ -6263,7 +6233,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Labelprofil - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 @@ -6273,7 +6243,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Neues Bauteil - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 @@ -6284,7 +6254,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Alle Teile - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 @@ -6294,7 +6264,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Dateianhänge - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 @@ -6305,7 +6275,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Statistik - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 @@ -6315,7 +6285,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Benutzer - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 @@ -6325,7 +6295,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Gruppen - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 @@ -6336,7 +6306,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Event log - + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 @@ -6347,7 +6317,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Neues Element - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 obsolete @@ -6357,7 +6327,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Externe Datei - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 obsolete @@ -6367,7 +6337,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bearbeiten - + Part-DB1\templates\_navbar.html.twig:27 templates\base.html.twig:88 @@ -6378,7 +6348,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Scanne Barcode - + Part-DB1\src\Form\UserSettingsType.php:119 src\Form\UserSettingsType.php:49 @@ -6389,7 +6359,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Theme - + Part-DB1\src\Form\UserSettingsType.php:129 src\Form\UserSettingsType.php:50 @@ -6400,53 +6370,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Serverweites Theme - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - M - M - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - k - k - - - - - - - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - m - m - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - µ - µ - - - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 new @@ -6454,10 +6378,10 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr log.user_login.ip - IP: + IP - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 @@ -6471,7 +6395,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Änderung rückgängig gemacht - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 @@ -6485,7 +6409,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Element zurückgesetzt - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 new @@ -6496,7 +6420,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Alter Bestand - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 new @@ -6507,7 +6431,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Alter Name - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 new @@ -6518,7 +6442,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Geänderte Eigenschaften - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 new @@ -6529,7 +6453,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Kommentar - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 new @@ -6540,7 +6464,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr gelöschtes Element - + templates\base.html.twig:81 obsolete @@ -6551,7 +6475,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Los! - + templates\base.html.twig:109 obsolete @@ -6562,7 +6486,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Englisch - + templates\base.html.twig:112 obsolete @@ -6573,7 +6497,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Deutsch - + obsolete obsolete @@ -6583,7 +6507,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Ihr Password muss geändert werden! - + obsolete obsolete @@ -6593,7 +6517,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Anhangstyp - + obsolete obsolete @@ -6603,7 +6527,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr verknüpftes Element - + obsolete obsolete @@ -6613,7 +6537,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Bild? - + obsolete obsolete @@ -6623,7 +6547,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr 3D Modell? - + obsolete obsolete @@ -6633,7 +6557,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Eingebaute Ressource? - + obsolete obsolete @@ -6643,7 +6567,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. Nützlich für Schaltnetzteile - + obsolete obsolete @@ -6653,17 +6577,17 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Neue Backupcodes erzeugen - + obsolete obsolete validator.noneofitschild.self - Ein Element kann nicht sich selbst als übergeordnet sein! + Ein Element kann nicht sein eigenenes übergeordnetes Element sein! - + obsolete obsolete @@ -6673,17 +6597,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Ein Kindelement kann nicht das übergeordnete Element sein! - - - obsolete - obsolete - - - validator.isSelectable - Das Element muss auswählbar sein! - - - + obsolete obsolete @@ -6693,7 +6607,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Der verwendete Lagerort wurde als voll markiert, daher kann der Bestand nicht erhöht werden. (Neuer Bestand maximal {{ old_amount }}) - + obsolete obsolete @@ -6703,7 +6617,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Der Lagerort ist voll, daher können keine neue Teile hinzugefügt werden. - + obsolete obsolete @@ -6713,7 +6627,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Der Lagerort wurde als "nur bestehende Teile" markiert, daher können keine neuen Teile hinzugefügt werden. - + obsolete obsolete @@ -6723,7 +6637,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Der Lagerort wurde als "Nur ein Bauteil" markiert, daher kann kein neues Bauteil hinzugefügt werden. - + obsolete obsolete @@ -6733,7 +6647,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Das Bauteil wird momentan und in absehbarer Zukunft produziert. - + obsolete obsolete @@ -6743,7 +6657,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Das Bauteil wurde angekündigt, ist aber noch nicht erhältlich. - + obsolete obsolete @@ -6753,7 +6667,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Das Bauteil wird nicht mehr hergestellt. - + obsolete obsolete @@ -6763,7 +6677,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Die Produktion des Bauteils wird bald eingestellt. - + obsolete obsolete @@ -6773,7 +6687,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Im Moment wird das Bauteil noch hergestellt, die Verwendung für neue Designs ist nicht mehr empfohlen. - + obsolete obsolete @@ -6783,7 +6697,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Der Produktionsstatus ist nicht bekannt. - + obsolete obsolete @@ -6793,7 +6707,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Erfolg - + obsolete obsolete @@ -6803,7 +6717,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Fehler - + obsolete obsolete @@ -6813,7 +6727,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Warnung - + obsolete obsolete @@ -6823,7 +6737,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Hinweis - + obsolete obsolete @@ -6833,7 +6747,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Info - + obsolete obsolete @@ -6843,7 +6757,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sie können sich nicht selbst die Berechtigung Berechtigungen zu bearbeiten entziehen, um sich nicht versehentlich auszusperren! - + obsolete obsolete @@ -6853,7 +6767,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Erlaubte Dateitypen - + obsolete obsolete @@ -6863,7 +6777,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Sie können hier eine kommaseparierte Liste von Dateiendungen oder Mimetypen angeben, die eine hochgeladene Datei mit diesem Anhangstyp haben muss. Um alle unterstützten Bilddateien zu erlauben, kann image/* benutzt werden. - + obsolete obsolete @@ -6873,7 +6787,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. .txt, application/pdf, image/* - + src\Form\PartType.php:63 obsolete @@ -6884,7 +6798,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. BC547 - + obsolete obsolete @@ -6894,7 +6808,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Nicht auswählbar - + obsolete obsolete @@ -6904,7 +6818,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wenn diese Option aktiviert ist, dann kann dieses Element keinem Bauteil als Eigenschaft zugewiesen werden. Hilfreich z.B. wenn dieses Element nur der reinen Sortierung dienen soll. - + obsolete obsolete @@ -6914,7 +6828,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Hier kann BBCode verwendet werden (z.B. [b]Fett[/b]) - + obsolete obsolete @@ -6924,7 +6838,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Element anlegen - + obsolete obsolete @@ -6934,7 +6848,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Speichern - + obsolete obsolete @@ -6944,7 +6858,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Deaktiviere Footprints - + obsolete obsolete @@ -6954,7 +6868,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wenn diese Option aktiviert ist, ist die Footprint Eigenschaft für alle Bauteile in dieser Kategorie, deaktiviert. - + obsolete obsolete @@ -6964,7 +6878,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Deaktiviere Hersteller - + obsolete obsolete @@ -6974,7 +6888,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wenn diese Option aktiviert ist, ist die Hersteller Eigenschaft für alle Bauteile in dieser Kategorie, deaktiviert. - + obsolete obsolete @@ -6984,7 +6898,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Deaktiviere Automatische Datenblatt links - + obsolete obsolete @@ -6994,7 +6908,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wenn diese Option aktiviert ist, werden für Bauteile mit dieser Kategorie keine automatischen Datenblattlinks erzeugt. - + obsolete obsolete @@ -7004,7 +6918,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Deaktiviere Eigenschaften - + obsolete obsolete @@ -7014,7 +6928,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wenn diese Option aktiviert ist, sind die Bauteileeigenschaften für alle Bauteile in dieser Kategorie, deaktiviert. - + obsolete obsolete @@ -7024,7 +6938,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Namenshinweis - + obsolete obsolete @@ -7034,7 +6948,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. 100nF - + obsolete obsolete @@ -7044,7 +6958,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Namensfilter - + obsolete obsolete @@ -7054,7 +6968,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Standard Beschreibung - + obsolete obsolete @@ -7064,7 +6978,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr z.B. Kondensator, 10mmx10mm, SMD - + obsolete obsolete @@ -7074,7 +6988,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Standard Kommentar - + obsolete obsolete @@ -7084,7 +6998,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Anschrift - + obsolete obsolete @@ -7095,7 +7009,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr 31415 Beispielhausen - + obsolete obsolete @@ -7105,7 +7019,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Telefonnummer - + obsolete obsolete @@ -7115,7 +7029,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr +49 12345 6789 - + obsolete obsolete @@ -7125,7 +7039,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Faxnummer - + obsolete obsolete @@ -7135,7 +7049,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr E-Mail - + obsolete obsolete @@ -7145,7 +7059,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr contact@foo.bar - + obsolete obsolete @@ -7155,7 +7069,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Website - + obsolete obsolete @@ -7165,7 +7079,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr https://www.foo.bar - + obsolete obsolete @@ -7175,7 +7089,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Produkt URL - + obsolete obsolete @@ -7185,7 +7099,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wenn diese URL gesetzt ist, wird sie benutzt, um die URL eines Bauteils auf der Website des Herstellers zu erzeugen. Dabei wird %PARTNUMBER% mit der Bestellnummer ersetzt. - + obsolete obsolete @@ -7195,7 +7109,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr https://foo.bar/product/%PARTNUMBER% - + obsolete obsolete @@ -7205,7 +7119,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr ISO Code - + obsolete obsolete @@ -7215,7 +7129,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Wechselkurs - + obsolete obsolete @@ -7225,7 +7139,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr 3D Modell - + obsolete obsolete @@ -7235,7 +7149,7 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr Eingabe - + obsolete obsolete @@ -7247,7 +7161,7 @@ Element 2 Element 3 - + obsolete obsolete @@ -7257,7 +7171,7 @@ Element 3 Anlegen - + obsolete obsolete @@ -7267,7 +7181,7 @@ Element 3 Ganzzahlig - + obsolete obsolete @@ -7277,7 +7191,7 @@ Element 3 Wenn diese Option aktiviert ist, werden alle Mengen in dieser Einheit auf ganze Zahlen gerundet. - + obsolete obsolete @@ -7287,7 +7201,7 @@ Element 3 Benutze SI Prefixe - + obsolete obsolete @@ -7297,7 +7211,7 @@ Element 3 Wenn diese Option aktiviert ist, werden bei Ausgabe der Zahlen SI Prefixe benutzt (z.B. 1,2kg anstatt 1200g) - + obsolete obsolete @@ -7307,7 +7221,7 @@ Element 3 Einheitensymbol - + obsolete obsolete @@ -7317,7 +7231,7 @@ Element 3 z.B. m - + obsolete obsolete @@ -7327,7 +7241,7 @@ Element 3 Lagerort voll - + obsolete obsolete @@ -7337,7 +7251,7 @@ Element 3 Wenn diese Option aktiviert ist, ist es weder möglich neue Bauteile zu diesem Lagerort hinzuzufügen, noch die Anzahl bereits vorhandener Bauteile zu erhöhen. - + obsolete obsolete @@ -7347,7 +7261,7 @@ Element 3 Nur bestehende Bauteile - + obsolete obsolete @@ -7357,7 +7271,7 @@ Element 3 Wenn diese Option aktiv ist, ist es nicht möglich neue Bauteile zu diesem Lagerort hinzuzufügen, es ist aber möglich die Anzahl bereits vorhandener Bauteile zu erhöhen. - + obsolete obsolete @@ -7367,7 +7281,7 @@ Element 3 Nur ein Bauteil - + obsolete obsolete @@ -7377,7 +7291,7 @@ Element 3 Wenn diese Option aktiviert ist, kann dieser Lagerort nur ein einzelnes Bauteil aber in beliebiger Menge fassen. Hilfreich für kleine SMD Fächer oder Feeder. - + obsolete obsolete @@ -7387,7 +7301,7 @@ Element 3 Lagertyp - + obsolete obsolete @@ -7397,7 +7311,7 @@ Element 3 Hier kann eine Maßeinheit gewählt werden, die ein Bauteil haben muss, damit es in diesem Lagerort gelagert werden kann. - + obsolete obsolete @@ -7407,7 +7321,7 @@ Element 3 Standardwährung - + obsolete obsolete @@ -7417,7 +7331,7 @@ Element 3 Versandkosten - + obsolete obsolete @@ -7427,7 +7341,7 @@ Element 3 z.B. m.muster - + obsolete obsolete @@ -7437,7 +7351,7 @@ Element 3 z.B. Max - + obsolete obsolete @@ -7447,7 +7361,7 @@ Element 3 z.B. Muster - + obsolete obsolete @@ -7457,7 +7371,7 @@ Element 3 z.B. m.muster@ecorp.com - + obsolete obsolete @@ -7467,7 +7381,7 @@ Element 3 z.B. Entwicklung - + obsolete obsolete @@ -7477,7 +7391,7 @@ Element 3 Neues Passwort - + obsolete obsolete @@ -7487,7 +7401,7 @@ Element 3 Neues Passwort bestätigen - + obsolete obsolete @@ -7497,7 +7411,7 @@ Element 3 Nutzer muss Passwort ändern - + obsolete obsolete @@ -7507,7 +7421,7 @@ Element 3 Benutzer deaktiviert (kein Login möglich) - + obsolete obsolete @@ -7517,7 +7431,7 @@ Element 3 Benutzer anlegen - + obsolete obsolete @@ -7527,7 +7441,7 @@ Element 3 Speichern - + obsolete obsolete @@ -7537,18 +7451,7 @@ Element 3 Änderungen verwerfen - - - templates\Parts\show_part_info.html.twig:161 - obsolete - obsolete - - - part.withdraw.caption: - Bauteile entnehmen: - - - + templates\Parts\show_part_info.html.twig:166 obsolete @@ -7559,7 +7462,7 @@ Element 3 Entnehmen - + templates\Parts\show_part_info.html.twig:171 obsolete @@ -7570,7 +7473,7 @@ Element 3 Kommentar/Zweck - + templates\Parts\show_part_info.html.twig:189 obsolete @@ -7581,7 +7484,7 @@ Element 3 Bauteil hinzufügen - + templates\Parts\show_part_info.html.twig:194 obsolete @@ -7592,7 +7495,7 @@ Element 3 Hinzufügen - + templates\Parts\show_part_info.html.twig:199 obsolete @@ -7603,7 +7506,7 @@ Element 3 Kommentar/Zweck - + templates\AdminPages\CompanyAdminBase.html.twig:15 obsolete @@ -7614,7 +7517,7 @@ Element 3 Notizen - + src\Form\PartType.php:83 obsolete @@ -7625,7 +7528,7 @@ Element 3 Herstellerlink - + src\Form\PartType.php:66 obsolete @@ -7636,7 +7539,7 @@ Element 3 z.B. NPN 45V 0,1A 0,5W - + src\Form\PartType.php:69 obsolete @@ -7647,7 +7550,7 @@ Element 3 z.B. 12 - + src\Form\PartType.php:72 obsolete @@ -7658,27 +7561,7 @@ Element 3 z.B. 10 - - - obsolete - obsolete - - - homepage.basedOn - basierend auf Arbeit von - - - - - obsolete - obsolete - - - homepage.others - und anderen - - - + obsolete obsolete @@ -7688,7 +7571,7 @@ Element 3 pro - + obsolete obsolete @@ -7698,7 +7581,7 @@ Element 3 Bauteile entnehmen - + obsolete obsolete @@ -7708,7 +7591,7 @@ Element 3 _MENU_ - + obsolete obsolete @@ -7718,7 +7601,7 @@ Element 3 Bauteile - + obsolete obsolete @@ -7728,7 +7611,7 @@ Element 3 Datenstrukturen - + obsolete obsolete @@ -7738,7 +7621,7 @@ Element 3 System - + obsolete obsolete @@ -7748,7 +7631,7 @@ Element 3 Allgemein - + obsolete obsolete @@ -7758,7 +7641,7 @@ Element 3 Anzeigen - + obsolete obsolete @@ -7768,7 +7651,7 @@ Element 3 Bearbeiten - + obsolete obsolete @@ -7778,7 +7661,7 @@ Element 3 Anlegen - + obsolete obsolete @@ -7788,7 +7671,7 @@ Element 3 Kategorie verändern - + obsolete obsolete @@ -7798,7 +7681,7 @@ Element 3 Löschen - + obsolete obsolete @@ -7808,7 +7691,7 @@ Element 3 Suchen - + obsolete obsolete @@ -7818,7 +7701,7 @@ Element 3 Alle Bauteile auflisten - + obsolete obsolete @@ -7828,7 +7711,7 @@ Element 3 Teile ohne Preis auflisten - + obsolete obsolete @@ -7838,7 +7721,7 @@ Element 3 Obsolete Teile auflisten - + obsolete obsolete @@ -7848,7 +7731,7 @@ Element 3 Bauteile mit unbekanntem Bestand auflisten - + obsolete obsolete @@ -7858,7 +7741,7 @@ Element 3 Favoritenstatus ändern - + obsolete obsolete @@ -7868,7 +7751,7 @@ Element 3 Favoriten anzeigen - + obsolete obsolete @@ -7878,7 +7761,7 @@ Element 3 Zeige zuletzt bearbeitete/hinzugefügte Bauteile - + obsolete obsolete @@ -7888,7 +7771,7 @@ Element 3 Letzten bearbeitenden Nutzer anzeigen - + obsolete obsolete @@ -7898,7 +7781,7 @@ Element 3 Historie anzeigen - + obsolete obsolete @@ -7908,7 +7791,7 @@ Element 3 Name - + obsolete obsolete @@ -7918,7 +7801,7 @@ Element 3 Beschreibung - + obsolete obsolete @@ -7928,7 +7811,7 @@ Element 3 Vorhanden - + obsolete obsolete @@ -7938,7 +7821,7 @@ Element 3 Min. Bestand - + obsolete obsolete @@ -7948,7 +7831,7 @@ Element 3 Notizen - + obsolete obsolete @@ -7958,7 +7841,7 @@ Element 3 Lagerort - + obsolete obsolete @@ -7968,7 +7851,7 @@ Element 3 Hersteller - + obsolete obsolete @@ -7978,7 +7861,7 @@ Element 3 Bestellinformationen - + obsolete obsolete @@ -7988,7 +7871,7 @@ Element 3 Preise - + obsolete obsolete @@ -7998,7 +7881,7 @@ Element 3 Dateianhänge - + obsolete obsolete @@ -8008,7 +7891,7 @@ Element 3 Bestellungen - + obsolete obsolete @@ -8018,7 +7901,7 @@ Element 3 Lagerorte - + obsolete obsolete @@ -8028,7 +7911,7 @@ Element 3 Verschieben - + obsolete obsolete @@ -8038,7 +7921,7 @@ Element 3 Teile auflisten - + obsolete obsolete @@ -8048,7 +7931,7 @@ Element 3 Footprints - + obsolete obsolete @@ -8058,7 +7941,7 @@ Element 3 Kategorien - + obsolete obsolete @@ -8068,7 +7951,7 @@ Element 3 Lieferanten - + obsolete obsolete @@ -8078,7 +7961,7 @@ Element 3 Hersteller - + obsolete obsolete @@ -8088,7 +7971,7 @@ Element 3 Projekte - + obsolete obsolete @@ -8098,7 +7981,7 @@ Element 3 Dateitypen - + obsolete obsolete @@ -8108,7 +7991,7 @@ Element 3 Import - + obsolete obsolete @@ -8118,7 +8001,7 @@ Element 3 Labels - + obsolete obsolete @@ -8128,7 +8011,7 @@ Element 3 Widerstandsrechner - + obsolete obsolete @@ -8138,7 +8021,7 @@ Element 3 Footprints - + obsolete obsolete @@ -8148,7 +8031,7 @@ Element 3 IC-Logos - + obsolete obsolete @@ -8158,7 +8041,7 @@ Element 3 Statistik - + obsolete obsolete @@ -8168,7 +8051,7 @@ Element 3 Berechtigungen ändern - + obsolete obsolete @@ -8178,7 +8061,7 @@ Element 3 Nutzernamen ändern - + obsolete obsolete @@ -8188,7 +8071,7 @@ Element 3 Gruppe ändern - + obsolete obsolete @@ -8198,7 +8081,7 @@ Element 3 Informationen ändern - + obsolete obsolete @@ -8208,7 +8091,7 @@ Element 3 Berechtigungen ändern - + obsolete obsolete @@ -8218,7 +8101,7 @@ Element 3 Passwort ändern - + obsolete obsolete @@ -8228,7 +8111,7 @@ Element 3 Benutzereinstellungen ändern - + obsolete obsolete @@ -8238,7 +8121,7 @@ Element 3 Status anzeigen - + obsolete obsolete @@ -8248,7 +8131,7 @@ Element 3 Datenbank updaten - + obsolete obsolete @@ -8258,7 +8141,7 @@ Element 3 Einstellungen anzeigen - + obsolete obsolete @@ -8268,7 +8151,7 @@ Element 3 Einstellungen ändern - + obsolete obsolete @@ -8278,7 +8161,7 @@ Element 3 Konfiguration anzeigen - + obsolete obsolete @@ -8288,7 +8171,7 @@ Element 3 Konfiguration ändern - + obsolete obsolete @@ -8298,7 +8181,7 @@ Element 3 Server info - + obsolete obsolete @@ -8308,7 +8191,7 @@ Element 3 Debugtools benutzen - + obsolete obsolete @@ -8318,7 +8201,7 @@ Element 3 Logs anzeigen - + obsolete obsolete @@ -8328,7 +8211,7 @@ Element 3 Logeinträge löschen - + obsolete obsolete @@ -8338,7 +8221,7 @@ Element 3 Informationen ändern - + obsolete obsolete @@ -8348,7 +8231,7 @@ Element 3 Benutzernamen ändern - + obsolete obsolete @@ -8358,7 +8241,7 @@ Element 3 Berechtigungen anzeigen - + obsolete obsolete @@ -8368,7 +8251,7 @@ Element 3 Logs anzeigen - + obsolete obsolete @@ -8378,7 +8261,7 @@ Element 3 Labels erstellen - + obsolete obsolete @@ -8388,7 +8271,7 @@ Element 3 Einstellungen ändern - + obsolete obsolete @@ -8398,7 +8281,7 @@ Element 3 Profile löschen - + obsolete obsolete @@ -8408,7 +8291,7 @@ Element 3 Profile bearbeiten - + obsolete obsolete @@ -8418,7 +8301,7 @@ Element 3 Tools - + obsolete obsolete @@ -8428,7 +8311,7 @@ Element 3 Gruppen - + obsolete obsolete @@ -8438,7 +8321,7 @@ Element 3 Benutzer - + obsolete obsolete @@ -8448,7 +8331,7 @@ Element 3 Datenbank - + obsolete obsolete @@ -8458,7 +8341,7 @@ Element 3 Einstellungen - + obsolete obsolete @@ -8468,7 +8351,7 @@ Element 3 System - + obsolete obsolete @@ -8478,7 +8361,7 @@ Element 3 Eigenen Benutzer bearbeiten - + obsolete obsolete @@ -8488,7 +8371,7 @@ Element 3 Labels - + obsolete obsolete @@ -8498,7 +8381,7 @@ Element 3 Kategorie - + obsolete obsolete @@ -8508,7 +8391,7 @@ Element 3 Mindestbestand - + obsolete obsolete @@ -8518,7 +8401,7 @@ Element 3 Footprint - + obsolete obsolete @@ -8528,7 +8411,7 @@ Element 3 MPN - + obsolete obsolete @@ -8538,7 +8421,7 @@ Element 3 Herstellungsstatus - + obsolete obsolete @@ -8548,7 +8431,7 @@ Element 3 Tags - + obsolete obsolete @@ -8558,7 +8441,7 @@ Element 3 Maßeinheit - + obsolete obsolete @@ -8568,7 +8451,7 @@ Element 3 Gewicht - + obsolete obsolete @@ -8578,7 +8461,7 @@ Element 3 Lagerorte - + obsolete obsolete @@ -8588,7 +8471,7 @@ Element 3 Letzten bearbeitenden Nutzer anzeigen - + obsolete obsolete @@ -8598,7 +8481,7 @@ Element 3 Währungen - + obsolete obsolete @@ -8608,7 +8491,7 @@ Element 3 Maßeinheiten - + obsolete obsolete @@ -8618,7 +8501,7 @@ Element 3 Altes Passwort - + obsolete obsolete @@ -8628,7 +8511,7 @@ Element 3 Passwort zurücksetzen - + obsolete obsolete @@ -8638,7 +8521,7 @@ Element 3 Sicherheitsschlüssel (U2F) - + obsolete obsolete @@ -8648,13 +8531,13 @@ Element 3 Google - + tfa.provider.webauthn_two_factor_provider Sicherheitsschlüssel - + obsolete obsolete @@ -8664,17 +8547,17 @@ Element 3 Authenticator App - + obsolete obsolete Login successful - Erfolgreich eingeloggt! + Login erfolgreich. - + obsolete obsolete @@ -8684,7 +8567,7 @@ Element 3 Unbehandelte Exception (veraltet) - + obsolete obsolete @@ -8694,7 +8577,7 @@ Element 3 Nutzer eingeloggt - + obsolete obsolete @@ -8704,7 +8587,7 @@ Element 3 Nutzer ausgeloggt - + obsolete obsolete @@ -8714,7 +8597,7 @@ Element 3 Unbekannt - + obsolete obsolete @@ -8724,7 +8607,7 @@ Element 3 Element angelegt - + obsolete obsolete @@ -8734,7 +8617,7 @@ Element 3 Element bearbeitet - + obsolete obsolete @@ -8744,7 +8627,7 @@ Element 3 Element gelöscht - + obsolete obsolete @@ -8754,7 +8637,7 @@ Element 3 Datenbank aktualisiert - + obsolete @@ -8763,7 +8646,7 @@ Element 3 Element zurücksetzen - + obsolete @@ -8772,7 +8655,7 @@ Element 3 Historie anzeigen - + obsolete @@ -8781,7 +8664,7 @@ Element 3 Letzte Aktivität anzeigen - + obsolete @@ -8790,16 +8673,7 @@ Element 3 Alte Versionsstände anzeigen (Zeitreisen) - - - obsolete - - - log.type. - __log.type. - - - + obsolete @@ -8808,7 +8682,7 @@ Element 3 Sicherheitsschlüssel erfolgreich hinzugefügt. - + obsolete @@ -8817,7 +8691,7 @@ Element 3 Benutzername - + obsolete @@ -8826,7 +8700,7 @@ Element 3 Authenticator App deaktiviert - + obsolete @@ -8835,7 +8709,7 @@ Element 3 Sicherheitsschlüssel gelöscht - + obsolete @@ -8844,7 +8718,7 @@ Element 3 Sicherheitsschlüssel hinzugefügt - + obsolete @@ -8853,7 +8727,7 @@ Element 3 Neue Backupkeys erzeugt - + obsolete @@ -8862,7 +8736,7 @@ Element 3 Authenticator App aktiviert - + obsolete @@ -8871,7 +8745,7 @@ Element 3 Passwort geändert - + obsolete @@ -8880,7 +8754,7 @@ Element 3 Vertrauenswürdige Geräte zurückgesetzt - + obsolete @@ -8889,7 +8763,7 @@ Element 3 Kollektionselement gelöscht - + obsolete @@ -8898,7 +8772,7 @@ Element 3 Passwort zurückgesetzt - + obsolete @@ -8907,7 +8781,7 @@ Element 3 Zwei-Faktor-Authentifizierung durch Administrator zurückgesetzt - + obsolete @@ -8916,7 +8790,7 @@ Element 3 Unerlaubter Zugriffsversuch - + obsolete @@ -8925,7 +8799,7 @@ Element 3 Erfolgreich - + obsolete @@ -8934,7 +8808,7 @@ Element 3 2D - + obsolete @@ -8943,7 +8817,7 @@ Element 3 1D - + obsolete @@ -8952,7 +8826,7 @@ Element 3 Parameter - + obsolete @@ -8961,7 +8835,7 @@ Element 3 Private Anhänge zeigen - + obsolete @@ -8970,7 +8844,7 @@ Element 3 Labelscanner - + obsolete @@ -8979,7 +8853,7 @@ Element 3 Profile anzeigen - + obsolete @@ -8988,7 +8862,7 @@ Element 3 Profil anlegen - + obsolete @@ -8997,1973 +8871,3499 @@ Element 3 Twig Modus benutzen - + label_profile.showInDropdown In Barcode Schnellauswahl anzeigen - + group.edit.enforce_2fa Erzwinge Zwei-Faktor-Authentifizierung (2FA) - + group.edit.enforce_2fa.help Wenn diese Option aktiv ist, muss jedes direkte Mitglied dieser Gruppe, mindestens einen zweiten Faktor zur Authentifizierung einrichten. Empfohlen z.B. für administrative Gruppen mit weitreichenden Berechtigungen. - + selectpicker.empty Nichts ausgewählt - + selectpicker.nothing_selected Nichts ausgewählt - + entity.delete.must_not_contain_parts Element "%PATH%" enthält noch Bauteile. Bearbeite die Bauteile, um dieses Element löschen zu können. - + entity.delete.must_not_contain_attachments Dateityp enthält noch Bauteile. Ändere deren Dateityp, um diesen Dateityp löschen zu können. - + entity.delete.must_not_contain_prices Währung enthält noch Bauteile. Ändere deren Währung, um diese Währung löschen zu können. - + entity.delete.must_not_contain_users Benutzer sind noch Teil dieser Gruppe. Ändere deren Gruppe, um diese Gruppe löschen zu können. - + part.table.edit Ändern - + part.table.edit.title Bauteil ändern - + part_list.action.action.title Aktion auswählen - + part_list.action.action.group.favorite Favorit - + part_list.action.action.favorite Bauteile favorisieren - + part_list.action.action.unfavorite Favorisierung aufheben - + part_list.action.action.group.change_field Change field - + part_list.action.action.change_category Kategorie ändern - + part_list.action.action.change_footprint Footprint ändern - + part_list.action.action.change_manufacturer Hersteller ändern - + part_list.action.action.change_unit Maßeinheit ändern - + part_list.action.action.delete Löschen - + part_list.action.submit Ok - + part_list.action.part_count %count% Bauteile ausgewählt! - + company.edit.quick.website Website öffnen - + company.edit.quick.email E-Mail senden - + company.edit.quick.phone Call phone - + company.edit.quick.fax Fax senden - + company.fax_number.placeholder z.B. +49 1234 567890 - + part.edit.save_and_clone Speichern und duplizieren - + validator.file_ext_not_allowed Dateierweiterung nicht erlaubt für diesen Anhangstyp. - + tools.reel_calc.title SMD Reel Rechner - + tools.reel_calc.inner_dia Innerer Durchmesser - + tools.reel_calc.outer_dia Äußerer Durchmesser - + tools.reel_calc.tape_thick Tape Dicke - + tools.reel_calc.part_distance Bauteile Abstand - + tools.reel_calc.update Update - + tools.reel_calc.parts_per_meter Bauteile pro Meter - + tools.reel_calc.result_length Tape Länge - + tools.reel_calc.result_amount Ungefähre Anzahl Bauteile - + tools.reel_calc.outer_greater_inner_error Fehler: Äußerer Durchmesser muss großer sein als der innere Durchmesser! - + tools.reel_calc.missing_values.error Bitte alle Werte angeben! - + tools.reel_calc.load_preset Preset laden - + tools.reel_calc.explanation Dieser Rechner erlaubt es Abzuschätzen wie viele Bauteile noch auf einer SMD Rolle (Reel) vorhanden sind. Messen Sie die angegeben Dimensionen auf der Rolle nach (oder nutzen Sie die Vorgaben) und drücken Sie "Update". - + perm.tools.reel_calculator SMD Reel Rechner - + tree.tools.tools.reel_calculator SMD Reel Rechner - + user.pw_change_needed.flash Passwortänderung benötigt! Bitte setze ein neues Passwort. - + tree.root_node.text Wurzel - + part_list.action.select_null Keine Elemente vorhanden! - + part_list.action.delete-title Möchten Sie diese Bauteile wirklich löschen? - + part_list.action.delete-message Diese Bauteile und alle verknüpften Informationen (Anhänge, Preisinformationen, etc.) werden gelöscht. Dies kann nicht rückgängig gemacht werden! - + part.table.actions.success Aktionen erfolgreich. - + attachment.edit.delete.confirm Möchten Sie diesen Anhang wirklich löschen? - + filter.text_constraint.value.operator.EQ Gleich - + filter.text_constraint.value.operator.NEQ Ungleich - + filter.text_constraint.value.operator.STARTS Beginnt mit - + filter.text_constraint.value.operator.CONTAINS Enthält - + filter.text_constraint.value.operator.ENDS Endet mit - + filter.text_constraint.value.operator.LIKE LIKE Ausdruck - + filter.text_constraint.value.operator.REGEX Regulärer Ausdruck - + filter.number_constraint.value.operator.BETWEEN Zwischen - + filter.number_constraint.AND und - + filter.entity_constraint.operator.EQ Gleich (ohne Kindelemente) - + filter.entity_constraint.operator.NEQ Ungleich (ohne Kindelemente) - + filter.entity_constraint.operator.INCLUDING_CHILDREN Gleich (inklusive Kindelementen) - + filter.entity_constraint.operator.EXCLUDING_CHILDREN Nicht gleich (inklusive Kindelemente) - + part.filter.dbId Datenbank ID - + filter.tags_constraint.operator.ANY Irgendeiner der Tags - + filter.tags_constraint.operator.ALL Alle der Tags - + filter.tags_constraint.operator.NONE Keine der Tags - + part.filter.lot_count Anzahl der Lagerbestände - + part.filter.attachments_count Anzahl der Anhänge - + part.filter.orderdetails_count Anzahl der Bestellinformationen - + part.filter.lotExpirationDate Bauteilebestand Ablaufdatum - + part.filter.lotNeedsRefill Lagerbestand benötigt Auffüllung - + part.filter.lotUnknwonAmount Lagerbestand mit unbekannter Anzahl - + part.filter.attachmentName Name des Anhangs - + filter.choice_constraint.operator.ANY Einer der Ausgewählten - + filter.choice_constraint.operator.NONE Keine der Ausgewählten - + part.filter.amount_sum Gesamtmenge - + filter.submit Update - + filter.discard Änderungen verwerfen - + filter.clear_filters Alle Filter zurücksetzen - + filter.title Filter - + filter.parameter_value_constraint.operator.= Typ. Wert = - + filter.parameter_value_constraint.operator.!= Typ. Wert != - + filter.parameter_value_constraint.operator.< Typ. Wert < - + filter.parameter_value_constraint.operator.> Typ. Wert > - + filter.parameter_value_constraint.operator.<= Typ. Wert <= - + filter.parameter_value_constraint.operator.>= Typ. Wert >= - + filter.parameter_value_constraint.operator.BETWEEN Typ. Wert zwischen - + filter.parameter_value_constraint.operator.IN_RANGE Im Wertebereich - + filter.parameter_value_constraint.operator.NOT_IN_RANGE Nicht im Wertebereich - + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE Größer als der Wertebereich - + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE Größer gleich der Wertebereich - + filter.parameter_value_constraint.operator.LESS_THAN_RANGE Kleiner als der Wertebereich - + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE Kleiner gleich der Wertebereich - + filter.parameter_value_constraint.operator.RANGE_IN_RANGE Bereich ist komplett im Wertebereich - + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE Bereich schneidet Wertebereich - + filter.text_constraint.value Kein Wert gesetzt - + filter.number_constraint.value1 Kein Wert gesetzt - + filter.number_constraint.value2 Maximaler Wert - + filter.datetime_constraint.value1 Kein Datum/Uhrzeit gesetzt - + filter.datetime_constraint.value2 Maximale Datum/Uhrzeit - + filter.constraint.add Filter hinzufügen - + part.filter.parameters_count Anzahl der Parameter - + part.filter.lotDescription Beschreibung des Bauteilebestand - + parts_list.search.searching_for - Searching parts with keyword <b>%keyword%</b> + Suche Teile mit dem Suchbegriff <b>%keyword%</b> - + parts_list.search_options.caption Aktivierte Suchoptionen - + attachment.table.element_type Typ des verknüpften Elements - + log.level.debug Debug - + log.level.info Info - + log.level.notice Hinweis - + log.level.warning Warnung - + log.level.error Fehler - + log.level.critical Kritisch - + log.level.alert Alarm - + log.level.emergency Notfall - + log.type.security Sicherheitsereignis - + log.type.instock_changed [ALT] Bestand geändert - + log.target_id ID des Zielelements - + entity.info.parts_count_recursive Bauteile mit diesem Element oder dessen Kindelementen - + tools.server_infos.title Server Infos - + permission.preset.read_only Nur Lesen - + permission.preset.read_only.desc Erlaube nur lesende Operationen auf Daten - + permission.preset.all_inherit Alle Erben - + permission.preset.all_inherit.desc Setze all Berechtigungen auf Erben - + permission.preset.all_forbid Alles Verbieten - + permission.preset.all_forbid.desc Setze alle Berechtigungen auf Verbieten - + permission.preset.all_allow Alles Erlauben - + permission.preset.all_allow.desc Setze alle Berechtigungen auf Erlauben - + perm.server_infos Server Infos - + permission.preset.editor Bearbeiter - + permission.preset.editor.desc Erlauben Bauteile und Datenstrukturen zu bearbeiten - + permission.preset.admin Administrator - + permission.preset.admin.desc Administrative Aktionen erlauben - + permission.preset.button Vorlage Anwenden - + perm.attachments.show_private Private Anhänge anzeigen - + perm.attachments.list_attachments Liste aller Anhänge anzeigen - + user.edit.permission_success Berechtigungsvorlage erfolgreich angewendet. Prüfen Sie, dass die neuen Berechtigungen ihrer Erwartung entsprechen. - + perm.group.data Daten - + part_list.action.action.group.needs_review Review benötigt - + part_list.action.action.set_needs_review Review benötigt Status setzen - + part_list.action.action.unset_needs_review Review benötigt Status entfernen - + part.edit.ipn Internal Part Number (IPN) - + part.ipn.not_defined Nicht definiert - + part.table.ipn IPN - + currency.edit.update_rate Wechselkurs abfragen - + currency.edit.exchange_rate_update.unsupported_currency Die Währung wird vom Wechselkursprovider nicht unterstützt. Bitte überprüfen Sie die Konfiguration der Wechselkursprovider. - + currency.edit.exchange_rate_update.generic_error Wechselkurs kann nicht abgefragt werden. Bitte überprüfen Sie die Konfiguration der Wechselkursprovider. - + currency.edit.exchange_rate_updated.success Wechselkurs erfolgreich aktualisiert. - + project.bom.quantity BOM Menge - + project.bom.mountnames Bestückungsnamen - + project.bom.name Name - + project.bom.comment Notizen - + project.bom.part Bauteil - + project.bom.add_entry Eintrag hinzufügen - + part_list.action.group.projects Projekte - + part_list.action.projects.add_to_project Bauteile zu Projekt hinzufügen - + project.bom.delete.confirm Wollen sie diesen BOM Eintrag wirklich löschen? - + project.add_parts_to_project Bauteile zur Projekt BOM hinzufügen - + part.info.add_part_to_project Dieses Bauteil zu einem Projekt hinzufügen - + project_bom_entry.label BOM Eintrag - + project.edit.status Projektstatus - + project.status.draft Entwurf - + project.status.planning In Planung - + project.status.in_production In Produktion - + project.status.finished Abgeschlossen - + project.status.archived Archiviert - + part.new_build_part.error.build_part_already_exists Dieses Projekt hat bereits ein verknüpftes Bauteil. - + project.edit.associated_build_part Verknüpftes Produktionsbauteil - + project.edit.associated_build_part.add Produktionsbauteil hinzufügen - + project.edit.associated_build.hint Dieses Bauteil repräsentiert die gebauten Instanzen des Projektes, die irgendwo gelagert werden - + part.info.projectBuildPart.hint Dieses Bauteil repräsentiert die gebauten Instanzen folgendes Projektes und ist mit diesem verknüpft - + part.is_build_part Ist Produktionsbauteil - + project.info.title Projektinfo - + project.info.bom_entries_count BOM Einträge - + project.info.sub_projects_count Unterprojekte - + project.info.bom_add_parts BOM Einträge hinzufügen - + project.info.info.label Info - + project.info.sub_projects.label Unterprojekte - + project.bom.price Preis - + part.info.withdraw_modal.title.withdraw Bauteile aus Lot entnehmen - + part.info.withdraw_modal.title.add Bauteile zu Lot hinzufügen - + part.info.withdraw_modal.title.move Verschiebe Bauteile in ein anderes Lot - + part.info.withdraw_modal.amount Menge - + part.info.withdraw_modal.move_to Verschieben in - + part.info.withdraw_modal.comment Kommentar - + part.info.withdraw_modal.comment.hint Sie können hier einen Kommentar eintragen, der beschreibt warum diese Aktion durchgeführt wurde (z.B. wofür dieses Bauteil benötigt wurde). Diese Information wird im Log gespeichert. - + modal.close Schließen - + modal.submit Absenden - + part.withdraw.success Bauteile erfolgreich entnommen/hinzugefügt/verschoben. - + perm.parts_stock Bauteilbestand - + perm.parts_stock.withdraw Bauteile aus Bestand entnehmen - + perm.parts_stock.add Bauteile zu Bestand hinzufügen - + perm.parts_stock.move Bauteile zwischen Beständen verschieben - + user.permissions_schema_updated Das Berechtigungsschema deines Benutzeraccounts wurde auf die neuste Version aktualisiert. - + log.type.part_stock_changed Bauteilebestand geändert - + log.part_stock_changed.withdraw Bauteile entnommen - + log.part_stock_changed.add Bauteile hinzugefügt - + log.part_stock_changed.move Bauteile verschoben - + log.part_stock_changed.comment Kommentar - + log.part_stock_changed.change Änderung - + log.part_stock_changed.move_target Verschoben in - + tools.builtin_footprints_viewer.title Mitgelieferte Footprint Bilder - + tools.builtin_footprints_viewer.hint Diese Galerie listet alle mitgelieferten Footprint Bilder auf. Wenn Sie diese in einem Anhang nutzen wollen, tippen Sie den Namen (oder ein Stichwort) in das URL-Feld des Anhangs ein und wählen Sie das gewünschte Bild aus dem Dropdown-Menü aus. - + tools.ic_logos.title IC Logos - + part_list.action.group.labels Labels - + part_list.action.projects.generate_label Labels erzeugen (für Bauteile) - + part_list.action.projects.generate_label_lot Labels erzeugen (für Bestände der Bauteile) - + part_list.action.generate_label.empty Leeres Label - + project.info.builds.label Bau - + project.builds.build_not_possible Bau nicht möglich: Nicht genügend Bauteile vorhanden - + project.builds.following_bom_entries_miss_instock Folgende Bauteile haben nicht genug Bestand um dieses Projekt mindestens einmal zu bauen: - + project.builds.stocked vorhanden - + project.builds.needed benötigt - + project.builds.build_possible Bau möglich - + project.builds.number_of_builds_possible Sie haben genug Bauteile auf Lager, um <b>%max_builds%</b> Exemplare dieses Projektes zu bauen. - + project.builds.check_project_status Der aktuelle Projektstatus ist <b>"%project_status%"</b>. Sie sollten überprüfen, ob sie das Projekt mit diesem Status wirklich bauen wollen! - + project.builds.following_bom_entries_miss_instock_n Es sind nicht genügend Bauteile auf Lager, um dieses Projekt %number_of_builds% mal zu bauen. Von folgenden Bauteilen ist nicht genügend auf Lager. - + project.build.flash.invalid_input Projekt kann nicht gebaut werden. Bitte Eingaben überprüfen! - + project.build.required_qty Benötigte Anzahl - + project.build.btn_build Bauen - + project.build.help Wählen Sie aus, aus welchen Beständen die zum Bau notwendigen Bauteile genommen werden sollen (und in welcher Anzahl). Setzen Sie den Haken für jeden BOM Eintrag, wenn sie die Bauteile entnommen haben, oder nutzen Sie die oberste Checkbox, um alle Haken auf einmal zu setzen. - + project.build.buildsPartLot.new_lot Neuen Bestand anlegen - + project.build.add_builds_to_builds_part Gebaute Instanzen zum Produktionsbauteil des Projektes hinzufügen - + project.build.builds_part_lot Ziel-Bestand - + project.builds.number_of_builds Zu bauende Anzahl - + project.builds.no_stocked_builds Anzahl gelagerter gebauter Instanzen - + user.change_avatar.label Profilbild ändern - + user_settings.change_avatar.label Profilbild ändern - + user_settings.remove_avatar.label Profilbild löschen - + part.edit.name.category_hint Hinweis von Kategorie - + category.edit.partname_regex.placeholder z.B. "/Kondensator \d+ nF/i" - + category.edit.partname_regex.help Ein PCRE-kompatibler regulärer Ausdruck, den der Bauteilename erfüllen muss. - + entity.select.add_hint Nutzen Sie -> um verschachtelte Strukturen anzulegen, z.B. "Element 1->Element 1.1" - + entity.select.group.new_not_added_to_DB Neu (noch nicht zur DB hinzugefügt) - + part.edit.save_and_new Speichern und neues leeres Bauteil erstellen - + homepage.first_steps.title Erste Schritte - + homepage.first_steps.introduction Die Datenbank ist momentan noch leer. Sie möchten möglicherweise die <a href="%url%">Dokumentation</a> lesen oder anfangen, die folgenden Datenstrukturen anzulegen. - + homepage.first_steps.create_part Oder Sie könne direkt ein <a href="%url%">neues Bauteil erstellen</a>. - + homepage.first_steps.hide_hint Dieser Hinweis wird verborgen, sobald Sie das erste Bauteil erstellt haben. - + homepage.forum.text Für Fragen rund um Part-DB, nutze das <a class="link-external" rel="noopener" target="_blank" href="%href%">Diskussionsforum</a> - + log.element_edited.changed_fields.category Kategorie - + log.element_edited.changed_fields.footprint Footprint - + log.element_edited.changed_fields.manufacturer Hersteller - + log.element_edited.changed_fields.value_typical typ. Wert - + log.element_edited.changed_fields.pw_reset_expires Passwort Reset - + log.element_edited.changed_fields.comment Notizen - + log.element_edited.changed_fields.supplierpartnr Bestellnummer - + log.element_edited.changed_fields.supplier_product_url Produkt URL - + log.element_edited.changed_fields.price Preis - + log.element_edited.changed_fields.min_discount_quantity Mindestbestellmenge - + log.element_edited.changed_fields.original_filename Originaler Dateiname - + log.element_edited.changed_fields.path Dateipfad - + log.element_edited.changed_fields.description Beschreibung - + log.element_edited.changed_fields.manufacturing_status Herstellungsstatus - + log.element_edited.changed_fields.options.barcode_type Barcodetyp - + log.element_edited.changed_fields.status Status - + log.element_edited.changed_fields.quantity BOM Menge - + log.element_edited.changed_fields.mountnames Bestückungsnamen - + log.element_edited.changed_fields.name Name - + log.element_edited.changed_fields.part Bauteil - + log.element_edited.changed_fields.price_currency Währung des Preises - + log.element_edited.changed_fields.partname_hint Namenshinweis - + log.element_edited.changed_fields.partname_regex Namensfilter - + log.element_edited.changed_fields.disable_footprints Deaktiviere Footprints - + log.element_edited.changed_fields.disable_manufacturers Deaktiviere Hersteller - + log.element_edited.changed_fields.disable_autodatasheets Deaktiviere Automatische Datenblatt links - + log.element_edited.changed_fields.disable_properties Deaktiviere Eigenschaften - + log.element_edited.changed_fields.default_description Standard Beschreibung - + log.element_edited.changed_fields.default_comment Standard Kommentar - + log.element_edited.changed_fields.filetype_filter Erlaubte Dateitypen - + log.element_edited.changed_fields.not_selectable Nicht auswählbar - + log.element_edited.changed_fields.parent Übergeordnetes Element - + log.element_edited.changed_fields.shipping_costs Versandkosten - + log.element_edited.changed_fields.default_currency Standardwährung - + log.element_edited.changed_fields.address Anschrift - + log.element_edited.changed_fields.phone_number Telefonnummer - + log.element_edited.changed_fields.fax_number Faxnummer - + log.element_edited.changed_fields.email_address E-Mail - + log.element_edited.changed_fields.website Website - + log.element_edited.changed_fields.auto_product_url Produkt URL - + log.element_edited.changed_fields.is_full Lagerort voll - + log.element_edited.changed_fields.limit_to_existing_parts Nur bestehende Bauteile - + log.element_edited.changed_fields.only_single_part Nur ein Bauteil - + log.element_edited.changed_fields.storage_type Lagertyp - + log.element_edited.changed_fields.footprint_3d 3D Modell - + log.element_edited.changed_fields.master_picture_attachment Vorschaubild - + log.element_edited.changed_fields.exchange_rate Wechselkurs - + log.element_edited.changed_fields.iso_code ISO Code - + log.element_edited.changed_fields.unit Einheitensymbol - + log.element_edited.changed_fields.is_integer Ganzzahlig - + log.element_edited.changed_fields.use_si_prefix Benutze SI Prefixe - + log.element_edited.changed_fields.options.width Breite - + log.element_edited.changed_fields.options.height Höhe - + log.element_edited.changed_fields.options.supported_element Elementtyp - + log.element_edited.changed_fields.options.additional_css Zusätzliches CSS - + log.element_edited.changed_fields.options.lines Content - + log.element_edited.changed_fields.permissions.data Berechtigungen - + log.element_edited.changed_fields.disabled Deaktiviert - + log.element_edited.changed_fields.theme Theme - + log.element_edited.changed_fields.timezone Zeitzone - + log.element_edited.changed_fields.language Sprache - + log.element_edited.changed_fields.email E-Mail - + log.element_edited.changed_fields.department Abteilung - + log.element_edited.changed_fields.last_name Vorname - + log.element_edited.changed_fields.first_name Nachname - + log.element_edited.changed_fields.group Gruppe - + log.element_edited.changed_fields.currency Währung - + log.element_edited.changed_fields.enforce2FA Erzwinge 2FA - + log.element_edited.changed_fields.symbol Symbol - + log.element_edited.changed_fields.value_min Min. Wert - + log.element_edited.changed_fields.value_max Max. Wert - + log.element_edited.changed_fields.value_text Text Wert - + log.element_edited.changed_fields.show_in_table In Tabelle anzeigen - + log.element_edited.changed_fields.attachment_type Dateityp - + log.element_edited.changed_fields.needs_review Review benötigt - + log.element_edited.changed_fields.tags Tags - + log.element_edited.changed_fields.mass Masse - + log.element_edited.changed_fields.ipn IPN - + log.element_edited.changed_fields.favorite Favorit - + log.element_edited.changed_fields.minamount Mindestbestand - + log.element_edited.changed_fields.manufacturer_product_url Link zur Produktseite - + log.element_edited.changed_fields.manufacturer_product_number MPN - + log.element_edited.changed_fields.partUnit Maßeinheit - + log.element_edited.changed_fields.expiration_date Ablaufdatum - + log.element_edited.changed_fields.amount Menge - + log.element_edited.changed_fields.storage_location Lagerort + + + attachment.max_file_size + Maximale Dateigröße + + + + + user.saml_user + SSO / SAML Benutzer + + + + + user.saml_user.pw_change_hint + Sie verwenden Single Sign-On (SSO) zum Einloggen. Sie können ihr Passwort und Zwei-Faktor-Authentifizierungen deshalb hier nicht konfigurieren. Nutzen Sie stattdessen die zentrale Seite ihres SSO Anbieters! + + + + + login.sso_saml_login + Single Sign-On Login (SSO) + + + + + login.local_login_hint + Das untenstehende Formular kann nur für den Login mit einem lokalen Benutzer verwendet werden. Wenn Sie sich stattdessen via Single Sign-On einloggen wollen, nutzen Sie den obenstehenden Button. + + + + + part_list.action.action.export + Bauteile exportieren + + + + + part_list.action.export_json + Export als JSON + + + + + part_list.action.export_csv + Export als CSV + + + + + part_list.action.export_yaml + Export als YAML + + + + + part_list.action.export_xml + Export als XML + + + + + parts.import.title + Bauteile importieren + + + + + parts.import.errors.title + Probleme beim Import + + + + + parts.import.flash.error + Es sind Fehler während des Exports aufgetreten. Dies ist vermutlich durch fehlerhafte Daten verursacht. + + + + + parts.import.format.auto + Automatisch (basierend auf Dateiendung) + + + + + parts.import.flash.error.unknown_format + Das Format konnte nicht automatisch bestimmt werden. Bitte wählen Sie das korrekte Format manuell! + + + + + parts.import.flash.error.invalid_file + Datei fehlerhaft / falsch formatiert. Überprüfen Sie, dass sie das richtige Format gewählt haben. + + + + + parts.import.part_category.label + Erzwinge Kategorie + + + + + parts.import.part_category.help + Wenn Sie hier eine Kategorie auswählen, dann werden alle importierten Bauteile dieser Kategorie zugewiesen, unabhängig davon, was in den Importdaten steht. + + + + + import.create_unknown_datastructures + Erstelle unbekannte Datenstrukturen + + + + + import.create_unknown_datastructures.help + Wenn diese Option gewählt ist, dann werden Datenstrukturen (z.B. Kategorien, Footprints, etc.), die noch nicht in der Datenbank existieren, automatisch angelegt. Wenn diese Option nicht gewählt ist, werden nur Datenstrukturen, die bereits in der Datenbank existieren, verwendet, und falls keine passende Struktur existiert, wird das entsprechende Feld des Bauteils leer gelassen. + + + + + import.path_delimiter + Pfadbegrenzer + + + + + import.path_delimiter.help + Der Begrenzer wird benutzt, um die verschiedenen Ebenen von Datenstrukturen (wie Kategorien, Footprints, etc.) in Pfadangaben zu trennen. + + + + + parts.import.help_documentation + Konsultieren Sie die <a href="%link%">Dokumentation</a> für weiter Informationen über das Dateiformat. + + + + + parts.import.help + Mit diesem Tool können sie Bauteile aus bestehenden Dateien importieren. Die Bauteile werden direkt in die Datenbank gespeichert (ohne die Möglichkeit, sie vorher noch einmal zu überprüfen). Prüfen Sie daher ihre Importdatei vor dem Upload hier! + + + + + parts.import.flash.success + Bauteileimport erfolgreich! + + + + + parts.import.errors.imported_entities + Importierte Bauteile + + + + + perm.import + Daten importieren + + + + + parts.import.part_needs_review.label + Markiere alle Bauteile als "Review benötigt" + + + + + parts.import.part_needs_review.help + Wenn diese Option ausgewählt ist, werden alle importierten Bauteile als "Review benötigt" markiert, unabhängig davon, was in den Importdaten steht. + + + + + project.bom_import.flash.success + %count% BOM Einträge erfolgreich importiert. + + + + + project.bom_import.type + Typ + + + + + project.bom_import.type.kicad_pcbnew + KiCAD Pcbnew BOM (CSV Datei) + + + + + project.bom_import.clear_existing_bom + Lösche existierende BOM Einträge vor dem Import + + + + + project.bom_import.clear_existing_bom.help + Wenn diese Option ausgewählt ist, werden alle bereits im Projekt existierenden BOM Einträge gelöscht und mit den importierten BOM Daten überschrieben. + + + + + project.bom_import.flash.invalid_file + Datei konnte nicht importiert werden. Überprüfen Sie, dass Sie den richtigen Dateityp gewählt haben. Fehlermeldung: %message% + + + + + project.bom_import.flash.invalid_entries + Validierungsfehler! Bitte überprüfen Sie die importierte Datei! + + + + + project.import_bom + Importiere BOM für Projekt + + + + + project.edit.bom.import_bom + BOM importieren + + + + + measurement_unit.new + Neue Maßeinheit + + + + + measurement_unit.edit + Bearbeite Maßeinheit + + + + + user.aboutMe.label + Über mich + + + + + storelocation.owner.label + Besitzer + + + + + storelocation.part_owner_must_match.label + Bauteilebesitzer muss mit Lagerortbesitzer übereinstimmen + + + + + part_lot.owner + Besitzer + + + + + part_lot.owner.help + Nur der Besitzer kann Bauteile aus diesem Bestand entnehmen oder hinzufügen. + + + + + log.element_edited.changed_fields.owner + Besitzer + + + + + log.element_edited.changed_fields.instock_unknown + Menge unbekannt + + + + + log.element_edited.changed_fields.needs_refill + Muss aufgefüllt werden + + + + + part.withdraw.access_denied + Sie sind nicht berechtigt die gewünschte Aktion durchzuführen! Bitte überprüfen Sie Ihre Berechtigungen und den Besitzer des Bauteilebestandes. + + + + + part.info.amount.less_than_desired + Weniger als erwünscht + + + + + log.cli_user + CLI Benutzer + + + + + log.element_edited.changed_fields.part_owner_must_match + Der Bauteilebesitzer muss dem Lagerortbesitzer entsprechen! + + + + + part.filter.lessThanDesired + Weniger vorhanden als gewünscht (Gesamtmenge < Mindestmenge) + + + + + part.filter.lotOwner + Besitzer des Bestands + + + + + user.show_email_on_profile.label + E-Mail-Adresse auf öffentlicher Profilseite anzeigen + + + + + log.details.title + Logdetails + + + + + log.user_login.login_from_ip + Login von IP-Adresse + + + + + log.user_login.ip_anonymize_hint + Wenn die letzten Stellen der IP-Adresse fehlen, dann ist der DSGVO Modus aktiviert, bei dem IP-Adressen anonymisiert werden. + + + + + log.user_not_allowed.unauthorized_access_attempt_to + Unerlaubter Zugriffsversuch auf Seite + + + + + log.user_not_allowed.hint + Die Anfrage wurde blockiert. Es sollte keine weitere Aktion nötig sein. + + + + + log.no_comment + Kein Kommentar + + + + + log.element_changed.field + Feld + + + + + log.element_changed.data_before + Daten vor Änderung + + + + + error_table.error + Während der Anfrage trat ein Fehler auf. + + + + + part.table.invalid_regex + Ungültiger regulärer Ausdruck (regex) + + + + + log.element_changed.data_after + Daten nach Änderung + + + + + log.element_changed.diff + Unterschied + + + + + log.undo.undo.short + Rückgängig machen + + + + + log.undo.revert.short + Auf Version zurücksetzen + + + + + log.view_version + Version anzeigen + + + + + log.undo.undelete.short + Wiederherstellen + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + Besitzer + + + + + log.element_edited.changed_fields.parent_id + Übergeordnetes Element + + + + + log.details.delete_entry + Logeintrag löschen + + + + + log.delete.message.title + Wollen Sie diesen Logeintrag wirklich löschen? + + + + + log.delete.message + Wenn dies ein Historieeintrag für ein Element ist, dann wird das Löschen zu Datenverlust der Historie führen! Dies kann unerwartete Ergebnisse liefern, wenn die Zeitreisefunktion verwendet wird. + + + + + log.collection_deleted.on_collection + in Kollektion + + + + + log.element_edited.changed_fields.attachments + Dateianhänge + + + + + tfa_u2f.add_key.registration_error + Während der Registrierung des Sicherheitsschlüssels trat ein Fehler auf. Versuchen Sie es erneut oder verwenden Sie einen anderen Schlüssel! + + + + + log.target_type.none + Keine + + + + + ui.darkmode.light + Hell + + + + + ui.darkmode.dark + Dunkel + + + + + ui.darkmode.auto + Auto (basierend auf Systemeinstellungen) + + + + + label_generator.no_lines_given + Kein Textinhalt angegeben! Die erzeugten Label werden leer sein. + + + + + user.password_strength.very_weak + Sehr schwach + + + + + user.password_strength.weak + Schwach + + + + + user.password_strength.medium + Mittel + + + + + user.password_strength.strong + Stark + + + + + user.password_strength.very_strong + Sehr stark + + + + + perm.users.impersonate + Als anderer Benutzer ausgeben + + + + + user.impersonated_by.label + Als Benutzer ausgegeben durch + + + + + user.stop_impersonation + Beende "Als Nutzer ausgeben" + + + + + user.impersonate.btn + Als Benutzer ausgeben + + + + + user.impersonate.confirm.title + Möchten Sie sich wirklich als dieser Benutzer ausgeben? + + + + + user.impersonate.confirm.message + Dies wird geloggt. Sie sollten dies nur mit einem Grund tun. + +Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben können. Wenn Sie dies versuchen, erhalten Sie eine "Zugriff verweigert" Nachricht. + + + + + log.type.security.user_impersonated + Als Benutzer ausgegeben + + + + + info_providers.providers_list.title + Informationsquellen + + + + + info_providers.providers_list.active + Aktiv + + + + + info_providers.providers_list.disabled + Deaktiviert + + + + + info_providers.capabilities.basic + Basis + + + + + info_providers.capabilities.footprint + Footprint + + + + + info_providers.capabilities.picture + Bilder + + + + + info_providers.capabilities.datasheet + Datenblätter + + + + + info_providers.capabilities.price + Preise + + + + + part.info_provider_reference.badge + Die Informationsquelle, die verwendet wurde, um dieses Bauteil zu erstellen. + + + + + part.info_provider_reference + Erzeugt aus Informationsquelle + + + + + oauth_client.connect.btn + OAuth verbinden + + + + + info_providers.table.provider.label + Quelle + + + + + info_providers.search.keyword + Suchwort + + + + + info_providers.search.submit + Suchen + + + + + info_providers.search.providers.help + Wählen Sie die Informationsquellen aus, die durchsucht werden sollen. + + + + + info_providers.search.providers + Quellen + + + + + info_providers.search.info_providers_list + Alle verfügbaren Informationsquellen anzeigen + + + + + info_providers.search.title + Bauteil aus Informationsquelle erstellen + + + + + oauth_client.flash.connection_successful + Verbindung zur OAuth Anwendung erfolgreich hergestellt! + + + + + perm.part.info_providers + Informationsquellen + + + + + perm.part.info_providers.create_parts + Bauteile erstellen + + + + + entity.edit.alternative_names.label + Alternative Namen + + + + + entity.edit.alternative_names.help + Die hier angegeben alternativen Namen, werden benutzt, um dieses Element automatisch anhand der von Informationsquellen zurückgelieferten Daten auszuwählen. + + + + + info_providers.form.help_prefix + Quelle + + + + + update_manager.new_version_available.title + Neue Version verfügbar + + + + + update_manager.new_version_available.text + Eine neue Version von Part-DB ist verfügbar. Mehr Informationen gibt es hier + + + + + update_manager.new_version_available.only_administrators_can_see + Nur Administratoren können diese Nachricht sehen. + + + + + perm.system.show_available_updates + Verfügbare Part-DB Updates anzeigen + + + + + user.settings.api_tokens + API Token + + + + + user.settings.api_tokens.description + Mithilfe eines API-Tokens können andere Anwendungen auf Part-DB mit ihren Benutzerrechten zugreifen, um verschiedene Aktionen mittels der Part-DB REST API durchzuführen. Wenn Sie einen API-Token löschen, kann die Applikation, die den Token verwendet, nicht mehr in ihrem Namen auf Part-DB zugreifen. + + + + + api_tokens.name + Name + + + + + api_tokens.access_level + Zugriffslevel + + + + + api_tokens.expiration_date + Ablaufdatum + + + + + api_tokens.added_date + Erstellt am + + + + + api_tokens.last_time_used + Letzte Verwendung + + + + + datetime.never + Niemals + + + + + api_token.valid + Gültig + + + + + api_token.expired + Abgelaufen + + + + + user.settings.show_api_documentation + API Dokumentation anzeigen + + + + + api_token.create_new + Neuen API Token erstellen + + + + + api_token.level.read_only + Nur-Lesen + + + + + api_token.level.edit + Bearbeiten + + + + + api_token.level.admin + Admin + + + + + api_token.level.full + Voll + + + + + api_tokens.access_level.help + Hiermit lässt sich einschränken, worauf der API Token Zugriff ermöglicht. Der Zugriff ist immer durch die Berechtigungen des Nutzers begrenzt. + + + + + api_tokens.expiration_date.help + Nach diesem Datum ist das Token nicht mehr nutzbar. Wird dieses Feld leer gelassen, läuft das Token niemals ab. + + + + + api_tokens.your_token_is + Ihr API Token ist + + + + + api_tokens.please_save_it + Bitte speichern Sie dieses. Sie werden es nicht erneut sehen können! + + + + + api_tokens.create_new.back_to_user_settings + Zurück zu den Benutzereinstellungen + + + + + project.build.dont_check_quantity + Mengen nicht überprüfen + + + + + project.build.dont_check_quantity.help + Wenn diese Option gewählt wird, werden die gewählten Mengen aus dem Lager entfernt, egal ob mehr oder weniger Bauteile sind, als für den Bau des Projekts eigentlich benötigt werden. + + + + + part_list.action.invert_selection + Auswahl umkehren + + + + + perm.api + API + + + + + perm.api.access_api + API Zugriff + + + + + perm.api.manage_tokens + API Token verwalten + + + + + user.settings.api_tokens.delete.title + Möchten Sie dieses API-Token wirklich löschen? + + + + + user.settings.api_tokens.delete + Löschen + + + + + user.settings.api_tokens.delete.message + Die Applikation, die dieses Token verwendet, wird keinen Zugriff mehr auf Part-DB haben. Dies kann nicht rückgängig gemacht werden! + + + + + api_tokens.deleted + API Token erfolgreich entfernt! + + + + + user.settings.api_tokens.no_api_tokens_yet + Bisher wurden keine API Token erstellt. + + + + + api_token.ends_with + Endet mit + + + + + entity.select.creating_new_entities_not_allowed + Sie sind nicht berechtigt, neue Elemente dieses Typs anzulegen! Bitte wählen Sie ein vorgegebenes Element aus. + + + + + scan_dialog.mode + Barcode-Typ + + + + + scan_dialog.mode.auto + Automatische Erkennung + + + + + scan_dialog.mode.ipn + IPN Barcode + + + + + scan_dialog.mode.internal + Part-DB Barcode + + + + + part_association.label + Bauteileverknüpfung + + + + + part.edit.tab.associations + Verknüpfte Bauteile + + + + + part_association.edit.other_part + Verknüpftes Bauteil + + + + + part_association.edit.type + Art der Beziehung + + + + + part_association.edit.comment + Notizen + + + + + part_association.edit.type.help + Sie können hier auswählen, in welcher Art von Verbindung die Bauteile stehen. + + + + + part_association.table.from_this_part + Verknüpfungen von diesem Bauteil zu anderen + + + + + part_association.table.from + Von + + + + + part_association.table.type + Beziehung + + + + + part_association.table.to + Zu + + + + + part_association.type.compatible + Ist kompatibel mit + + + + + part_association.table.to_this_part + Verknüpfungen zu diesem Bauteil von anderen + + + + + part_association.type.other + Andere (eigener Wert) + + + + + part_association.type.supersedes + Ersetzt + + + + + part_association.edit.other_type + Selbstgewählte Beziehung + + + + + part_association.edit.delete.confirm + Möchten Sie diese Verknüpfung wirklich löschen? Dies kann nicht rückgängig gemacht werden. + + + + + part_lot.edit.advanced + Erweiterte Optionen zeigen + + + + + part_lot.edit.vendor_barcode + Lieferanten Barcode + + + + + part_lot.edit.vendor_barcode.help + Wenn dieser Bestand bereits ein Barcode hat (der z.B. vom Lieferanten aufgebracht wurde), können Sie hier den Inhalt des Barcodes angeben, damit Sie diesen Bestand durch scannen des Barcodes finden. + + + + + scan_dialog.mode.vendor + Lieferanten Barcode (in Bauteilebestand konfiguriert) + + + + + project.bom.instockAmount + Bestand im Lager + + + + + collection_type.new_element.tooltip + Dieses Element wurde neu erstellt und ist noch nicht in der Datenbank gespeichert. + + + + + part.merge.title + Führe Bauteil + + + + + part.merge.title.into + zusammen in + + + + + part.merge.confirm.title + Möchten Sie wirklich <b>%other%</b> in <b>%target%</b> zusammenführen? + + + + + part.merge.confirm.message + <b>%other%</b> wird gelöscht, und das aktuelle Bauteil wird mit den angezeigten Daten gespeichert. + + + + + part.info.merge_modal.title + Bauteile zusammenführen + + + + + part.info.merge_modal.other_part + Anderes Bauteil + + + + + part.info.merge_modal.other_into_this + Führe anderes Bauteil in diesem zusammen (anderes Bauteil löschen, dieses behalten) + + + + + part.info.merge_modal.this_into_other + Führe dieses Bauteil in anderem zusammen (dieses Bauteil löschen, anderes behalten) + + + + + part.info.merge_btn + Bauteil zusammenführen + + + + + part.update_part_from_info_provider.btn + Bauteil aus Informationsquelle aktualisieren + + + + + info_providers.update_part.title + Bauteil aus Informationsquelle aktualisieren + + + + + part.merge.flash.please_review + Daten wurden noch nicht gespeichert. Bitte überprüfen Sie die Änderungen und klicken Sie speichern, um die Daten zu sichern. + + + + + user.edit.flash.permissions_fixed + Berechtigungen, die für andere Berechtigungen erforderlich sind, fehlten. Dies wurde korrigiert. Bitte prüfen Sie, ob die Berechtigungen Ihren Vorstellungen entsprechen. + + + + + permission.legend.dependency_note + Bitte beachten Sie, dass einige Berechtigungsoperationen voneinander abhängen. Wenn Sie eine Warnung erhalten, dass fehlende Berechtigungen korrigiert wurden und eine Berechtigung wieder auf erlaubt gesetzt wurde, müssen Sie die abhängige Operation ebenfalls auf verboten setzen. Die Abhängigkeiten befinden sich normalerweise rechts von einer Operation. + + + + + log.part_stock_changed.timestamp + Zeitpunkt + + + + + part.info.withdraw_modal.timestamp + Aktionszeitpunkt + + + + + part.info.withdraw_modal.timestamp.hint + In diesem Feld können Sie das tatsächliche Datum angeben, an dem die Lageroperation tatsächlich durchgeführt wurde, und nicht nur das Datum, an dem sie protokolliert wurde. Dieser Wert wird im Extrafeld des Logeintrags gespeichert. + + + + + part.info.withdraw_modal.delete_lot_if_empty + Lösche diesen Bestand, wenn er durch Operation leer wird + + + + + info_providers.search.error.client_exception + Bei der Kommunikation mit dem Informationsanbieter ist ein Fehler aufgetreten. Überprüfen Sie die Konfiguration für diesen Anbieter und erneuern Sie die OAuth-Tokens, falls möglich. + + + + + eda_info.reference_prefix.placeholder + z. B. R + + + + + eda_info.reference_prefix + Referenzpräfix + + + + + eda_info.kicad_section.title + KiCad spezifische Einstellungen + + + + + eda_info.value + Wert + + + + + eda_info.value.placeholder + z.B. 100n + + + + + eda_info.exclude_from_bom + Bauteil von BOM ausschließen + + + + + eda_info.exclude_from_board + Bauteil von Platine ausschließen + + + + + eda_info.exclude_from_sim + Bauteil von Simulation ausschließen + + + + + eda_info.kicad_symbol + KiCad-Schaltsymbol + + + + + eda_info.kicad_symbol.placeholder + z.B. Transistor_BJT:BC547 + + + + + eda_info.kicad_footprint + KiCad-Footprint + + + + + eda_info.kicad_footprint.placeholder + z.B. Package_TO_SOT_THT:TO-92 + + + + + part.edit.tab.eda + EDA Informationen + + + + + api.api_endpoints.title + API Endpunkte + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + KiCad API root URL + + + + + eda_info.visibility + Erzwinge Sichtbarkeit + + + + + eda_info.visibility.help + Standardmäßig wird die Sichtbarkeit innerhalb der EDA Software automatisch ermittelt. Mittels dieser Checkbox, lässt es sich erzwingen, dass das Bauteil sichtbar oder unsichtbar ist. + + + + + part.withdraw.zero_amount + Sie haben versucht eine Menge von Null zu entnehmen/hinzuzufügen! Es wurde keine Aktion durchgeführt. + + + + + login.flash.access_denied_please_login + Zugriff verweigert! Bitte melden Sie sich an, um fortzufahren. + + + + + attachment.upload_multiple_files + Dateien hochladen + + + + + entity.mass_creation_flash + %COUNT% Elemente erfolgreich erstellt. + + + + + info_providers.search.number_of_results + %number% Ergebnisse + + + + + info_providers.search.no_results + Keine Ergebnisse in den gegebenen Quellen gefunden! Prüfen Sie ihr Suchwort oder versuchen Sie weitere Informationsquellen auszuwählen. + + + + + tfa.check.code.confirmation + Erzeugter Code + + + + + info_providers.search.show_existing_part + Bestehendes Bauteil anzeigen + + + + + info_providers.search.edit_existing_part + Bestehendes Bauteil bearbeiten + + + + + info_providers.search.existing_part_found.short + Bauteil existiert bereits + + + + + info_providers.search.existing_part_found + Dieses Bauteil (oder ein sehr ähnliches) existiert bereits in der Datenbank. Bitte überprüfen Sie, ob es das gleiche ist und ob Sie dies erneut erstellen möchten! + + + + + info_providers.search.update_existing_part + Bestehendes Bauteil von Informationsquelle aktualisieren + + + + + part.create_from_info_provider.no_category_yet + Die Kategorie konnte nicht automatisch von der Informationsquelle ermittelt werden. Überprüfen Sie die Daten und wählen Sie die Kategorie händisch aus. + + + + + part_lot.edit.user_barcode + Benutzer-Barcode + + + + + scan_dialog.mode.user + Benutzer definierter Barcode (in Bauteilebestand konfiguriert) + + + + + scan_dialog.mode.eigp + EIGP 114 Barcode (z. B. der Datamatrix code auf Digikey und Mouser Bauteilen) + + + + + scan_dialog.info_mode + Info Modus (Barcode dekodieren und Inhalte anzeigen, aber nicht zum Bauteil weiterleiten) + + + + + label_scanner.decoded_info.title + Dekodierte Informationen + + + + + label_generator.edit_profiles + Profile bearbeiten + + + + + label_generator.profile_name_empty + Der Profilname darf nicht leer sein! + + + + + label_generator.save_profile_name + Profilname + + + + + label_generator.save_profile + Als neues Profil speichern + + + + + label_generator.profile_saved + Profil gespeichert! + + + + + entity.export.flash.error.no_entities + Es gibt keine Entitäten zu exportieren! + + + + + attachment.table.internal_file + Interne Datei + + + + + attachment.table.external_link + Externer link + + + + + attachment.view_external.view_at + Auf %host% anzeigen + + + + + attachment.view_external + Externe Version anzeigen + + + + + part.table.actions.error + Es traten %count% Fehler bei der Aktion auf! + + + + + part.table.actions.error_detail + %part_name% (ID: %part_id%): %message% + + + + + part_list.action.action.change_location + Lagerort ändern (nur für Bauteile mit einzelnem Bestand) + + + + + parts.table.action_handler.error.part_lots_multiple + Dieses Bauteil enthält mehr als einen Bestand. Ändere den Lagerort bei Hand, um auszuwählen, welcher Bestand geändert werden soll. + + diff --git a/translations/messages.el.xlf b/translations/messages.el.xlf index 8deb6a3a..cc17d9be 100644 --- a/translations/messages.el.xlf +++ b/translations/messages.el.xlf @@ -1535,11 +1535,5 @@ Επεξεργασία - - - - - - diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index 16e88fbf..e974d34a 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -1,7 +1,7 @@ - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 @@ -12,7 +12,7 @@ File types for attachments - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 new @@ -22,7 +22,7 @@ Edit file type - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 new @@ -32,7 +32,7 @@ New file type - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 Part-DB1\templates\_sidebar.html.twig:22 @@ -51,7 +51,7 @@ Categories - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 @@ -64,7 +64,7 @@ Options - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 @@ -77,7 +77,7 @@ Advanced - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 new @@ -87,7 +87,7 @@ Edit category - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 new @@ -97,7 +97,7 @@ New category - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 @@ -107,7 +107,7 @@ Currency - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 @@ -117,7 +117,7 @@ ISO code - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 @@ -127,7 +127,7 @@ Currency symbol - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 new @@ -137,7 +137,7 @@ Edit currency - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 new @@ -147,7 +147,7 @@ New currency - + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 @@ -158,7 +158,7 @@ Project - + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 new @@ -168,7 +168,7 @@ Edit project - + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 new @@ -178,7 +178,7 @@ New project - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 Part-DB1\templates\_navbar_search.html.twig:67 @@ -201,7 +201,7 @@ Search - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 Part-DB1\templates\_sidebar.html.twig:3 @@ -217,7 +217,7 @@ Expand All - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 Part-DB1\templates\_sidebar.html.twig:4 @@ -233,7 +233,7 @@ Reduce All - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 Part-DB1\templates\Parts\info\_sidebar.html.twig:4 @@ -242,10 +242,10 @@ part.info.timetravel_hint - This is how the part appeared before %timestamp%. <i>Please note that this feature is experimental, so the infos are maybe not correct.</i> + This is how the part appeared before %timestamp%. <i>Please note that this feature is experimental, so the info may not be correct.</i> - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 @@ -256,7 +256,7 @@ Properties - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 @@ -264,10 +264,10 @@ infos.label - Infos + Info - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 @@ -278,7 +278,7 @@ History - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 @@ -289,7 +289,7 @@ Export - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 @@ -300,7 +300,7 @@ Import / Export - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 @@ -310,7 +310,7 @@ Mass creation - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 @@ -321,7 +321,7 @@ Common - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 @@ -331,7 +331,7 @@ Attachments - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 @@ -340,7 +340,7 @@ Parameters - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 @@ -351,7 +351,7 @@ Export all elements - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 @@ -361,7 +361,7 @@ Each line will be interpreted as a name of an element, which will be created. You can create nested structures by indentations. - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 @@ -372,7 +372,7 @@ Edit element "%name" - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 @@ -383,7 +383,7 @@ New element - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 Part-DB1\templates\_sidebar.html.twig:9 @@ -398,7 +398,7 @@ Footprints - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 new @@ -408,7 +408,7 @@ Edit footprint - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 new @@ -418,7 +418,7 @@ New footprint - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 @@ -428,7 +428,7 @@ Groups - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 @@ -440,7 +440,7 @@ Permissions - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 new @@ -450,7 +450,7 @@ Edit group - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 new @@ -460,7 +460,7 @@ New group - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 @@ -469,7 +469,7 @@ Label profiles - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 @@ -478,7 +478,7 @@ Advanced - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 @@ -487,7 +487,7 @@ Notes - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 new @@ -497,7 +497,7 @@ Edit label profile - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 new @@ -507,7 +507,7 @@ New label profile - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 @@ -518,7 +518,7 @@ Manufacturers - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 new @@ -528,7 +528,7 @@ Edit manufacturer - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 new @@ -538,7 +538,7 @@ New manufacturer - + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 @@ -548,7 +548,7 @@ Measurement Unit - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 Part-DB1\templates\_sidebar.html.twig:8 @@ -560,30 +560,30 @@ storelocation.labelp - Store locations + Storage locations - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 new storelocation.edit - Edit store location + Edit storage location - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 new storelocation.new - New store location + New storage location - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 @@ -594,7 +594,7 @@ Suppliers - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 new @@ -604,7 +604,7 @@ Edit supplier - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 new @@ -614,7 +614,7 @@ New supplier - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 @@ -624,7 +624,7 @@ Users - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 @@ -634,7 +634,7 @@ Configuration - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 @@ -644,7 +644,7 @@ Password - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 @@ -654,7 +654,7 @@ Two-factor authentication - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 @@ -664,7 +664,7 @@ Authenticator app active - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 Part-DB1\templates\Users\backup_codes.html.twig:15 @@ -678,7 +678,7 @@ Remaining backup codes count - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 Part-DB1\templates\Users\backup_codes.html.twig:17 @@ -692,7 +692,7 @@ Generation date of the backup codes - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 @@ -704,7 +704,7 @@ Method not enabled - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 @@ -714,7 +714,7 @@ Active security keys - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 @@ -724,20 +724,20 @@ Do you really want to proceed? - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 user.edit.tfa.disable_tfa_message - This will disable <b>all active two-factor authentication methods of the user</b> and delete the <b>backup codes</b>! + This will disable <b>all active two-factor authentication methods of the user</b> and delete the <b>backup codes</b>! <br> The user will have to set up all two-factor authentication methods again and print new backup codes! <br><br> <b>Only do this if you are absolutely sure about the identity of the user (seeking help), otherwise the account could be compromised by an attacker!</b> - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 @@ -747,7 +747,7 @@ The user will have to set up all two-factor authentication methods again and pri Disable all two-factor authentication methods - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 new @@ -757,7 +757,7 @@ The user will have to set up all two-factor authentication methods again and pri Edit user - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 new @@ -767,7 +767,7 @@ The user will have to set up all two-factor authentication methods again and pri New user - + Part-DB1\templates\AdminPages\_attachments.html.twig:4 Part-DB1\templates\Parts\edit\_attachments.html.twig:4 @@ -780,21 +780,13 @@ The user will have to set up all two-factor authentication methods again and pri Delete - - - Part-DB1\templates\AdminPages\_attachments.html.twig:41 - Part-DB1\templates\Parts\edit\_attachments.html.twig:38 - Part-DB1\templates\Parts\info\_attachments_info.html.twig:35 - Part-DB1\src\DataTables\AttachmentDataTable.php:159 - Part-DB1\templates\Parts\edit\_attachments.html.twig:38 - Part-DB1\src\DataTables\AttachmentDataTable.php:159 - + - attachment.external - External + attachment.external_only + External only - + Part-DB1\templates\AdminPages\_attachments.html.twig:49 Part-DB1\templates\Parts\edit\_attachments.html.twig:47 @@ -806,7 +798,7 @@ The user will have to set up all two-factor authentication methods again and pri Attachment thumbnail - + Part-DB1\templates\AdminPages\_attachments.html.twig:52 Part-DB1\templates\Parts\edit\_attachments.html.twig:50 @@ -816,11 +808,11 @@ The user will have to set up all two-factor authentication methods again and pri Part-DB1\templates\Parts\info\_attachments_info.html.twig:45 - attachment.view - View + attachment.view_local + View Local Copy - + Part-DB1\templates\AdminPages\_attachments.html.twig:58 Part-DB1\templates\Parts\edit\_attachments.html.twig:56 @@ -836,7 +828,7 @@ The user will have to set up all two-factor authentication methods again and pri File not found - + Part-DB1\templates\AdminPages\_attachments.html.twig:66 Part-DB1\templates\Parts\edit\_attachments.html.twig:64 @@ -848,7 +840,7 @@ The user will have to set up all two-factor authentication methods again and pri Private attachment - + Part-DB1\templates\AdminPages\_attachments.html.twig:79 Part-DB1\templates\Parts\edit\_attachments.html.twig:77 @@ -860,7 +852,7 @@ The user will have to set up all two-factor authentication methods again and pri Add attachment - + Part-DB1\templates\AdminPages\_attachments.html.twig:84 Part-DB1\templates\Parts\edit\_attachments.html.twig:82 @@ -874,7 +866,7 @@ The user will have to set up all two-factor authentication methods again and pri Do you really want to delete this stock? This can not be undone! - + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 Part-DB1\templates\AdminPages\_delete_form.html.twig:2 @@ -885,7 +877,7 @@ The user will have to set up all two-factor authentication methods again and pri You really want to delete %name%? - + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 Part-DB1\templates\AdminPages\_delete_form.html.twig:3 @@ -898,7 +890,7 @@ The user will have to set up all two-factor authentication methods again and pri Sub elements will be moved upwards. - + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 Part-DB1\templates\AdminPages\_delete_form.html.twig:11 @@ -909,7 +901,7 @@ Sub elements will be moved upwards. Delete element - + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 Part-DB1\templates\Parts\info\_tools.html.twig:45 @@ -924,7 +916,7 @@ Sub elements will be moved upwards. Change comment - + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 Part-DB1\templates\AdminPages\_delete_form.html.twig:24 @@ -935,7 +927,7 @@ Sub elements will be moved upwards. Delete recursive (all sub elements) - + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 @@ -944,7 +936,7 @@ Sub elements will be moved upwards. Duplicate element - + Part-DB1\templates\AdminPages\_export_form.html.twig:4 Part-DB1\src\Form\AdminPages\ImportType.php:76 @@ -958,7 +950,7 @@ Sub elements will be moved upwards. File format - + Part-DB1\templates\AdminPages\_export_form.html.twig:16 Part-DB1\templates\AdminPages\_export_form.html.twig:16 @@ -969,7 +961,7 @@ Sub elements will be moved upwards. Verbosity level - + Part-DB1\templates\AdminPages\_export_form.html.twig:19 Part-DB1\templates\AdminPages\_export_form.html.twig:19 @@ -980,7 +972,7 @@ Sub elements will be moved upwards. Simple - + Part-DB1\templates\AdminPages\_export_form.html.twig:20 Part-DB1\templates\AdminPages\_export_form.html.twig:20 @@ -991,7 +983,7 @@ Sub elements will be moved upwards. Extended - + Part-DB1\templates\AdminPages\_export_form.html.twig:21 Part-DB1\templates\AdminPages\_export_form.html.twig:21 @@ -1002,7 +994,7 @@ Sub elements will be moved upwards. Full - + Part-DB1\templates\AdminPages\_export_form.html.twig:31 Part-DB1\templates\AdminPages\_export_form.html.twig:31 @@ -1013,7 +1005,7 @@ Sub elements will be moved upwards. Include children elements in export - + Part-DB1\templates\AdminPages\_export_form.html.twig:39 Part-DB1\templates\AdminPages\_export_form.html.twig:39 @@ -1024,7 +1016,7 @@ Sub elements will be moved upwards. Export - + Part-DB1\templates\AdminPages\_info.html.twig:4 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 @@ -1043,7 +1035,7 @@ Sub elements will be moved upwards. ID - + Part-DB1\templates\AdminPages\_info.html.twig:11 Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 @@ -1067,7 +1059,7 @@ Sub elements will be moved upwards. Created At - + Part-DB1\templates\AdminPages\_info.html.twig:25 Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 @@ -1085,7 +1077,7 @@ Sub elements will be moved upwards. Last modified - + Part-DB1\templates\AdminPages\_info.html.twig:38 Part-DB1\templates\AdminPages\_info.html.twig:38 @@ -1095,7 +1087,7 @@ Sub elements will be moved upwards. Number of parts with this element - + Part-DB1\templates\AdminPages\_parameters.html.twig:6 Part-DB1\templates\helper.twig:125 @@ -1106,7 +1098,7 @@ Sub elements will be moved upwards. Parameter - + Part-DB1\templates\AdminPages\_parameters.html.twig:7 Part-DB1\templates\Parts\edit\_specifications.html.twig:7 @@ -1116,7 +1108,7 @@ Sub elements will be moved upwards. Symbol - + Part-DB1\templates\AdminPages\_parameters.html.twig:8 Part-DB1\templates\Parts\edit\_specifications.html.twig:8 @@ -1126,7 +1118,7 @@ Sub elements will be moved upwards. Min. - + Part-DB1\templates\AdminPages\_parameters.html.twig:9 Part-DB1\templates\Parts\edit\_specifications.html.twig:9 @@ -1136,7 +1128,7 @@ Sub elements will be moved upwards. Typ. - + Part-DB1\templates\AdminPages\_parameters.html.twig:10 Part-DB1\templates\Parts\edit\_specifications.html.twig:10 @@ -1146,7 +1138,7 @@ Sub elements will be moved upwards. Max. - + Part-DB1\templates\AdminPages\_parameters.html.twig:11 Part-DB1\templates\Parts\edit\_specifications.html.twig:11 @@ -1156,7 +1148,7 @@ Sub elements will be moved upwards. Unit - + Part-DB1\templates\AdminPages\_parameters.html.twig:12 Part-DB1\templates\Parts\edit\_specifications.html.twig:12 @@ -1166,7 +1158,7 @@ Sub elements will be moved upwards. Text - + Part-DB1\templates\AdminPages\_parameters.html.twig:13 Part-DB1\templates\Parts\edit\_specifications.html.twig:13 @@ -1176,7 +1168,7 @@ Sub elements will be moved upwards. Group - + Part-DB1\templates\AdminPages\_parameters.html.twig:26 Part-DB1\templates\Parts\edit\_specifications.html.twig:26 @@ -1186,7 +1178,7 @@ Sub elements will be moved upwards. New Parameter - + Part-DB1\templates\AdminPages\_parameters.html.twig:31 Part-DB1\templates\Parts\edit\_specifications.html.twig:31 @@ -1196,7 +1188,7 @@ Sub elements will be moved upwards. Do you really want to delete this parameter? - + Part-DB1\templates\attachment_list.html.twig:3 Part-DB1\templates\attachment_list.html.twig:3 @@ -1206,7 +1198,7 @@ Sub elements will be moved upwards. Attachments list - + Part-DB1\templates\attachment_list.html.twig:10 Part-DB1\templates\LogSystem\_log_table.html.twig:8 @@ -1220,7 +1212,7 @@ Sub elements will be moved upwards. Loading - + Part-DB1\templates\attachment_list.html.twig:11 Part-DB1\templates\LogSystem\_log_table.html.twig:9 @@ -1234,7 +1226,7 @@ Sub elements will be moved upwards. This can take a moment. If this message do not disappear, try to reload the page. - + Part-DB1\templates\base.html.twig:68 Part-DB1\templates\base.html.twig:68 @@ -1245,7 +1237,7 @@ Sub elements will be moved upwards. Please activate Javascript to use all features! - + Part-DB1\templates\base.html.twig:73 Part-DB1\templates\base.html.twig:73 @@ -1255,7 +1247,7 @@ Sub elements will be moved upwards. Show/Hide sidebar - + Part-DB1\templates\base.html.twig:95 Part-DB1\templates\base.html.twig:95 @@ -1266,7 +1258,7 @@ Sub elements will be moved upwards. Loading: - + Part-DB1\templates\base.html.twig:96 Part-DB1\templates\base.html.twig:96 @@ -1277,7 +1269,7 @@ Sub elements will be moved upwards. This can take a while. If this messages stays for a long time, try to reload the page. - + Part-DB1\templates\base.html.twig:101 Part-DB1\templates\base.html.twig:101 @@ -1288,7 +1280,7 @@ Sub elements will be moved upwards. Loading... - + Part-DB1\templates\base.html.twig:112 Part-DB1\templates\base.html.twig:112 @@ -1299,7 +1291,7 @@ Sub elements will be moved upwards. Back to page's top - + Part-DB1\templates\Form\permissionLayout.html.twig:35 Part-DB1\templates\Form\permissionLayout.html.twig:35 @@ -1309,7 +1301,7 @@ Sub elements will be moved upwards. Permissions - + Part-DB1\templates\Form\permissionLayout.html.twig:36 Part-DB1\templates\Form\permissionLayout.html.twig:36 @@ -1319,17 +1311,17 @@ Sub elements will be moved upwards. Value - + Part-DB1\templates\Form\permissionLayout.html.twig:53 Part-DB1\templates\Form\permissionLayout.html.twig:53 permission.legend.title - Explanation of the states: + Explanation of the states - + Part-DB1\templates\Form\permissionLayout.html.twig:57 Part-DB1\templates\Form\permissionLayout.html.twig:57 @@ -1339,7 +1331,7 @@ Sub elements will be moved upwards. Forbidden - + Part-DB1\templates\Form\permissionLayout.html.twig:61 Part-DB1\templates\Form\permissionLayout.html.twig:61 @@ -1349,7 +1341,7 @@ Sub elements will be moved upwards. Allowed - + Part-DB1\templates\Form\permissionLayout.html.twig:65 Part-DB1\templates\Form\permissionLayout.html.twig:65 @@ -1359,7 +1351,7 @@ Sub elements will be moved upwards. Inherit from (parent) group - + Part-DB1\templates\helper.twig:3 Part-DB1\templates\helper.twig:3 @@ -1369,7 +1361,7 @@ Sub elements will be moved upwards. True - + Part-DB1\templates\helper.twig:5 Part-DB1\templates\helper.twig:5 @@ -1379,7 +1371,7 @@ Sub elements will be moved upwards. False - + Part-DB1\templates\helper.twig:92 Part-DB1\templates\helper.twig:87 @@ -1389,7 +1381,7 @@ Sub elements will be moved upwards. Yes - + Part-DB1\templates\helper.twig:94 Part-DB1\templates\helper.twig:89 @@ -1399,7 +1391,7 @@ Sub elements will be moved upwards. No - + Part-DB1\templates\helper.twig:126 @@ -1408,7 +1400,7 @@ Sub elements will be moved upwards. Value - + Part-DB1\templates\homepage.html.twig:7 Part-DB1\templates\homepage.html.twig:7 @@ -1419,7 +1411,7 @@ Sub elements will be moved upwards. Version - + Part-DB1\templates\homepage.html.twig:22 Part-DB1\templates\homepage.html.twig:22 @@ -1430,7 +1422,7 @@ Sub elements will be moved upwards. License information - + Part-DB1\templates\homepage.html.twig:31 Part-DB1\templates\homepage.html.twig:31 @@ -1441,7 +1433,7 @@ Sub elements will be moved upwards. Project page - + Part-DB1\templates\homepage.html.twig:31 Part-DB1\templates\homepage.html.twig:31 @@ -1452,7 +1444,7 @@ Sub elements will be moved upwards. Source, downloads, bug reports, to-do-list etc. can be found on <a href="%href%" class="link-external" target="_blank">GitHub project page</a> - + Part-DB1\templates\homepage.html.twig:32 Part-DB1\templates\homepage.html.twig:32 @@ -1463,7 +1455,7 @@ Sub elements will be moved upwards. Help - + Part-DB1\templates\homepage.html.twig:32 Part-DB1\templates\homepage.html.twig:32 @@ -1474,7 +1466,7 @@ Sub elements will be moved upwards. Help and tips can be found in Wiki the <a href="%href%" class="link-external" target="_blank">GitHub page</a> - + Part-DB1\templates\homepage.html.twig:33 Part-DB1\templates\homepage.html.twig:33 @@ -1485,29 +1477,7 @@ Sub elements will be moved upwards. Forum - - - Part-DB1\templates\homepage.html.twig:36 - Part-DB1\templates\homepage.html.twig:36 - templates\homepage.html.twig:33 - - - homepage.basedOn - Based on the original Part-DB by - - - - - Part-DB1\templates\homepage.html.twig:39 - Part-DB1\templates\homepage.html.twig:39 - templates\homepage.html.twig:36 - - - homepage.others - and others - - - + Part-DB1\templates\homepage.html.twig:45 Part-DB1\templates\homepage.html.twig:45 @@ -1518,7 +1488,7 @@ Sub elements will be moved upwards. Last activity - + Part-DB1\templates\LabelSystem\dialog.html.twig:3 Part-DB1\templates\LabelSystem\dialog.html.twig:6 @@ -1528,16 +1498,16 @@ Sub elements will be moved upwards. Label generator - + Part-DB1\templates\LabelSystem\dialog.html.twig:16 - + label_generator.common Common - + Part-DB1\templates\LabelSystem\dialog.html.twig:20 @@ -1546,7 +1516,7 @@ Sub elements will be moved upwards. Advanced - + Part-DB1\templates\LabelSystem\dialog.html.twig:24 @@ -1555,7 +1525,7 @@ Sub elements will be moved upwards. Profiles - + Part-DB1\templates\LabelSystem\dialog.html.twig:58 @@ -1564,7 +1534,7 @@ Sub elements will be moved upwards. Currently selected profile - + Part-DB1\templates\LabelSystem\dialog.html.twig:62 @@ -1573,7 +1543,7 @@ Sub elements will be moved upwards. Edit profile - + Part-DB1\templates\LabelSystem\dialog.html.twig:75 @@ -1582,7 +1552,7 @@ Sub elements will be moved upwards. Load profile - + Part-DB1\templates\LabelSystem\dialog.html.twig:102 @@ -1591,7 +1561,7 @@ Sub elements will be moved upwards. Download - + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 @@ -1601,7 +1571,7 @@ Sub elements will be moved upwards. Generate label - + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 @@ -1610,7 +1580,7 @@ Sub elements will be moved upwards. New empty label - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 @@ -1619,7 +1589,7 @@ Sub elements will be moved upwards. Label scanner - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 @@ -1628,7 +1598,7 @@ Sub elements will be moved upwards. No webcam found - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 @@ -1637,7 +1607,7 @@ Sub elements will be moved upwards. You need a webcam and give permission to use the scanner function. You can input the barcode code manually below. - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 @@ -1646,7 +1616,7 @@ Sub elements will be moved upwards. Select source - + Part-DB1\templates\LogSystem\log_list.html.twig:3 Part-DB1\templates\LogSystem\log_list.html.twig:3 @@ -1656,7 +1626,7 @@ Sub elements will be moved upwards. System log - + Part-DB1\templates\LogSystem\_log_table.html.twig:1 Part-DB1\templates\LogSystem\_log_table.html.twig:1 @@ -1667,7 +1637,7 @@ Sub elements will be moved upwards. Really undo change / revert to timestamp? - + Part-DB1\templates\LogSystem\_log_table.html.twig:2 Part-DB1\templates\LogSystem\_log_table.html.twig:2 @@ -1678,7 +1648,7 @@ Sub elements will be moved upwards. Do you really want to undo the given change / reset the element to the given timestamp? - + Part-DB1\templates\mail\base.html.twig:24 Part-DB1\templates\mail\base.html.twig:24 @@ -1688,7 +1658,7 @@ Sub elements will be moved upwards. This email was sent automatically by - + Part-DB1\templates\mail\base.html.twig:24 Part-DB1\templates\mail\base.html.twig:24 @@ -1698,7 +1668,7 @@ Sub elements will be moved upwards. Do not answer to this email. - + Part-DB1\templates\mail\pw_reset.html.twig:6 Part-DB1\templates\mail\pw_reset.html.twig:6 @@ -1708,7 +1678,7 @@ Sub elements will be moved upwards. Hi %name% - + Part-DB1\templates\mail\pw_reset.html.twig:7 Part-DB1\templates\mail\pw_reset.html.twig:7 @@ -1718,7 +1688,7 @@ Sub elements will be moved upwards. somebody (hopefully you) requested a reset of your password. If this request was not made by you, ignore this mail. - + Part-DB1\templates\mail\pw_reset.html.twig:9 Part-DB1\templates\mail\pw_reset.html.twig:9 @@ -1728,7 +1698,7 @@ Sub elements will be moved upwards. Click here to reset password - + Part-DB1\templates\mail\pw_reset.html.twig:11 Part-DB1\templates\mail\pw_reset.html.twig:11 @@ -1738,7 +1708,7 @@ Sub elements will be moved upwards. If this does not work for you, go to <a href="%url%">%url%</a> and enter the following info - + Part-DB1\templates\mail\pw_reset.html.twig:16 Part-DB1\templates\mail\pw_reset.html.twig:16 @@ -1748,7 +1718,7 @@ Sub elements will be moved upwards. Username - + Part-DB1\templates\mail\pw_reset.html.twig:19 Part-DB1\templates\mail\pw_reset.html.twig:19 @@ -1758,7 +1728,7 @@ Sub elements will be moved upwards. Token - + Part-DB1\templates\mail\pw_reset.html.twig:24 Part-DB1\templates\mail\pw_reset.html.twig:24 @@ -1768,7 +1738,7 @@ Sub elements will be moved upwards. The reset token will be valid until <i>%date%</i>. - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 @@ -1780,7 +1750,7 @@ Sub elements will be moved upwards. Delete - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 @@ -1790,7 +1760,7 @@ Sub elements will be moved upwards. Minimum discount quantity - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 @@ -1800,7 +1770,7 @@ Sub elements will be moved upwards. Price - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 @@ -1810,7 +1780,7 @@ Sub elements will be moved upwards. for amount - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 @@ -1820,7 +1790,7 @@ Sub elements will be moved upwards. Add price - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 @@ -1831,7 +1801,7 @@ Sub elements will be moved upwards. Edit part - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 @@ -1842,7 +1812,7 @@ Sub elements will be moved upwards. Edit part - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 @@ -1852,7 +1822,7 @@ Sub elements will be moved upwards. Common - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 @@ -1862,7 +1832,7 @@ Sub elements will be moved upwards. Manufacturer - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 @@ -1872,7 +1842,7 @@ Sub elements will be moved upwards. Advanced - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 @@ -1882,7 +1852,7 @@ Sub elements will be moved upwards. Stocks - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 @@ -1892,17 +1862,17 @@ Sub elements will be moved upwards. Attachments - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 part.edit.tab.orderdetails - Purchase informations + Purchase information - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 @@ -1911,7 +1881,7 @@ Sub elements will be moved upwards. Parameters - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 @@ -1921,7 +1891,7 @@ Sub elements will be moved upwards. Notes - + Part-DB1\templates\Parts\edit\new_part.html.twig:8 Part-DB1\templates\Parts\edit\new_part.html.twig:8 @@ -1932,7 +1902,7 @@ Sub elements will be moved upwards. Create new part - + Part-DB1\templates\Parts\edit\_lots.html.twig:5 Part-DB1\templates\Parts\edit\_lots.html.twig:5 @@ -1942,7 +1912,7 @@ Sub elements will be moved upwards. Delete - + Part-DB1\templates\Parts\edit\_lots.html.twig:28 Part-DB1\templates\Parts\edit\_lots.html.twig:28 @@ -1952,7 +1922,7 @@ Sub elements will be moved upwards. Add stock - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 @@ -1962,7 +1932,7 @@ Sub elements will be moved upwards. Add distributor - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 @@ -1972,7 +1942,7 @@ Sub elements will be moved upwards. Do you really want to delete this price? This can not be undone. - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 @@ -1982,7 +1952,7 @@ Sub elements will be moved upwards. Do you really want to delete this distributor info? This can not be undone! - + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 Part-DB1\templates\Parts\info\show_part_info.html.twig:19 @@ -1996,7 +1966,7 @@ Sub elements will be moved upwards. Detail info for part - + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 Part-DB1\templates\Parts\info\show_part_info.html.twig:47 @@ -2006,7 +1976,7 @@ Sub elements will be moved upwards. Stocks - + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 Part-DB1\templates\Parts\lists\_info_card.html.twig:43 @@ -2021,7 +1991,7 @@ Sub elements will be moved upwards. Notes - + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 @@ -2030,7 +2000,7 @@ Sub elements will be moved upwards. Parameters - + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 Part-DB1\templates\Parts\info\show_part_info.html.twig:64 @@ -2041,7 +2011,7 @@ Sub elements will be moved upwards. Attachments - + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 Part-DB1\templates\Parts\info\show_part_info.html.twig:71 @@ -2049,10 +2019,10 @@ Sub elements will be moved upwards. vendor.partinfo.shopping_infos - Shopping informations + Shopping information - + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 Part-DB1\templates\Parts\info\show_part_info.html.twig:78 @@ -2063,7 +2033,7 @@ Sub elements will be moved upwards. History - + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 Part-DB1\templates\_sidebar.html.twig:54 @@ -2082,17 +2052,17 @@ Sub elements will be moved upwards. Tools - + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 Part-DB1\templates\Parts\info\show_part_info.html.twig:90 extended_info.label - Extended infos + Extended info - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 @@ -2102,7 +2072,7 @@ Sub elements will be moved upwards. Name - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 @@ -2112,7 +2082,7 @@ Sub elements will be moved upwards. Attachment Type - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 @@ -2122,7 +2092,7 @@ Sub elements will be moved upwards. File name - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 @@ -2132,7 +2102,7 @@ Sub elements will be moved upwards. File size - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 @@ -2141,17 +2111,17 @@ Sub elements will be moved upwards. Preview picture - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 - attachment.download - Download + attachment.download_local + Download Local Copy - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 @@ -2162,7 +2132,7 @@ Sub elements will be moved upwards. User who created this part - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 @@ -2176,7 +2146,7 @@ Sub elements will be moved upwards. Unknown - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 @@ -2189,7 +2159,7 @@ Sub elements will be moved upwards. Access Denied - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 @@ -2200,7 +2170,7 @@ Sub elements will be moved upwards. User who edited this part last - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 @@ -2210,7 +2180,7 @@ Sub elements will be moved upwards. Favorite - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 @@ -2220,7 +2190,7 @@ Sub elements will be moved upwards. Minimum order amount - + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 Part-DB1\templates\_navbar_search.html.twig:46 @@ -2237,7 +2207,7 @@ Sub elements will be moved upwards. Manufacturer - + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 Part-DB1\templates\_navbar_search.html.twig:11 @@ -2249,7 +2219,7 @@ Sub elements will be moved upwards. Name - + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 Part-DB1\templates\Parts\info\_main_infos.html.twig:27 @@ -2260,7 +2230,7 @@ Sub elements will be moved upwards. Back to current version - + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 Part-DB1\templates\_navbar_search.html.twig:19 @@ -2275,7 +2245,7 @@ Sub elements will be moved upwards. Description - + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 Part-DB1\templates\_navbar_search.html.twig:15 @@ -2292,7 +2262,7 @@ Sub elements will be moved upwards. Category - + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 Part-DB1\templates\Parts\info\_main_infos.html.twig:39 @@ -2304,7 +2274,7 @@ Sub elements will be moved upwards. Instock - + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 Part-DB1\templates\Parts\info\_main_infos.html.twig:41 @@ -2316,7 +2286,7 @@ Sub elements will be moved upwards. Minimum Instock - + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 Part-DB1\templates\_navbar_search.html.twig:52 @@ -2332,7 +2302,7 @@ Sub elements will be moved upwards. Footprint - + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 Part-DB1\templates\Parts\info\_main_infos.html.twig:59 @@ -2345,7 +2315,7 @@ Sub elements will be moved upwards. Average Price - + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 Part-DB1\templates\Parts\info\_order_infos.html.twig:5 @@ -2355,7 +2325,7 @@ Sub elements will be moved upwards. Name - + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 Part-DB1\templates\Parts\info\_order_infos.html.twig:6 @@ -2365,7 +2335,7 @@ Sub elements will be moved upwards. Partnr. - + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 Part-DB1\templates\Parts\info\_order_infos.html.twig:28 @@ -2375,7 +2345,7 @@ Sub elements will be moved upwards. Minimum amount - + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 Part-DB1\templates\Parts\info\_order_infos.html.twig:29 @@ -2385,7 +2355,7 @@ Sub elements will be moved upwards. Price - + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 Part-DB1\templates\Parts\info\_order_infos.html.twig:31 @@ -2395,7 +2365,7 @@ Sub elements will be moved upwards. Unit Price - + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 Part-DB1\templates\Parts\info\_order_infos.html.twig:71 @@ -2405,7 +2375,7 @@ Sub elements will be moved upwards. Edit - + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 Part-DB1\templates\Parts\info\_order_infos.html.twig:72 @@ -2415,7 +2385,7 @@ Sub elements will be moved upwards. Delete - + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 Part-DB1\templates\Parts\info\_part_lots.html.twig:6 @@ -2425,7 +2395,7 @@ Sub elements will be moved upwards. Description - + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 Part-DB1\templates\Parts\info\_part_lots.html.twig:7 @@ -2435,7 +2405,7 @@ Sub elements will be moved upwards. Storage location - + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 Part-DB1\templates\Parts\info\_part_lots.html.twig:8 @@ -2445,7 +2415,7 @@ Sub elements will be moved upwards. Amount - + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 Part-DB1\templates\Parts\info\_part_lots.html.twig:22 @@ -2455,7 +2425,7 @@ Sub elements will be moved upwards. Storage location unknown - + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 Part-DB1\templates\Parts\info\_part_lots.html.twig:29 @@ -2465,7 +2435,7 @@ Sub elements will be moved upwards. Amount unknown - + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 Part-DB1\templates\Parts\info\_part_lots.html.twig:38 @@ -2475,7 +2445,7 @@ Sub elements will be moved upwards. Expiration date - + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 Part-DB1\templates\Parts\info\_part_lots.html.twig:46 @@ -2485,7 +2455,7 @@ Sub elements will be moved upwards. Expired - + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 Part-DB1\templates\Parts\info\_part_lots.html.twig:53 @@ -2495,7 +2465,7 @@ Sub elements will be moved upwards. Needs refill - + Part-DB1\templates\Parts\info\_picture.html.twig:15 Part-DB1\templates\Parts\info\_picture.html.twig:15 @@ -2505,7 +2475,7 @@ Sub elements will be moved upwards. Previous picture - + Part-DB1\templates\Parts\info\_picture.html.twig:19 Part-DB1\templates\Parts\info\_picture.html.twig:19 @@ -2515,7 +2485,7 @@ Sub elements will be moved upwards. Next picture - + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 Part-DB1\templates\Parts\info\_sidebar.html.twig:21 @@ -2525,7 +2495,7 @@ Sub elements will be moved upwards. Mass - + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 Part-DB1\templates\Parts\info\_sidebar.html.twig:30 @@ -2535,7 +2505,7 @@ Sub elements will be moved upwards. Needs review - + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 Part-DB1\templates\Parts\info\_sidebar.html.twig:39 @@ -2545,7 +2515,7 @@ Sub elements will be moved upwards. Favorite - + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 Part-DB1\templates\Parts\info\_sidebar.html.twig:47 @@ -2555,7 +2525,7 @@ Sub elements will be moved upwards. No longer available - + Part-DB1\templates\Parts\info\_specifications.html.twig:10 @@ -2564,7 +2534,7 @@ Sub elements will be moved upwards. Automatically extracted from description - + Part-DB1\templates\Parts\info\_specifications.html.twig:15 @@ -2573,7 +2543,7 @@ Sub elements will be moved upwards. Automatically extracted from notes - + Part-DB1\templates\Parts\info\_tools.html.twig:6 Part-DB1\templates\Parts\info\_tools.html.twig:4 @@ -2584,7 +2554,7 @@ Sub elements will be moved upwards. Edit part - + Part-DB1\templates\Parts\info\_tools.html.twig:16 Part-DB1\templates\Parts\info\_tools.html.twig:14 @@ -2595,7 +2565,7 @@ Sub elements will be moved upwards. Clone part - + Part-DB1\templates\Parts\info\_tools.html.twig:24 Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 @@ -2606,7 +2576,7 @@ Sub elements will be moved upwards. Create new part - + Part-DB1\templates\Parts\info\_tools.html.twig:31 Part-DB1\templates\Parts\info\_tools.html.twig:29 @@ -2616,17 +2586,17 @@ Sub elements will be moved upwards. Do you really want to delete this part? - + Part-DB1\templates\Parts\info\_tools.html.twig:32 Part-DB1\templates\Parts\info\_tools.html.twig:30 part.delete.message - This part and any associated information (like attachments, price informations, etc.) will be deleted. This can not be undone! + This part and any associated information (like attachments, price information, etc.) will be deleted. This can not be undone! - + Part-DB1\templates\Parts\info\_tools.html.twig:39 Part-DB1\templates\Parts\info\_tools.html.twig:37 @@ -2636,7 +2606,7 @@ Sub elements will be moved upwards. Delete part - + Part-DB1\templates\Parts\lists\all_list.html.twig:4 Part-DB1\templates\Parts\lists\all_list.html.twig:4 @@ -2646,7 +2616,7 @@ Sub elements will be moved upwards. All parts - + Part-DB1\templates\Parts\lists\category_list.html.twig:4 Part-DB1\templates\Parts\lists\category_list.html.twig:4 @@ -2656,7 +2626,7 @@ Sub elements will be moved upwards. Parts with category - + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 @@ -2666,7 +2636,7 @@ Sub elements will be moved upwards. Parts with footprint - + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 @@ -2676,7 +2646,7 @@ Sub elements will be moved upwards. Parts with manufacturer - + Part-DB1\templates\Parts\lists\search_list.html.twig:4 Part-DB1\templates\Parts\lists\search_list.html.twig:4 @@ -2686,17 +2656,17 @@ Sub elements will be moved upwards. Search Parts - + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 parts_list.storelocation.title - Parts with store locations + Parts with storage locations - + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 @@ -2706,7 +2676,7 @@ Sub elements will be moved upwards. Parts with supplier - + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 Part-DB1\templates\Parts\lists\tags_list.html.twig:4 @@ -2716,7 +2686,7 @@ Sub elements will be moved upwards. Parts with tag - + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 Part-DB1\templates\Parts\lists\_info_card.html.twig:17 @@ -2726,7 +2696,7 @@ Sub elements will be moved upwards. Common - + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 Part-DB1\templates\Parts\lists\_info_card.html.twig:20 @@ -2736,7 +2706,7 @@ Sub elements will be moved upwards. Statistics - + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 @@ -2745,7 +2715,7 @@ Sub elements will be moved upwards. Attachments - + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 @@ -2754,7 +2724,7 @@ Sub elements will be moved upwards. Parameters - + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 Part-DB1\templates\Parts\lists\_info_card.html.twig:30 @@ -2764,7 +2734,7 @@ Sub elements will be moved upwards. Name - + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 Part-DB1\templates\Parts\lists\_info_card.html.twig:96 @@ -2776,7 +2746,7 @@ Sub elements will be moved upwards. Parent - + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 Part-DB1\templates\Parts\lists\_info_card.html.twig:46 @@ -2786,7 +2756,7 @@ Sub elements will be moved upwards. Edit - + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 Part-DB1\templates\Parts\lists\_info_card.html.twig:63 @@ -2796,7 +2766,7 @@ Sub elements will be moved upwards. Count of children elements - + Part-DB1\templates\security\2fa_base_form.html.twig:3 Part-DB1\templates\security\2fa_base_form.html.twig:5 @@ -2808,7 +2778,7 @@ Sub elements will be moved upwards. Two-factor authentication needed - + Part-DB1\templates\security\2fa_base_form.html.twig:39 Part-DB1\templates\security\2fa_base_form.html.twig:39 @@ -2818,7 +2788,7 @@ Sub elements will be moved upwards. This is a trusted computer (if this is enabled, no further two-factor queries are performed on this computer) - + Part-DB1\templates\security\2fa_base_form.html.twig:52 Part-DB1\templates\security\login.html.twig:58 @@ -2830,7 +2800,7 @@ Sub elements will be moved upwards. Login - + Part-DB1\templates\security\2fa_base_form.html.twig:53 Part-DB1\templates\security\U2F\u2f_login.html.twig:13 @@ -2844,7 +2814,7 @@ Sub elements will be moved upwards. Logout - + Part-DB1\templates\security\2fa_form.html.twig:6 Part-DB1\templates\security\2fa_form.html.twig:6 @@ -2854,17 +2824,17 @@ Sub elements will be moved upwards. Authenticator app code - + Part-DB1\templates\security\2fa_form.html.twig:10 Part-DB1\templates\security\2fa_form.html.twig:10 tfa.check.code.help - Enter the 6-digit code from your Authenticator App or one of your backup codes if the Authenticator is not available. + Enter the 6-digit code from your Authenticator app or one of your backup codes if the Authenticator is not available. - + Part-DB1\templates\security\login.html.twig:3 Part-DB1\templates\security\login.html.twig:3 @@ -2875,7 +2845,7 @@ Sub elements will be moved upwards. Login - + Part-DB1\templates\security\login.html.twig:7 Part-DB1\templates\security\login.html.twig:7 @@ -2886,7 +2856,7 @@ Sub elements will be moved upwards. Login - + Part-DB1\templates\security\login.html.twig:31 Part-DB1\templates\security\login.html.twig:31 @@ -2897,7 +2867,7 @@ Sub elements will be moved upwards. Username - + Part-DB1\templates\security\login.html.twig:34 Part-DB1\templates\security\login.html.twig:34 @@ -2908,7 +2878,7 @@ Sub elements will be moved upwards. Username - + Part-DB1\templates\security\login.html.twig:38 Part-DB1\templates\security\login.html.twig:38 @@ -2919,7 +2889,7 @@ Sub elements will be moved upwards. Password - + Part-DB1\templates\security\login.html.twig:40 Part-DB1\templates\security\login.html.twig:40 @@ -2930,7 +2900,7 @@ Sub elements will be moved upwards. Password - + Part-DB1\templates\security\login.html.twig:50 Part-DB1\templates\security\login.html.twig:50 @@ -2941,7 +2911,7 @@ Sub elements will be moved upwards. Remember me (should not be used on shared computers) - + Part-DB1\templates\security\login.html.twig:64 Part-DB1\templates\security\login.html.twig:64 @@ -2951,7 +2921,7 @@ Sub elements will be moved upwards. Forgot username/password? - + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 @@ -2961,7 +2931,7 @@ Sub elements will be moved upwards. Set new password - + Part-DB1\templates\security\pw_reset_request.html.twig:5 Part-DB1\templates\security\pw_reset_request.html.twig:5 @@ -2971,7 +2941,7 @@ Sub elements will be moved upwards. Request a new password - + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 Part-DB1\templates\security\U2F\u2f_register.html.twig:10 @@ -2983,7 +2953,7 @@ Sub elements will be moved upwards. You are accessing this page using the insecure HTTP method, so U2F will most likely not work (Bad Request error message). Ask an administrator to set up the secure HTTPS method if you want to use security keys. - + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 Part-DB1\templates\security\U2F\u2f_register.html.twig:22 @@ -2995,7 +2965,7 @@ Sub elements will be moved upwards. Please plug in your security key and press its button! - + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 Part-DB1\templates\security\U2F\u2f_register.html.twig:3 @@ -3005,7 +2975,7 @@ Sub elements will be moved upwards. Add security key - + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 Part-DB1\templates\Users\_2fa_settings.html.twig:111 @@ -3017,7 +2987,7 @@ Sub elements will be moved upwards. With the help of a U2F/FIDO compatible security key (e.g. YubiKey or NitroKey), user-friendly and secure two-factor authentication can be achieved. The security keys can be registered here, and if two-factor verification is required, the key only needs to be inserted via USB or typed against the device via NFC. - + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 Part-DB1\templates\security\U2F\u2f_register.html.twig:7 @@ -3027,7 +2997,7 @@ Sub elements will be moved upwards. To ensure access even if the key is lost, it is recommended to register a second key as backup and store it in a safe place! - + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 Part-DB1\templates\security\U2F\u2f_register.html.twig:16 @@ -3037,7 +3007,7 @@ Sub elements will be moved upwards. Shown key name (e.g. Backup) - + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 Part-DB1\templates\security\U2F\u2f_register.html.twig:19 @@ -3047,7 +3017,7 @@ Sub elements will be moved upwards. Add security key - + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 Part-DB1\templates\security\U2F\u2f_register.html.twig:27 @@ -3057,7 +3027,7 @@ Sub elements will be moved upwards. Back to settings - + Part-DB1\templates\Statistics\statistics.html.twig:5 Part-DB1\templates\Statistics\statistics.html.twig:8 @@ -3070,7 +3040,7 @@ Sub elements will be moved upwards. Statistics - + Part-DB1\templates\Statistics\statistics.html.twig:14 Part-DB1\templates\Statistics\statistics.html.twig:14 @@ -3081,7 +3051,7 @@ Sub elements will be moved upwards. Parts - + Part-DB1\templates\Statistics\statistics.html.twig:19 Part-DB1\templates\Statistics\statistics.html.twig:19 @@ -3092,7 +3062,7 @@ Sub elements will be moved upwards. Data structures - + Part-DB1\templates\Statistics\statistics.html.twig:24 Part-DB1\templates\Statistics\statistics.html.twig:24 @@ -3103,7 +3073,7 @@ Sub elements will be moved upwards. Attachments - + Part-DB1\templates\Statistics\statistics.html.twig:34 Part-DB1\templates\Statistics\statistics.html.twig:59 @@ -3118,7 +3088,7 @@ Sub elements will be moved upwards. Property - + Part-DB1\templates\Statistics\statistics.html.twig:35 Part-DB1\templates\Statistics\statistics.html.twig:60 @@ -3133,7 +3103,7 @@ Sub elements will be moved upwards. Value - + Part-DB1\templates\Statistics\statistics.html.twig:40 Part-DB1\templates\Statistics\statistics.html.twig:40 @@ -3144,7 +3114,7 @@ Sub elements will be moved upwards. Number of distinct parts - + Part-DB1\templates\Statistics\statistics.html.twig:44 Part-DB1\templates\Statistics\statistics.html.twig:44 @@ -3152,10 +3122,10 @@ Sub elements will be moved upwards. statistics.parts_instock_sum - Sum of all part instocks + Sum of all parts stocks - + Part-DB1\templates\Statistics\statistics.html.twig:48 Part-DB1\templates\Statistics\statistics.html.twig:48 @@ -3166,7 +3136,7 @@ Sub elements will be moved upwards. Number of parts with price information - + Part-DB1\templates\Statistics\statistics.html.twig:65 Part-DB1\templates\Statistics\statistics.html.twig:65 @@ -3177,7 +3147,7 @@ Sub elements will be moved upwards. Number of categories - + Part-DB1\templates\Statistics\statistics.html.twig:69 Part-DB1\templates\Statistics\statistics.html.twig:69 @@ -3188,7 +3158,7 @@ Sub elements will be moved upwards. Number of footprints - + Part-DB1\templates\Statistics\statistics.html.twig:73 Part-DB1\templates\Statistics\statistics.html.twig:73 @@ -3199,7 +3169,7 @@ Sub elements will be moved upwards. Number of manufacturers - + Part-DB1\templates\Statistics\statistics.html.twig:77 Part-DB1\templates\Statistics\statistics.html.twig:77 @@ -3207,10 +3177,10 @@ Sub elements will be moved upwards. statistics.storelocations_count - Number of storelocations + Number of storage locations - + Part-DB1\templates\Statistics\statistics.html.twig:81 Part-DB1\templates\Statistics\statistics.html.twig:81 @@ -3221,7 +3191,7 @@ Sub elements will be moved upwards. Number of suppliers - + Part-DB1\templates\Statistics\statistics.html.twig:85 Part-DB1\templates\Statistics\statistics.html.twig:85 @@ -3232,7 +3202,7 @@ Sub elements will be moved upwards. Number of currencies - + Part-DB1\templates\Statistics\statistics.html.twig:89 Part-DB1\templates\Statistics\statistics.html.twig:89 @@ -3243,7 +3213,7 @@ Sub elements will be moved upwards. Number of measurement units - + Part-DB1\templates\Statistics\statistics.html.twig:93 Part-DB1\templates\Statistics\statistics.html.twig:93 @@ -3254,7 +3224,7 @@ Sub elements will be moved upwards. Number of projects - + Part-DB1\templates\Statistics\statistics.html.twig:110 Part-DB1\templates\Statistics\statistics.html.twig:110 @@ -3265,7 +3235,7 @@ Sub elements will be moved upwards. Number of attachment types - + Part-DB1\templates\Statistics\statistics.html.twig:114 Part-DB1\templates\Statistics\statistics.html.twig:114 @@ -3276,7 +3246,7 @@ Sub elements will be moved upwards. Number of all attachments - + Part-DB1\templates\Statistics\statistics.html.twig:118 Part-DB1\templates\Statistics\statistics.html.twig:118 @@ -3287,7 +3257,7 @@ Sub elements will be moved upwards. Number of user uploaded attachments - + Part-DB1\templates\Statistics\statistics.html.twig:122 Part-DB1\templates\Statistics\statistics.html.twig:122 @@ -3298,7 +3268,7 @@ Sub elements will be moved upwards. Number of private attachments - + Part-DB1\templates\Statistics\statistics.html.twig:126 Part-DB1\templates\Statistics\statistics.html.twig:126 @@ -3309,7 +3279,7 @@ Sub elements will be moved upwards. Number of external attachments (URL) - + Part-DB1\templates\Users\backup_codes.html.twig:3 Part-DB1\templates\Users\backup_codes.html.twig:9 @@ -3321,7 +3291,7 @@ Sub elements will be moved upwards. Backup codes - + Part-DB1\templates\Users\backup_codes.html.twig:12 Part-DB1\templates\Users\backup_codes.html.twig:12 @@ -3331,7 +3301,7 @@ Sub elements will be moved upwards. Print out these codes and keep them in a safe place! - + Part-DB1\templates\Users\backup_codes.html.twig:13 Part-DB1\templates\Users\backup_codes.html.twig:13 @@ -3341,7 +3311,7 @@ Sub elements will be moved upwards. If you no longer have access to your device with the Authenticator App (lost smartphone, data loss, etc.) you can use one of these codes to access your account and possibly set up a new Authenticator App. Each of these codes can be used once, it is recommended to delete used codes. Anyone with access to these codes can potentially access your account, so keep them in a safe place. - + Part-DB1\templates\Users\backup_codes.html.twig:16 Part-DB1\templates\Users\backup_codes.html.twig:16 @@ -3351,7 +3321,7 @@ Sub elements will be moved upwards. Username - + Part-DB1\templates\Users\backup_codes.html.twig:29 Part-DB1\templates\Users\backup_codes.html.twig:29 @@ -3361,7 +3331,7 @@ Sub elements will be moved upwards. Page generated on %date% - + Part-DB1\templates\Users\backup_codes.html.twig:32 Part-DB1\templates\Users\backup_codes.html.twig:32 @@ -3371,7 +3341,7 @@ Sub elements will be moved upwards. Print - + Part-DB1\templates\Users\backup_codes.html.twig:35 Part-DB1\templates\Users\backup_codes.html.twig:35 @@ -3381,7 +3351,7 @@ Sub elements will be moved upwards. Copy to clipboard - + Part-DB1\templates\Users\user_info.html.twig:3 Part-DB1\templates\Users\user_info.html.twig:6 @@ -3395,10 +3365,10 @@ Sub elements will be moved upwards. user.info.label - User informations + User information - + Part-DB1\templates\Users\user_info.html.twig:18 Part-DB1\src\Form\UserSettingsType.php:77 @@ -3412,7 +3382,7 @@ Sub elements will be moved upwards. First name - + Part-DB1\templates\Users\user_info.html.twig:24 Part-DB1\src\Form\UserSettingsType.php:82 @@ -3426,7 +3396,7 @@ Sub elements will be moved upwards. Last name - + Part-DB1\templates\Users\user_info.html.twig:30 Part-DB1\src\Form\UserSettingsType.php:92 @@ -3440,7 +3410,7 @@ Sub elements will be moved upwards. Email - + Part-DB1\templates\Users\user_info.html.twig:37 Part-DB1\src\Form\UserSettingsType.php:87 @@ -3454,7 +3424,7 @@ Sub elements will be moved upwards. Department - + Part-DB1\templates\Users\user_info.html.twig:47 Part-DB1\src\Form\UserSettingsType.php:73 @@ -3465,10 +3435,10 @@ Sub elements will be moved upwards. user.username.label - User name + Username - + Part-DB1\templates\Users\user_info.html.twig:53 Part-DB1\src\Services\ElementTypeNameGenerator.php:93 @@ -3481,7 +3451,7 @@ Sub elements will be moved upwards. Group: - + Part-DB1\templates\Users\user_info.html.twig:67 Part-DB1\templates\Users\user_info.html.twig:67 @@ -3491,7 +3461,7 @@ Sub elements will be moved upwards. Permissions - + Part-DB1\templates\Users\user_settings.html.twig:3 Part-DB1\templates\Users\user_settings.html.twig:6 @@ -3508,7 +3478,7 @@ Sub elements will be moved upwards. User settings - + Part-DB1\templates\Users\user_settings.html.twig:18 Part-DB1\templates\Users\user_settings.html.twig:18 @@ -3519,7 +3489,7 @@ Sub elements will be moved upwards. Personal data - + Part-DB1\templates\Users\user_settings.html.twig:22 Part-DB1\templates\Users\user_settings.html.twig:22 @@ -3530,7 +3500,7 @@ Sub elements will be moved upwards. Configuration - + Part-DB1\templates\Users\user_settings.html.twig:55 Part-DB1\templates\Users\user_settings.html.twig:55 @@ -3541,7 +3511,7 @@ Sub elements will be moved upwards. Change password - + Part-DB1\templates\Users\_2fa_settings.html.twig:6 Part-DB1\templates\Users\_2fa_settings.html.twig:6 @@ -3551,7 +3521,7 @@ Sub elements will be moved upwards. Two-Factor Authentication - + Part-DB1\templates\Users\_2fa_settings.html.twig:13 Part-DB1\templates\Users\_2fa_settings.html.twig:13 @@ -3561,7 +3531,7 @@ Sub elements will be moved upwards. Authenticator app - + Part-DB1\templates\Users\_2fa_settings.html.twig:17 Part-DB1\templates\Users\_2fa_settings.html.twig:17 @@ -3571,7 +3541,7 @@ Sub elements will be moved upwards. Backup codes - + Part-DB1\templates\Users\_2fa_settings.html.twig:21 Part-DB1\templates\Users\_2fa_settings.html.twig:21 @@ -3581,7 +3551,7 @@ Sub elements will be moved upwards. Security keys (U2F) - + Part-DB1\templates\Users\_2fa_settings.html.twig:25 Part-DB1\templates\Users\_2fa_settings.html.twig:25 @@ -3591,7 +3561,7 @@ Sub elements will be moved upwards. Trusted devices - + Part-DB1\templates\Users\_2fa_settings.html.twig:33 Part-DB1\templates\Users\_2fa_settings.html.twig:33 @@ -3601,7 +3571,7 @@ Sub elements will be moved upwards. Do you really want to disable the Authenticator App? - + Part-DB1\templates\Users\_2fa_settings.html.twig:33 Part-DB1\templates\Users\_2fa_settings.html.twig:33 @@ -3609,10 +3579,10 @@ Sub elements will be moved upwards. tfa_google.disable.confirm_message If you disable the Authenticator App, all backup codes will be deleted, so you may need to reprint them.<br> -Also note that without two-factor authentication your account is not as well protected against attackers! +Also note that without two-factor authentication, your account is no longer as well protected against attackers! - + Part-DB1\templates\Users\_2fa_settings.html.twig:39 Part-DB1\templates\Users\_2fa_settings.html.twig:39 @@ -3622,7 +3592,7 @@ Also note that without two-factor authentication your account is not as well pro Authenticator app deactivated! - + Part-DB1\templates\Users\_2fa_settings.html.twig:48 Part-DB1\templates\Users\_2fa_settings.html.twig:48 @@ -3632,17 +3602,17 @@ Also note that without two-factor authentication your account is not as well pro Download an authenticator app (e.g. <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> oder <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp">FreeOTP Authenticator</a>) - + Part-DB1\templates\Users\_2fa_settings.html.twig:49 Part-DB1\templates\Users\_2fa_settings.html.twig:49 tfa_google.step.scan - Scan the adjoining QR Code with the app or enter the data manually + Scan the adjacent QR Code with the app or enter the data manually - + Part-DB1\templates\Users\_2fa_settings.html.twig:50 Part-DB1\templates\Users\_2fa_settings.html.twig:50 @@ -3652,7 +3622,7 @@ Also note that without two-factor authentication your account is not as well pro Enter the generated code in the field below and confirm - + Part-DB1\templates\Users\_2fa_settings.html.twig:51 Part-DB1\templates\Users\_2fa_settings.html.twig:51 @@ -3662,7 +3632,7 @@ Also note that without two-factor authentication your account is not as well pro Print out your backup codes and store them in a safe place - + Part-DB1\templates\Users\_2fa_settings.html.twig:58 Part-DB1\templates\Users\_2fa_settings.html.twig:58 @@ -3672,7 +3642,7 @@ Also note that without two-factor authentication your account is not as well pro Manual setup - + Part-DB1\templates\Users\_2fa_settings.html.twig:62 Part-DB1\templates\Users\_2fa_settings.html.twig:62 @@ -3682,7 +3652,7 @@ Also note that without two-factor authentication your account is not as well pro Type - + Part-DB1\templates\Users\_2fa_settings.html.twig:63 Part-DB1\templates\Users\_2fa_settings.html.twig:63 @@ -3692,7 +3662,7 @@ Also note that without two-factor authentication your account is not as well pro Username - + Part-DB1\templates\Users\_2fa_settings.html.twig:64 Part-DB1\templates\Users\_2fa_settings.html.twig:64 @@ -3702,7 +3672,7 @@ Also note that without two-factor authentication your account is not as well pro Secret - + Part-DB1\templates\Users\_2fa_settings.html.twig:65 Part-DB1\templates\Users\_2fa_settings.html.twig:65 @@ -3712,7 +3682,7 @@ Also note that without two-factor authentication your account is not as well pro Digit count - + Part-DB1\templates\Users\_2fa_settings.html.twig:74 Part-DB1\templates\Users\_2fa_settings.html.twig:74 @@ -3722,7 +3692,7 @@ Also note that without two-factor authentication your account is not as well pro Authenticator App enabled - + Part-DB1\templates\Users\_2fa_settings.html.twig:83 Part-DB1\templates\Users\_2fa_settings.html.twig:83 @@ -3732,7 +3702,7 @@ Also note that without two-factor authentication your account is not as well pro Backup codes disabled. Setup authenticator app to enable backup codes. - + Part-DB1\templates\Users\_2fa_settings.html.twig:84 Part-DB1\templates\Users\_2fa_settings.html.twig:92 @@ -3744,7 +3714,7 @@ Also note that without two-factor authentication your account is not as well pro You can use these backup codes to access your account even if you lose the device with the Authenticator App. Print out the codes and keep them in a safe place. - + Part-DB1\templates\Users\_2fa_settings.html.twig:88 Part-DB1\templates\Users\_2fa_settings.html.twig:88 @@ -3754,7 +3724,7 @@ Also note that without two-factor authentication your account is not as well pro Really reset codes? - + Part-DB1\templates\Users\_2fa_settings.html.twig:88 Part-DB1\templates\Users\_2fa_settings.html.twig:88 @@ -3764,7 +3734,7 @@ Also note that without two-factor authentication your account is not as well pro This will delete all previous codes and generate a set of new codes. This cannot be undone. Remember to print out the new codes and store them in a safe place! - + Part-DB1\templates\Users\_2fa_settings.html.twig:91 Part-DB1\templates\Users\_2fa_settings.html.twig:91 @@ -3774,7 +3744,7 @@ Also note that without two-factor authentication your account is not as well pro Backup codes enabled - + Part-DB1\templates\Users\_2fa_settings.html.twig:99 Part-DB1\templates\Users\_2fa_settings.html.twig:99 @@ -3784,7 +3754,7 @@ Also note that without two-factor authentication your account is not as well pro Show backup codes - + Part-DB1\templates\Users\_2fa_settings.html.twig:114 Part-DB1\templates\Users\_2fa_settings.html.twig:114 @@ -3794,7 +3764,7 @@ Also note that without two-factor authentication your account is not as well pro Registered security keys - + Part-DB1\templates\Users\_2fa_settings.html.twig:115 Part-DB1\templates\Users\_2fa_settings.html.twig:115 @@ -3804,7 +3774,7 @@ Also note that without two-factor authentication your account is not as well pro Really remove this security key? - + Part-DB1\templates\Users\_2fa_settings.html.twig:116 Part-DB1\templates\Users\_2fa_settings.html.twig:116 @@ -3814,7 +3784,7 @@ Also note that without two-factor authentication your account is not as well pro If you remove this key, then no more login with this key will be possible. If no security keys remain, two-factor authentication will be disabled. - + Part-DB1\templates\Users\_2fa_settings.html.twig:123 Part-DB1\templates\Users\_2fa_settings.html.twig:123 @@ -3824,7 +3794,7 @@ Also note that without two-factor authentication your account is not as well pro Key name - + Part-DB1\templates\Users\_2fa_settings.html.twig:124 Part-DB1\templates\Users\_2fa_settings.html.twig:124 @@ -3834,7 +3804,7 @@ Also note that without two-factor authentication your account is not as well pro Registration date - + Part-DB1\templates\Users\_2fa_settings.html.twig:134 Part-DB1\templates\Users\_2fa_settings.html.twig:134 @@ -3844,7 +3814,7 @@ Also note that without two-factor authentication your account is not as well pro Delete key - + Part-DB1\templates\Users\_2fa_settings.html.twig:141 Part-DB1\templates\Users\_2fa_settings.html.twig:141 @@ -3854,7 +3824,7 @@ Also note that without two-factor authentication your account is not as well pro No keys registered yet. - + Part-DB1\templates\Users\_2fa_settings.html.twig:144 Part-DB1\templates\Users\_2fa_settings.html.twig:144 @@ -3864,7 +3834,7 @@ Also note that without two-factor authentication your account is not as well pro Register new security key - + Part-DB1\templates\Users\_2fa_settings.html.twig:148 Part-DB1\templates\Users\_2fa_settings.html.twig:148 @@ -3875,7 +3845,7 @@ Also note that without two-factor authentication your account is not as well pro If you have done this incorrectly or if a computer is no longer trusted, you can reset the status of <i>all </i>computers here. - + Part-DB1\templates\Users\_2fa_settings.html.twig:149 Part-DB1\templates\Users\_2fa_settings.html.twig:149 @@ -3885,7 +3855,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Really remove all trusted computers? - + Part-DB1\templates\Users\_2fa_settings.html.twig:150 Part-DB1\templates\Users\_2fa_settings.html.twig:150 @@ -3895,7 +3865,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can You will have to perform two-factor authentication again on all computers. Make sure you have your two-factor device at hand. - + Part-DB1\templates\Users\_2fa_settings.html.twig:154 Part-DB1\templates\Users\_2fa_settings.html.twig:154 @@ -3905,7 +3875,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Reset trusted devices - + Part-DB1\templates\_navbar.html.twig:4 Part-DB1\templates\_navbar.html.twig:4 @@ -3916,7 +3886,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Toggle Sidebar - + Part-DB1\templates\_navbar.html.twig:22 @@ -3925,7 +3895,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Scanner - + Part-DB1\templates\_navbar.html.twig:38 Part-DB1\templates\_navbar.html.twig:36 @@ -3936,7 +3906,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Logged in as - + Part-DB1\templates\_navbar.html.twig:44 Part-DB1\templates\_navbar.html.twig:42 @@ -3947,7 +3917,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Login - + Part-DB1\templates\_navbar.html.twig:50 Part-DB1\templates\_navbar.html.twig:48 @@ -3957,7 +3927,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Darkmode - + Part-DB1\templates\_navbar.html.twig:54 Part-DB1\src\Form\UserSettingsType.php:97 @@ -3971,7 +3941,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Switch Language - + Part-DB1\templates\_navbar_search.html.twig:4 Part-DB1\templates\_navbar_search.html.twig:4 @@ -3982,7 +3952,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Search options - + Part-DB1\templates\_navbar_search.html.twig:23 @@ -3991,7 +3961,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Tags - + Part-DB1\templates\_navbar_search.html.twig:27 Part-DB1\src\Form\LabelOptionsType.php:68 @@ -4003,10 +3973,10 @@ If you have done this incorrectly or if a computer is no longer trusted, you can storelocation.label - Store location + Storage location - + Part-DB1\templates\_navbar_search.html.twig:36 Part-DB1\templates\_navbar_search.html.twig:31 @@ -4017,7 +3987,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can supplier partnr. - + Part-DB1\templates\_navbar_search.html.twig:40 Part-DB1\src\Services\ElementTypeNameGenerator.php:89 @@ -4030,7 +4000,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Supplier - + Part-DB1\templates\_navbar_search.html.twig:57 Part-DB1\templates\_navbar_search.html.twig:52 @@ -4041,7 +4011,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Deact. Barcode - + Part-DB1\templates\_navbar_search.html.twig:61 Part-DB1\templates\_navbar_search.html.twig:56 @@ -4052,7 +4022,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Reg.Ex. Matching - + Part-DB1\templates\_navbar_search.html.twig:68 Part-DB1\templates\_navbar_search.html.twig:62 @@ -4062,7 +4032,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Go! - + Part-DB1\templates\_sidebar.html.twig:37 Part-DB1\templates\_sidebar.html.twig:12 @@ -4078,7 +4048,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Projects - + Part-DB1\templates\_sidebar.html.twig:2 Part-DB1\templates\_sidebar.html.twig:2 @@ -4091,7 +4061,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Actions - + Part-DB1\templates\_sidebar.html.twig:6 Part-DB1\templates\_sidebar.html.twig:6 @@ -4104,7 +4074,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Data source - + Part-DB1\templates\_sidebar.html.twig:10 Part-DB1\templates\_sidebar.html.twig:10 @@ -4117,7 +4087,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Manufacturers - + Part-DB1\templates\_sidebar.html.twig:11 Part-DB1\templates\_sidebar.html.twig:11 @@ -4130,7 +4100,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Suppliers - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 @@ -4146,7 +4116,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Download of the external attachment failed. - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 @@ -4156,7 +4126,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Changes saved successful. - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 @@ -4166,7 +4136,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Can not save changed. Please check your input! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 @@ -4176,7 +4146,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Element created. - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 @@ -4186,7 +4156,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Could not create element. Please check your input! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 @@ -4197,7 +4167,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Element deleted! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 Part-DB1\src\Controller\UserController.php:109 @@ -4213,7 +4183,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can CSFR Token invalid. Please reload this page or contact an administrator if this message stays. - + Part-DB1\src\Controller\LabelController.php:125 @@ -4222,7 +4192,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can No entities matching the range found. - + Part-DB1\src\Controller\LogController.php:149 Part-DB1\src\Controller\LogController.php:154 @@ -4233,7 +4203,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Target element could not be found in DB! - + Part-DB1\src\Controller\LogController.php:156 Part-DB1\src\Controller\LogController.php:160 @@ -4244,7 +4214,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Reverted to timestamp successfully. - + Part-DB1\src\Controller\LogController.php:176 Part-DB1\src\Controller\LogController.php:180 @@ -4255,7 +4225,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Undeleted element successfully. - + Part-DB1\src\Controller\LogController.php:178 Part-DB1\src\Controller\LogController.php:182 @@ -4266,7 +4236,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Element was already undeleted! - + Part-DB1\src\Controller\LogController.php:185 Part-DB1\src\Controller\LogController.php:189 @@ -4277,7 +4247,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Element deleted successfully. - + Part-DB1\src\Controller\LogController.php:187 Part-DB1\src\Controller\LogController.php:191 @@ -4288,7 +4258,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Element was already deleted! - + Part-DB1\src\Controller\LogController.php:194 Part-DB1\src\Controller\LogController.php:198 @@ -4299,7 +4269,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Change undone successfully! - + Part-DB1\src\Controller\LogController.php:196 Part-DB1\src\Controller\LogController.php:200 @@ -4310,7 +4280,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can You have to undelete the element before you can undo this change! - + Part-DB1\src\Controller\LogController.php:199 Part-DB1\src\Controller\LogController.php:203 @@ -4321,7 +4291,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can This log entry can not be undone! - + Part-DB1\src\Controller\PartController.php:182 Part-DB1\src\Controller\PartController.php:182 @@ -4332,7 +4302,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Saved changes! - + Part-DB1\src\Controller\PartController.php:186 Part-DB1\src\Controller\PartController.php:186 @@ -4342,7 +4312,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Error during saving: Please check your inputs! - + Part-DB1\src\Controller\PartController.php:216 Part-DB1\src\Controller\PartController.php:219 @@ -4352,7 +4322,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Part deleted successful. - + Part-DB1\src\Controller\PartController.php:302 Part-DB1\src\Controller\PartController.php:277 @@ -4365,7 +4335,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Part created! - + Part-DB1\src\Controller\PartController.php:308 Part-DB1\src\Controller\PartController.php:283 @@ -4375,7 +4345,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Error during creation: Please check your inputs! - + Part-DB1\src\Controller\ScanController.php:68 Part-DB1\src\Controller\ScanController.php:90 @@ -4385,7 +4355,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can No element found for the given barcode. - + Part-DB1\src\Controller\ScanController.php:71 @@ -4394,7 +4364,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Format unknown! - + Part-DB1\src\Controller\ScanController.php:86 @@ -4403,7 +4373,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Element found. - + Part-DB1\src\Controller\SecurityController.php:114 Part-DB1\src\Controller\SecurityController.php:109 @@ -4413,7 +4383,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Username / Email - + Part-DB1\src\Controller\SecurityController.php:131 Part-DB1\src\Controller\SecurityController.php:126 @@ -4423,7 +4393,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Reset request was successful! Please check your emails for further instructions. - + Part-DB1\src\Controller\SecurityController.php:162 Part-DB1\src\Controller\SecurityController.php:160 @@ -4433,7 +4403,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Username - + Part-DB1\src\Controller\SecurityController.php:165 Part-DB1\src\Controller\SecurityController.php:163 @@ -4443,7 +4413,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Token - + Part-DB1\src\Controller\SecurityController.php:194 Part-DB1\src\Controller\SecurityController.php:192 @@ -4453,7 +4423,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Username or Token invalid! Please check your input. - + Part-DB1\src\Controller\SecurityController.php:196 Part-DB1\src\Controller\SecurityController.php:194 @@ -4463,7 +4433,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Password was reset successfully. You can now login with your new password. - + Part-DB1\src\Controller\UserController.php:107 Part-DB1\src\Controller\UserController.php:99 @@ -4473,7 +4443,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can All two-factor authentication methods were successfully disabled. - + Part-DB1\src\Controller\UserSettingsController.php:101 Part-DB1\src\Controller\UserSettingsController.php:92 @@ -4483,7 +4453,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can No backup codes enabled! - + Part-DB1\src\Controller\UserSettingsController.php:138 Part-DB1\src\Controller\UserSettingsController.php:132 @@ -4493,7 +4463,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can No security key with this ID is existing. - + Part-DB1\src\Controller\UserSettingsController.php:145 Part-DB1\src\Controller\UserSettingsController.php:139 @@ -4503,7 +4473,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can You can not delete the security keys of other users! - + Part-DB1\src\Controller\UserSettingsController.php:153 Part-DB1\src\Controller\UserSettingsController.php:147 @@ -4513,7 +4483,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Security key successfully removed. - + Part-DB1\src\Controller\UserSettingsController.php:188 Part-DB1\src\Controller\UserSettingsController.php:180 @@ -4523,7 +4493,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Trusted devices successfully reset. - + Part-DB1\src\Controller\UserSettingsController.php:235 Part-DB1\src\Controller\UserSettingsController.php:226 @@ -4534,7 +4504,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Settings saved! - + Part-DB1\src\Controller\UserSettingsController.php:297 Part-DB1\src\Controller\UserSettingsController.php:288 @@ -4545,7 +4515,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Password changed! - + Part-DB1\src\Controller\UserSettingsController.php:317 Part-DB1\src\Controller\UserSettingsController.php:306 @@ -4555,7 +4525,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Authenticator App successfully activated. - + Part-DB1\src\Controller\UserSettingsController.php:328 Part-DB1\src\Controller\UserSettingsController.php:315 @@ -4565,7 +4535,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Authenticator App successfully deactivated. - + Part-DB1\src\Controller\UserSettingsController.php:346 Part-DB1\src\Controller\UserSettingsController.php:332 @@ -4575,7 +4545,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can New backup codes successfully generated. - + Part-DB1\src\DataTables\AttachmentDataTable.php:148 Part-DB1\src\DataTables\AttachmentDataTable.php:148 @@ -4585,7 +4555,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can File name - + Part-DB1\src\DataTables\AttachmentDataTable.php:153 Part-DB1\src\DataTables\AttachmentDataTable.php:153 @@ -4595,7 +4565,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can File size - + Part-DB1\src\DataTables\AttachmentDataTable.php:183 Part-DB1\src\DataTables\AttachmentDataTable.php:191 @@ -4615,7 +4585,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can true - + Part-DB1\src\DataTables\AttachmentDataTable.php:184 Part-DB1\src\DataTables\AttachmentDataTable.php:192 @@ -4637,7 +4607,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can false - + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 @@ -4647,7 +4617,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can deleted - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 @@ -4658,7 +4628,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Undelete element - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 @@ -4669,7 +4639,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Undo change - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 @@ -4680,7 +4650,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Revert element to this timestamp - + Part-DB1\src\DataTables\LogDataTable.php:173 Part-DB1\src\DataTables\LogDataTable.php:161 @@ -4690,7 +4660,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can ID - + Part-DB1\src\DataTables\LogDataTable.php:178 Part-DB1\src\DataTables\LogDataTable.php:166 @@ -4700,7 +4670,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Timestamp - + Part-DB1\src\DataTables\LogDataTable.php:183 Part-DB1\src\DataTables\LogDataTable.php:171 @@ -4710,7 +4680,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Event - + Part-DB1\src\DataTables\LogDataTable.php:191 Part-DB1\src\DataTables\LogDataTable.php:179 @@ -4720,7 +4690,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Level - + Part-DB1\src\DataTables\LogDataTable.php:200 Part-DB1\src\DataTables\LogDataTable.php:188 @@ -4730,7 +4700,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can User - + Part-DB1\src\DataTables\LogDataTable.php:213 Part-DB1\src\DataTables\LogDataTable.php:201 @@ -4740,7 +4710,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Target type - + Part-DB1\src\DataTables\LogDataTable.php:226 Part-DB1\src\DataTables\LogDataTable.php:214 @@ -4750,7 +4720,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Target - + Part-DB1\src\DataTables\LogDataTable.php:231 Part-DB1\src\DataTables\LogDataTable.php:218 @@ -4761,7 +4731,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Extra - + Part-DB1\src\DataTables\PartsDataTable.php:168 Part-DB1\src\DataTables\PartsDataTable.php:116 @@ -4771,7 +4741,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Name - + Part-DB1\src\DataTables\PartsDataTable.php:178 Part-DB1\src\DataTables\PartsDataTable.php:126 @@ -4781,7 +4751,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Id - + Part-DB1\src\DataTables\PartsDataTable.php:182 Part-DB1\src\DataTables\PartsDataTable.php:130 @@ -4791,7 +4761,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Description - + Part-DB1\src\DataTables\PartsDataTable.php:185 Part-DB1\src\DataTables\PartsDataTable.php:133 @@ -4801,7 +4771,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Category - + Part-DB1\src\DataTables\PartsDataTable.php:190 Part-DB1\src\DataTables\PartsDataTable.php:138 @@ -4811,7 +4781,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Footprint - + Part-DB1\src\DataTables\PartsDataTable.php:194 Part-DB1\src\DataTables\PartsDataTable.php:142 @@ -4821,17 +4791,17 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Manufacturer - + Part-DB1\src\DataTables\PartsDataTable.php:197 Part-DB1\src\DataTables\PartsDataTable.php:145 part.table.storeLocations - Store locations + Storage locations - + Part-DB1\src\DataTables\PartsDataTable.php:216 Part-DB1\src\DataTables\PartsDataTable.php:164 @@ -4841,7 +4811,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Amount - + Part-DB1\src\DataTables\PartsDataTable.php:224 Part-DB1\src\DataTables\PartsDataTable.php:172 @@ -4851,7 +4821,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Min. Amount - + Part-DB1\src\DataTables\PartsDataTable.php:232 Part-DB1\src\DataTables\PartsDataTable.php:180 @@ -4861,7 +4831,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Measurement Unit - + Part-DB1\src\DataTables\PartsDataTable.php:236 Part-DB1\src\DataTables\PartsDataTable.php:184 @@ -4871,7 +4841,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Created at - + Part-DB1\src\DataTables\PartsDataTable.php:240 Part-DB1\src\DataTables\PartsDataTable.php:188 @@ -4881,7 +4851,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Last modified - + Part-DB1\src\DataTables\PartsDataTable.php:244 Part-DB1\src\DataTables\PartsDataTable.php:192 @@ -4891,7 +4861,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Needs review - + Part-DB1\src\DataTables\PartsDataTable.php:251 Part-DB1\src\DataTables\PartsDataTable.php:199 @@ -4901,7 +4871,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Favorite - + Part-DB1\src\DataTables\PartsDataTable.php:258 Part-DB1\src\DataTables\PartsDataTable.php:206 @@ -4911,7 +4881,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Status - + Part-DB1\src\DataTables\PartsDataTable.php:260 Part-DB1\src\DataTables\PartsDataTable.php:262 @@ -4925,7 +4895,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Unknown - + Part-DB1\src\DataTables\PartsDataTable.php:263 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4937,7 +4907,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Announced - + Part-DB1\src\DataTables\PartsDataTable.php:264 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4949,7 +4919,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Active - + Part-DB1\src\DataTables\PartsDataTable.php:265 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4961,7 +4931,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Not recommended for new designs - + Part-DB1\src\DataTables\PartsDataTable.php:266 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4973,7 +4943,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can End of life - + Part-DB1\src\DataTables\PartsDataTable.php:267 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4985,7 +4955,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Discontinued - + Part-DB1\src\DataTables\PartsDataTable.php:271 Part-DB1\src\DataTables\PartsDataTable.php:219 @@ -4995,7 +4965,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can MPN - + Part-DB1\src\DataTables\PartsDataTable.php:275 Part-DB1\src\DataTables\PartsDataTable.php:223 @@ -5005,7 +4975,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Mass - + Part-DB1\src\DataTables\PartsDataTable.php:279 Part-DB1\src\DataTables\PartsDataTable.php:227 @@ -5015,7 +4985,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Tags - + Part-DB1\src\DataTables\PartsDataTable.php:283 Part-DB1\src\DataTables\PartsDataTable.php:231 @@ -5025,7 +4995,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Attachments - + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 @@ -5035,7 +5005,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Login successful - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5046,7 +5016,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can JSON - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5057,7 +5027,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can XML - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5068,7 +5038,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can CSV - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5079,7 +5049,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can YAML - + Part-DB1\src\Form\AdminPages\ImportType.php:124 Part-DB1\src\Form\AdminPages\ImportType.php:124 @@ -5089,7 +5059,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can When this option is activated, the whole import process is aborted if invalid data is detected. If not selected, the invalid data is ignored and the importer will try to import the other elements. - + Part-DB1\src\Form\AdminPages\ImportType.php:86 Part-DB1\src\Form\AdminPages\ImportType.php:86 @@ -5100,7 +5070,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can CSV separator - + Part-DB1\src\Form\AdminPages\ImportType.php:93 Part-DB1\src\Form\AdminPages\ImportType.php:93 @@ -5111,7 +5081,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Parent element - + Part-DB1\src\Form\AdminPages\ImportType.php:101 Part-DB1\src\Form\AdminPages\ImportType.php:101 @@ -5122,7 +5092,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can File - + Part-DB1\src\Form\AdminPages\ImportType.php:111 Part-DB1\src\Form\AdminPages\ImportType.php:111 @@ -5133,7 +5103,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Preserve child elements on import - + Part-DB1\src\Form\AdminPages\ImportType.php:120 Part-DB1\src\Form\AdminPages\ImportType.php:120 @@ -5144,7 +5114,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Abort on invalid data - + Part-DB1\src\Form\AdminPages\ImportType.php:132 Part-DB1\src\Form\AdminPages\ImportType.php:132 @@ -5155,7 +5125,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Import - + Part-DB1\src\Form\AttachmentFormType.php:113 Part-DB1\src\Form\AttachmentFormType.php:109 @@ -5165,7 +5135,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can An attachment marked private can only be accessed by authenticated users with the proper permission. If this is activated no thumbnails are generated and access to file is less performant. - + Part-DB1\src\Form\AttachmentFormType.php:127 Part-DB1\src\Form\AttachmentFormType.php:123 @@ -5175,7 +5145,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can You can specify an URL to an external file here, or input an keyword which is used to search in built-in resources (e.g. footprints) - + Part-DB1\src\Form\AttachmentFormType.php:82 Part-DB1\src\Form\AttachmentFormType.php:79 @@ -5185,7 +5155,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Name - + Part-DB1\src\Form\AttachmentFormType.php:85 Part-DB1\src\Form\AttachmentFormType.php:82 @@ -5195,7 +5165,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Attachment type - + Part-DB1\src\Form\AttachmentFormType.php:94 Part-DB1\src\Form\AttachmentFormType.php:91 @@ -5205,7 +5175,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Show in table - + Part-DB1\src\Form\AttachmentFormType.php:105 Part-DB1\src\Form\AttachmentFormType.php:102 @@ -5215,7 +5185,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Private attachment - + Part-DB1\src\Form\AttachmentFormType.php:119 Part-DB1\src\Form\AttachmentFormType.php:115 @@ -5225,7 +5195,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can URL - + Part-DB1\src\Form\AttachmentFormType.php:133 Part-DB1\src\Form\AttachmentFormType.php:129 @@ -5235,7 +5205,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Download external file - + Part-DB1\src\Form\AttachmentFormType.php:146 Part-DB1\src\Form\AttachmentFormType.php:142 @@ -5245,7 +5215,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Upload file - + Part-DB1\src\Form\LabelOptionsType.php:68 Part-DB1\src\Services\ElementTypeNameGenerator.php:86 @@ -5255,7 +5225,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Part - + Part-DB1\src\Form\LabelOptionsType.php:68 Part-DB1\src\Services\ElementTypeNameGenerator.php:87 @@ -5265,7 +5235,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Part lot - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5274,7 +5244,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can None - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5283,7 +5253,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can QR Code (recommended) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5292,7 +5262,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Code 128 (recommended) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5301,7 +5271,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Code 39 (recommended) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5310,7 +5280,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Code 93 - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5319,7 +5289,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Datamatrix - + Part-DB1\src\Form\LabelOptionsType.php:122 @@ -5328,7 +5298,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Placeholders - + Part-DB1\src\Form\LabelOptionsType.php:122 @@ -5337,7 +5307,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Twig - + Part-DB1\src\Form\LabelOptionsType.php:126 @@ -5346,7 +5316,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can If you select Twig here, the content field is interpreted as Twig template. See <a href="https://twig.symfony.com/doc/3.x/templates.html">Twig documentation</a> and <a href="https://docs.part-db.de/usage/labels.html#twig-mode">Wiki</a> for more information. - + Part-DB1\src\Form\LabelOptionsType.php:47 @@ -5355,7 +5325,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Label size - + Part-DB1\src\Form\LabelOptionsType.php:66 @@ -5364,7 +5334,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Target type - + Part-DB1\src\Form\LabelOptionsType.php:75 @@ -5373,7 +5343,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Barcode - + Part-DB1\src\Form\LabelOptionsType.php:102 @@ -5382,7 +5352,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Content - + Part-DB1\src\Form\LabelOptionsType.php:111 @@ -5391,7 +5361,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Additional styles (CSS) - + Part-DB1\src\Form\LabelOptionsType.php:120 @@ -5400,7 +5370,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Parser mode - + Part-DB1\src\Form\LabelOptionsType.php:51 @@ -5409,7 +5379,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Width - + Part-DB1\src\Form\LabelOptionsType.php:60 @@ -5418,7 +5388,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Height - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 @@ -5427,7 +5397,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can You can specify multiple IDs (e.g. 1,2,3) and/or a range (1-3) here to generate labels for multiple elements at once. - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 @@ -5436,7 +5406,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Target IDs - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 @@ -5445,7 +5415,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Update - + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 @@ -5454,7 +5424,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Input - + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 @@ -5463,7 +5433,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Submit - + Part-DB1\src\Form\ParameterType.php:41 @@ -5472,7 +5442,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. DC Current Gain - + Part-DB1\src\Form\ParameterType.php:50 @@ -5481,7 +5451,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. h_{FE} - + Part-DB1\src\Form\ParameterType.php:60 @@ -5490,7 +5460,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. Test conditions - + Part-DB1\src\Form\ParameterType.php:71 @@ -5499,7 +5469,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. 350 - + Part-DB1\src\Form\ParameterType.php:82 @@ -5508,7 +5478,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. 100 - + Part-DB1\src\Form\ParameterType.php:93 @@ -5517,7 +5487,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. 200 - + Part-DB1\src\Form\ParameterType.php:103 @@ -5526,7 +5496,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. V - + Part-DB1\src\Form\ParameterType.php:114 @@ -5535,7 +5505,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. Technical Specifications - + Part-DB1\src\Form\Part\OrderdetailType.php:72 Part-DB1\src\Form\Part\OrderdetailType.php:75 @@ -5545,7 +5515,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Supplier part number - + Part-DB1\src\Form\Part\OrderdetailType.php:81 Part-DB1\src\Form\Part\OrderdetailType.php:84 @@ -5555,7 +5525,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Supplier - + Part-DB1\src\Form\Part\OrderdetailType.php:87 Part-DB1\src\Form\Part\OrderdetailType.php:90 @@ -5565,7 +5535,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Link to offer - + Part-DB1\src\Form\Part\OrderdetailType.php:93 Part-DB1\src\Form\Part\OrderdetailType.php:96 @@ -5575,7 +5545,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can No longer available - + Part-DB1\src\Form\Part\OrderdetailType.php:75 Part-DB1\src\Form\Part\OrderdetailType.php:78 @@ -5585,7 +5555,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. BC 547 - + Part-DB1\src\Form\Part\PartBaseType.php:101 Part-DB1\src\Form\Part\PartBaseType.php:99 @@ -5595,7 +5565,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Name - + Part-DB1\src\Form\Part\PartBaseType.php:109 Part-DB1\src\Form\Part\PartBaseType.php:107 @@ -5605,7 +5575,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Description - + Part-DB1\src\Form\Part\PartBaseType.php:120 Part-DB1\src\Form\Part\PartBaseType.php:118 @@ -5615,7 +5585,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Minimum stock - + Part-DB1\src\Form\Part\PartBaseType.php:129 Part-DB1\src\Form\Part\PartBaseType.php:127 @@ -5625,7 +5595,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Category - + Part-DB1\src\Form\Part\PartBaseType.php:135 Part-DB1\src\Form\Part\PartBaseType.php:133 @@ -5635,7 +5605,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Footprint - + Part-DB1\src\Form\Part\PartBaseType.php:142 Part-DB1\src\Form\Part\PartBaseType.php:140 @@ -5645,7 +5615,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Tags - + Part-DB1\src\Form\Part\PartBaseType.php:154 Part-DB1\src\Form\Part\PartBaseType.php:152 @@ -5655,7 +5625,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Manufacturer - + Part-DB1\src\Form\Part\PartBaseType.php:161 Part-DB1\src\Form\Part\PartBaseType.php:159 @@ -5665,7 +5635,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Link to product page - + Part-DB1\src\Form\Part\PartBaseType.php:167 Part-DB1\src\Form\Part\PartBaseType.php:165 @@ -5675,7 +5645,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Manufacturer part number - + Part-DB1\src\Form\Part\PartBaseType.php:173 Part-DB1\src\Form\Part\PartBaseType.php:171 @@ -5685,7 +5655,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Manufacturing status - + Part-DB1\src\Form\Part\PartBaseType.php:181 Part-DB1\src\Form\Part\PartBaseType.php:179 @@ -5695,7 +5665,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Needs review - + Part-DB1\src\Form\Part\PartBaseType.php:189 Part-DB1\src\Form\Part\PartBaseType.php:187 @@ -5705,7 +5675,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Favorite - + Part-DB1\src\Form\Part\PartBaseType.php:197 Part-DB1\src\Form\Part\PartBaseType.php:195 @@ -5715,7 +5685,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Mass - + Part-DB1\src\Form\Part\PartBaseType.php:203 Part-DB1\src\Form\Part\PartBaseType.php:201 @@ -5725,7 +5695,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Measuring unit - + Part-DB1\src\Form\Part\PartBaseType.php:212 Part-DB1\src\Form\Part\PartBaseType.php:210 @@ -5735,7 +5705,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Notes - + Part-DB1\src\Form\Part\PartBaseType.php:250 Part-DB1\src\Form\Part\PartBaseType.php:246 @@ -5745,7 +5715,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Preview image - + Part-DB1\src\Form\Part\PartBaseType.php:295 Part-DB1\src\Form\Part\PartBaseType.php:276 @@ -5756,7 +5726,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Save changes - + Part-DB1\src\Form\Part\PartBaseType.php:296 Part-DB1\src\Form\Part\PartBaseType.php:277 @@ -5767,7 +5737,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Reset changes - + Part-DB1\src\Form\Part\PartBaseType.php:105 Part-DB1\src\Form\Part\PartBaseType.php:103 @@ -5777,7 +5747,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. BC547 - + Part-DB1\src\Form\Part\PartBaseType.php:115 Part-DB1\src\Form\Part\PartBaseType.php:113 @@ -5787,7 +5757,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. NPN 45V, 0,1A, 0,5W - + Part-DB1\src\Form\Part\PartBaseType.php:123 Part-DB1\src\Form\Part\PartBaseType.php:121 @@ -5797,7 +5767,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. 1 - + Part-DB1\src\Form\Part\PartLotType.php:69 Part-DB1\src\Form\Part\PartLotType.php:69 @@ -5807,7 +5777,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Description - + Part-DB1\src\Form\Part\PartLotType.php:78 Part-DB1\src\Form\Part\PartLotType.php:78 @@ -5817,7 +5787,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Storage location - + Part-DB1\src\Form\Part\PartLotType.php:89 Part-DB1\src\Form\Part\PartLotType.php:89 @@ -5827,7 +5797,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Amount - + Part-DB1\src\Form\Part\PartLotType.php:98 Part-DB1\src\Form\Part\PartLotType.php:97 @@ -5837,7 +5807,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Amount unknown - + Part-DB1\src\Form\Part\PartLotType.php:109 Part-DB1\src\Form\Part\PartLotType.php:108 @@ -5847,7 +5817,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Needs refill - + Part-DB1\src\Form\Part\PartLotType.php:120 Part-DB1\src\Form\Part\PartLotType.php:119 @@ -5857,7 +5827,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Expiration date - + Part-DB1\src\Form\Part\PartLotType.php:128 Part-DB1\src\Form\Part\PartLotType.php:125 @@ -5867,7 +5837,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Notes - + Part-DB1\src\Form\Permissions\PermissionsType.php:99 Part-DB1\src\Form\Permissions\PermissionsType.php:99 @@ -5877,7 +5847,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Miscellaneous - + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 Part-DB1\src\Form\TFAGoogleSettingsType.php:97 @@ -5887,7 +5857,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Enable authenticator app - + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 Part-DB1\src\Form\TFAGoogleSettingsType.php:101 @@ -5897,7 +5867,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Deactivate authenticator app - + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 Part-DB1\src\Form\TFAGoogleSettingsType.php:74 @@ -5907,7 +5877,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Confirmation code - + Part-DB1\src\Form\UserSettingsType.php:108 Part-DB1\src\Form\UserSettingsType.php:108 @@ -5918,7 +5888,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Timezone - + Part-DB1\src\Form\UserSettingsType.php:133 Part-DB1\src\Form\UserSettingsType.php:132 @@ -5928,7 +5898,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Preferred currency - + Part-DB1\src\Form\UserSettingsType.php:140 Part-DB1\src\Form\UserSettingsType.php:139 @@ -5939,7 +5909,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Apply changes - + Part-DB1\src\Form\UserSettingsType.php:141 Part-DB1\src\Form\UserSettingsType.php:140 @@ -5950,7 +5920,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Discard changes - + Part-DB1\src\Form\UserSettingsType.php:104 Part-DB1\src\Form\UserSettingsType.php:104 @@ -5961,7 +5931,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Serverwide language - + Part-DB1\src\Form\UserSettingsType.php:115 Part-DB1\src\Form\UserSettingsType.php:115 @@ -5972,7 +5942,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Serverwide Timezone - + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 Part-DB1\src\Services\ElementTypeNameGenerator.php:79 @@ -5982,7 +5952,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Attachment - + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 Part-DB1\src\Services\ElementTypeNameGenerator.php:81 @@ -5992,7 +5962,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Attachment type - + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 Part-DB1\src\Services\ElementTypeNameGenerator.php:82 @@ -6002,7 +5972,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Project - + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 Part-DB1\src\Services\ElementTypeNameGenerator.php:85 @@ -6012,7 +5982,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Measurement unit - + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 Part-DB1\src\Services\ElementTypeNameGenerator.php:90 @@ -6022,7 +5992,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Currency - + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 Part-DB1\src\Services\ElementTypeNameGenerator.php:91 @@ -6032,7 +6002,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Order detail - + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 Part-DB1\src\Services\ElementTypeNameGenerator.php:92 @@ -6042,7 +6012,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Price detail - + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 Part-DB1\src\Services\ElementTypeNameGenerator.php:94 @@ -6052,7 +6022,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can User - + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 @@ -6061,7 +6031,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Parameter - + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 @@ -6070,7 +6040,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Label profile - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 @@ -6081,7 +6051,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Unknown - + Part-DB1\src\Services\MarkdownParser.php:73 Part-DB1\src\Services\MarkdownParser.php:73 @@ -6091,7 +6061,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Loading markdown. If this message does not disappear, try to reload the page. - + Part-DB1\src\Services\PasswordResetManager.php:98 Part-DB1\src\Services\PasswordResetManager.php:98 @@ -6101,7 +6071,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Password reset for your Part-DB account - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 @@ -6110,7 +6080,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Tools - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 @@ -6121,7 +6091,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Edit - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 @@ -6132,7 +6102,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Show - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 @@ -6142,7 +6112,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can System - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 @@ -6151,7 +6121,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Label generator - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 @@ -6160,7 +6130,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Scanner - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 @@ -6171,7 +6141,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Attachment types - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 @@ -6182,7 +6152,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Categories - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 @@ -6193,7 +6163,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Projects - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 @@ -6204,7 +6174,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Suppliers - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 @@ -6215,7 +6185,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Manufacturers - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 @@ -6225,7 +6195,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Storage locations - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 @@ -6235,7 +6205,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Footprints - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 @@ -6245,7 +6215,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Currencies - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 @@ -6255,7 +6225,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Measurement Unit - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 @@ -6264,7 +6234,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Label profiles - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 @@ -6274,7 +6244,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can New part - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 @@ -6285,7 +6255,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Show all parts - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 @@ -6295,7 +6265,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Attachments - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 @@ -6306,7 +6276,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Statistics - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 @@ -6316,7 +6286,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Users - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 @@ -6326,7 +6296,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Groups - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 @@ -6337,7 +6307,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Event log - + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 @@ -6348,7 +6318,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can New Element - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 obsolete @@ -6358,7 +6328,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can External file - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 obsolete @@ -6368,7 +6338,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Edit - + Part-DB1\templates\_navbar.html.twig:27 templates\base.html.twig:88 @@ -6379,7 +6349,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Scan Barcode - + Part-DB1\src\Form\UserSettingsType.php:119 src\Form\UserSettingsType.php:49 @@ -6390,7 +6360,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Theme - + Part-DB1\src\Form\UserSettingsType.php:129 src\Form\UserSettingsType.php:50 @@ -6401,53 +6371,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Serverwide Theme - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - M - M - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - k - k - - - - - - - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - m - m - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - µ - µ - - - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 new @@ -6458,7 +6382,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can IP - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 @@ -6472,7 +6396,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Change undone - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 @@ -6486,7 +6410,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Element reverted - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 new @@ -6497,7 +6421,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Old instock - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 new @@ -6508,7 +6432,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Old name - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 new @@ -6519,7 +6443,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Changed fields - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 new @@ -6530,7 +6454,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Comment - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 new @@ -6541,7 +6465,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Deleted element: - + templates\base.html.twig:81 obsolete @@ -6552,7 +6476,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Go! - + templates\base.html.twig:109 obsolete @@ -6563,7 +6487,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can English - + templates\base.html.twig:112 obsolete @@ -6574,7 +6498,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can German - + obsolete obsolete @@ -6584,7 +6508,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Password change needed! - + obsolete obsolete @@ -6594,7 +6518,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Attachment type - + obsolete obsolete @@ -6604,7 +6528,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Associated element - + obsolete obsolete @@ -6614,7 +6538,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Picture? - + obsolete obsolete @@ -6624,7 +6548,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can 3D model? - + obsolete obsolete @@ -6634,7 +6558,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Builtin? - + obsolete obsolete @@ -6644,7 +6568,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. useful for switching - + obsolete obsolete @@ -6654,7 +6578,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Generate new backup codes - + obsolete obsolete @@ -6664,7 +6588,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can A element can not be its own parent. - + obsolete obsolete @@ -6674,17 +6598,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The parent can not be one of the children of itself. - - - obsolete - obsolete - - - validator.isSelectable - The element must be selectable. - - - + obsolete obsolete @@ -6694,7 +6608,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The storage location was marked as full, so you can not increase the instock amount. (New amount max. {{ old_amount }}) - + obsolete obsolete @@ -6704,7 +6618,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The storage location was marked as full, so you can not add a new part to it. - + obsolete obsolete @@ -6714,7 +6628,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The storage location was marked as "only existing", so you can not add new part to it. - + obsolete obsolete @@ -6724,7 +6638,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The storage location was marked as "single part", so you can not add a new part to it. - + obsolete obsolete @@ -6734,7 +6648,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The part is currently and in the foreseeable future in production - + obsolete obsolete @@ -6744,7 +6658,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The part was announced but is not available yet. - + obsolete obsolete @@ -6754,7 +6668,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The part is discontinued and not produced anymore. - + obsolete obsolete @@ -6764,7 +6678,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The product has reached its end-of-life and the production will be stopped soon. - + obsolete obsolete @@ -6774,7 +6688,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The part is currently in production but is not recommended for new designs. - + obsolete obsolete @@ -6784,7 +6698,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can The manufacturing status of the part is not known. - + obsolete obsolete @@ -6794,7 +6708,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Success - + obsolete obsolete @@ -6804,7 +6718,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Error - + obsolete obsolete @@ -6814,7 +6728,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Warning - + obsolete obsolete @@ -6824,7 +6738,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Notice - + obsolete obsolete @@ -6834,7 +6748,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Info - + obsolete obsolete @@ -6844,7 +6758,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can You can not withdraw yourself the "change permission" permission, to prevent that you lockout yourself accidentally. - + obsolete obsolete @@ -6854,7 +6768,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Allowed file extensions - + obsolete obsolete @@ -6864,7 +6778,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can You can specify a comma separated list of file extension or mime types, which an uploaded file must have when assigned to this attachment type. To allow all supported image files, you can use image/*. - + obsolete obsolete @@ -6874,7 +6788,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. .txt, application/pdf, image/* - + src\Form\PartType.php:63 obsolete @@ -6885,7 +6799,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. BC547 - + obsolete obsolete @@ -6895,7 +6809,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Not selectable - + obsolete obsolete @@ -6905,7 +6819,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can If this option is activated, this element can not be assigned to a part property. Useful if this element is just used for grouping. - + obsolete obsolete @@ -6915,7 +6829,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can You can use BBCode here (e.g. [b]Bold[/b]) - + obsolete obsolete @@ -6925,7 +6839,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Create element - + obsolete obsolete @@ -6935,7 +6849,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Save - + obsolete obsolete @@ -6945,7 +6859,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Disable footprints - + obsolete obsolete @@ -6955,7 +6869,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can If this option is activated, the footprint property is disabled for all parts with this category. - + obsolete obsolete @@ -6965,7 +6879,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Disable manufacturers - + obsolete obsolete @@ -6975,7 +6889,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can If this option is activated, the manufacturer property is disabled for all parts with this category. - + obsolete obsolete @@ -6985,7 +6899,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Disable automatic datasheet links - + obsolete obsolete @@ -6995,7 +6909,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can If this option is activated, no automatic links to datasheets are created for parts with this category. - + obsolete obsolete @@ -7005,7 +6919,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Disable properties - + obsolete obsolete @@ -7015,7 +6929,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can If this option is activated, the part properties are disabled for parts with this category. - + obsolete obsolete @@ -7025,7 +6939,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Part name hint - + obsolete obsolete @@ -7035,7 +6949,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. 100nF - + obsolete obsolete @@ -7045,7 +6959,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Name filter - + obsolete obsolete @@ -7055,7 +6969,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Default description - + obsolete obsolete @@ -7065,7 +6979,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can e.g. Capacitor, 10mm x 10mm, SMD - + obsolete obsolete @@ -7075,7 +6989,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Default notes - + obsolete obsolete @@ -7085,7 +6999,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Address - + obsolete obsolete @@ -7096,7 +7010,7 @@ If you have done this incorrectly or if a computer is no longer trusted, you can Exampletown - + obsolete obsolete @@ -7106,7 +7020,7 @@ Exampletown Phone number - + obsolete obsolete @@ -7116,7 +7030,7 @@ Exampletown +49 12345 6789 - + obsolete obsolete @@ -7126,7 +7040,7 @@ Exampletown Fax number - + obsolete obsolete @@ -7136,7 +7050,7 @@ Exampletown Email - + obsolete obsolete @@ -7146,7 +7060,7 @@ Exampletown e.g. contact@foo.bar - + obsolete obsolete @@ -7156,7 +7070,7 @@ Exampletown Website - + obsolete obsolete @@ -7166,7 +7080,7 @@ Exampletown https://www.foo.bar - + obsolete obsolete @@ -7176,7 +7090,7 @@ Exampletown Product URL - + obsolete obsolete @@ -7186,7 +7100,7 @@ Exampletown This field is used to determine a link to the part on the company page. %PARTNUMBER% will be replaced with the order number. - + obsolete obsolete @@ -7196,7 +7110,7 @@ Exampletown https://foo.bar/product/%PARTNUMBER% - + obsolete obsolete @@ -7206,7 +7120,7 @@ Exampletown ISO code - + obsolete obsolete @@ -7216,7 +7130,7 @@ Exampletown Exchange rate - + obsolete obsolete @@ -7226,7 +7140,7 @@ Exampletown 3D model - + obsolete obsolete @@ -7236,7 +7150,7 @@ Exampletown Input - + obsolete obsolete @@ -7251,7 +7165,7 @@ Element 2 Element 3 - + obsolete obsolete @@ -7261,7 +7175,7 @@ Element 3 Create - + obsolete obsolete @@ -7271,7 +7185,7 @@ Element 3 Is integer - + obsolete obsolete @@ -7281,7 +7195,7 @@ Element 3 If this option is activated, all values with this unit will be rounded to whole numbers. - + obsolete obsolete @@ -7291,7 +7205,7 @@ Element 3 Use SI prefix - + obsolete obsolete @@ -7301,7 +7215,7 @@ Element 3 If this option is activated, values are outputted with SI prefixes (e.g. 1,2kg instead of 1200g) - + obsolete obsolete @@ -7311,7 +7225,7 @@ Element 3 Unit symbol - + obsolete obsolete @@ -7321,7 +7235,7 @@ Element 3 e.g. m - + obsolete obsolete @@ -7331,7 +7245,7 @@ Element 3 Storelocation full - + obsolete obsolete @@ -7341,7 +7255,7 @@ Element 3 If this option is selected, it is neither possible to add new parts to this storelocation or to increase the amount of existing parts. - + obsolete obsolete @@ -7351,7 +7265,7 @@ Element 3 Limit to existing parts - + obsolete obsolete @@ -7361,7 +7275,7 @@ Element 3 If this option is activated, it is not possible to add new parts to this storelocation, but the amount of existing parts can be increased. - + obsolete obsolete @@ -7371,17 +7285,17 @@ Element 3 Only single part - + obsolete obsolete storelocation.only_single_part.help - If this option is activated, only a single part (with every amount) can be assigned to this store location. Useful for small SMD boxes or feeders. + If this option is activated, only a single part (with every amount) can be assigned to this storage location. Useful for small SMD boxes or feeders. - + obsolete obsolete @@ -7391,7 +7305,7 @@ Element 3 Storage type - + obsolete obsolete @@ -7401,7 +7315,7 @@ Element 3 You can select a measurement unit here, which a part must have to be able to be assigned to this storage location - + obsolete obsolete @@ -7411,7 +7325,7 @@ Element 3 Default currency - + obsolete obsolete @@ -7421,7 +7335,7 @@ Element 3 Shipping Costs - + obsolete obsolete @@ -7431,7 +7345,7 @@ Element 3 e.g. j.doe - + obsolete obsolete @@ -7441,7 +7355,7 @@ Element 3 e.g John - + obsolete obsolete @@ -7451,7 +7365,7 @@ Element 3 e.g. Doe - + obsolete obsolete @@ -7461,7 +7375,7 @@ Element 3 j.doe@ecorp.com - + obsolete obsolete @@ -7471,7 +7385,7 @@ Element 3 e.g. Development - + obsolete obsolete @@ -7481,7 +7395,7 @@ Element 3 New password - + obsolete obsolete @@ -7491,7 +7405,7 @@ Element 3 Confirm new password - + obsolete obsolete @@ -7501,7 +7415,7 @@ Element 3 User needs to change password - + obsolete obsolete @@ -7511,7 +7425,7 @@ Element 3 User disabled (no login possible) - + obsolete obsolete @@ -7521,7 +7435,7 @@ Element 3 Create user - + obsolete obsolete @@ -7531,7 +7445,7 @@ Element 3 Save - + obsolete obsolete @@ -7541,18 +7455,7 @@ Element 3 Discard changes - - - templates\Parts\show_part_info.html.twig:161 - obsolete - obsolete - - - part.withdraw.caption: - Withdraw parts: - - - + templates\Parts\show_part_info.html.twig:166 obsolete @@ -7563,7 +7466,7 @@ Element 3 Withdraw - + templates\Parts\show_part_info.html.twig:171 obsolete @@ -7574,7 +7477,7 @@ Element 3 Comment/Purpose - + templates\Parts\show_part_info.html.twig:189 obsolete @@ -7585,7 +7488,7 @@ Element 3 Add parts - + templates\Parts\show_part_info.html.twig:194 obsolete @@ -7596,7 +7499,7 @@ Element 3 Add - + templates\Parts\show_part_info.html.twig:199 obsolete @@ -7607,7 +7510,7 @@ Element 3 Comment/Purpose - + templates\AdminPages\CompanyAdminBase.html.twig:15 obsolete @@ -7618,7 +7521,7 @@ Element 3 Notes - + src\Form\PartType.php:83 obsolete @@ -7629,7 +7532,7 @@ Element 3 Manufacturer link - + src\Form\PartType.php:66 obsolete @@ -7640,7 +7543,7 @@ Element 3 e.g. NPN 45V 0,1A 0,5W - + src\Form\PartType.php:69 obsolete @@ -7651,7 +7554,7 @@ Element 3 e.g. 10 - + src\Form\PartType.php:72 obsolete @@ -7662,27 +7565,7 @@ Element 3 e.g. 5 - - - obsolete - obsolete - - - homepage.basedOn - Based on work of - - - - - obsolete - obsolete - - - homepage.others - and others - - - + obsolete obsolete @@ -7692,7 +7575,7 @@ Element 3 Price per - + obsolete obsolete @@ -7702,7 +7585,7 @@ Element 3 Withdraw parts - + obsolete obsolete @@ -7712,7 +7595,7 @@ Element 3 _MENU_ - + obsolete obsolete @@ -7722,7 +7605,7 @@ Element 3 Parts - + obsolete obsolete @@ -7732,7 +7615,7 @@ Element 3 Data structures - + obsolete obsolete @@ -7742,7 +7625,7 @@ Element 3 System - + obsolete obsolete @@ -7752,7 +7635,7 @@ Element 3 Parts - + obsolete obsolete @@ -7762,7 +7645,7 @@ Element 3 View - + obsolete obsolete @@ -7772,7 +7655,7 @@ Element 3 Edit - + obsolete obsolete @@ -7782,7 +7665,7 @@ Element 3 Create - + obsolete obsolete @@ -7792,7 +7675,7 @@ Element 3 Change category - + obsolete obsolete @@ -7802,7 +7685,7 @@ Element 3 Delete - + obsolete obsolete @@ -7812,7 +7695,7 @@ Element 3 Search - + obsolete obsolete @@ -7822,17 +7705,17 @@ Element 3 List all parts - + obsolete obsolete perm.part.no_price_parts - List parts without price infos + List parts without price info - + obsolete obsolete @@ -7842,7 +7725,7 @@ Element 3 List obsolete parts - + obsolete obsolete @@ -7852,7 +7735,7 @@ Element 3 Show parts with unknown instock - + obsolete obsolete @@ -7862,7 +7745,7 @@ Element 3 Change favorite status - + obsolete obsolete @@ -7872,7 +7755,7 @@ Element 3 List favorite parts - + obsolete obsolete @@ -7882,7 +7765,7 @@ Element 3 Show last edited/added parts - + obsolete obsolete @@ -7892,7 +7775,7 @@ Element 3 Show last modifying user - + obsolete obsolete @@ -7902,7 +7785,7 @@ Element 3 Show history - + obsolete obsolete @@ -7912,7 +7795,7 @@ Element 3 Name - + obsolete obsolete @@ -7922,7 +7805,7 @@ Element 3 Description - + obsolete obsolete @@ -7932,7 +7815,7 @@ Element 3 Instock - + obsolete obsolete @@ -7942,7 +7825,7 @@ Element 3 Minimum instock - + obsolete obsolete @@ -7952,17 +7835,17 @@ Element 3 Notes - + obsolete obsolete perm.part.storelocation - Storelocation + Storage location - + obsolete obsolete @@ -7972,17 +7855,17 @@ Element 3 Manufacturer - + obsolete obsolete perm.part.orderdetails - Order informations + Order information - + obsolete obsolete @@ -7992,7 +7875,7 @@ Element 3 Prices - + obsolete obsolete @@ -8002,7 +7885,7 @@ Element 3 File attachments - + obsolete obsolete @@ -8012,17 +7895,17 @@ Element 3 Orders - + obsolete obsolete perm.storelocations - Storelocations + Storage locations - + obsolete obsolete @@ -8032,7 +7915,7 @@ Element 3 Move - + obsolete obsolete @@ -8042,7 +7925,7 @@ Element 3 List parts - + obsolete obsolete @@ -8052,7 +7935,7 @@ Element 3 Footprints - + obsolete obsolete @@ -8062,7 +7945,7 @@ Element 3 Categories - + obsolete obsolete @@ -8072,7 +7955,7 @@ Element 3 Suppliers - + obsolete obsolete @@ -8082,7 +7965,7 @@ Element 3 Manufacturers - + obsolete obsolete @@ -8092,7 +7975,7 @@ Element 3 Projects - + obsolete obsolete @@ -8102,7 +7985,7 @@ Element 3 Attachment types - + obsolete obsolete @@ -8112,7 +7995,7 @@ Element 3 Import - + obsolete obsolete @@ -8122,7 +8005,7 @@ Element 3 Labels - + obsolete obsolete @@ -8132,7 +8015,7 @@ Element 3 Resistor calculator - + obsolete obsolete @@ -8142,7 +8025,7 @@ Element 3 Footprints - + obsolete obsolete @@ -8152,7 +8035,7 @@ Element 3 IC logos - + obsolete obsolete @@ -8162,7 +8045,7 @@ Element 3 Statistics - + obsolete obsolete @@ -8172,7 +8055,7 @@ Element 3 Edit permissions - + obsolete obsolete @@ -8182,7 +8065,7 @@ Element 3 Edit user name - + obsolete obsolete @@ -8192,17 +8075,17 @@ Element 3 Change group - + obsolete obsolete perm.users.edit_infos - Edit infos + Edit info - + obsolete obsolete @@ -8212,7 +8095,7 @@ Element 3 Edit permissions - + obsolete obsolete @@ -8222,7 +8105,7 @@ Element 3 Set password - + obsolete obsolete @@ -8232,7 +8115,7 @@ Element 3 Change user settings - + obsolete obsolete @@ -8242,7 +8125,7 @@ Element 3 Show status - + obsolete obsolete @@ -8252,7 +8135,7 @@ Element 3 Update DB - + obsolete obsolete @@ -8262,7 +8145,7 @@ Element 3 Read DB settings - + obsolete obsolete @@ -8272,7 +8155,7 @@ Element 3 Write DB settings - + obsolete obsolete @@ -8282,7 +8165,7 @@ Element 3 Read config - + obsolete obsolete @@ -8292,7 +8175,7 @@ Element 3 Edit config - + obsolete obsolete @@ -8302,7 +8185,7 @@ Element 3 Server info - + obsolete obsolete @@ -8312,7 +8195,7 @@ Element 3 Use debug tools - + obsolete obsolete @@ -8322,7 +8205,7 @@ Element 3 Show logs - + obsolete obsolete @@ -8332,17 +8215,17 @@ Element 3 Delete logs - + obsolete obsolete perm.self.edit_infos - Edit infos + Edit info - + obsolete obsolete @@ -8352,7 +8235,7 @@ Element 3 Edit username - + obsolete obsolete @@ -8362,7 +8245,7 @@ Element 3 View permissions - + obsolete obsolete @@ -8372,7 +8255,7 @@ Element 3 Show own log entries - + obsolete obsolete @@ -8382,7 +8265,7 @@ Element 3 Create labels - + obsolete obsolete @@ -8392,7 +8275,7 @@ Element 3 Edit options - + obsolete obsolete @@ -8402,7 +8285,7 @@ Element 3 Delete profiles - + obsolete obsolete @@ -8412,7 +8295,7 @@ Element 3 Edit profiles - + obsolete obsolete @@ -8422,7 +8305,7 @@ Element 3 Tools - + obsolete obsolete @@ -8432,7 +8315,7 @@ Element 3 Groups - + obsolete obsolete @@ -8442,7 +8325,7 @@ Element 3 Users - + obsolete obsolete @@ -8452,7 +8335,7 @@ Element 3 Database - + obsolete obsolete @@ -8462,7 +8345,7 @@ Element 3 Configuration - + obsolete obsolete @@ -8472,7 +8355,7 @@ Element 3 System - + obsolete obsolete @@ -8482,7 +8365,7 @@ Element 3 Own user - + obsolete obsolete @@ -8492,7 +8375,7 @@ Element 3 Labels - + obsolete obsolete @@ -8502,7 +8385,7 @@ Element 3 Category - + obsolete obsolete @@ -8512,7 +8395,7 @@ Element 3 Minimum amount - + obsolete obsolete @@ -8522,7 +8405,7 @@ Element 3 Footprint - + obsolete obsolete @@ -8532,7 +8415,7 @@ Element 3 MPN - + obsolete obsolete @@ -8542,7 +8425,7 @@ Element 3 Manufacturing status - + obsolete obsolete @@ -8552,7 +8435,7 @@ Element 3 Tags - + obsolete obsolete @@ -8562,7 +8445,7 @@ Element 3 Part unit - + obsolete obsolete @@ -8572,7 +8455,7 @@ Element 3 Mass - + obsolete obsolete @@ -8582,7 +8465,7 @@ Element 3 Part lots - + obsolete obsolete @@ -8592,7 +8475,7 @@ Element 3 Show last modifying user - + obsolete obsolete @@ -8602,7 +8485,7 @@ Element 3 Currencies - + obsolete obsolete @@ -8612,7 +8495,7 @@ Element 3 Measurement unit - + obsolete obsolete @@ -8622,7 +8505,7 @@ Element 3 Old password - + obsolete obsolete @@ -8632,7 +8515,7 @@ Element 3 Reset password - + obsolete obsolete @@ -8642,7 +8525,7 @@ Element 3 Security key (U2F) - + obsolete obsolete @@ -8652,13 +8535,13 @@ Element 3 Google - + tfa.provider.webauthn_two_factor_provider Security key - + obsolete obsolete @@ -8668,7 +8551,7 @@ Element 3 Authenticator app - + obsolete obsolete @@ -8678,7 +8561,7 @@ Element 3 Login successful - + obsolete obsolete @@ -8688,7 +8571,7 @@ Element 3 Unhandled exception (obsolete) - + obsolete obsolete @@ -8698,7 +8581,7 @@ Element 3 User login - + obsolete obsolete @@ -8708,7 +8591,7 @@ Element 3 User logout - + obsolete obsolete @@ -8718,7 +8601,7 @@ Element 3 Unknown - + obsolete obsolete @@ -8728,7 +8611,7 @@ Element 3 Element created - + obsolete obsolete @@ -8738,7 +8621,7 @@ Element 3 Element edited - + obsolete obsolete @@ -8748,7 +8631,7 @@ Element 3 Element deleted - + obsolete obsolete @@ -8758,7 +8641,7 @@ Element 3 Database updated - + obsolete @@ -8767,7 +8650,7 @@ Element 3 Revert element - + obsolete @@ -8776,7 +8659,7 @@ Element 3 Show history - + obsolete @@ -8785,7 +8668,7 @@ Element 3 Show last activity - + obsolete @@ -8794,16 +8677,7 @@ Element 3 Show old element versions (time travel) - - - obsolete - - - log.type. - __log.type. - - - + obsolete @@ -8812,7 +8686,7 @@ Element 3 Security key added successfully. - + obsolete @@ -8821,7 +8695,7 @@ Element 3 Username - + obsolete @@ -8830,7 +8704,7 @@ Element 3 Authenticator App disabled - + obsolete @@ -8839,7 +8713,7 @@ Element 3 Security key removed - + obsolete @@ -8848,7 +8722,7 @@ Element 3 Security key added - + obsolete @@ -8857,7 +8731,7 @@ Element 3 Backup keys regenerated - + obsolete @@ -8866,7 +8740,7 @@ Element 3 Authenticator App enabled - + obsolete @@ -8875,7 +8749,7 @@ Element 3 Password changed - + obsolete @@ -8884,7 +8758,7 @@ Element 3 Trusted devices resetted - + obsolete @@ -8893,7 +8767,7 @@ Element 3 Element of Collection deleted - + obsolete @@ -8902,7 +8776,7 @@ Element 3 Password reset - + obsolete @@ -8911,7 +8785,7 @@ Element 3 Two Factor Reset by Administrator - + obsolete @@ -8920,7 +8794,7 @@ Element 3 Unauthorized access attempt - + obsolete @@ -8929,7 +8803,7 @@ Element 3 Success - + obsolete @@ -8938,7 +8812,7 @@ Element 3 2D - + obsolete @@ -8947,7 +8821,7 @@ Element 3 1D - + obsolete @@ -8956,7 +8830,7 @@ Element 3 Parameters - + obsolete @@ -8965,7 +8839,7 @@ Element 3 View private attachments - + obsolete @@ -8974,7 +8848,7 @@ Element 3 Label scanner - + obsolete @@ -8983,7 +8857,7 @@ Element 3 Read profiles - + obsolete @@ -8992,7 +8866,7 @@ Element 3 Create profiles - + obsolete @@ -9001,1973 +8875,3499 @@ Element 3 Use twig mode - + label_profile.showInDropdown Show in quick select - + group.edit.enforce_2fa Enforce Two-factor authentication (2FA) - + group.edit.enforce_2fa.help If this option is enabled, every direct member of this group, has to configure at least one second-factor for authentication. Recommended for administrative groups with much permissions. - + selectpicker.empty Nothing selected - + selectpicker.nothing_selected Nothing selected - + entity.delete.must_not_contain_parts Element "%PATH%" still contains parts! You have to move the parts, to be able to delete this element. - + entity.delete.must_not_contain_attachments Attachment type still contains attachments. Change their type, to be able to delete this attachment type. - + entity.delete.must_not_contain_prices Currency still contains price details. You have to change their currency to be able to delete this element. - + entity.delete.must_not_contain_users Users still uses this group! Change their group, to be able to delete this group. - + part.table.edit Edit - + part.table.edit.title Edit part - + part_list.action.action.title Select action - + part_list.action.action.group.favorite Favorite status - + part_list.action.action.favorite Favorite - + part_list.action.action.unfavorite Unfavorite - + part_list.action.action.group.change_field Change field - + part_list.action.action.change_category Change category - + part_list.action.action.change_footprint Change footprint - + part_list.action.action.change_manufacturer Change manufacturer - + part_list.action.action.change_unit Change part unit - + part_list.action.action.delete Delete - + part_list.action.submit Submit - + part_list.action.part_count %count% parts selected! - + company.edit.quick.website Open website - + company.edit.quick.email Send email - + company.edit.quick.phone Call phone - + company.edit.quick.fax Send fax - + company.fax_number.placeholder e.g. +49 1234 567890 - + part.edit.save_and_clone Save and clone - + validator.file_ext_not_allowed File extension not allowed for this attachment type. - + tools.reel_calc.title SMD Reel calculator - + tools.reel_calc.inner_dia Inner diameter - + tools.reel_calc.outer_dia Outer diameter - + tools.reel_calc.tape_thick Tape thickness - + tools.reel_calc.part_distance Part distance - + tools.reel_calc.update Update - + tools.reel_calc.parts_per_meter Parts per meter - + tools.reel_calc.result_length Tape length - + tools.reel_calc.result_amount Approx. parts count - + tools.reel_calc.outer_greater_inner_error Error: Outer diameter must be greater than inner diameter! - + tools.reel_calc.missing_values.error Please fill in all values! - + tools.reel_calc.load_preset Load preset - + tools.reel_calc.explanation This calculator gives you an estimation, how many parts are remaining on an SMD reel. Measure the noted the dimensions on the reel (or use some of the presets) and click "Update" to get an result. - + perm.tools.reel_calculator SMD Reel calculator - + tree.tools.tools.reel_calculator SMD Reel calculator - + user.pw_change_needed.flash Your password needs to be changed! Please set a new password. - + tree.root_node.text Root node - + part_list.action.select_null Empty element - + part_list.action.delete-title Do you really want to delete these parts? - + part_list.action.delete-message These parts and any associated information (like attachments, price information, etc.) will be deleted. This can not be undone! - + part.table.actions.success Actions finished successfully. - + attachment.edit.delete.confirm Do you really want to delete this attachment? - + filter.text_constraint.value.operator.EQ Is - + filter.text_constraint.value.operator.NEQ Is not - + filter.text_constraint.value.operator.STARTS Starts with - + filter.text_constraint.value.operator.CONTAINS Contains - + filter.text_constraint.value.operator.ENDS Ends with - + filter.text_constraint.value.operator.LIKE LIKE pattern - + filter.text_constraint.value.operator.REGEX Regular expression - + filter.number_constraint.value.operator.BETWEEN Between - + filter.number_constraint.AND and - + filter.entity_constraint.operator.EQ Is (excluding children) - + filter.entity_constraint.operator.NEQ Is not (excluding children) - + filter.entity_constraint.operator.INCLUDING_CHILDREN Is (including children) - + filter.entity_constraint.operator.EXCLUDING_CHILDREN - Is not (excluding children) + Is not (including children) - + part.filter.dbId Database ID - + filter.tags_constraint.operator.ANY Any of the tags - + filter.tags_constraint.operator.ALL All the tags - + filter.tags_constraint.operator.NONE None of the tags - + part.filter.lot_count Number of lots - + part.filter.attachments_count Number of attachments - + part.filter.orderdetails_count - Number of orderdetails + Number of order details - + part.filter.lotExpirationDate Lot expiration date - + part.filter.lotNeedsRefill Any lot needs refill - + part.filter.lotUnknwonAmount Any lot has unknown amount - + part.filter.attachmentName Attachment name - + filter.choice_constraint.operator.ANY Any of - + filter.choice_constraint.operator.NONE None of - + part.filter.amount_sum Total amount - + filter.submit Update - + filter.discard Discard changes - + filter.clear_filters Clear all filters - + filter.title Filter - + filter.parameter_value_constraint.operator.= Typ. Value = - + filter.parameter_value_constraint.operator.!= Typ. Value != - + filter.parameter_value_constraint.operator.< Typ. Value < - + filter.parameter_value_constraint.operator.> Typ. Value > - + filter.parameter_value_constraint.operator.<= Typ. Value <= - + filter.parameter_value_constraint.operator.>= Typ. Value >= - + filter.parameter_value_constraint.operator.BETWEEN Typ. Value is between - + filter.parameter_value_constraint.operator.IN_RANGE In Value range - + filter.parameter_value_constraint.operator.NOT_IN_RANGE Not in Value range - + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE Greater than Value range - + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE Greater equal than Value range - + filter.parameter_value_constraint.operator.LESS_THAN_RANGE Less than Value range - + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE Less equal than Value range - + filter.parameter_value_constraint.operator.RANGE_IN_RANGE Range is completely in Value range - + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE Range intersects Value range - + filter.text_constraint.value No value set - + filter.number_constraint.value1 No value set - + filter.number_constraint.value2 Maximum value - + filter.datetime_constraint.value1 No datetime set - + filter.datetime_constraint.value2 Maximum datetime - + filter.constraint.add Add constraint - + part.filter.parameters_count Number of parameters - + part.filter.lotDescription Lot description - + parts_list.search.searching_for Searching parts with keyword <b>%keyword%</b> - + parts_list.search_options.caption Enabled search options - + attachment.table.element_type Associated element type - + log.level.debug Debug - + log.level.info Info - + log.level.notice Notice - + log.level.warning Warning - + log.level.error Error - + log.level.critical Critical - + log.level.alert Alert - + log.level.emergency Emergency - + log.type.security Security related event - + log.type.instock_changed [LEGACY] Instock changed - + log.target_id Target element ID - + entity.info.parts_count_recursive Number of parts with this element or its sub elements - + tools.server_infos.title Server info - + permission.preset.read_only Read-Only - + permission.preset.read_only.desc Only allow read operations on data - + permission.preset.all_inherit Inherit all - + permission.preset.all_inherit.desc Set all permissions to Inherit - + permission.preset.all_forbid Forbid all - + permission.preset.all_forbid.desc Set all permissions to Forbid - + permission.preset.all_allow Allow all - + permission.preset.all_allow.desc Set all permissions to allow - + perm.server_infos Server info - + permission.preset.editor Editor - + permission.preset.editor.desc Allow changing parts and data structures - + permission.preset.admin Admin - + permission.preset.admin.desc Allow administrative actions - + permission.preset.button Apply preset - + perm.attachments.show_private Show private attachments - + perm.attachments.list_attachments Show list of all attachments - + user.edit.permission_success Permission preset applied successfully. Check if the new permissions fit your needs. - + perm.group.data Data - + part_list.action.action.group.needs_review Needs Review - + part_list.action.action.set_needs_review Set Needs Review Status - + part_list.action.action.unset_needs_review Unset Needs Review Status - + part.edit.ipn Internal Part Number (IPN) - + part.ipn.not_defined Not defined - + part.table.ipn IPN - + currency.edit.update_rate Retrieve exchange rate - + currency.edit.exchange_rate_update.unsupported_currency The currency is unsupported by the exchange rate provider. Check your exchange rate provider configuration. - + currency.edit.exchange_rate_update.generic_error Unable to retrieve the exchange rate. Check your exchange rate provider configuration. - + currency.edit.exchange_rate_updated.success Retrieved the exchange rate successfully. - + project.bom.quantity BOM Qty. - + project.bom.mountnames Mount names - + project.bom.name Name - + project.bom.comment Notes - + project.bom.part Part - + project.bom.add_entry Add entry - + part_list.action.group.projects Projects - + part_list.action.projects.add_to_project Add parts to project - + project.bom.delete.confirm Do you really want to delete this BOM entry? - + project.add_parts_to_project Add parts to project BOM - + part.info.add_part_to_project Add this part to a project - + project_bom_entry.label BOM entry - + project.edit.status Project status - + project.status.draft Draft - + project.status.planning Planning - + project.status.in_production In production - + project.status.finished Finished - + project.status.archived Archived - + part.new_build_part.error.build_part_already_exists The project already has a build part! - + project.edit.associated_build_part Associated builds part - + project.edit.associated_build_part.add Add builds part - + project.edit.associated_build.hint This part represents the builds of this project, which are stored somewhere. - + part.info.projectBuildPart.hint This part represents the builds of the following project and is associated with it - + part.is_build_part Is project builds part - + project.info.title Project info - + project.info.bom_entries_count BOM entries - + project.info.sub_projects_count Subprojects - + project.info.bom_add_parts Add BOM entries - + project.info.info.label Info - + project.info.sub_projects.label Subprojects - + project.bom.price Price - + part.info.withdraw_modal.title.withdraw Withdraw parts from lot - + part.info.withdraw_modal.title.add Add parts to lot - + part.info.withdraw_modal.title.move Move parts from lot to another lot - + part.info.withdraw_modal.amount Amount - + part.info.withdraw_modal.move_to Move to - + part.info.withdraw_modal.comment Comment - + part.info.withdraw_modal.comment.hint You can set a comment here describing why you are doing this operation (e.g. for what you need the parts). This info will be saved in the log. - + modal.close Close - + modal.submit Submit - + part.withdraw.success Added/Moved/Withdrawn parts successfully. - + perm.parts_stock Parts Stock - + perm.parts_stock.withdraw Withdraw parts from stock - + perm.parts_stock.add Add parts to stock - + perm.parts_stock.move Move parts between lots - + user.permissions_schema_updated The permission schema of your user were upgraded to the latest version. - + log.type.part_stock_changed Part Stock changed - + log.part_stock_changed.withdraw Stock withdrawn - + log.part_stock_changed.add Stock added - + log.part_stock_changed.move Stock moved - + log.part_stock_changed.comment Comment - + log.part_stock_changed.change Change - + log.part_stock_changed.move_target Move target - + tools.builtin_footprints_viewer.title Builtin footprint image gallery - + tools.builtin_footprints_viewer.hint This gallery lists all available built-in footprint images. If you want to use them in an attachment, type in the name (or a keyword) in the path field of the attachment and select the image from the dropdown select. - + tools.ic_logos.title IC logos - + part_list.action.group.labels Labels - + part_list.action.projects.generate_label Generate labels (for parts) - + part_list.action.projects.generate_label_lot Generate labels (for part lots) - + part_list.action.generate_label.empty Empty label - + project.info.builds.label Build - + project.builds.build_not_possible Build not possible: Parts not stocked - + project.builds.following_bom_entries_miss_instock The following parts have not enough stock to build this project at least once: - + project.builds.stocked stocked - + project.builds.needed needed - + project.builds.build_possible Build possible - + project.builds.number_of_builds_possible You have enough stocked to build <b>%max_builds%</b> builds of this project. - + project.builds.check_project_status The current project status is <b>"%project_status%"</b>. You should check if you really want to build the project with this status! - + project.builds.following_bom_entries_miss_instock_n You do not have enough parts stocked to build this project %number_of_builds% times. The following parts have missing instock: - + project.build.flash.invalid_input Can not build project. Check input! - + project.build.required_qty Required quantity - + project.build.btn_build Build - + project.build.help Choose from which part lots the stock to build this project should be taken (and in which amount). Check the checkbox for each BOM Entry, when you are finished withdrawing the parts, or use the top checkbox to check all boxes at once. - + project.build.buildsPartLot.new_lot Create new lot - + project.build.add_builds_to_builds_part Add builds to project builds part - + project.build.builds_part_lot Target lot - + project.builds.number_of_builds Build amount - + project.builds.no_stocked_builds Number of stocked builds - + user.change_avatar.label Change profile picture - + user_settings.change_avatar.label Change profile picture - + user_settings.remove_avatar.label Remove profile picture - + part.edit.name.category_hint Hint from category - + category.edit.partname_regex.placeholder e.g "/Capacitor \d+ nF/i" - + category.edit.partname_regex.help A PCRE-compatible regular expression, which a part name have to match. - + entity.select.add_hint Use -> to create nested structures, e.g. "Node 1->Node 1.1" - + entity.select.group.new_not_added_to_DB New (not added to DB yet) - + part.edit.save_and_new Save and create new empty part - + homepage.first_steps.title First steps - + homepage.first_steps.introduction Your database is still empty. You might want to read the <a href="%url%">documentation</a> or start to creating the following data structures: - + homepage.first_steps.create_part Or you can directly <a href="%url%">create a new part</a>. - + homepage.first_steps.hide_hint This box will hide as soon as you have created your first part. - + homepage.forum.text For questions about Part-DB use the <a href="%href%" class="link-external" target="_blank">discussion forum</a> - + log.element_edited.changed_fields.category Category - + log.element_edited.changed_fields.footprint Footprint - + log.element_edited.changed_fields.manufacturer Manufacturer - + log.element_edited.changed_fields.value_typical typ. value - + log.element_edited.changed_fields.pw_reset_expires Password reset - + log.element_edited.changed_fields.comment Notes - + log.element_edited.changed_fields.supplierpartnr Supplier part number - + log.element_edited.changed_fields.supplier_product_url Link to offer - + log.element_edited.changed_fields.price Price - + log.element_edited.changed_fields.min_discount_quantity Minimum discount amount - + log.element_edited.changed_fields.original_filename Original filename - + log.element_edited.changed_fields.path Filepath - + log.element_edited.changed_fields.description Description - + log.element_edited.changed_fields.manufacturing_status Manufacturing status - + log.element_edited.changed_fields.options.barcode_type Barcode type - + log.element_edited.changed_fields.status Status - + log.element_edited.changed_fields.quantity BOM Qty. - + log.element_edited.changed_fields.mountnames Mountnames - + log.element_edited.changed_fields.name Name - + log.element_edited.changed_fields.part Part - + log.element_edited.changed_fields.price_currency Currency of price - + log.element_edited.changed_fields.partname_hint Part name hint - + log.element_edited.changed_fields.partname_regex Name filter - + log.element_edited.changed_fields.disable_footprints Disable footprints - + log.element_edited.changed_fields.disable_manufacturers Disable manufacturers - + log.element_edited.changed_fields.disable_autodatasheets Disable automatic datasheet links - + log.element_edited.changed_fields.disable_properties Disable properties - + log.element_edited.changed_fields.default_description Default description - + log.element_edited.changed_fields.default_comment Default notes - + log.element_edited.changed_fields.filetype_filter Allowed file extensions - + log.element_edited.changed_fields.not_selectable Not selected - + log.element_edited.changed_fields.parent Parent element - + log.element_edited.changed_fields.shipping_costs Shipping costs - + log.element_edited.changed_fields.default_currency Default currency - + log.element_edited.changed_fields.address Address - + log.element_edited.changed_fields.phone_number Phone number - + log.element_edited.changed_fields.fax_number Fax number - + log.element_edited.changed_fields.email_address Email - + log.element_edited.changed_fields.website Website - + log.element_edited.changed_fields.auto_product_url Product URL - + log.element_edited.changed_fields.is_full Storelocation full - + log.element_edited.changed_fields.limit_to_existing_parts Limit to existing parts - + log.element_edited.changed_fields.only_single_part Only single part - + log.element_edited.changed_fields.storage_type Storage type - + log.element_edited.changed_fields.footprint_3d 3D model - + log.element_edited.changed_fields.master_picture_attachment Preview image - + log.element_edited.changed_fields.exchange_rate Exchange rate - + log.element_edited.changed_fields.iso_code Exchange rate - + log.element_edited.changed_fields.unit Unit symbol - + log.element_edited.changed_fields.is_integer Is integer - + log.element_edited.changed_fields.use_si_prefix Use SI prefix - + log.element_edited.changed_fields.options.width Width - + log.element_edited.changed_fields.options.height Height - + log.element_edited.changed_fields.options.supported_element Target type - + log.element_edited.changed_fields.options.additional_css Additional styles (CSS) - + log.element_edited.changed_fields.options.lines Content - + log.element_edited.changed_fields.permissions.data Permissions - + log.element_edited.changed_fields.disabled Disabled - + log.element_edited.changed_fields.theme Theme - + log.element_edited.changed_fields.timezone Timezone - + log.element_edited.changed_fields.language Language - + log.element_edited.changed_fields.email Email - + log.element_edited.changed_fields.department Department - + log.element_edited.changed_fields.last_name Last name - + log.element_edited.changed_fields.first_name First name - + log.element_edited.changed_fields.group Group - + log.element_edited.changed_fields.currency Preferred currency - + log.element_edited.changed_fields.enforce2FA Enforce 2FA - + log.element_edited.changed_fields.symbol Symbol - + log.element_edited.changed_fields.value_min Min. value - + log.element_edited.changed_fields.value_max Max. value - + log.element_edited.changed_fields.value_text Text value - + log.element_edited.changed_fields.show_in_table Show in table - + log.element_edited.changed_fields.attachment_type Show in table - + log.element_edited.changed_fields.needs_review Needs review - + log.element_edited.changed_fields.tags Tags - + log.element_edited.changed_fields.mass Mass - + log.element_edited.changed_fields.ipn IPN - + log.element_edited.changed_fields.favorite Favorite - + log.element_edited.changed_fields.minamount Minimum stock - + log.element_edited.changed_fields.manufacturer_product_url Link to product page - + log.element_edited.changed_fields.manufacturer_product_number MPN - + log.element_edited.changed_fields.partUnit Measuring Unit - + log.element_edited.changed_fields.expiration_date Expiration date - + log.element_edited.changed_fields.amount Amount - + log.element_edited.changed_fields.storage_location Storage location + + + attachment.max_file_size + Maximum file size + + + + + user.saml_user + SSO / SAML user + + + + + user.saml_user.pw_change_hint + Your user uses single sign-on (SSO). You can not change the password and 2FA settings here. Configure them on your central SSO provider instead! + + + + + login.sso_saml_login + Single Sign-On Login (SSO) + + + + + login.local_login_hint + The form below is only for log in with a local user. If you want to log in via single sign-on, press the button above. + + + + + part_list.action.action.export + Export parts + + + + + part_list.action.export_json + Export to JSON + + + + + part_list.action.export_csv + Export to CSV + + + + + part_list.action.export_yaml + Export to YAML + + + + + part_list.action.export_xml + Export to XML + + + + + parts.import.title + Import parts + + + + + parts.import.errors.title + Import violations + + + + + parts.import.flash.error + Errors during import. This is most likely caused by some invalid data. + + + + + parts.import.format.auto + Automatic (based on file extension) + + + + + parts.import.flash.error.unknown_format + Could not determine the format from the given file! + + + + + parts.import.flash.error.invalid_file + File invalid. Please check that you have selected the right format! + + + + + parts.import.part_category.label + Category override + + + + + parts.import.part_category.help + If you select a value here, all imported parts will be assigned to this category. No matter what was set in the data. + + + + + import.create_unknown_datastructures + Create unknown datastructures + + + + + import.create_unknown_datastructures.help + If this is selected, datastructures (like categories, footprints, etc.) which does not exist in the database yet, will be automatically created. If this is not selected, only existing data structures will be used, and if no matching data structure is found, the part will get assigned nothing + + + + + import.path_delimiter + Path delimiter + + + + + import.path_delimiter.help + The delimiter used to mark different levels in data structure pathes like category, footprint, etc. + + + + + parts.import.help_documentation + See the <a href="%link%">documentation</a> for more information on the file format. + + + + + parts.import.help + You can import parts from existing files with this tool. The parts will be directly written to database, so please check your file beforehand for correct content before uploading it here. + + + + + parts.import.flash.success + Part import successful! + + + + + parts.import.errors.imported_entities + Imported parts + + + + + perm.import + Import data + + + + + parts.import.part_needs_review.label + Mark all imported parts as "Needs review" + + + + + parts.import.part_needs_review.help + If this option is selected, then all parts will be marked as "Needs review", no matter what was set in the data. + + + + + project.bom_import.flash.success + Imported %count% BOM entries successfully. + + + + + project.bom_import.type + Type + + + + + project.bom_import.type.kicad_pcbnew + KiCAD Pcbnew BOM (CSV file) + + + + + project.bom_import.clear_existing_bom + Clear existing BOM entries before importing + + + + + project.bom_import.clear_existing_bom.help + Selecting this option will remove all existing BOM entries in the project and overwrite them with the imported BOM file! + + + + + project.bom_import.flash.invalid_file + File could not be imported. Please check that you have selected the right file type. Error message: %message% + + + + + project.bom_import.flash.invalid_entries + Validation error! Please check your data! + + + + + project.import_bom + Import BOM for project + + + + + project.edit.bom.import_bom + Import BOM + + + + + measurement_unit.new + New Measurement Unit + + + + + measurement_unit.edit + Edit Measurement Unit + + + + + user.aboutMe.label + About Me + + + + + storelocation.owner.label + Owner + + + + + storelocation.part_owner_must_match.label + Part Lot owner must match storage location owner + + + + + part_lot.owner + Owner + + + + + part_lot.owner.help + Only the owner can withdraw or add stock to this lot. + + + + + log.element_edited.changed_fields.owner + Owner + + + + + log.element_edited.changed_fields.instock_unknown + Amount unknown + + + + + log.element_edited.changed_fields.needs_refill + Refill needed + + + + + part.withdraw.access_denied + Not allowed to do the desired action. Please check your permissions and the owner of the part lots. + + + + + part.info.amount.less_than_desired + Less than desired + + + + + log.cli_user + CLI user + + + + + log.element_edited.changed_fields.part_owner_must_match + Part owner must match storage location owner + + + + + part.filter.lessThanDesired + In stock less than desired (total amount < min. amount) + + + + + part.filter.lotOwner + Lot owner + + + + + user.show_email_on_profile.label + Show email on public profile page + + + + + log.details.title + Log details + + + + + log.user_login.login_from_ip + Login from IP address + + + + + log.user_login.ip_anonymize_hint + If the last digits of the IP address are missing, then the GDPR mode is enabled, in which IP addresses are anynomized. + + + + + log.user_not_allowed.unauthorized_access_attempt_to + Unauthorized access attempt to page + + + + + log.user_not_allowed.hint + The request was blocked. No action should be required. + + + + + log.no_comment + No comment + + + + + log.element_changed.field + Field + + + + + log.element_changed.data_before + Data before change + + + + + error_table.error + An error occured during your request. + + + + + part.table.invalid_regex + Invalid regular expression (regex) + + + + + log.element_changed.data_after + Data after change + + + + + log.element_changed.diff + Difference + + + + + log.undo.undo.short + Undo + + + + + log.undo.revert.short + Revert to this timestamp + + + + + log.view_version + View version + + + + + log.undo.undelete.short + Undelete + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + Owner + + + + + log.element_edited.changed_fields.parent_id + Parent + + + + + log.details.delete_entry + Delete log entry + + + + + log.delete.message.title + Do you really want to delete the log entry? + + + + + log.delete.message + If this is an element history entry, this breaks the element history! This can lead to unexpected results when using the time travel function. + + + + + log.collection_deleted.on_collection + on Collection + + + + + log.element_edited.changed_fields.attachments + Attachments + + + + + tfa_u2f.add_key.registration_error + An error occurred during the registration of the security key. Try again or use another security key! + + + + + log.target_type.none + None + + + + + ui.darkmode.light + Light + + + + + ui.darkmode.dark + Dark + + + + + ui.darkmode.auto + Auto (decide based on system settings) + + + + + label_generator.no_lines_given + No text content given! The labels will remain empty. + + + + + user.password_strength.very_weak + Very weak + + + + + user.password_strength.weak + Weak + + + + + user.password_strength.medium + Medium + + + + + user.password_strength.strong + Strong + + + + + user.password_strength.very_strong + Very strong + + + + + perm.users.impersonate + Impersonate other users + + + + + user.impersonated_by.label + Impersonated by + + + + + user.stop_impersonation + Stop impersonation + + + + + user.impersonate.btn + Impersonate + + + + + user.impersonate.confirm.title + Do you really want to impersonate this user? + + + + + user.impersonate.confirm.message + This will be logged. You should only do this with a good reason. + +Please note, that you can not impersonate a disabled user. If you try you will get an "Access Denied" message. + + + + + log.type.security.user_impersonated + User impersonated + + + + + info_providers.providers_list.title + Information providers + + + + + info_providers.providers_list.active + Active + + + + + info_providers.providers_list.disabled + Disabled + + + + + info_providers.capabilities.basic + Basic + + + + + info_providers.capabilities.footprint + Footprint + + + + + info_providers.capabilities.picture + Picture + + + + + info_providers.capabilities.datasheet + Datasheets + + + + + info_providers.capabilities.price + Prices + + + + + part.info_provider_reference.badge + The information provider used to create this part. + + + + + part.info_provider_reference + Created by Information provider + + + + + oauth_client.connect.btn + Connect OAuth + + + + + info_providers.table.provider.label + Provider + + + + + info_providers.search.keyword + Keyword + + + + + info_providers.search.submit + Search + + + + + info_providers.search.providers.help + Select the providers in which should be searched. + + + + + info_providers.search.providers + Providers + + + + + info_providers.search.info_providers_list + Show all available info providers + + + + + info_providers.search.title + Create parts from info provider + + + + + oauth_client.flash.connection_successful + Connected to OAuth application successfully! + + + + + perm.part.info_providers + Info providers + + + + + perm.part.info_providers.create_parts + Create parts from info provider + + + + + entity.edit.alternative_names.label + Alternative names + + + + + entity.edit.alternative_names.help + The alternative names given here, are used to find this element based on the results of the information providers. + + + + + info_providers.form.help_prefix + Provider + + + + + update_manager.new_version_available.title + New version available + + + + + update_manager.new_version_available.text + A new version of Part-DB is available. Check it out here + + + + + update_manager.new_version_available.only_administrators_can_see + Only administrators can see this message. + + + + + perm.system.show_available_updates + Show available Part-DB updates + + + + + user.settings.api_tokens + API tokens + + + + + user.settings.api_tokens.description + Using an API token, other applications can access Part-DB with your user rights, to perform various actions using the Part-DB REST API. If you delete an API token here, the application, which uses the token, will no longer be able to access Part-DB on your behalf. + + + + + api_tokens.name + Name + + + + + api_tokens.access_level + Access level + + + + + api_tokens.expiration_date + Expiration date + + + + + api_tokens.added_date + Added at + + + + + api_tokens.last_time_used + Last time used + + + + + datetime.never + Never + + + + + api_token.valid + Valid + + + + + api_token.expired + Expired + + + + + user.settings.show_api_documentation + Show API documentation + + + + + api_token.create_new + Create new API token + + + + + api_token.level.read_only + Read-Only + + + + + api_token.level.edit + Edit + + + + + api_token.level.admin + Admin + + + + + api_token.level.full + Full + + + + + api_tokens.access_level.help + You can restrict, what the API token can access. The access is always limited by the permissions of your user. + + + + + api_tokens.expiration_date.help + After this date, the token is not usable anymore. Leave empty if the token should never expire. + + + + + api_tokens.your_token_is + Your API token is + + + + + api_tokens.please_save_it + Please save it. You will not be able to see it again! + + + + + api_tokens.create_new.back_to_user_settings + Back to user settings + + + + + project.build.dont_check_quantity + Do not check quantities + + + + + project.build.dont_check_quantity.help + If this option is selected, the given withdraw quantities are used as given, no matter if more or less parts are actually required to build this project. + + + + + part_list.action.invert_selection + Invert selection + + + + + perm.api + API + + + + + perm.api.access_api + Access API + + + + + perm.api.manage_tokens + Manage API tokens + + + + + user.settings.api_tokens.delete.title + Do you really want to delete this API token? + + + + + user.settings.api_tokens.delete + Delete + + + + + user.settings.api_tokens.delete.message + The application, which uses this API token, will no longer have access to Part-DB. This action can not be undone! + + + + + api_tokens.deleted + API token deleted successfully! + + + + + user.settings.api_tokens.no_api_tokens_yet + No API tokens configured yet. + + + + + api_token.ends_with + Ends with + + + + + entity.select.creating_new_entities_not_allowed + You are not allowed to create new entities of this type! Please choose a pre-existing one. + + + + + scan_dialog.mode + Barcode type + + + + + scan_dialog.mode.auto + Auto detect + + + + + scan_dialog.mode.ipn + IPN barcode + + + + + scan_dialog.mode.internal + Part-DB barcode + + + + + part_association.label + Part association + + + + + part.edit.tab.associations + Associated parts + + + + + part_association.edit.other_part + Associated part + + + + + part_association.edit.type + Relation Type + + + + + part_association.edit.comment + Notes + + + + + part_association.edit.type.help + You can select here, how the chosen part is related to this part. + + + + + part_association.table.from_this_part + Associations from this part to others + + + + + part_association.table.from + From + + + + + part_association.table.type + Relation + + + + + part_association.table.to + To + + + + + part_association.type.compatible + Is compatible with + + + + + part_association.table.to_this_part + Associations to this part from others + + + + + part_association.type.other + Other (custom value) + + + + + part_association.type.supersedes + Supersedes + + + + + part_association.edit.other_type + Custom type + + + + + part_association.edit.delete.confirm + Do you really want to delete this association? This can not be undone. + + + + + part_lot.edit.advanced + Expand advanced options + + + + + part_lot.edit.vendor_barcode + Vendor barcode + + + + + part_lot.edit.vendor_barcode.help + If this lot already have a barcode (e.g. put there by the vendor), you can input its content here, to easily scan it. + + + + + scan_dialog.mode.vendor + Vendor barcode (configured in part lot) + + + + + project.bom.instockAmount + Stocked amount + + + + + collection_type.new_element.tooltip + This element was newly created and was not persisted to the database yet. + + + + + part.merge.title + Merge part + + + + + part.merge.title.into + into + + + + + part.merge.confirm.title + Do you really want to merge <b>%other%</b> into <b>%target%</b>? + + + + + part.merge.confirm.message + <b>%other%</b> will be deleted, and the part will be saved with the shown information. + + + + + part.info.merge_modal.title + Merge parts + + + + + part.info.merge_modal.other_part + Other part + + + + + part.info.merge_modal.other_into_this + Merge other part into this one (delete other part, keep this one) + + + + + part.info.merge_modal.this_into_other + Merge this part into other one (delete this part, keep other one) + + + + + part.info.merge_btn + Merge part + + + + + part.update_part_from_info_provider.btn + Update part from info providers + + + + + info_providers.update_part.title + Update existing part from info provider + + + + + part.merge.flash.please_review + Data not saved yet. Review the changes and click save to persist the new data. + + + + + user.edit.flash.permissions_fixed + Permissions required by other permissions were missing. This was corrected. Please check if the permissions are as you intended. + + + + + permission.legend.dependency_note + Please note that some permission operations depend on each other. If you encounter a warning that missing permissions were corrected and a permission was set to allow again, you have to set the dependent operation to forbid too. The dependents can normally found right of an operation. + + + + + log.part_stock_changed.timestamp + Timestamp + + + + + part.info.withdraw_modal.timestamp + Action timestamp + + + + + part.info.withdraw_modal.timestamp.hint + This field allows you to specify the real date, when the stock operation actually was performed, and not just when it was logged. This value is saved in the extra field of the log entry. + + + + + part.info.withdraw_modal.delete_lot_if_empty + Delete this lot, if it becomes empty + + + + + info_providers.search.error.client_exception + An error occurred while communicating with the information provider. Check the configuration for this provider and refresh the OAuth tokens if possible. + + + + + eda_info.reference_prefix.placeholder + e.g. R + + + + + eda_info.reference_prefix + Reference prefix + + + + + eda_info.kicad_section.title + KiCad specific settings + + + + + eda_info.value + Value + + + + + eda_info.value.placeholder + e.g. 100n + + + + + eda_info.exclude_from_bom + Exclude part from BOM + + + + + eda_info.exclude_from_board + Exclude part from PCB/Board + + + + + eda_info.exclude_from_sim + Exclude part from simulation + + + + + eda_info.kicad_symbol + KiCad schematic symbol + + + + + eda_info.kicad_symbol.placeholder + e.g. Transistor_BJT:BC547 + + + + + eda_info.kicad_footprint + KiCad footprint + + + + + eda_info.kicad_footprint.placeholder + e.g. Package_TO_SOT_THT:TO-92 + + + + + part.edit.tab.eda + EDA information + + + + + api.api_endpoints.title + API endpoints + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + KiCad API root URL + + + + + eda_info.visibility + Force visibility + + + + + eda_info.visibility.help + By default, the visibility to the EDA software is automatically determined. With this checkbox, you can force the part to be visible or invisible. + + + + + part.withdraw.zero_amount + You tried to withdraw/add an amount of zero! No action was performed. + + + + + login.flash.access_denied_please_login + Access denied! Please log in to continue. + + + + + attachment.upload_multiple_files + Upload files + + + + + entity.mass_creation_flash + Created %COUNT% entities successfully. + + + + + info_providers.search.number_of_results + %number% results + + + + + info_providers.search.no_results + No results found at the selected providers! Check your search term or try to choose additional providers. + + + + + tfa.check.code.confirmation + Generated code + + + + + info_providers.search.show_existing_part + Show existing part + + + + + info_providers.search.edit_existing_part + Edit existing part + + + + + info_providers.search.existing_part_found.short + Part already existing + + + + + info_providers.search.existing_part_found + This part (or a very similar one) was already found in the database. Please check if it is the same and if you want to create it again! + + + + + info_providers.search.update_existing_part + Update existing part from info provider + + + + + part.create_from_info_provider.no_category_yet + Category could not be automatically determined by the info provider. Review the data and select the category manually. + + + + + part_lot.edit.user_barcode + User barcode + + + + + scan_dialog.mode.user + User defined barcode (configured at part lot) + + + + + scan_dialog.mode.eigp + EIGP 114 barcode (e.g. the datamatrix codes on digikey and mouser orders) + + + + + scan_dialog.info_mode + Info mode (Decode barcode and show its contents, but do not redirect to part) + + + + + label_scanner.decoded_info.title + Decoded information + + + + + label_generator.edit_profiles + Edit profiles + + + + + label_generator.profile_name_empty + Profile name must not be empty! + + + + + label_generator.save_profile_name + Profile name + + + + + label_generator.save_profile + Save as new profile + + + + + label_generator.profile_saved + Profile saved! + + + + + entity.export.flash.error.no_entities + There are no entities to export! + + + + + attachment.table.internal_file + Internal file + + + + + attachment.table.external_link + External link + + + + + attachment.view_external.view_at + View at %host% + + + + + attachment.view_external + View external version + + + + + part.table.actions.error + %count% errors occured, while performing action: + + + + + part.table.actions.error_detail + %part_name% (ID: %part_id%): %message% + + + + + part_list.action.action.change_location + Change location (only for parts with single stock) + + + + + parts.table.action_handler.error.part_lots_multiple + This part contains more than one stock. Change the location by hand to select, which stock to choose. + + diff --git a/translations/messages.es.xlf b/translations/messages.es.xlf new file mode 100644 index 00000000..3694a7f3 --- /dev/null +++ b/translations/messages.es.xlf @@ -0,0 +1,12348 @@ + + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + + + attachment_type.caption + Tipo de archivo para adjuntos + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 + new + + + attachment_type.edit + Editar tipo de archivo + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 + new + + + attachment_type.new + Nuevo tipo de archivo + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + templates\AdminPages\CategoryAdmin.html.twig:4 + templates\base.html.twig:163 + templates\base.html.twig:170 + templates\base.html.twig:197 + templates\base.html.twig:225 + + + category.labelp + Categorías + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:11 + templates\AdminPages\CategoryAdmin.html.twig:8 + + + admin.options + Opciones + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + templates\AdminPages\CategoryAdmin.html.twig:9 + + + admin.advanced + Avanzado + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 + new + + + category.edit + Editar categoría + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 + new + + + category.new + Nueva categoría + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + + + currency.caption + Divisa + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + + + currency.iso_code.caption + Código ISO + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + + + currency.symbol.caption + Símbolo de divisa + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 + new + + + currency.edit + Editar divisa + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 + new + + + currency.new + Nueva divisa + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + templates\AdminPages\DeviceAdmin.html.twig:4 + + + project.caption + Proyecto + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 + new + + + project.edit + Editar proyecto + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 + new + + + project.new + Nuevo proyecto + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:67 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + templates\AdminPages\EntityAdminBase.html.twig:9 + templates\base.html.twig:80 + templates\base.html.twig:179 + templates\base.html.twig:206 + templates\base.html.twig:237 + + + search.placeholder + Buscar + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + templates\AdminPages\EntityAdminBase.html.twig:13 + templates\base.html.twig:166 + templates\base.html.twig:193 + templates\base.html.twig:221 + + + expandAll + Expandir todo + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + templates\AdminPages\EntityAdminBase.html.twig:17 + templates\base.html.twig:167 + templates\base.html.twig:194 + templates\base.html.twig:222 + + + reduceAll + Reducir todo + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + + + part.info.timetravel_hint + Así aparecía el componente antes de: %timestamp%. <i>Esta función es experimental, así que la información podría no ser correcta.</i> + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + templates\AdminPages\EntityAdminBase.html.twig:42 + + + standard.label + Propiedades + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + templates\AdminPages\EntityAdminBase.html.twig:43 + + + infos.label + Información + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + new + + + history.label + Historial + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + templates\AdminPages\EntityAdminBase.html.twig:45 + + + export.label + Exportar + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + templates\AdminPages\EntityAdminBase.html.twig:47 + + + import_export.label + Importar / Exportar + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + + + mass_creation.label + Creación en masa + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + templates\AdminPages\EntityAdminBase.html.twig:59 + + + admin.common + Común + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + + + admin.attachments + Adjuntos + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 + + + admin.parameters + Parámetros + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 + templates\AdminPages\EntityAdminBase.html.twig:142 + + + export_all.label + Exportar todos los elementos + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 + + + mass_creation.help + Cada línea será interpretada como el nombre de un elemento, el cual será creado. Puedes crear estructuras anidadas con identificadores. + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + templates\AdminPages\EntityAdminBase.html.twig:35 + + + edit.caption + Editar elemento "%name" + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + templates\AdminPages\EntityAdminBase.html.twig:37 + + + new.caption + Nuevo elemento + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + templates\base.html.twig:172 + templates\base.html.twig:199 + templates\base.html.twig:227 + + + footprint.labelp + Footprints + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 + new + + + footprint.edit + Editar footprint + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 + new + + + footprint.new + Nuevo footprint + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + + + group.edit.caption + Grupos + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + + + user.edit.permissions + Permisos + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 + new + + + group.edit + Editar grupo + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 + new + + + group.new + Nuevo grupo + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 + + + label_profile.caption + Perfiles de etiqueta + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 + + + label_profile.advanced + Avanzado + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 + + + label_profile.comment + Notas + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 + new + + + label_profile.edit + Editar perfil de etiqueta + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 + new + + + label_profile.new + Nuevo perfil de etiqueta + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + templates\AdminPages\ManufacturerAdmin.html.twig:4 + + + manufacturer.caption + Fabricantes + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 + new + + + manufacturer.edit + Editar fabricante + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 + new + + + manufacturer.new + Nuevo fabricante + + + + + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + + + measurement_unit.caption + Unidad de medida + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 + Part-DB1\templates\_sidebar.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:8 + templates\base.html.twig:171 + templates\base.html.twig:198 + templates\base.html.twig:226 + + + storelocation.labelp + Ubicación del almacén + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 + new + + + storelocation.edit + Editar ubicación del almacén + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 + new + + + storelocation.new + Nueva ubicación de almacén + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + templates\AdminPages\SupplierAdmin.html.twig:4 + + + supplier.caption + Proveedores + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 + new + + + supplier.edit + Editar proveedor + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 + new + + + supplier.new + Nuevo proveedor + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + + + user.edit.caption + Usuarios + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + + + user.edit.configuration + Configuración + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + + + user.edit.password + Contraseña + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + + + user.edit.tfa.caption + Autenticación en dos pasos + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + + + user.edit.tfa.google_active + App de autenticación activa + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + + + tfa_backup.remaining_tokens + Cuenta de códigos backup restantes + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + + + tfa_backup.generation_date + Fecha de creación de los códigos backup + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + + + user.edit.tfa.disabled + Método no habilitado + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + + + user.edit.tfa.u2f_keys_count + Llaves de seguridad activas + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_title + ¿Estás seguro de que quieres continuar? + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_message + Esto deshabilitará <b>todos los métodos de autenticación en dos pasos del usuario</b> y eliminará los <b>códigos backup</b>! +<br> +¡El usuario deberá configurar todos los métodos de autenticación de nuevo e imprimir nuevos códigos backup! <br><br> +<b>¡Solo haz esto si estás seguro de la identidad del usuario (buscando ayuda), si no la cuenta podría estar comprometida por ataques externos!</b> + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + + + user.edit.tfa.disable_tfa.btn + Deshabilitar todos los métodos de autenticación en dos pasos + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 + new + + + user.edit + Editar usuario + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 + new + + + user.new + Nuevo usuario + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:63 + + + attachment.delete + Eliminar + + + + + attachment.external_only + Solo externo + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:49 + Part-DB1\templates\Parts\edit\_attachments.html.twig:47 + Part-DB1\templates\AdminPages\_attachments.html.twig:47 + Part-DB1\templates\Parts\edit\_attachments.html.twig:45 + + + attachment.preview.alt + Miniatura del adjunto + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:52 + Part-DB1\templates\Parts\edit\_attachments.html.twig:50 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + Part-DB1\templates\AdminPages\_attachments.html.twig:50 + Part-DB1\templates\Parts\edit\_attachments.html.twig:48 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:45 + + + attachment.view_local + Ver copia local + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:58 + Part-DB1\templates\Parts\edit\_attachments.html.twig:56 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:43 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + Part-DB1\templates\AdminPages\_attachments.html.twig:56 + Part-DB1\templates\Parts\edit\_attachments.html.twig:54 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + + + attachment.file_not_found + Archivo no encontrado + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:66 + Part-DB1\templates\Parts\edit\_attachments.html.twig:64 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:48 + Part-DB1\templates\Parts\edit\_attachments.html.twig:62 + + + attachment.secure + Adjunto privado + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:79 + Part-DB1\templates\Parts\edit\_attachments.html.twig:77 + Part-DB1\templates\AdminPages\_attachments.html.twig:77 + Part-DB1\templates\Parts\edit\_attachments.html.twig:75 + + + attachment.create + Añadir adjunto + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:84 + Part-DB1\templates\Parts\edit\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + Part-DB1\templates\AdminPages\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_attachments.html.twig:80 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + + + part_lot.edit.delete.confirm + ¿Estás seguro de que quieres eliminar este stock? ¡No se puede deshacer! + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + templates\AdminPages\_delete_form.html.twig:2 + + + entity.delete.confirm_title + ¿Estás seguro de que quieres eliminar %name%? + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + templates\AdminPages\_delete_form.html.twig:3 + + + entity.delete.message + ¡No se puede deshacer! +<br> +Subelementos serán desplazados hacia arriba. + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + templates\AdminPages\_delete_form.html.twig:9 + + + entity.delete + Eliminar elemento + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:45 + Part-DB1\src\Form\Part\PartBaseType.php:286 + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:43 + Part-DB1\src\Form\Part\PartBaseType.php:267 + new + + + edit.log_comment + Cambiar comentario + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + templates\AdminPages\_delete_form.html.twig:12 + + + entity.delete.recursive + Eliminar recursivamente (todos los subelementos) + + + + + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 + + + entity.duplicate + Duplicar elemento + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + templates\AdminPages\_export_form.html.twig:4 + src\Form\ImportType.php:67 + + + export.format + Formato de archivo + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + templates\AdminPages\_export_form.html.twig:16 + + + export.level + Nivel de verbosidad + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + templates\AdminPages\_export_form.html.twig:19 + + + export.level.simple + Simple + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + templates\AdminPages\_export_form.html.twig:20 + + + export.level.extended + Extendido + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + templates\AdminPages\_export_form.html.twig:21 + + + export.level.full + Completo + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + templates\AdminPages\_export_form.html.twig:31 + + + export.include_children + Incluir elementos hijo en la exportación + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + templates\AdminPages\_export_form.html.twig:39 + + + export.btn + Exportar + + + + + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + templates\AdminPages\EntityAdminBase.html.twig:94 + templates\Parts\edit_part_info.html.twig:12 + templates\Parts\show_part_info.html.twig:11 + + + id.label + ID + + + + + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:77 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:77 + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:59 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:60 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:53 + templates\AdminPages\EntityAdminBase.html.twig:101 + templates\Parts\show_part_info.html.twig:248 + + + createdAt + Creado en + + + + + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:73 + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:49 + templates\AdminPages\EntityAdminBase.html.twig:114 + templates\Parts\show_part_info.html.twig:263 + + + lastModified + Última modificación + + + + + Part-DB1\templates\AdminPages\_info.html.twig:38 + Part-DB1\templates\AdminPages\_info.html.twig:38 + + + entity.info.parts_count + Número de componentes de este elemento + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:6 + Part-DB1\templates\helper.twig:125 + Part-DB1\templates\Parts\edit\_specifications.html.twig:6 + + + specifications.property + Parámetro + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:7 + Part-DB1\templates\Parts\edit\_specifications.html.twig:7 + + + specifications.symbol + Símbolo + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:8 + Part-DB1\templates\Parts\edit\_specifications.html.twig:8 + + + specifications.value_min + Min. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:9 + Part-DB1\templates\Parts\edit\_specifications.html.twig:9 + + + specifications.value_typ + Typ. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:10 + Part-DB1\templates\Parts\edit\_specifications.html.twig:10 + + + specifications.value_max + Max. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:11 + Part-DB1\templates\Parts\edit\_specifications.html.twig:11 + + + specifications.unit + Unidad + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:12 + Part-DB1\templates\Parts\edit\_specifications.html.twig:12 + + + specifications.text + Texto + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:13 + Part-DB1\templates\Parts\edit\_specifications.html.twig:13 + + + specifications.group + Grupo + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:26 + Part-DB1\templates\Parts\edit\_specifications.html.twig:26 + + + specification.create + Nuevo parámetro + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:31 + Part-DB1\templates\Parts\edit\_specifications.html.twig:31 + + + parameter.delete.confirm + ¿Estás seguro de que quieres eliminar este parámetro? + + + + + Part-DB1\templates\attachment_list.html.twig:3 + Part-DB1\templates\attachment_list.html.twig:3 + + + attachment.list.title + Lista de adjuntos + + + + + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + + + part_list.loading.caption + Cargando + + + + + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + + + part_list.loading.message + Esto puede llevar unos instantes. Si este mensaje no desaparece, prueba a refrescar la página. + + + + + Part-DB1\templates\base.html.twig:68 + Part-DB1\templates\base.html.twig:68 + templates\base.html.twig:246 + + + vendor.base.javascript_hint + ¡Por favor, activa Javascript para poder usar todas las funciones! + + + + + Part-DB1\templates\base.html.twig:73 + Part-DB1\templates\base.html.twig:73 + + + sidebar.big.toggle + Mostrar/Esconder barra lateral + + + + + Part-DB1\templates\base.html.twig:95 + Part-DB1\templates\base.html.twig:95 + templates\base.html.twig:271 + + + loading.caption + Cargando: + + + + + Part-DB1\templates\base.html.twig:96 + Part-DB1\templates\base.html.twig:96 + templates\base.html.twig:272 + + + loading.message + Esto puede llevar un rato. Si este mensaje tarda mucho en desaparecer, prueba a refrescar la página. + + + + + Part-DB1\templates\base.html.twig:101 + Part-DB1\templates\base.html.twig:101 + templates\base.html.twig:277 + + + loading.bar + Cargando... + + + + + Part-DB1\templates\base.html.twig:112 + Part-DB1\templates\base.html.twig:112 + templates\base.html.twig:288 + + + back_to_top + Volver al inicio de la página + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:35 + Part-DB1\templates\Form\permissionLayout.html.twig:35 + + + permission.edit.permission + Permisos + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:36 + Part-DB1\templates\Form\permissionLayout.html.twig:36 + + + permission.edit.value + Valor + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:53 + Part-DB1\templates\Form\permissionLayout.html.twig:53 + + + permission.legend.title + Explicación de los estados + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:57 + Part-DB1\templates\Form\permissionLayout.html.twig:57 + + + permission.legend.disallow + Prohibido + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:61 + Part-DB1\templates\Form\permissionLayout.html.twig:61 + + + permission.legend.allow + Autorizado + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:65 + Part-DB1\templates\Form\permissionLayout.html.twig:65 + + + permission.legend.inherit + Heredar de grupo padre + + + + + Part-DB1\templates\helper.twig:3 + Part-DB1\templates\helper.twig:3 + + + bool.true + Verdadero + + + + + Part-DB1\templates\helper.twig:5 + Part-DB1\templates\helper.twig:5 + + + bool.false + Falso + + + + + Part-DB1\templates\helper.twig:92 + Part-DB1\templates\helper.twig:87 + + + Yes + + + + + + Part-DB1\templates\helper.twig:94 + Part-DB1\templates\helper.twig:89 + + + No + No + + + + + Part-DB1\templates\helper.twig:126 + + + specifications.value + Valor + + + + + Part-DB1\templates\homepage.html.twig:7 + Part-DB1\templates\homepage.html.twig:7 + templates\homepage.html.twig:7 + + + version.caption + Versión + + + + + Part-DB1\templates\homepage.html.twig:22 + Part-DB1\templates\homepage.html.twig:22 + templates\homepage.html.twig:19 + + + homepage.license + Información de licencia + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.caption + Página de proyecto + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.text + Fuente, descargas, informes de error, listas de quehaceres etc. pueden ser encontrados en <a href="%href%" class="link-external" target="_blank">GitHub página de proyecto</a> + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.caption + Ayuda + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.text + Ayuda y sugerencias pueden ser encontradas en la Wiki de <a href="%href%" class="link-external" target="_blank">GitHub página</a> + + + + + Part-DB1\templates\homepage.html.twig:33 + Part-DB1\templates\homepage.html.twig:33 + templates\homepage.html.twig:30 + + + homepage.forum.caption + Foro + + + + + Part-DB1\templates\homepage.html.twig:45 + Part-DB1\templates\homepage.html.twig:45 + new + + + homepage.last_activity + Última actividad + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:3 + Part-DB1\templates\LabelSystem\dialog.html.twig:6 + + + label_generator.title + Generador de etiquetas + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:16 + + + label_generator.common + Común + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:20 + + + label_generator.advanced + Avanzado + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:24 + + + label_generator.profiles + Perfiles + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:58 + + + label_generator.selected_profile + Perfil seleccionado + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:62 + + + label_generator.edit_profile + Editar perfil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:75 + + + label_generator.load_profile + Cargar perfil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:102 + + + label_generator.download + Descargar + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 + + + label_generator.label_btn + Generar etiqueta + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 + + + label_generator.label_empty + Nueva etiqueta vacía + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 + + + label_scanner.title + Lector de etiquetas + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.title + Webcam no encontrada + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.text + Necesitas una webcam y dar permiso para utilizar la función del lector. Puedes introducir el código de barras manualmente abajo. + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 + + + label_scanner.source_select + Selecciona una fuente + + + + + Part-DB1\templates\LogSystem\log_list.html.twig:3 + Part-DB1\templates\LogSystem\log_list.html.twig:3 + + + log.list.title + Registro de sistema + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + new + + + log.undo.confirm_title + ¿Quieres deshacer el cambio / volver a una fecha anterior? + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + new + + + log.undo.confirm_message + ¿Estás seguro de que quieres deshacer el cambio / reiniciar el elemento a una fecha dada? + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.email_sent_by + Este e-mail fue enviado automáticamente por + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.dont_reply + No respondas a este e-mail. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:6 + Part-DB1\templates\mail\pw_reset.html.twig:6 + + + email.hi %name% + Hi %name% + + + + + Part-DB1\templates\mail\pw_reset.html.twig:7 + Part-DB1\templates\mail\pw_reset.html.twig:7 + + + email.pw_reset.message + Alguien (esperemos que tú) ha solicitado cambiar la contraseña. Si tú no lo has solicitado, ignora este email. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:9 + Part-DB1\templates\mail\pw_reset.html.twig:9 + + + email.pw_reset.button + Pulsa aquí para restablecer la contraseña + + + + + Part-DB1\templates\mail\pw_reset.html.twig:11 + Part-DB1\templates\mail\pw_reset.html.twig:11 + + + email.pw_reset.fallback + Si esto no te funciona, ves a <a href="%url%">%url%</a> e introduce la siguiente información + + + + + Part-DB1\templates\mail\pw_reset.html.twig:16 + Part-DB1\templates\mail\pw_reset.html.twig:16 + + + email.pw_reset.username + Nombre de usuario + + + + + Part-DB1\templates\mail\pw_reset.html.twig:19 + Part-DB1\templates\mail\pw_reset.html.twig:19 + + + email.pw_reset.token + Token + + + + + Part-DB1\templates\mail\pw_reset.html.twig:24 + Part-DB1\templates\mail\pw_reset.html.twig:24 + + + email.pw_reset.valid_unit %date% + El token de reinicio será válido hasta <i>%date%</i>. + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:78 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + + + orderdetail.delete + Eliminar + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + + + pricedetails.edit.min_qty + Cantidad mínima de descuento + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + + + pricedetails.edit.price + Precio + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + + + pricedetails.edit.price_qty + Por la cantidad + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + + + pricedetail.create + Añadir precio + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + templates\Parts\edit_part_info.html.twig:4 + + + part.edit.title + Editar componente + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + templates\Parts\edit_part_info.html.twig:9 + + + part.edit.card_title + Editar componente + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + + + part.edit.tab.common + Común + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + + + part.edit.tab.manufacturer + Fabricante + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + + + part.edit.tab.advanced + Avanzado + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + + + part.edit.tab.part_lots + Stock + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + + + part.edit.tab.attachments + Adjuntos + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + + + part.edit.tab.orderdetails + Información del pedido + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.specifications + Parámetros + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.comment + Notas + + + + + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + templates\Parts\new_part.html.twig:8 + + + part.new.card_title + Crear nuevo componente + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + + + part_lot.delete + Eliminar + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + + + part_lot.create + Añadir stock + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + + + orderdetail.create + Añadir distribuidor + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + + + pricedetails.edit.delete.confirm + ¿Estás seguro de que quieres eliminar este precio? No se puede deshacer. + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 + + + orderdetails.edit.delete.confirm + ¿Estás seguro de que quieres eliminar la información de este distribuidor? ¡No se puede deshacer! + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + templates\Parts\show_part_info.html.twig:4 + templates\Parts\show_part_info.html.twig:9 + + + part.info.title + Información detallada del componente + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + + + part.part_lots.label + Stocks + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 + Part-DB1\templates\Parts\lists\_info_card.html.twig:43 + Part-DB1\templates\_navbar_search.html.twig:31 + Part-DB1\templates\_navbar_search.html.twig:26 + templates\base.html.twig:62 + templates\Parts\show_part_info.html.twig:74 + src\Form\PartType.php:86 + + + comment.label + Notas + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + + + part.info.specifications + Parámetros + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + templates\Parts\show_part_info.html.twig:82 + + + attachment.labelp + Adjuntos + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 + Part-DB1\templates\Parts\info\show_part_info.html.twig:71 + templates\Parts\show_part_info.html.twig:88 + + + vendor.partinfo.shopping_infos + Información de la compra + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 + Part-DB1\templates\Parts\info\show_part_info.html.twig:78 + templates\Parts\show_part_info.html.twig:94 + + + vendor.partinfo.history + Historial + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + Part-DB1\templates\Parts\info\show_part_info.html.twig:84 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + templates\base.html.twig:176 + templates\base.html.twig:203 + templates\base.html.twig:217 + templates\base.html.twig:231 + templates\Parts\show_part_info.html.twig:100 + + + tools.label + Herramientas + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 + Part-DB1\templates\Parts\info\show_part_info.html.twig:90 + + + extended_info.label + Información adicional + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + + + attachment.name + Nombre + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + + + attachment.attachment_type + Tipo de archivo adjunto + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + + + attachment.file_name + Nombre de archivo + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + + + attachment.file_size + Tamaño de archivo + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 + + + attachment.preview + Vista previa + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 + + + attachment.download_local + Descargar copia en local + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + new + + + user.creating_user + Usuario que ha creado este componente + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + + + Unknown + Desconocido + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + new + + + accessDenied + Acceso denegado + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + new + + + user.last_editing_user + Último usuario que ha editado este componente + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + + + part.isFavorite + Favorito + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + + + part.minOrderAmount + Cantidad mínima de pedido + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:46 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:41 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + templates\base.html.twig:70 + templates\Parts\show_part_info.html.twig:24 + src\Form\PartType.php:80 + + + manufacturer.label + Fabricante + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 + Part-DB1\templates\_navbar_search.html.twig:11 + templates\base.html.twig:54 + src\Form\PartType.php:62 + + + name.label + Nombre + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + new + + + part.back_to_info + Volver a la versión actual + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:19 + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:18 + templates\base.html.twig:58 + templates\Parts\show_part_info.html.twig:31 + src\Form\PartType.php:65 + + + description.label + Descripción + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:15 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:14 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + templates\base.html.twig:56 + templates\Parts\show_part_info.html.twig:32 + src\Form\PartType.php:74 + + + category.label + Categoría + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + templates\Parts\show_part_info.html.twig:42 + src\Form\PartType.php:69 + + + instock.label + En stock + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + templates\Parts\show_part_info.html.twig:44 + src\Form\PartType.php:72 + + + mininstock.label + Stock mínimo + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:52 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:47 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + templates\base.html.twig:73 + templates\Parts\show_part_info.html.twig:47 + + + footprint.label + Footprint + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 + Part-DB1\templates\Parts\info\_main_infos.html.twig:59 + Part-DB1\templates\Parts\info\_main_infos.html.twig:57 + Part-DB1\templates\Parts\info\_main_infos.html.twig:60 + templates\Parts\show_part_info.html.twig:51 + + + part.avg_price.label + Precio promedio + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + + + part.supplier.name + Nombre + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + + + part.supplier.partnr + Nº de pedido + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + + + part.order.minamount + Cantidad mínima + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + + + part.order.price + Precio + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + + + part.order.single_price + Precio unitario + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + + + edit.caption_short + Editar + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + + + delete.caption + Eliminar + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + Part-DB1\templates\Parts\info\_part_lots.html.twig:6 + + + part_lots.description + Descripción + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + + + part_lots.storage_location + Ubicación de almacenamiento + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + + + part_lots.amount + Cantidad + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 + Part-DB1\templates\Parts\info\_part_lots.html.twig:22 + + + part_lots.location_unknown + Ubicación del almacén desconocida + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 + Part-DB1\templates\Parts\info\_part_lots.html.twig:29 + + + part_lots.instock_unknown + Cantidad desconocida + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 + Part-DB1\templates\Parts\info\_part_lots.html.twig:38 + + + part_lots.expiration_date + Fecha de vencimiento + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 + Part-DB1\templates\Parts\info\_part_lots.html.twig:46 + + + part_lots.is_expired + Caducado + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 + Part-DB1\templates\Parts\info\_part_lots.html.twig:53 + + + part_lots.need_refill + Necesita ser recargado + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:15 + Part-DB1\templates\Parts\info\_picture.html.twig:15 + + + part.info.prev_picture + Imagen previa + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:19 + Part-DB1\templates\Parts\info\_picture.html.twig:19 + + + part.info.next_picture + Siguiente imagen + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + + + part.mass.tooltip + Peso + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + + + part.needs_review.badge + Necesita revisión + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + + + part.favorite.badge + Favorito + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + + + part.obsolete.badge + No disponible + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:10 + + + parameters.extracted_from_description + Extraído automáticamente de la descripción + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:15 + + + parameters.auto_extracted_from_comment + Extraído automáticamente de las notas + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:6 + Part-DB1\templates\Parts\info\_tools.html.twig:4 + templates\Parts\show_part_info.html.twig:125 + + + part.edit.btn + Editar componente + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:14 + templates\Parts\show_part_info.html.twig:135 + + + part.clone.btn + Clonar componente + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:24 + Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 + templates\Parts\show_part_info.html.twig:143 + + + part.create.btn + Crear nuevo componente + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:31 + Part-DB1\templates\Parts\info\_tools.html.twig:29 + + + part.delete.confirm_title + ¿De verdad quieres eliminar este componente? + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:32 + Part-DB1\templates\Parts\info\_tools.html.twig:30 + + + part.delete.message + Este componente y la información asociada (adjuntos, precio, etc.) serán eliminados. ¡Esto no se puede deshacer! + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:39 + Part-DB1\templates\Parts\info\_tools.html.twig:37 + + + part.delete + Eliminar componente + + + + + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + + + parts_list.all.title + Todos los componentes + + + + + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + + + parts_list.category.title + Componentes con categoría + + + + + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + + + parts_list.footprint.title + Componentes con footprint + + + + + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + + + parts_list.manufacturer.title + Componentes con fabricante + + + + + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + + + parts_list.search.title + Buscar componentes + + + + + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + + + parts_list.storelocation.title + Componentes con ubicación de almacenaje + + + + + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + + + parts_list.supplier.title + Componentes con proveedor + + + + + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + + + parts_list.tags.title + Componentes con etiqueta + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 + Part-DB1\templates\Parts\lists\_info_card.html.twig:17 + + + entity.info.common.tab + General + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 + Part-DB1\templates\Parts\lists\_info_card.html.twig:20 + + + entity.info.statistics.tab + Estadísticas + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 + + + entity.info.attachments.tab + Adjuntos + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 + + + entity.info.parameters.tab + Parámetros + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 + Part-DB1\templates\Parts\lists\_info_card.html.twig:30 + + + entity.info.name + Nombre + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 + Part-DB1\templates\Parts\lists\_info_card.html.twig:96 + Part-DB1\templates\Parts\lists\_info_card.html.twig:34 + Part-DB1\templates\Parts\lists\_info_card.html.twig:67 + + + entity.info.parent + Padre + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 + Part-DB1\templates\Parts\lists\_info_card.html.twig:46 + + + entity.edit.btn + Editar + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 + Part-DB1\templates\Parts\lists\_info_card.html.twig:63 + + + entity.info.children_count + Número de elementos hijo + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + + + tfa.check.title + Autenticación en dos pasos requerida + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:39 + Part-DB1\templates\security\2fa_base_form.html.twig:39 + + + tfa.code.trusted_pc + Este es un dispositivo fiable (si esto se habilita, no se realizarán más consultas en dos pasos en este dispositivo) + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + + + login.btn + Login + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:42 + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:40 + + + user.logout + Cerrar sesión + + + + + Part-DB1\templates\security\2fa_form.html.twig:6 + Part-DB1\templates\security\2fa_form.html.twig:6 + + + tfa.check.code.label + Código de la app de autenticación + + + + + Part-DB1\templates\security\2fa_form.html.twig:10 + Part-DB1\templates\security\2fa_form.html.twig:10 + + + tfa.check.code.help + Introduce tu código de seis dígitos de tu app de autenticación o de uno de tus códigos backup si el autenticador no está disponible. + + + + + Part-DB1\templates\security\login.html.twig:3 + Part-DB1\templates\security\login.html.twig:3 + templates\security\login.html.twig:3 + + + login.title + Login + + + + + Part-DB1\templates\security\login.html.twig:7 + Part-DB1\templates\security\login.html.twig:7 + templates\security\login.html.twig:7 + + + login.card_title + Login + + + + + Part-DB1\templates\security\login.html.twig:31 + Part-DB1\templates\security\login.html.twig:31 + templates\security\login.html.twig:31 + + + login.username.label + Nombre de usuario + + + + + Part-DB1\templates\security\login.html.twig:34 + Part-DB1\templates\security\login.html.twig:34 + templates\security\login.html.twig:34 + + + login.username.placeholder + Nombre de usuario + + + + + Part-DB1\templates\security\login.html.twig:38 + Part-DB1\templates\security\login.html.twig:38 + templates\security\login.html.twig:38 + + + login.password.label + Contraseña + + + + + Part-DB1\templates\security\login.html.twig:40 + Part-DB1\templates\security\login.html.twig:40 + templates\security\login.html.twig:40 + + + login.password.placeholder + Contraseña + + + + + Part-DB1\templates\security\login.html.twig:50 + Part-DB1\templates\security\login.html.twig:50 + templates\security\login.html.twig:50 + + + login.rememberme + Recuérdame (no aconsejado en dispositivos compartidos) + + + + + Part-DB1\templates\security\login.html.twig:64 + Part-DB1\templates\security\login.html.twig:64 + + + pw_reset.password_forget + ¿Has olvidado el nombre de usuario/contraseña? + + + + + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + + + pw_reset.new_pw.header.title + Establecer nueva contraseña + + + + + Part-DB1\templates\security\pw_reset_request.html.twig:5 + Part-DB1\templates\security\pw_reset_request.html.twig:5 + + + pw_reset.request.header.title + Solicitar nueva contraseña + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + + + tfa_u2f.http_warning + Estás accediendo a esta página usando el método inseguro HTTP, por lo que seguramente U2F no funcione correctamente (mensaje de error Bad Request). Pídele a un administrador que establezca el método seguro HTTPS si quieres utilizar claves de seguridad. + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + + + r_u2f_two_factor.pressbutton + ¡Por favor, introduce tu clave de seguridad y pulsa el botón! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + + + tfa_u2f.add_key.title + Añadir clave de seguridad + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + + + tfa_u2f.explanation + Con la ayuda de una clave de seguridad U2F/FIDO compatible (e.g. YubiKey o NitroKey), se puede obtener una autentiación en dos pasos segura y fácil de usar. Las claves de seguridad pueden ser registradas aquí, y si se requiere una verificación en dos pasos, solo necesitarás insertar la clave vía USB o introducirla en el dispositivo mediante NFC. + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + + + tfa_u2f.add_key.backup_hint + Para garantizar el acceso incluso si has perdido la clave, ¡se recomienda registrar una segunda clave como copia de seguridad y guardarla en un lugar seguro! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + + + r_u2f_two_factor.name + Nombre de la clave vista (e.g. Backup) + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + + + tfa_u2f.add_key.add_button + Añadir clave de seguridad + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + + + tfa_u2f.add_key.back_to_settings + Volver a ajustes + + + + + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + new + + + statistics.title + Estadísticas + + + + + Part-DB1\templates\Statistics\statistics.html.twig:14 + Part-DB1\templates\Statistics\statistics.html.twig:14 + new + + + statistics.parts + Componentes + + + + + Part-DB1\templates\Statistics\statistics.html.twig:19 + Part-DB1\templates\Statistics\statistics.html.twig:19 + new + + + statistics.data_structures + Estructuras de datos + + + + + Part-DB1\templates\Statistics\statistics.html.twig:24 + Part-DB1\templates\Statistics\statistics.html.twig:24 + new + + + statistics.attachments + Adjuntos + + + + + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + new + + + statistics.property + Propiedad + + + + + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + new + + + statistics.value + Valor + + + + + Part-DB1\templates\Statistics\statistics.html.twig:40 + Part-DB1\templates\Statistics\statistics.html.twig:40 + new + + + statistics.distinct_parts_count + Número de componentes distintos + + + + + Part-DB1\templates\Statistics\statistics.html.twig:44 + Part-DB1\templates\Statistics\statistics.html.twig:44 + new + + + statistics.parts_instock_sum + Suma de todos los componentes en stock + + + + + Part-DB1\templates\Statistics\statistics.html.twig:48 + Part-DB1\templates\Statistics\statistics.html.twig:48 + new + + + statistics.parts_with_price + Número de componentes con información de precio + + + + + Part-DB1\templates\Statistics\statistics.html.twig:65 + Part-DB1\templates\Statistics\statistics.html.twig:65 + new + + + statistics.categories_count + Número de categorías + + + + + Part-DB1\templates\Statistics\statistics.html.twig:69 + Part-DB1\templates\Statistics\statistics.html.twig:69 + new + + + statistics.footprints_count + Número de footprints + + + + + Part-DB1\templates\Statistics\statistics.html.twig:73 + Part-DB1\templates\Statistics\statistics.html.twig:73 + new + + + statistics.manufacturers_count + Número de fabricantes + + + + + Part-DB1\templates\Statistics\statistics.html.twig:77 + Part-DB1\templates\Statistics\statistics.html.twig:77 + new + + + statistics.storelocations_count + Número de ubicaciones de almacenaje + + + + + Part-DB1\templates\Statistics\statistics.html.twig:81 + Part-DB1\templates\Statistics\statistics.html.twig:81 + new + + + statistics.suppliers_count + Número de proveedores + + + + + Part-DB1\templates\Statistics\statistics.html.twig:85 + Part-DB1\templates\Statistics\statistics.html.twig:85 + new + + + statistics.currencies_count + Número de divisas + + + + + Part-DB1\templates\Statistics\statistics.html.twig:89 + Part-DB1\templates\Statistics\statistics.html.twig:89 + new + + + statistics.measurement_units_count + Número de unidades de medida + + + + + Part-DB1\templates\Statistics\statistics.html.twig:93 + Part-DB1\templates\Statistics\statistics.html.twig:93 + new + + + statistics.devices_count + Número de proyectos + + + + + Part-DB1\templates\Statistics\statistics.html.twig:110 + Part-DB1\templates\Statistics\statistics.html.twig:110 + new + + + statistics.attachment_types_count + Número de tipos de adjunto + + + + + Part-DB1\templates\Statistics\statistics.html.twig:114 + Part-DB1\templates\Statistics\statistics.html.twig:114 + new + + + statistics.all_attachments_count + Número de adjuntos + + + + + Part-DB1\templates\Statistics\statistics.html.twig:118 + Part-DB1\templates\Statistics\statistics.html.twig:118 + new + + + statistics.user_uploaded_attachments_count + Número de adjuntos subidos por usuarios + + + + + Part-DB1\templates\Statistics\statistics.html.twig:122 + Part-DB1\templates\Statistics\statistics.html.twig:122 + new + + + statistics.private_attachments_count + Número de adjuntos privados + + + + + Part-DB1\templates\Statistics\statistics.html.twig:126 + Part-DB1\templates\Statistics\statistics.html.twig:126 + new + + + statistics.external_attachments_count + Número de adjuntos externos (URL) + + + + + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + + + tfa_backup.codes.title + Códigos backup + + + + + Part-DB1\templates\Users\backup_codes.html.twig:12 + Part-DB1\templates\Users\backup_codes.html.twig:12 + + + tfa_backup.codes.explanation + ¡Imprime estos códigos y guárdalos en un lugar seguro! + + + + + Part-DB1\templates\Users\backup_codes.html.twig:13 + Part-DB1\templates\Users\backup_codes.html.twig:13 + + + tfa_backup.codes.help + Si ya no tienes acceso a tu dispositivo con la app de autenticación (has perdido el teléfono, pérdida de datos, etc.) puedes usar uno de estos códigos para acceder a tu cuenta y configurar una nueva app de autenticación. Cada uno de estos códigos puede usarse una única vez. Se recomienda eliminar dichos códigos. Cualquiera con acceso a estos códigos podría acceder a tu cuenta, así que guárdalos en un lugar seguro. + + + + + Part-DB1\templates\Users\backup_codes.html.twig:16 + Part-DB1\templates\Users\backup_codes.html.twig:16 + + + tfa_backup.username + Nombre de usuario + + + + + Part-DB1\templates\Users\backup_codes.html.twig:29 + Part-DB1\templates\Users\backup_codes.html.twig:29 + + + tfa_backup.codes.page_generated_on + Página generada el %date% + + + + + Part-DB1\templates\Users\backup_codes.html.twig:32 + Part-DB1\templates\Users\backup_codes.html.twig:32 + + + tfa_backup.codes.print + Imprimir + + + + + Part-DB1\templates\Users\backup_codes.html.twig:35 + Part-DB1\templates\Users\backup_codes.html.twig:35 + + + tfa_backup.codes.copy_clipboard + Copiar al portapapeles + + + + + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:40 + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:38 + templates\base.html.twig:99 + templates\Users\user_info.html.twig:3 + templates\Users\user_info.html.twig:6 + + + user.info.label + Información del usuario + + + + + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + templates\Users\user_info.html.twig:18 + src\Form\UserSettingsType.php:32 + + + user.firstName.label + Nombre + + + + + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + templates\Users\user_info.html.twig:24 + src\Form\UserSettingsType.php:35 + + + user.lastName.label + Apellido + + + + + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + templates\Users\user_info.html.twig:30 + src\Form\UserSettingsType.php:41 + + + user.email.label + Email + + + + + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + templates\Users\user_info.html.twig:37 + src\Form\UserSettingsType.php:38 + + + user.department.label + Departamento + + + + + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + templates\Users\user_info.html.twig:47 + src\Form\UserSettingsType.php:30 + + + user.username.label + Nombre de usuario + + + + + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + templates\Users\user_info.html.twig:53 + + + group.label + Grupo: + + + + + Part-DB1\templates\Users\user_info.html.twig:67 + Part-DB1\templates\Users\user_info.html.twig:67 + + + user.permissions + Permisos + + + + + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:39 + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:37 + templates\base.html.twig:98 + templates\Users\user_settings.html.twig:3 + templates\Users\user_settings.html.twig:6 + + + user.settings.label + Configuración del usuario + + + + + Part-DB1\templates\Users\user_settings.html.twig:18 + Part-DB1\templates\Users\user_settings.html.twig:18 + templates\Users\user_settings.html.twig:14 + + + user_settings.data.label + Información personal + + + + + Part-DB1\templates\Users\user_settings.html.twig:22 + Part-DB1\templates\Users\user_settings.html.twig:22 + templates\Users\user_settings.html.twig:18 + + + user_settings.configuration.label + Configuración + + + + + Part-DB1\templates\Users\user_settings.html.twig:55 + Part-DB1\templates\Users\user_settings.html.twig:55 + templates\Users\user_settings.html.twig:48 + + + user.settings.change_pw + Restablecer contraseña + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + + + user.settings.2fa_settings + Autenticación en dos pasos + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + + + tfa.settings.google.tab + App de autenticación + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + + + tfa.settings.bakup.tab + Códigos backup + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + + + tfa.settings.u2f.tab + Claves de seguridad (U2F) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + + + tfa.settings.trustedDevices.tab + Dispositivos confiables + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_title + ¿Estás seguro de que quieres deshabilitar la app de autenticación? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_message + Si deshabilitas la app de autenticación, todos tus códigos backup serán eliminados, por lo que necesitarás reimprimirlos todos.<br> +¡Ten en cuenta que sin la autenticación en dos pasos tu cuenta no estará tan protegida contra atacantes! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + + + tfa_google.disabled_message + ¡App de autenticación desactivada! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + + + tfa_google.step.download + Descarga una app de autenticación (e.g. <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> oder <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp">FreeOTP Authenticator</a>) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + + + tfa_google.step.scan + Escanea el código QR adjunto con la app o introduce los datos manualmente + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + + + tfa_google.step.input_code + Introduce el código generado en el campo de abajo y confirma + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + + + tfa_google.step.download_backup + Imprime tus códigos backup y guárdalos en un lugar seguro + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + + + tfa_google.manual_setup + Configuración manual + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + + + tfa_google.manual_setup.type + Tipo + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + + + tfa_google.manual_setup.username + Nombre de usuario + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + + + tfa_google.manual_setup.secret + Secreto + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + + + tfa_google.manual_setup.digit_count + Número de caracteres + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + + + tfa_google.enabled_message + App de autenticación activada + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + + + tfa_backup.disabled + Códigos backup desactivados. Configura la app de autenticación para activar los códigos backup de nuevo. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + + + tfa_backup.explanation + Puedes usar estos códigos backup para acceder a tu cuenta incluso si pierdes el dispositivo con la app de autenticación. Imprime los códigos y guárdalos en un lugar seguro. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_title + ¿Quieres restablecer los códigos? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_message + Esto eliminará los códigos previos y generará un nuevo conjunto de códigos. Esto no se puede deshacer. ¡Recuerda imprimir los nuevos códigos y guárdalos en un lugar seguro! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + + + tfa_backup.enabled + Códigos backup activados + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + + + tfa_backup.show_codes + Mostrar códigos backup + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + + + tfa_u2f.table_caption + Claves de seguridad registradas + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + + + tfa_u2f.delete_u2f.confirm_title + ¿Quieres eliminar esta clave de seguridad? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + + + tfa_u2f.delete_u2f.confirm_message + Si eliminas esta clave, ya no podrás iniciar sesión con esta clave. Si no quedan claves de seguridad, la autenticación en dos pasos se desactivará. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + + + tfa_u2f.keys.name + Nombre de la clave + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + + + tfa_u2f.keys.added_date + Fecha de registro + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + + + tfa_u2f.key_delete + Eliminar clave + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + + + tfa_u2f.no_keys_registered + No hay claves registradas. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + + + tfa_u2f.add_new_key + Registrar una nueva clave de seguridad + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + + + tfa_trustedDevices.explanation + Cuando compruebes el segundo factor, el dispositivo actual será marcado como fiable, por lo que no se necesitará comprobarlo más en el mismo dispositivo. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + + + tfa_trustedDevices.invalidate.confirm_title + ¿Quieres eliminar todos los dispositivos fiables? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + + + tfa_trustedDevices.invalidate.confirm_message + Tendrás que realizar de nuevo la autenticación en dos pasos en todos los dispositivos. Asegúrate de que tienes el dispositivo autenticador a mano. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + + + tfa_trustedDevices.invalidate.btn + Restablecer los dispositivos fiables + + + + + Part-DB1\templates\_navbar.html.twig:4 + Part-DB1\templates\_navbar.html.twig:4 + templates\base.html.twig:29 + + + sidebar.toggle + Activar/desactivar la barra lateral + + + + + Part-DB1\templates\_navbar.html.twig:22 + + + navbar.scanner.link + Escáner + + + + + Part-DB1\templates\_navbar.html.twig:38 + Part-DB1\templates\_navbar.html.twig:36 + templates\base.html.twig:97 + + + user.loggedin.label + Se ha iniciado sesión como + + + + + Part-DB1\templates\_navbar.html.twig:44 + Part-DB1\templates\_navbar.html.twig:42 + templates\base.html.twig:103 + + + user.login + Iniciar sesión + + + + + Part-DB1\templates\_navbar.html.twig:50 + Part-DB1\templates\_navbar.html.twig:48 + + + ui.toggle_darkmode + Modo oscuro + + + + + Part-DB1\templates\_navbar.html.twig:54 + Part-DB1\src\Form\UserSettingsType.php:97 + Part-DB1\templates\_navbar.html.twig:52 + Part-DB1\src\Form\UserSettingsType.php:97 + templates\base.html.twig:106 + src\Form\UserSettingsType.php:44 + + + user.language_select + Cambiar idioma + + + + + Part-DB1\templates\_navbar_search.html.twig:4 + Part-DB1\templates\_navbar_search.html.twig:4 + templates\base.html.twig:49 + + + search.options.label + Opciones de búsqueda + + + + + Part-DB1\templates\_navbar_search.html.twig:23 + + + tags.label + Etiquetas + + + + + Part-DB1\templates\_navbar_search.html.twig:27 + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + templates\base.html.twig:60 + templates\Parts\show_part_info.html.twig:36 + src\Form\PartType.php:77 + + + storelocation.label + Ubicación de almacenaje + + + + + Part-DB1\templates\_navbar_search.html.twig:36 + Part-DB1\templates\_navbar_search.html.twig:31 + templates\base.html.twig:65 + + + ordernumber.label.short + Código del proveedor + + + + + Part-DB1\templates\_navbar_search.html.twig:40 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + Part-DB1\templates\_navbar_search.html.twig:35 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + templates\base.html.twig:67 + + + supplier.label + Proveedor + + + + + Part-DB1\templates\_navbar_search.html.twig:57 + Part-DB1\templates\_navbar_search.html.twig:52 + templates\base.html.twig:75 + + + search.deactivateBarcode + Desactivar código de barras + + + + + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_navbar_search.html.twig:56 + templates\base.html.twig:77 + + + search.regexmatching + Reg.Ex. Matching + + + + + Part-DB1\templates\_navbar_search.html.twig:68 + Part-DB1\templates\_navbar_search.html.twig:62 + + + search.submit + ¡Vamos! + + + + + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + templates\base.html.twig:175 + templates\base.html.twig:189 + templates\base.html.twig:202 + templates\base.html.twig:230 + + + project.labelp + Proyectos + + + + + Part-DB1\templates\_sidebar.html.twig:2 + Part-DB1\templates\_sidebar.html.twig:2 + templates\base.html.twig:165 + templates\base.html.twig:192 + templates\base.html.twig:220 + + + actions + Acciones + + + + + Part-DB1\templates\_sidebar.html.twig:6 + Part-DB1\templates\_sidebar.html.twig:6 + templates\base.html.twig:169 + templates\base.html.twig:196 + templates\base.html.twig:224 + + + datasource + Fuente de datos + + + + + Part-DB1\templates\_sidebar.html.twig:10 + Part-DB1\templates\_sidebar.html.twig:10 + templates\base.html.twig:173 + templates\base.html.twig:200 + templates\base.html.twig:228 + + + manufacturer.labelp + Fabricantes + + + + + Part-DB1\templates\_sidebar.html.twig:11 + Part-DB1\templates\_sidebar.html.twig:11 + templates\base.html.twig:174 + templates\base.html.twig:201 + templates\base.html.twig:229 + + + supplier.labelp + Proveedores + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:293 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:181 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:243 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:268 + + + attachment.download_failed + Descarga fallida de adjuntos externos + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 + + + entity.edit_flash + Cambios guardados con éxito. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 + + + entity.edit_flash.invalid + Los cambios no han podido guardarse. ¡Por favor, comprueba la información! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 + + + entity.created_flash + Elemento creado + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 + + + entity.created_flash.invalid + No se ha podido crear el elemento. ¡Por favor, comprueba los datos! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 + src\Controller\BaseAdminController.php:154 + + + attachment_type.deleted + ¡Elemento eliminado! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 + Part-DB1\src\Controller\UserController.php:109 + Part-DB1\src\Controller\UserSettingsController.php:159 + Part-DB1\src\Controller\UserSettingsController.php:193 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:354 + Part-DB1\src\Controller\UserController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:150 + Part-DB1\src\Controller\UserSettingsController.php:182 + + + csfr_invalid + Token CSFR no válido. Por favor, refresca esta página o contacta con un administrador si este mensaje no desaparece. + + + + + Part-DB1\src\Controller\LabelController.php:125 + + + label_generator.no_entities_found + No se encontraron entidades que coincidan. + + + + + Part-DB1\src\Controller\LogController.php:149 + Part-DB1\src\Controller\LogController.php:154 + new + + + log.undo.target_not_found + ¡No se pudo encontrar el elemento en la base de datos! + + + + + Part-DB1\src\Controller\LogController.php:156 + Part-DB1\src\Controller\LogController.php:160 + new + + + log.undo.revert_success + Componente restablecido con éxito. + + + + + Part-DB1\src\Controller\LogController.php:176 + Part-DB1\src\Controller\LogController.php:180 + new + + + log.undo.element_undelete_success + Elemento recuperado con éxito. + + + + + Part-DB1\src\Controller\LogController.php:178 + Part-DB1\src\Controller\LogController.php:182 + new + + + log.undo.element_element_already_undeleted + ¡El elemento ya ha sido recuperado! + + + + + Part-DB1\src\Controller\LogController.php:185 + Part-DB1\src\Controller\LogController.php:189 + new + + + log.undo.element_delete_success + Elemento eliminado con éxito. + + + + + Part-DB1\src\Controller\LogController.php:187 + Part-DB1\src\Controller\LogController.php:191 + new + + + log.undo.element.element_already_delted + ¡El elemento ya ha sido eliminado! + + + + + Part-DB1\src\Controller\LogController.php:194 + Part-DB1\src\Controller\LogController.php:198 + new + + + log.undo.element_change_undone + ¡Cambio deshecho con éxito! + + + + + Part-DB1\src\Controller\LogController.php:196 + Part-DB1\src\Controller\LogController.php:200 + new + + + log.undo.do_undelete_before + ¡Debes recuperar el elemento antes de poder deshacer el cambio! + + + + + Part-DB1\src\Controller\LogController.php:199 + Part-DB1\src\Controller\LogController.php:203 + new + + + log.undo.log_type_invalid + ¡Esta entrada del registro no se puede deshacer! + + + + + Part-DB1\src\Controller\PartController.php:182 + Part-DB1\src\Controller\PartController.php:182 + src\Controller\PartController.php:80 + + + part.edited_flash + ¡Los cambios han sido guardados! + + + + + Part-DB1\src\Controller\PartController.php:186 + Part-DB1\src\Controller\PartController.php:186 + + + part.edited_flash.invalid + Error en el guardado: ¡Por favor, comprueba los datos! + + + + + Part-DB1\src\Controller\PartController.php:216 + Part-DB1\src\Controller\PartController.php:219 + + + part.deleted + Componente eliminado con éxito. + + + + + Part-DB1\src\Controller\PartController.php:302 + Part-DB1\src\Controller\PartController.php:277 + Part-DB1\src\Controller\PartController.php:317 + src\Controller\PartController.php:113 + src\Controller\PartController.php:142 + + + part.created_flash + ¡Componente creado! + + + + + Part-DB1\src\Controller\PartController.php:308 + Part-DB1\src\Controller\PartController.php:283 + + + part.created_flash.invalid + Error durante la creación: ¡Por favor, comprueba los datos! + + + + + Part-DB1\src\Controller\ScanController.php:68 + Part-DB1\src\Controller\ScanController.php:90 + + + scan.qr_not_found + No se ha encontrado elemento con ese código de barras. + + + + + Part-DB1\src\Controller\ScanController.php:71 + + + scan.format_unknown + ¡Formato desconocido! + + + + + Part-DB1\src\Controller\ScanController.php:86 + + + scan.qr_success + ¡Elemento encontrado! + + + + + Part-DB1\src\Controller\SecurityController.php:114 + Part-DB1\src\Controller\SecurityController.php:109 + + + pw_reset.user_or_email + Nombre de usuario / Email + + + + + Part-DB1\src\Controller\SecurityController.php:131 + Part-DB1\src\Controller\SecurityController.php:126 + + + pw_reset.request.success + ¡La solicitud de reinicio fue exitosa! Por favor, comprueba tus emails para más instrucciones. + + + + + Part-DB1\src\Controller\SecurityController.php:162 + Part-DB1\src\Controller\SecurityController.php:160 + + + pw_reset.username + Nombre de usuario + + + + + Part-DB1\src\Controller\SecurityController.php:165 + Part-DB1\src\Controller\SecurityController.php:163 + + + pw_reset.token + Token + + + + + Part-DB1\src\Controller\SecurityController.php:194 + Part-DB1\src\Controller\SecurityController.php:192 + + + pw_reset.new_pw.error + ¡Nombre de usuario o token no válidos! Por favor, comprueba los datos. + + + + + Part-DB1\src\Controller\SecurityController.php:196 + Part-DB1\src\Controller\SecurityController.php:194 + + + pw_reset.new_pw.success + La contraseña ha sido restablecida con éxito. Puedes ahora iniciar sesión con tu nueva contraseña. + + + + + Part-DB1\src\Controller\UserController.php:107 + Part-DB1\src\Controller\UserController.php:99 + + + user.edit.reset_success + Todos los métodos de autenticación en dos pasos han sido desactivados. + + + + + Part-DB1\src\Controller\UserSettingsController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:92 + + + tfa_backup.no_codes_enabled + ¡No hay ningún código backup activado! + + + + + Part-DB1\src\Controller\UserSettingsController.php:138 + Part-DB1\src\Controller\UserSettingsController.php:132 + + + tfa_u2f.u2f_delete.not_existing + No existe ninguna clave de seguridad con este ID. + + + + + Part-DB1\src\Controller\UserSettingsController.php:145 + Part-DB1\src\Controller\UserSettingsController.php:139 + + + tfa_u2f.u2f_delete.access_denied + ¡No puedes eliminar las claves de seguridad de otros usuarios! + + + + + Part-DB1\src\Controller\UserSettingsController.php:153 + Part-DB1\src\Controller\UserSettingsController.php:147 + + + tfa.u2f.u2f_delete.success + La clave de seguridad ha sido eliminada con éxito. + + + + + Part-DB1\src\Controller\UserSettingsController.php:188 + Part-DB1\src\Controller\UserSettingsController.php:180 + + + tfa_trustedDevice.invalidate.success + Los dispositivos fiables han sido restablecidos con éxito. + + + + + Part-DB1\src\Controller\UserSettingsController.php:235 + Part-DB1\src\Controller\UserSettingsController.php:226 + src\Controller\UserController.php:98 + + + user.settings.saved_flash + ¡Configuración guardada! + + + + + Part-DB1\src\Controller\UserSettingsController.php:297 + Part-DB1\src\Controller\UserSettingsController.php:288 + src\Controller\UserController.php:130 + + + user.settings.pw_changed_flash + ¡Contraseña cambiada! + + + + + Part-DB1\src\Controller\UserSettingsController.php:317 + Part-DB1\src\Controller\UserSettingsController.php:306 + + + user.settings.2fa.google.activated + La app de autenticación ha sido activada con éxito. + + + + + Part-DB1\src\Controller\UserSettingsController.php:328 + Part-DB1\src\Controller\UserSettingsController.php:315 + + + user.settings.2fa.google.disabled + La app de autenticación ha sido desactivada con éxito. + + + + + Part-DB1\src\Controller\UserSettingsController.php:346 + Part-DB1\src\Controller\UserSettingsController.php:332 + + + user.settings.2fa.backup_codes.regenerated + Se han generado nuevos códigos backup con éxito. + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + + + attachment.table.filename + Nombre del archivo + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + + + attachment.table.filesize + Tamaño del archivo + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:245 + Part-DB1\src\DataTables\PartsDataTable.php:252 + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:193 + Part-DB1\src\DataTables\PartsDataTable.php:200 + + + true + Verdadero + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:246 + Part-DB1\src\DataTables\PartsDataTable.php:253 + Part-DB1\src\Form\Type\SIUnitType.php:139 + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:201 + Part-DB1\src\Form\Type\SIUnitType.php:139 + + + false + Falso + + + + + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 + + + log.target_deleted + Eliminado + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 + new + + + log.undo.undelete + Recuperar elemento + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 + new + + + log.undo.undo + Deshacer cambio + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 + new + + + log.undo.revert + Restablecer elemento a esta fecha + + + + + Part-DB1\src\DataTables\LogDataTable.php:173 + Part-DB1\src\DataTables\LogDataTable.php:161 + + + log.id + ID + + + + + Part-DB1\src\DataTables\LogDataTable.php:178 + Part-DB1\src\DataTables\LogDataTable.php:166 + + + log.timestamp + Fecha + + + + + Part-DB1\src\DataTables\LogDataTable.php:183 + Part-DB1\src\DataTables\LogDataTable.php:171 + + + log.type + Evento + + + + + Part-DB1\src\DataTables\LogDataTable.php:191 + Part-DB1\src\DataTables\LogDataTable.php:179 + + + log.level + Nivel + + + + + Part-DB1\src\DataTables\LogDataTable.php:200 + Part-DB1\src\DataTables\LogDataTable.php:188 + + + log.user + Usuario + + + + + Part-DB1\src\DataTables\LogDataTable.php:213 + Part-DB1\src\DataTables\LogDataTable.php:201 + + + log.target_type + Tipo de objetivo + + + + + Part-DB1\src\DataTables\LogDataTable.php:226 + Part-DB1\src\DataTables\LogDataTable.php:214 + + + log.target + Objetivo + + + + + Part-DB1\src\DataTables\LogDataTable.php:231 + Part-DB1\src\DataTables\LogDataTable.php:218 + new + + + log.extra + Extra + + + + + Part-DB1\src\DataTables\PartsDataTable.php:168 + Part-DB1\src\DataTables\PartsDataTable.php:116 + + + part.table.name + Nombre + + + + + Part-DB1\src\DataTables\PartsDataTable.php:178 + Part-DB1\src\DataTables\PartsDataTable.php:126 + + + part.table.id + Id + + + + + Part-DB1\src\DataTables\PartsDataTable.php:182 + Part-DB1\src\DataTables\PartsDataTable.php:130 + + + part.table.description + Descripción + + + + + Part-DB1\src\DataTables\PartsDataTable.php:185 + Part-DB1\src\DataTables\PartsDataTable.php:133 + + + part.table.category + Categoría + + + + + Part-DB1\src\DataTables\PartsDataTable.php:190 + Part-DB1\src\DataTables\PartsDataTable.php:138 + + + part.table.footprint + Footprint + + + + + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:142 + + + part.table.manufacturer + Fabricante + + + + + Part-DB1\src\DataTables\PartsDataTable.php:197 + Part-DB1\src\DataTables\PartsDataTable.php:145 + + + part.table.storeLocations + Ubicación de almacenaje + + + + + Part-DB1\src\DataTables\PartsDataTable.php:216 + Part-DB1\src\DataTables\PartsDataTable.php:164 + + + part.table.amount + Cantidad + + + + + Part-DB1\src\DataTables\PartsDataTable.php:224 + Part-DB1\src\DataTables\PartsDataTable.php:172 + + + part.table.minamount + Cantidad mínima + + + + + Part-DB1\src\DataTables\PartsDataTable.php:232 + Part-DB1\src\DataTables\PartsDataTable.php:180 + + + part.table.partUnit + Unidad de Medida + + + + + Part-DB1\src\DataTables\PartsDataTable.php:236 + Part-DB1\src\DataTables\PartsDataTable.php:184 + + + part.table.addedDate + Creado en + + + + + Part-DB1\src\DataTables\PartsDataTable.php:240 + Part-DB1\src\DataTables\PartsDataTable.php:188 + + + part.table.lastModified + Última edición + + + + + Part-DB1\src\DataTables\PartsDataTable.php:244 + Part-DB1\src\DataTables\PartsDataTable.php:192 + + + part.table.needsReview + Necesita revisión + + + + + Part-DB1\src\DataTables\PartsDataTable.php:251 + Part-DB1\src\DataTables\PartsDataTable.php:199 + + + part.table.favorite + Favorito + + + + + Part-DB1\src\DataTables\PartsDataTable.php:258 + Part-DB1\src\DataTables\PartsDataTable.php:206 + + + part.table.manufacturingStatus + Estado + + + + + Part-DB1\src\DataTables\PartsDataTable.php:260 + Part-DB1\src\DataTables\PartsDataTable.php:262 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:208 + Part-DB1\src\DataTables\PartsDataTable.php:210 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.unknown + Desconocido + + + + + Part-DB1\src\DataTables\PartsDataTable.php:263 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:211 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.announced + Anunciado + + + + + Part-DB1\src\DataTables\PartsDataTable.php:264 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.active + Activo + + + + + Part-DB1\src\DataTables\PartsDataTable.php:265 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:213 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.nrfnd + No recomendado para nuevos diseños + + + + + Part-DB1\src\DataTables\PartsDataTable.php:266 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:214 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.eol + Final de vida + + + + + Part-DB1\src\DataTables\PartsDataTable.php:267 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:215 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.discontinued + Descontinuado + + + + + Part-DB1\src\DataTables\PartsDataTable.php:271 + Part-DB1\src\DataTables\PartsDataTable.php:219 + + + part.table.mpn + MPN + + + + + Part-DB1\src\DataTables\PartsDataTable.php:275 + Part-DB1\src\DataTables\PartsDataTable.php:223 + + + part.table.mass + Peso + + + + + Part-DB1\src\DataTables\PartsDataTable.php:279 + Part-DB1\src\DataTables\PartsDataTable.php:227 + + + part.table.tags + Etiquetas + + + + + Part-DB1\src\DataTables\PartsDataTable.php:283 + Part-DB1\src\DataTables\PartsDataTable.php:231 + + + part.table.attachments + Adjuntos + + + + + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 + Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 + + + flash.login_successful + Se ha iniciado sesión correctamente + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + JSON + JSON + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + XML + XML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + CSV + CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + YAML + YAML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:124 + Part-DB1\src\Form\AdminPages\ImportType.php:124 + + + import.abort_on_validation.help + Cuando esta opción se active, el proceso de importación será abortado si se selecciona información no válida. Si no se ha seleccionado, la información incorrecta se ignorará y el importador intentará importar el resto de elementos. + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:86 + Part-DB1\src\Form\AdminPages\ImportType.php:86 + src\Form\ImportType.php:70 + + + import.csv_separator + Separador CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:93 + Part-DB1\src\Form\AdminPages\ImportType.php:93 + src\Form\ImportType.php:72 + + + parent.label + Elemento padre + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:101 + Part-DB1\src\Form\AdminPages\ImportType.php:101 + src\Form\ImportType.php:75 + + + import.file + Archivo + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:111 + Part-DB1\src\Form\AdminPages\ImportType.php:111 + src\Form\ImportType.php:78 + + + import.preserve_children + Preservar elementos hijos al importar + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:120 + Part-DB1\src\Form\AdminPages\ImportType.php:120 + src\Form\ImportType.php:80 + + + import.abort_on_validation + Anular en caso de datos inválidos + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:132 + Part-DB1\src\Form\AdminPages\ImportType.php:132 + src\Form\ImportType.php:85 + + + import.btn + Importar + + + + + Part-DB1\src\Form\AttachmentFormType.php:113 + Part-DB1\src\Form\AttachmentFormType.php:109 + + + attachment.edit.secure_file.help + Un adjunto marcado como privado solo puede ser accedido por usuarios autenticados que tengan el permiso necesario. Si esta opción es activada, ninguna miniatura será generada y el acceso al archivo será más lento. + + + + + Part-DB1\src\Form\AttachmentFormType.php:127 + Part-DB1\src\Form\AttachmentFormType.php:123 + + + attachment.edit.url.help + Puedes especificar una URL a un archivo externo aquí, o introducir una palabra clave para buscar recursos incorporados (p.ej. footprints) + + + + + Part-DB1\src\Form\AttachmentFormType.php:82 + Part-DB1\src\Form\AttachmentFormType.php:79 + + + attachment.edit.name + Nombre + + + + + Part-DB1\src\Form\AttachmentFormType.php:85 + Part-DB1\src\Form\AttachmentFormType.php:82 + + + attachment.edit.attachment_type + Tipo de adjunto + + + + + Part-DB1\src\Form\AttachmentFormType.php:94 + Part-DB1\src\Form\AttachmentFormType.php:91 + + + attachment.edit.show_in_table + Mostrar en la tabla + + + + + Part-DB1\src\Form\AttachmentFormType.php:105 + Part-DB1\src\Form\AttachmentFormType.php:102 + + + attachment.edit.secure_file + Adjunto privado + + + + + Part-DB1\src\Form\AttachmentFormType.php:119 + Part-DB1\src\Form\AttachmentFormType.php:115 + + + attachment.edit.url + URL + + + + + Part-DB1\src\Form\AttachmentFormType.php:133 + Part-DB1\src\Form\AttachmentFormType.php:129 + + + attachment.edit.download_url + Descargar archivo externo + + + + + Part-DB1\src\Form\AttachmentFormType.php:146 + Part-DB1\src\Form\AttachmentFormType.php:142 + + + attachment.edit.file + Subir archivo + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:86 + + + part.label + Componente + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:87 + + + part_lot.label + Lote del componente + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.none + Ninguno + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.qr + Código QR (recomendado) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code128 + Código 128 (recomendado) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code39 + Código 39 (recomendado) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code93 + Código 93 + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.datamatrix + Datamatrix + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label_options.lines_mode.html + Marcador de posición + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label.options.lines_mode.twig + Twig + + + + + Part-DB1\src\Form\LabelOptionsType.php:126 + + + label_options.lines_mode.help + Si aquí seleccionas Twig, el campo de contenido se interpreta como una plantilla Twig. Visita <a href="https://twig.symfony.com/doc/3.x/templates.html">Twig documentation</a> y <a href="https://docs.part-db.de/usage/labels.html#twig-mode">Wiki</a> para más información. + + + + + Part-DB1\src\Form\LabelOptionsType.php:47 + + + label_options.page_size.label + Tamaño de etiqueta + + + + + Part-DB1\src\Form\LabelOptionsType.php:66 + + + label_options.supported_elements.label + Tipo de objetivo + + + + + Part-DB1\src\Form\LabelOptionsType.php:75 + + + label_options.barcode_type.label + Código de barras + + + + + Part-DB1\src\Form\LabelOptionsType.php:102 + + + label_profile.lines.label + Contenido + + + + + Part-DB1\src\Form\LabelOptionsType.php:111 + + + label_options.additional_css.label + Estilos adicionales (CSS) + + + + + Part-DB1\src\Form\LabelOptionsType.php:120 + + + label_options.lines_mode.label + Modo parser + + + + + Part-DB1\src\Form\LabelOptionsType.php:51 + + + label_options.width.placeholder + Anchura + + + + + Part-DB1\src\Form\LabelOptionsType.php:60 + + + label_options.height.placeholder + Altura + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 + + + label_generator.target_id.range_hint + Puedes especificar múltiples ID (ej. 1, 2, 3) y/o un intervalo (1-3) aquí para generar etiquetas para múltiples elementos a la vez. + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 + + + label_generator.target_id.label + ID del objetivo + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 + + + label_generator.update + Actualizar + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 + + + scan_dialog.input + Input + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 + + + scan_dialog.submit + Subir + + + + + Part-DB1\src\Form\ParameterType.php:41 + + + parameters.name.placeholder + p.ej. DC Current Gain + + + + + Part-DB1\src\Form\ParameterType.php:50 + + + parameters.symbol.placeholder + p.ej. h_{FE} + + + + + Part-DB1\src\Form\ParameterType.php:60 + + + parameters.text.placeholder + p.ej. Test conditions + + + + + Part-DB1\src\Form\ParameterType.php:71 + + + parameters.max.placeholder + p.ej. 350 + + + + + Part-DB1\src\Form\ParameterType.php:82 + + + parameters.min.placeholder + p.ej. 100 + + + + + Part-DB1\src\Form\ParameterType.php:93 + + + parameters.typical.placeholder + p.ej. 200 + + + + + Part-DB1\src\Form\ParameterType.php:103 + + + parameters.unit.placeholder + p.ej. V + + + + + Part-DB1\src\Form\ParameterType.php:114 + + + parameter.group.placeholder + p.ej. Especificaciones técnicas + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:72 + Part-DB1\src\Form\Part\OrderdetailType.php:75 + + + orderdetails.edit.supplierpartnr + Número de componente de proveedor + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:81 + Part-DB1\src\Form\Part\OrderdetailType.php:84 + + + orderdetails.edit.supplier + Proveedor + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:87 + Part-DB1\src\Form\Part\OrderdetailType.php:90 + + + orderdetails.edit.url + Enlace a la oferta + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:93 + Part-DB1\src\Form\Part\OrderdetailType.php:96 + + + orderdetails.edit.obsolete + Ya no disponible + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:75 + Part-DB1\src\Form\Part\OrderdetailType.php:78 + + + orderdetails.edit.supplierpartnr.placeholder + p.ej. BC 547C + + + + + Part-DB1\src\Form\Part\PartBaseType.php:101 + Part-DB1\src\Form\Part\PartBaseType.php:99 + + + part.edit.name + Nombre + + + + + Part-DB1\src\Form\Part\PartBaseType.php:109 + Part-DB1\src\Form\Part\PartBaseType.php:107 + + + part.edit.description + Descripción + + + + + Part-DB1\src\Form\Part\PartBaseType.php:120 + Part-DB1\src\Form\Part\PartBaseType.php:118 + + + part.edit.mininstock + Stock mínimo + + + + + Part-DB1\src\Form\Part\PartBaseType.php:129 + Part-DB1\src\Form\Part\PartBaseType.php:127 + + + part.edit.category + Categoría + + + + + Part-DB1\src\Form\Part\PartBaseType.php:135 + Part-DB1\src\Form\Part\PartBaseType.php:133 + + + part.edit.footprint + Footprint + + + + + Part-DB1\src\Form\Part\PartBaseType.php:142 + Part-DB1\src\Form\Part\PartBaseType.php:140 + + + part.edit.tags + Etiquetas + + + + + Part-DB1\src\Form\Part\PartBaseType.php:154 + Part-DB1\src\Form\Part\PartBaseType.php:152 + + + part.edit.manufacturer.label + Fabricante + + + + + Part-DB1\src\Form\Part\PartBaseType.php:161 + Part-DB1\src\Form\Part\PartBaseType.php:159 + + + part.edit.manufacturer_url.label + Link a la página del producto + + + + + Part-DB1\src\Form\Part\PartBaseType.php:167 + Part-DB1\src\Form\Part\PartBaseType.php:165 + + + part.edit.mpn + Número del fabricante del componente + + + + + Part-DB1\src\Form\Part\PartBaseType.php:173 + Part-DB1\src\Form\Part\PartBaseType.php:171 + + + part.edit.manufacturing_status + Estado de fabricación + + + + + Part-DB1\src\Form\Part\PartBaseType.php:181 + Part-DB1\src\Form\Part\PartBaseType.php:179 + + + part.edit.needs_review + Necesita revisión + + + + + Part-DB1\src\Form\Part\PartBaseType.php:189 + Part-DB1\src\Form\Part\PartBaseType.php:187 + + + part.edit.is_favorite + Favorito + + + + + Part-DB1\src\Form\Part\PartBaseType.php:197 + Part-DB1\src\Form\Part\PartBaseType.php:195 + + + part.edit.mass + Peso + + + + + Part-DB1\src\Form\Part\PartBaseType.php:203 + Part-DB1\src\Form\Part\PartBaseType.php:201 + + + part.edit.partUnit + Unidad de medida + + + + + Part-DB1\src\Form\Part\PartBaseType.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:210 + + + part.edit.comment + Notas + + + + + Part-DB1\src\Form\Part\PartBaseType.php:250 + Part-DB1\src\Form\Part\PartBaseType.php:246 + + + part.edit.master_attachment + Vista previa + + + + + Part-DB1\src\Form\Part\PartBaseType.php:295 + Part-DB1\src\Form\Part\PartBaseType.php:276 + src\Form\PartType.php:91 + + + part.edit.save + Guardar cambios + + + + + Part-DB1\src\Form\Part\PartBaseType.php:296 + Part-DB1\src\Form\Part\PartBaseType.php:277 + src\Form\PartType.php:92 + + + part.edit.reset + Restablecer cambios + + + + + Part-DB1\src\Form\Part\PartBaseType.php:105 + Part-DB1\src\Form\Part\PartBaseType.php:103 + + + part.edit.name.placeholder + p.ej. BC547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:115 + Part-DB1\src\Form\Part\PartBaseType.php:113 + + + part.edit.description.placeholder + p.ej NPN 45V, 0,1A, 0,5W + + + + + Part-DB1\src\Form\Part\PartBaseType.php:123 + Part-DB1\src\Form\Part\PartBaseType.php:121 + + + part.editmininstock.placeholder + p.ej. 1 + + + + + Part-DB1\src\Form\Part\PartLotType.php:69 + Part-DB1\src\Form\Part\PartLotType.php:69 + + + part_lot.edit.description + Descripción + + + + + Part-DB1\src\Form\Part\PartLotType.php:78 + Part-DB1\src\Form\Part\PartLotType.php:78 + + + part_lot.edit.location + Ubicación de almacenaje + + + + + Part-DB1\src\Form\Part\PartLotType.php:89 + Part-DB1\src\Form\Part\PartLotType.php:89 + + + part_lot.edit.amount + Cantidad + + + + + Part-DB1\src\Form\Part\PartLotType.php:98 + Part-DB1\src\Form\Part\PartLotType.php:97 + + + part_lot.edit.instock_unknown + Cantidad desconocida + + + + + Part-DB1\src\Form\Part\PartLotType.php:109 + Part-DB1\src\Form\Part\PartLotType.php:108 + + + part_lot.edit.needs_refill + Necesita repuesto + + + + + Part-DB1\src\Form\Part\PartLotType.php:120 + Part-DB1\src\Form\Part\PartLotType.php:119 + + + part_lot.edit.expiration_date + Fecha de caducidad + + + + + Part-DB1\src\Form\Part\PartLotType.php:128 + Part-DB1\src\Form\Part\PartLotType.php:125 + + + part_lot.edit.comment + Comentarios + + + + + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + + + perm.group.other + Varios + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + + + tfa_google.enable + Habilitar app de autenticación + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + + + tfa_google.disable + Desactivar aplicación de autenticación + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + + + google_confirmation + Código de confirmación + + + + + Part-DB1\src\Form\UserSettingsType.php:108 + Part-DB1\src\Form\UserSettingsType.php:108 + src\Form\UserSettingsType.php:46 + + + user.timezone.label + Zona horaria + + + + + Part-DB1\src\Form\UserSettingsType.php:133 + Part-DB1\src\Form\UserSettingsType.php:132 + + + user.currency.label + Divisa preferida + + + + + Part-DB1\src\Form\UserSettingsType.php:140 + Part-DB1\src\Form\UserSettingsType.php:139 + src\Form\UserSettingsType.php:53 + + + save + Aplicar cambios + + + + + Part-DB1\src\Form\UserSettingsType.php:141 + Part-DB1\src\Form\UserSettingsType.php:140 + src\Form\UserSettingsType.php:54 + + + reset + Descartar cambios + + + + + Part-DB1\src\Form\UserSettingsType.php:104 + Part-DB1\src\Form\UserSettingsType.php:104 + src\Form\UserSettingsType.php:45 + + + user_settings.language.placeholder + Idioma del servidor + + + + + Part-DB1\src\Form\UserSettingsType.php:115 + Part-DB1\src\Form\UserSettingsType.php:115 + src\Form\UserSettingsType.php:48 + + + user_settings.timezone.placeholder + Zona horaria del servidor + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + + + attachment.label + Adjunto + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + + + attachment_type.label + Tipo de adjunto + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + + + project.label + Proyecto + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + + + measurement_unit.label + Unidad de medida + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + + + currency.label + Divisa + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + + + orderdetail.label + Información del pedido + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + + + pricedetail.label + Información del precio + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + + + user.label + Usuario + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 + + + parameter.label + Parámetro + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 + + + label_profile.label + Perfil de etiqueta + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 + new + + + log.element_deleted.old_name.unknown + Desconocido + + + + + Part-DB1\src\Services\MarkdownParser.php:73 + Part-DB1\src\Services\MarkdownParser.php:73 + + + markdown.loading + Cargando markdown. Si este mensaje no desaparece, prueba a refrescar la página. + + + + + Part-DB1\src\Services\PasswordResetManager.php:98 + Part-DB1\src\Services\PasswordResetManager.php:98 + + + pw_reset.email.subject + Restablecer contraseña de tu cuenta Part-DB. + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + + + tree.tools.tools + Herramientas + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 + src\Services\ToolsTreeBuilder.php:74 + + + tree.tools.edit + Editar + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + src\Services\ToolsTreeBuilder.php:81 + + + tree.tools.show + Visualizar + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + + + tree.tools.system + Sistema + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 + + + tree.tools.tools.label_dialog + Generador de etiquetas + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 + + + tree.tools.tools.label_scanner + Escáner + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 + src\Services\ToolsTreeBuilder.php:62 + + + tree.tools.edit.attachment_types + Tipos de adjunto + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 + src\Services\ToolsTreeBuilder.php:64 + + + tree.tools.edit.categories + Categorías + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 + src\Services\ToolsTreeBuilder.php:66 + + + tree.tools.edit.projects + Proyectos + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 + src\Services\ToolsTreeBuilder.php:68 + + + tree.tools.edit.suppliers + Proveedores + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 + src\Services\ToolsTreeBuilder.php:70 + + + tree.tools.edit.manufacturer + Fabricantes + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 + + + tree.tools.edit.storelocation + Ubicaciones de almacenamiento + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 + + + tree.tools.edit.footprint + Footprints + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 + + + tree.tools.edit.currency + Divisas + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 + + + tree.tools.edit.measurement_unit + Unidad de medida + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.edit.label_profile + Perfiles de etiqueta + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 + + + tree.tools.edit.part + Nuevo componente + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + src\Services\ToolsTreeBuilder.php:77 + + + tree.tools.show.all_parts + Visualizar todos los componentes + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.show.all_attachments + Adjuntos + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 + new + + + tree.tools.show.statistics + Estadísticas + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 + + + tree.tools.system.users + Usuarios + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 + + + tree.tools.system.groups + Grupos + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 + new + + + tree.tools.system.event_log + Registro de eventos + + + + + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + src\Services\TreeBuilder.php:124 + + + entity.tree.new + Nuevo elemento + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 + obsolete + + + attachment.external_file + Archivo externo + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + obsolete + + + attachment.edit + Editar + + + + + Part-DB1\templates\_navbar.html.twig:27 + templates\base.html.twig:88 + obsolete + + + barcode.scan + Escanear código de barras + + + + + Part-DB1\src\Form\UserSettingsType.php:119 + src\Form\UserSettingsType.php:49 + obsolete + + + user.theme.label + Tema + + + + + Part-DB1\src\Form\UserSettingsType.php:129 + src\Form\UserSettingsType.php:50 + obsolete + + + user_settings.theme.placeholder + Tema del servidor + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 + new + obsolete + + + log.user_login.ip + IP + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:169 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:207 + new + obsolete + + + log.undo_mode.undo + Cambio deshecho + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:171 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:209 + new + obsolete + + + log.undo_mode.revert + Elemento restablecido + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 + new + obsolete + + + log.element_created.original_instock + Instock antiguo + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 + new + obsolete + + + log.element_deleted.old_name + Nombre antiguo + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 + new + obsolete + + + log.element_edited.changed_fields + Campos editados + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 + new + obsolete + + + log.instock_changed.comment + Comentario + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 + new + obsolete + + + log.collection_deleted.deleted + Elemento eliminado: + + + + + templates\base.html.twig:81 + obsolete + obsolete + + + go.exclamation + ¡Vamos! + + + + + templates\base.html.twig:109 + obsolete + obsolete + + + language.english + Inglés + + + + + templates\base.html.twig:112 + obsolete + obsolete + + + language.german + Alemán + + + + + obsolete + obsolete + + + flash.password_change_needed + ¡Se necesita cambiar la contraseña! + + + + + obsolete + obsolete + + + attachment.table.type + Tipo de adjunto + + + + + obsolete + obsolete + + + attachment.table.element + Elemento asociado + + + + + obsolete + obsolete + + + attachment.edit.isPicture + ¿Imagen? + + + + + obsolete + obsolete + + + attachment.edit.is3DModel + ¿Modelo 3D? + + + + + obsolete + obsolete + + + attachment.edit.isBuiltin + ¿Integrado? + + + + + obsolete + obsolete + + + category.edit.default_comment.placeholder + p.ej. útil para cambiar + + + + + obsolete + obsolete + + + tfa_backup.regenerate_codes + Generar nuevos códigos backup + + + + + obsolete + obsolete + + + validator.noneofitschild.self + Un elemento no puede ser su propio padre. + + + + + obsolete + obsolete + + + validator.noneofitschild.children + El elemento padre no puede ser uno de sus propios hijos. + + + + + obsolete + obsolete + + + validator.part_lot.location_full.no_increasment + La ubicación de almacenaje ha sido marcada como llena, así que no puedes incrementar la cantidad de instock. (Nueva cantidad máxima. {{ old_amount }}) + + + + + obsolete + obsolete + + + validator.part_lot.location_full + La ubicación de almacenaje ha sido marcada como llena, así que no puedes añadir un nuevo componente. + + + + + obsolete + obsolete + + + validator.part_lot.only_existing + La ubicación de almacenaje ha sido marcada como "solo existente", así que no puedes añadir un nuevo componente. + + + + + obsolete + obsolete + + + validator.part_lot.single_part + La ubicación de almacenaje ha sido marcada como "solo un componente", así que no puedes añadir un nuevo componente. + + + + + obsolete + obsolete + + + m_status.active.help + El componente se encuentra actualmente y durante el futuro próximo bajo producción. + + + + + obsolete + obsolete + + + m_status.announced.help + El componente fue anunciado pero aún no está disponible. + + + + + obsolete + obsolete + + + m_status.discontinued.help + El componente se encuentra descontinuado y ya no se produce. + + + + + obsolete + obsolete + + + m_status.eol.help + El producto ha alcanzado el final de su vida y su producción se detendrá pronto. + + + + + obsolete + obsolete + + + m_status.nrfnd.help + El componente está actualmente en producción pero no está recomendado para diseños nuevos. + + + + + obsolete + obsolete + + + m_status.unknown.help + El estatus de producción del componente es desconocido. + + + + + obsolete + obsolete + + + flash.success + Éxito + + + + + obsolete + obsolete + + + flash.error + Error + + + + + obsolete + obsolete + + + flash.warning + Atención + + + + + obsolete + obsolete + + + flash.notice + Aviso + + + + + obsolete + obsolete + + + flash.info + Info + + + + + obsolete + obsolete + + + validator.noLockout + No puedes retirarte del permiso "cambio de permiso" para evitar que te bloquees a ti mismo. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter + Tipos de fichero autorizados + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.help + Aquí puedes especificar una lista separada por comas de extensiones de fichero o tipos mime. Debe de ponerse en un fichero subido cuando se le asigna este tipo de adjunto. Para permitir todos los tipos de imágenes soportadas, puedes usar imagen/*. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.placeholder + p.ej. .txt, application/pdf, image/* + + + + + src\Form\PartType.php:63 + obsolete + obsolete + + + part.name.placeholder + p.ej. BC547 + + + + + obsolete + obsolete + + + entity.edit.not_selectable + No seleccionable + + + + + obsolete + obsolete + + + entity.edit.not_selectable.help + Si esta opción se activa, este elemento no puede ser asignado como una propiedad de un componente. Útil si este elemento se va a usar para agrupamiento. + + + + + obsolete + obsolete + + + bbcode.hint + Puedes usar BBCode aquí (p.ej. [b]Bold[/b]) + + + + + obsolete + obsolete + + + entity.create + Crear elemento + + + + + obsolete + obsolete + + + entity.edit.save + Guardar + + + + + obsolete + obsolete + + + category.edit.disable_footprints + Desactivar footprints + + + + + obsolete + obsolete + + + category.edit.disable_footprints.help + Si se activa esta opción, la propiedad footprint se desactivará para todos los componentes con esta categoría + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers + Desactivar fabricantes + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers.help + Si se activa esta opción, la propiedad fabricante se desactivará para todos los componentes con esta categoría. + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets + Desactivar enlaces automáticos a la hoja de datos + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets.help + Si se activa esta opción, no se crearán enlaces a la hoja de datos de forma automática para esta categoría. + + + + + obsolete + obsolete + + + category.edit.disable_properties + Desactivar propiedades + + + + + obsolete + obsolete + + + category.edit.disable_properties.help + Si se activa esta opción, las propiedades de los componentes se desactivarán para esta categoría. + + + + + obsolete + obsolete + + + category.edit.partname_hint + Sugerencia para nombre del componente + + + + + obsolete + obsolete + + + category.edit.partname_hint.placeholder + p.ej. 100nF + + + + + obsolete + obsolete + + + category.edit.partname_regex + Filtro de nombre + + + + + obsolete + obsolete + + + category.edit.default_description + Descripción por defecto + + + + + obsolete + obsolete + + + category.edit.default_description.placeholder + p.ej. Condensador, 10mm x 10mm, SMD + + + + + obsolete + obsolete + + + category.edit.default_comment + Notas por defecto + + + + + obsolete + obsolete + + + company.edit.address + Dirección + + + + + obsolete + obsolete + + + company.edit.address.placeholder + p.ej. Calleejemplo 314 +Ciudadejemplo + + + + + obsolete + obsolete + + + company.edit.phone_number + Número de teléfono + + + + + obsolete + obsolete + + + company.edit.phone_number.placeholder + +34 123 45 67 89 + + + + + obsolete + obsolete + + + company.edit.fax_number + Número de fax + + + + + obsolete + obsolete + + + company.edit.email + Email + + + + + obsolete + obsolete + + + company.edit.email.placeholder + p.ej. contact@foo.bar + + + + + obsolete + obsolete + + + company.edit.website + Sitio web + + + + + obsolete + obsolete + + + company.edit.website.placeholder + https://www.foo.bar + + + + + obsolete + obsolete + + + company.edit.auto_product_url + URL del producto + + + + + obsolete + obsolete + + + company.edit.auto_product_url.help + Este campo se usa para determinar un link al componente en la web de la empresa. %PARTNUMBER% será reemplazado por número del pedido. + + + + + obsolete + obsolete + + + company.edit.auto_product_url.placeholder + https://foo.bar/product/%PARTNUMBER% + + + + + obsolete + obsolete + + + currency.edit.iso_code + Código ISO + + + + + obsolete + obsolete + + + currency.edit.exchange_rate + Tasa de cambio + + + + + obsolete + obsolete + + + footprint.edit.3d_model + Modelo 3D + + + + + obsolete + obsolete + + + mass_creation.lines + Entrada + + + + + obsolete + obsolete + + + mass_creation.lines.placeholder + Elemento 1 + Elemento 1.1 + Elemento 1.1.1 + Elemento 1.2 +Elemento 2 +Elemento 3 + + + + + obsolete + obsolete + + + entity.mass_creation.btn + Crear + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer + Número entero + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer.help + Si esta opción está activada, todos los valores con esta unidad serán redondeados a un número entero. + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix + Utilizar prefijos SI + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix.help + Si esta opción está activada, los valores de salida utilizarán prefijos SI (p.ej. 1,2kg en vez de 1200g) + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol + Símbolo de la unidad + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol.placeholder + p.ej. m + + + + + obsolete + obsolete + + + storelocation.edit.is_full.label + Ubicación llena + + + + + obsolete + obsolete + + + storelocation.edit.is_full.help + Si esta opción es seleccionada, no será posible añadir componentes nuevas a esta ubicación o incrementar el número de componentes existentes. + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.label + Limitar a componentes existentes + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.help + Si esta opción se activa, no será posible añadir componentes nuevos a esta ubicación, pero el número de componentes existentes se podrá incrementar. + + + + + obsolete + obsolete + + + storelocation.only_single_part.label + Solo un componente + + + + + obsolete + obsolete + + + storelocation.only_single_part.help + Si esta opción se activa, solo un componente (de cualquier cantidad) podrá ser asignado a esta ubicación de almacenaje. Útil para cajas SMD pequeñas o alimentadores automáticos. + + + + + obsolete + obsolete + + + storelocation.storage_type.label + Tipo de almacén + + + + + obsolete + obsolete + + + storelocation.storage_type.help + Puedes seleccionar una unidad de medida aquí, la cual debe poderse asignar a esta ubicación de almacenaje. + + + + + obsolete + obsolete + + + supplier.edit.default_currency + Divisa predeterminada + + + + + obsolete + obsolete + + + supplier.shipping_costs.label + Costes de envío + + + + + obsolete + obsolete + + + user.username.placeholder + p.ej. j.doe + + + + + obsolete + obsolete + + + user.firstName.placeholder + p.ej. Mario + + + + + obsolete + obsolete + + + user.lastName.placeholder + p.ej. Gómez + + + + + obsolete + obsolete + + + user.email.placeholder + p.ej. m.gomez@ecorp.com + + + + + obsolete + obsolete + + + user.department.placeholder + p.ej. Desarrollo + + + + + obsolete + obsolete + + + user.settings.pw_new.label + Nueva contraseña + + + + + obsolete + obsolete + + + user.settings.pw_confirm.label + Confirmar nueva contraseña + + + + + obsolete + obsolete + + + user.edit.needs_pw_change + El usuario ha de cambiar su contraseña + + + + + obsolete + obsolete + + + user.edit.user_disabled + Usuario desactivado (no es posible iniciar sesión) + + + + + obsolete + obsolete + + + user.create + Crear usuario + + + + + obsolete + obsolete + + + user.edit.save + Guardar + + + + + obsolete + obsolete + + + entity.edit.reset + Descartar cambios + + + + + templates\Parts\show_part_info.html.twig:166 + obsolete + obsolete + + + part.withdraw.btn + Retirar + + + + + templates\Parts\show_part_info.html.twig:171 + obsolete + obsolete + + + part.withdraw.comment: + Comentario/Propósito + + + + + templates\Parts\show_part_info.html.twig:189 + obsolete + obsolete + + + part.add.caption + Añadir componentes + + + + + templates\Parts\show_part_info.html.twig:194 + obsolete + obsolete + + + part.add.btn + Agregar + + + + + templates\Parts\show_part_info.html.twig:199 + obsolete + obsolete + + + part.add.comment + Comentario/Propósito + + + + + templates\AdminPages\CompanyAdminBase.html.twig:15 + obsolete + obsolete + + + admin.comment + Notas + + + + + src\Form\PartType.php:83 + obsolete + obsolete + + + manufacturer_url.label + Enlace al fabricante + + + + + src\Form\PartType.php:66 + obsolete + obsolete + + + part.description.placeholder + ej. NPN 45V 0,1A 0,5W + + + + + src\Form\PartType.php:69 + obsolete + obsolete + + + part.instock.placeholder + ej. 10 + + + + + src\Form\PartType.php:72 + obsolete + obsolete + + + part.mininstock.placeholder + ej. 5 + + + + + obsolete + obsolete + + + part.order.price_per + Precio por + + + + + obsolete + obsolete + + + part.withdraw.caption + Retirar componentes + + + + + obsolete + obsolete + + + datatable.datatable.lengthMenu + _MENU_ + + + + + obsolete + obsolete + + + perm.group.parts + Componentes + + + + + obsolete + obsolete + + + perm.group.structures + Estructura de datos + + + + + obsolete + obsolete + + + perm.group.system + Sistema + + + + + obsolete + obsolete + + + perm.parts + Componentes + + + + + obsolete + obsolete + + + perm.read + Ver + + + + + obsolete + obsolete + + + perm.edit + Editar + + + + + obsolete + obsolete + + + perm.create + Crear + + + + + obsolete + obsolete + + + perm.part.move + Cambiar categoría + + + + + obsolete + obsolete + + + perm.delete + Eliminar + + + + + obsolete + obsolete + + + perm.part.search + Buscar + + + + + obsolete + obsolete + + + perm.part.all_parts + Lista de todos los componentes + + + + + obsolete + obsolete + + + perm.part.no_price_parts + Lista de componentes con precio + + + + + obsolete + obsolete + + + perm.part.obsolete_parts + Lista de componentes obsoletos + + + + + obsolete + obsolete + + + perm.part.unknown_instock_parts + Mostar componentes con stock desconocido + + + + + obsolete + obsolete + + + perm.part.change_favorite + Cambiar a estado de favorito + + + + + obsolete + obsolete + + + perm.part.show_favorite + Lista de componentes favoritos + + + + + obsolete + obsolete + + + perm.part.show_last_edit_parts + Mostar los últimos componentes editados/añadidos + + + + + obsolete + obsolete + + + perm.part.show_users + Mostar el último usuario modificado + + + + + obsolete + obsolete + + + perm.part.show_history + Mostrar historial + + + + + obsolete + obsolete + + + perm.part.name + Nombre + + + + + obsolete + obsolete + + + perm.part.description + Descripción + + + + + obsolete + obsolete + + + perm.part.instock + Disponibilidad + + + + + obsolete + obsolete + + + perm.part.mininstock + Disponibilidad mínima + + + + + obsolete + obsolete + + + perm.part.comment + Notas + + + + + obsolete + obsolete + + + perm.part.storelocation + Ubicación + + + + + obsolete + obsolete + + + perm.part.manufacturer + Fabricante + + + + + obsolete + obsolete + + + perm.part.orderdetails + Información ordenada + + + + + obsolete + obsolete + + + perm.part.prices + Precio + + + + + obsolete + obsolete + + + perm.part.attachments + Adjuntos de fichero + + + + + obsolete + obsolete + + + perm.part.order + Pedidos + + + + + obsolete + obsolete + + + perm.storelocations + Ubicaciones de almacén + + + + + obsolete + obsolete + + + perm.move + Mover + + + + + obsolete + obsolete + + + perm.list_parts + Lista de componentes + + + + + obsolete + obsolete + + + perm.part.footprints + Footprints + + + + + obsolete + obsolete + + + perm.part.categories + Categorías + + + + + obsolete + obsolete + + + perm.part.supplier + Proveedores + + + + + obsolete + obsolete + + + perm.part.manufacturers + Fabricantes + + + + + obsolete + obsolete + + + perm.projects + Proyectos + + + + + obsolete + obsolete + + + perm.part.attachment_types + Tipos de adjunto + + + + + obsolete + obsolete + + + perm.tools.import + Importar + + + + + obsolete + obsolete + + + perm.tools.labels + Etiquetas + + + + + obsolete + obsolete + + + perm.tools.calculator + Calculadora de resistencia + + + + + obsolete + obsolete + + + perm.tools.footprints + Footprints + + + + + obsolete + obsolete + + + perm.tools.ic_logos + Logos IC + + + + + obsolete + obsolete + + + perm.tools.statistics + Estadísticas + + + + + obsolete + obsolete + + + perm.edit_permissions + Editar autorizaciones + + + + + obsolete + obsolete + + + perm.users.edit_user_name + Editar nombre de usuario + + + + + obsolete + obsolete + + + perm.users.edit_change_group + Modificar grupo + + + + + obsolete + obsolete + + + perm.users.edit_infos + Editar información + + + + + obsolete + obsolete + + + perm.users.edit_permissions + Editar permisos + + + + + obsolete + obsolete + + + perm.users.set_password + Configurar contraseña + + + + + obsolete + obsolete + + + perm.users.change_user_settings + Cambiar ajustes de usuario + + + + + obsolete + obsolete + + + perm.database.see_status + Ver estado + + + + + obsolete + obsolete + + + perm.database.update_db + Actualizar base de datos + + + + + obsolete + obsolete + + + perm.database.read_db_settings + Leer ajustes de la base de datos + + + + + obsolete + obsolete + + + perm.database.write_db_settings + Escribir ajustes de la base de datos + + + + + obsolete + obsolete + + + perm.config.read_config + Leer configuración + + + + + obsolete + obsolete + + + perm.config.edit_config + Editar configuración + + + + + obsolete + obsolete + + + perm.config.server_info + Información del servidor + + + + + obsolete + obsolete + + + perm.config.use_debug + Usar herramientas de depuración + + + + + obsolete + obsolete + + + perm.show_logs + Mostrar registros + + + + + obsolete + obsolete + + + perm.delete_logs + Borrar registros + + + + + obsolete + obsolete + + + perm.self.edit_infos + Editar información + + + + + obsolete + obsolete + + + perm.self.edit_username + Editar nombre de usuario + + + + + obsolete + obsolete + + + perm.self.show_permissions + Mostrar permisos + + + + + obsolete + obsolete + + + perm.self.show_logs + Mostrar entradas de registro propias + + + + + obsolete + obsolete + + + perm.self.create_labels + Crear etiquetas + + + + + obsolete + obsolete + + + perm.self.edit_options + Editar opciones + + + + + obsolete + obsolete + + + perm.self.delete_profiles + Borrar perfiles + + + + + obsolete + obsolete + + + perm.self.edit_profiles + Editar perfiles + + + + + obsolete + obsolete + + + perm.part.tools + Herramientas + + + + + obsolete + obsolete + + + perm.groups + Grupos + + + + + obsolete + obsolete + + + perm.users + Usuarios + + + + + obsolete + obsolete + + + perm.database + Base de datos + + + + + obsolete + obsolete + + + perm.config + Configuración + + + + + obsolete + obsolete + + + perm.system + Sistema + + + + + obsolete + obsolete + + + perm.self + Edita tu propio usuario + + + + + obsolete + obsolete + + + perm.labels + Etiquetas + + + + + obsolete + obsolete + + + perm.part.category + Categoría + + + + + obsolete + obsolete + + + perm.part.minamount + Cantidad mínima + + + + + obsolete + obsolete + + + perm.part.footprint + Footprint + + + + + obsolete + obsolete + + + perm.part.mpn + MPN + + + + + obsolete + obsolete + + + perm.part.status + Estado de producción + + + + + obsolete + obsolete + + + perm.part.tags + Tags + + + + + obsolete + obsolete + + + perm.part.unit + Unidad de medida + + + + + obsolete + obsolete + + + perm.part.mass + Peso + + + + + obsolete + obsolete + + + perm.part.lots + Lote de componentes + + + + + obsolete + obsolete + + + perm.show_users + Mostrar último usuario que ha modificado + + + + + obsolete + obsolete + + + perm.currencies + Divisas + + + + + obsolete + obsolete + + + perm.measurement_units + Unidad de medida + + + + + obsolete + obsolete + + + user.settings.pw_old.label + Contraseña antigua + + + + + obsolete + obsolete + + + pw_reset.submit + Resetear la contraseña + + + + + obsolete + obsolete + + + u2f_two_factor + Clave de seguridad (U2F) + + + + + obsolete + obsolete + + + google + Google + + + + + tfa.provider.webauthn_two_factor_provider + Clave de seguridad + + + + + obsolete + obsolete + + + tfa.provider.google + App de autenticación + + + + + obsolete + obsolete + + + Login successful + Inicio de sesión correcto + + + + + obsolete + obsolete + + + log.type.exception + Excepción + + + + + obsolete + obsolete + + + log.type.user_login + Inicio de sesión de usuario + + + + + obsolete + obsolete + + + log.type.user_logout + Cerrar sesión de usuario + + + + + obsolete + obsolete + + + log.type.unknown + Desconocido + + + + + obsolete + obsolete + + + log.type.element_created + Elemento creado + + + + + obsolete + obsolete + + + log.type.element_edited + Elemento modificado + + + + + obsolete + obsolete + + + log.type.element_deleted + Elemento eliminado + + + + + obsolete + obsolete + + + log.type.database_updated + Base de datos actualizada + + + + + obsolete + + + perm.revert_elements + Restaurar elemento + + + + + obsolete + + + perm.show_history + Ver historial + + + + + obsolete + + + perm.tools.lastActivity + Mostrar última actividad + + + + + obsolete + + + perm.tools.timeTravel + Mostrar versiones antiguas de elementos (time travel) + + + + + obsolete + + + tfa_u2f.key_added_successful + Clave de seguridad añadida correctamente + + + + + obsolete + + + Username + Nombre de usuario + + + + + obsolete + + + log.type.security.google_disabled + App de autenticación desactivada + + + + + obsolete + + + log.type.security.u2f_removed + Clave de seguridad eliminada + + + + + obsolete + + + log.type.security.u2f_added + Clave de seguridad añadida + + + + + obsolete + + + log.type.security.backup_keys_reset + Claves de respaldo regeneradas + + + + + obsolete + + + log.type.security.google_enabled + App de autenticación activada + + + + + obsolete + + + log.type.security.password_changed + Contraseña cambiada + + + + + obsolete + + + log.type.security.trusted_device_reset + Dispositivos de confianza reseteados + + + + + obsolete + + + log.type.collection_element_deleted + Elemento de colección eliminado + + + + + obsolete + + + log.type.security.password_reset + Restablecer contraseña + + + + + obsolete + + + log.type.security.2fa_admin_reset + Autenticación de dos pasos reseteado por Administrador + + + + + obsolete + + + log.type.user_not_allowed + Intento de acceso no autorizado + + + + + obsolete + + + log.database_updated.success + Éxito + + + + + obsolete + + + label_options.barcode_type.2D + 2D + + + + + obsolete + + + label_options.barcode_type.1D + 1D + + + + + obsolete + + + perm.part.parameters + Parámetros + + + + + obsolete + + + perm.attachment_show_private + Ver adjuntos privados + + + + + obsolete + + + perm.tools.label_scanner + Escáner de etiquetas + + + + + obsolete + + + perm.self.read_profiles + Leer perfiles + + + + + obsolete + + + perm.self.create_profiles + Crear perfiles + + + + + obsolete + + + perm.labels.use_twig + Usar modo Twig + + + + + label_profile.showInDropdown + Mostrar en selección rápida + + + + + group.edit.enforce_2fa + Forzar autenticación de dos pasos (2FA) + + + + + group.edit.enforce_2fa.help + Si está opción está activada, todos los miembros directos de este grupo tendrá que configurar como mínimo un factor de autenticación de dos pasos. Recomendado para grupos administrativos con muchos permisos. + + + + + selectpicker.empty + Nada seleccionado + + + + + selectpicker.nothing_selected + Nada seleccionado + + + + + entity.delete.must_not_contain_parts + ¡Elemento "%PATH%" aún contiene partes! Tienes que mover las partes para poder eliminar este elemento. + + + + + entity.delete.must_not_contain_attachments + Tipo de adjunto aún contiene adjuntos. Cambia su tipo para ser capaz de borrar este tipo de adjunto. + + + + + entity.delete.must_not_contain_prices + Divisa aún contiene detalles de precios. Tienes que cambiar su divisa para poder eliminar este elemento. + + + + + entity.delete.must_not_contain_users + ¡Usuarios aún usan este grupo! Cambia su grupo para ser capaz de borrar este grupo. + + + + + part.table.edit + Editar + + + + + part.table.edit.title + Editar componente + + + + + part_list.action.action.title + Seleccionar acción + + + + + part_list.action.action.group.favorite + Favorito + + + + + part_list.action.action.favorite + Componente favorito + + + + + part_list.action.action.unfavorite + Anular indicación de favorito + + + + + part_list.action.action.group.change_field + Cambiar campo + + + + + part_list.action.action.change_category + Cambiar categoría + + + + + part_list.action.action.change_footprint + Cambiar footprint + + + + + part_list.action.action.change_manufacturer + Cambiar fabricante + + + + + part_list.action.action.change_unit + Cambiar unidad de componente + + + + + part_list.action.action.delete + Borrar + + + + + part_list.action.submit + Ok + + + + + part_list.action.part_count + ¡%count% componentes seleccionadas! + + + + + company.edit.quick.website + Abrir sitio web + + + + + company.edit.quick.email + Enviar e-mail + + + + + company.edit.quick.phone + Teléfono + + + + + company.edit.quick.fax + Enviar fax + + + + + company.fax_number.placeholder + ej. +34 123 45 67 89 + + + + + part.edit.save_and_clone + Guardar y duplicar + + + + + validator.file_ext_not_allowed + Extensión de archivo no permitido para este tipo de adjunto. + + + + + tools.reel_calc.title + Calculadora de cintas SMD + + + + + tools.reel_calc.inner_dia + Diámetro interno + + + + + tools.reel_calc.outer_dia + Diámetro externo + + + + + tools.reel_calc.tape_thick + Espesor de cinta + + + + + tools.reel_calc.part_distance + Distancia de componentes + + + + + tools.reel_calc.update + Actualizar + + + + + tools.reel_calc.parts_per_meter + Componentes por metro + + + + + tools.reel_calc.result_length + Longitud de la cinta + + + + + tools.reel_calc.result_amount + Número aproximado de componentes + + + + + tools.reel_calc.outer_greater_inner_error + Error: Diámetro exterior debe ser mayor que el diámetro interior! + + + + + tools.reel_calc.missing_values.error + ¡Por favor rellena todos los valores! + + + + + tools.reel_calc.load_preset + Cargar preset + + + + + tools.reel_calc.explanation + Esta calculadora da una estimación de cuántas piezas quedan en un carrete SMD. Mide las dimensiones anotadas en el carrete (o use algunos de los ajustes preestablecidos) y haz clic en "Actualizar" para obtener un resultado. + + + + + perm.tools.reel_calculator + Calculadora de carrete SMD + + + + + tree.tools.tools.reel_calculator + Calculadora de carrete SMD + + + + + user.pw_change_needed.flash + ¡Tienes que cambiar tu contraseña! Por favor establece una nueva contraseña. + + + + + tree.root_node.text + Nodo raíz + + + + + part_list.action.select_null + Elemento vacío + + + + + part_list.action.delete-title + ¿Estás seguro de que quieres eliminar estos elementos? + + + + + part_list.action.delete-message + Estos elementos y cualquier información asociada (como adjuntos, información de precios, etc.) serán eliminados. ¡Esto no se podrá deshacer! + + + + + part.table.actions.success + Acciones terminadas exitosamente. + + + + + attachment.edit.delete.confirm + ¿Estás seguro de que quieres borrar este adjunto? + + + + + filter.text_constraint.value.operator.EQ + Es + + + + + filter.text_constraint.value.operator.NEQ + No es + + + + + filter.text_constraint.value.operator.STARTS + Empieza con + + + + + filter.text_constraint.value.operator.CONTAINS + Contiene + + + + + filter.text_constraint.value.operator.ENDS + Termina con + + + + + filter.text_constraint.value.operator.LIKE + Patrón LIKE + + + + + filter.text_constraint.value.operator.REGEX + Expresión regular + + + + + filter.number_constraint.value.operator.BETWEEN + Entre + + + + + filter.number_constraint.AND + y + + + + + filter.entity_constraint.operator.EQ + Es (excluyendo hijos) + + + + + filter.entity_constraint.operator.NEQ + No es (excluyendo hijos) + + + + + filter.entity_constraint.operator.INCLUDING_CHILDREN + Es (incluyendo hijos) + + + + + filter.entity_constraint.operator.EXCLUDING_CHILDREN + No es (incluyendo hijos) + + + + + part.filter.dbId + ID de la base de datos + + + + + filter.tags_constraint.operator.ANY + Cualquiera de las etiquetas + + + + + filter.tags_constraint.operator.ALL + Todas las etiquetas + + + + + filter.tags_constraint.operator.NONE + Ninguna de las etiquetas + + + + + part.filter.lot_count + Número de lotes + + + + + part.filter.attachments_count + Número de adjuntos + + + + + part.filter.orderdetails_count + Número de información del pedido + + + + + part.filter.lotExpirationDate + Fecha de caducidad del lote + + + + + part.filter.lotNeedsRefill + Cualquier lote necesita ser rellenado + + + + + part.filter.lotUnknwonAmount + Cualquier lote tiene una cantidad desconocida + + + + + part.filter.attachmentName + Nombre del adjunto + + + + + filter.choice_constraint.operator.ANY + Cualquiera de + + + + + filter.choice_constraint.operator.NONE + Ninguno de + + + + + part.filter.amount_sum + Cantidad total + + + + + filter.submit + Actualizar + + + + + filter.discard + Descartar cambios + + + + + filter.clear_filters + Limpiar filtros + + + + + filter.title + Filtro + + + + + filter.parameter_value_constraint.operator.= + Tipo de valor = + + + + + filter.parameter_value_constraint.operator.!= + Tipo de valor != + + + + + filter.parameter_value_constraint.operator.< + Tipo de valor < + + + + + filter.parameter_value_constraint.operator.> + Tipo de valor > + + + + + filter.parameter_value_constraint.operator.<= + Tipo de valor <= + + + + + filter.parameter_value_constraint.operator.>= + Tipo de valor >= + + + + + filter.parameter_value_constraint.operator.BETWEEN + Tipo de valor está entre + + + + + filter.parameter_value_constraint.operator.IN_RANGE + En rango de valor + + + + + filter.parameter_value_constraint.operator.NOT_IN_RANGE + Fuera de rango de valor + + + + + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE + Mayor que el rango de valor + + + + + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE + Mayor igual al rango de valor + + + + + filter.parameter_value_constraint.operator.LESS_THAN_RANGE + Menor al rango de valor + + + + + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE + Menor igual al rango de valor + + + + + filter.parameter_value_constraint.operator.RANGE_IN_RANGE + Rango está completamente en rango de valor + + + + + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE + El rango intersecta el rango de valores + + + + + filter.text_constraint.value + Sin valor establecido + + + + + filter.number_constraint.value1 + Sin valor establecido + + + + + filter.number_constraint.value2 + Valor máximo + + + + + filter.datetime_constraint.value1 + No hay fecha/hora establecidas + + + + + filter.datetime_constraint.value2 + Máxima fecha/hora + + + + + filter.constraint.add + Añadir filtro + + + + + part.filter.parameters_count + Número de parámetros + + + + + part.filter.lotDescription + Descripción del lote del componente + + + + + parts_list.search.searching_for + Buscar componentes con palabra clave <b>%keyword%</b> + + + + + parts_list.search_options.caption + Opciones de búsqueda activadas + + + + + attachment.table.element_type + Tipo de elemento asociado + + + + + log.level.debug + Debug + + + + + log.level.info + Info + + + + + log.level.notice + Notificación + + + + + log.level.warning + Aviso + + + + + log.level.error + Error + + + + + log.level.critical + Crítico + + + + + log.level.alert + Alerta + + + + + log.level.emergency + Emergencia + + + + + log.type.security + Evento relacionado con seguridad + + + + + log.type.instock_changed + [ALT] Stock modificado + + + + + log.target_id + ID del elemento objetivo + + + + + entity.info.parts_count_recursive + Número de componentes con este elemento o sus subelementos + + + + + tools.server_infos.title + Información del servidor + + + + + permission.preset.read_only + Solo lectura + + + + + permission.preset.read_only.desc + Permitir solo operaciones de lectura sobre los datos + + + + + permission.preset.all_inherit + Heredar todo + + + + + permission.preset.all_inherit.desc + Cambiar todos los permisos a Heredar + + + + + permission.preset.all_forbid + Prohibir todo + + + + + permission.preset.all_forbid.desc + Cambiar todos los permisos a Prohibir + + + + + permission.preset.all_allow + Mostrar todo + + + + + permission.preset.all_allow.desc + Cambiar todos los permisos a permitir + + + + + perm.server_infos + Información de servidor + + + + + permission.preset.editor + Editor + + + + + permission.preset.editor.desc + Permitir cambiar elementos y estructuras de datos + + + + + permission.preset.admin + Admin + + + + + permission.preset.admin.desc + Permitir acciones administrativas + + + + + permission.preset.button + Aplicar preset + + + + + perm.attachments.show_private + Mostrar adjuntos privados + + + + + perm.attachments.list_attachments + Mostrar lista de todos los adjuntos + + + + + user.edit.permission_success + El permiso preestablecido se aplicó correctamente. Compruebe si los nuevos permisos se ajustan a tus necesidades. + + + + + perm.group.data + Datos + + + + + part_list.action.action.group.needs_review + Necesita revisión + + + + + part_list.action.action.set_needs_review + Establecer estado "necesita revisión" + + + + + part_list.action.action.unset_needs_review + Eliminar estatus "necesita revisión" + + + + + part.edit.ipn + Número de Componente Interno (IPN) + + + + + part.ipn.not_defined + No definido + + + + + part.table.ipn + IPN + + + + + currency.edit.update_rate + Recuperar tasa de cambio + + + + + currency.edit.exchange_rate_update.unsupported_currency + La divisa no está respaldada por el proveedor de tasas de cambio. Verifica la configuración de tu proveedor de tasas de cambio. + + + + + currency.edit.exchange_rate_update.generic_error + Imposible de recuperar la tasa de cambio. Verifica la configuración de tu proveedor de tasas de cambio. + + + + + currency.edit.exchange_rate_updated.success + Tasa de cambio recuperada con éxito. + + + + + project.bom.quantity + Cantidad BOM + + + + + project.bom.mountnames + Nombres de ensamblaje + + + + + project.bom.name + Nombre + + + + + project.bom.comment + Notas + + + + + project.bom.part + Componente + + + + + project.bom.add_entry + Añadir entrada + + + + + part_list.action.group.projects + Proyectos + + + + + part_list.action.projects.add_to_project + Añadir componentes al proyecto + + + + + project.bom.delete.confirm + ¿Estás seguro de que quieres eliminar esta entrada BOM? + + + + + project.add_parts_to_project + Añadir componentes al proyecto BOM + + + + + part.info.add_part_to_project + Añadir este componente a un proyecto + + + + + project_bom_entry.label + Entrada BOM + + + + + project.edit.status + Estatus de proyecto + + + + + project.status.draft + Esbozo + + + + + project.status.planning + En planificación + + + + + project.status.in_production + En producción + + + + + project.status.finished + Completado + + + + + project.status.archived + Archivado + + + + + part.new_build_part.error.build_part_already_exists + ¡Este proyecto ya tiene un componente vinculado! + + + + + project.edit.associated_build_part + Componente de producción vinculado + + + + + project.edit.associated_build_part.add + Añadir parte de producción + + + + + project.edit.associated_build.hint + Este componente representa las instancias construidas del proyecto, que están almacenadas en algún lugar + + + + + part.info.projectBuildPart.hint + Este componente representa las instancias construidas del siguiente proyecto y está vinculado a él. + + + + + part.is_build_part + Es un componente de producción + + + + + project.info.title + Información de proyecto + + + + + project.info.bom_entries_count + Entradas BOM + + + + + project.info.sub_projects_count + Subproyectos + + + + + project.info.bom_add_parts + Añadir entradas BOM + + + + + project.info.info.label + Info + + + + + project.info.sub_projects.label + Subproyectos + + + + + project.bom.price + Precio + + + + + part.info.withdraw_modal.title.withdraw + Retirar componentes del lote + + + + + part.info.withdraw_modal.title.add + Añadir componentes al lote + + + + + part.info.withdraw_modal.title.move + Mover componentes a un lote diferente + + + + + part.info.withdraw_modal.amount + Cantidad + + + + + part.info.withdraw_modal.move_to + Mover a + + + + + part.info.withdraw_modal.comment + Comentar + + + + + part.info.withdraw_modal.comment.hint + Puedes añadir un comentario aquí describiendo por qué estás haciendo esta operación (p.ej. por qué necesitas este componente). Esta información se almacenará en el registro. + + + + + modal.close + Cerrar + + + + + modal.submit + Entregar + + + + + part.withdraw.success + Componentes añadidos/movidos/retirados con éxito. + + + + + perm.parts_stock + Inventario de componentes + + + + + perm.parts_stock.withdraw + Retirar componentes del inventario. + + + + + perm.parts_stock.add + Añadir componentes a stock + + + + + perm.parts_stock.move + Mover componentes entre lotes + + + + + user.permissions_schema_updated + El esquema de permisos de tu usuario han sido actualizados a la última versión. + + + + + log.type.part_stock_changed + Stock de componente modificado + + + + + log.part_stock_changed.withdraw + Stock retirado + + + + + log.part_stock_changed.add + Stock añadido + + + + + log.part_stock_changed.move + Stock movido + + + + + log.part_stock_changed.comment + Comentario + + + + + log.part_stock_changed.change + Cambiar + + + + + log.part_stock_changed.move_target + Trasladado a + + + + + tools.builtin_footprints_viewer.title + Imágenes de footprints incluidas. + + + + + tools.builtin_footprints_viewer.hint + Esta galería enumera todas las imágenes de footprints incluidas. Si desea utilizar esto en un archivo adjunto, escriba el nombre (o una palabra clave) en el campo URL del archivo adjunto y seleccione la imagen deseada en el menú desplegable. + + + + + tools.ic_logos.title + Logo IC + + + + + part_list.action.group.labels + Etiquetas + + + + + part_list.action.projects.generate_label + Crear etiquetas (por partes) + + + + + part_list.action.projects.generate_label_lot + Crear etiquetas (por partes de lote) + + + + + part_list.action.generate_label.empty + Vaciar etiquetas + + + + + project.info.builds.label + Construir + + + + + project.builds.build_not_possible + Construcción no es posible: Faltan componentes + + + + + project.builds.following_bom_entries_miss_instock + Los siguientes componentes no tienen suficiente cantidad para construir este proyecto al menos una vez: + + + + + project.builds.stocked + Almacenado + + + + + project.builds.needed + Necesitado + + + + + project.builds.build_possible + Construcción posible + + + + + project.builds.number_of_builds_possible + Tienes suficiente inventario para construir <b>%max_builds%</b></b> construcciones de este proyecto + + + + + project.builds.check_project_status + El estado del proyecto actual es <b>"%project_status%"</b>. ¡Deberías comprobar si de verdad quieres crear este proyecto con este estado! + + + + + project.builds.following_bom_entries_miss_instock_n + No tienes suficientes componentes en stock para crear este proyecto %number_of_builds% veces. A los siguientes componentes les falta instock: + + + + + project.build.flash.invalid_input + No se pudo crear el proyecto. ¡Comprueba los datos de entrada! + + + + + project.build.required_qty + Cantidad requerida + + + + + project.build.btn_build + Construir + + + + + project.build.help + Elija de qué lotes se debe tomar el stock para construir este proyecto (y en qué cantidad). Comprueba la casilla para cada entrada BOM cuando hayas terminado de retirar los componentes, o utiliza la casilla de arriba para seleccionar todas las casillas a la vez. + + + + + project.build.buildsPartLot.new_lot + Crear nuevo lote + + + + + project.build.add_builds_to_builds_part + Añadir creaciones a las creaciones del proyecto + + + + + project.build.builds_part_lot + Lote objetivo + + + + + project.builds.number_of_builds + Cantidad de construcciones + + + + + project.builds.no_stocked_builds + Número de construcciones en stock + + + + + user.change_avatar.label + Cambiar foto de perfil + + + + + user_settings.change_avatar.label + Cambiar foto de perfil + + + + + user_settings.remove_avatar.label + Eliminar foto de perfil + + + + + part.edit.name.category_hint + Sugerencia de categoría + + + + + category.edit.partname_regex.placeholder + p.ej. "/Condensador \d+ nF/i" + + + + + category.edit.partname_regex.help + Una expresión regular compatible con PCRE, la cual debe coincidir con el nombre de un componente. + + + + + entity.select.add_hint + Usar -> para crear estructuras anidadas, p.ej. "Node 1->Node 1.1" + + + + + entity.select.group.new_not_added_to_DB + Nuevo (no añadido a la base de datos) + + + + + part.edit.save_and_new + Guardar y crear nuevo componente vacío. + + + + + homepage.first_steps.title + Primeros pasos + + + + + homepage.first_steps.introduction + Tu base de datos está aún vacía. Quizá quieras leer la <a href="%url%">documentation</a> o empezar a crear las siguientes estructuras de datos: + + + + + homepage.first_steps.create_part + O puedes directamente <a href="%url%">crear un nuevo componente</a> + + + + + homepage.first_steps.hide_hint + Esta casilla desaparecerá cuando hayas creado tu primer componente. + + + + + homepage.forum.text + Para preguntas sobre Part-DB utiliza el <a href="%href%" class="link-external" target="_blank">foro de debate</a> + + + + + log.element_edited.changed_fields.category + Categoría + + + + + log.element_edited.changed_fields.footprint + Footprint + + + + + log.element_edited.changed_fields.manufacturer + Fabricante + + + + + log.element_edited.changed_fields.value_typical + Tip. valor + + + + + log.element_edited.changed_fields.pw_reset_expires + Restablecer contraseña + + + + + log.element_edited.changed_fields.comment + Notas + + + + + log.element_edited.changed_fields.supplierpartnr + Número del distribuidor del componente + + + + + log.element_edited.changed_fields.supplier_product_url + URL del producto + + + + + log.element_edited.changed_fields.price + Precio + + + + + log.element_edited.changed_fields.min_discount_quantity + Cantidad mínima de descuento + + + + + log.element_edited.changed_fields.original_filename + Nombre de archivo original + + + + + log.element_edited.changed_fields.path + Ruta de fichero + + + + + log.element_edited.changed_fields.description + Descripción + + + + + log.element_edited.changed_fields.manufacturing_status + Estatus de producción + + + + + log.element_edited.changed_fields.options.barcode_type + Tipo de código de barras + + + + + log.element_edited.changed_fields.status + Estatus + + + + + log.element_edited.changed_fields.quantity + Cantidad BOM + + + + + log.element_edited.changed_fields.mountnames + Nombres de ensamblaje + + + + + log.element_edited.changed_fields.name + Nombre + + + + + log.element_edited.changed_fields.part + Componente + + + + + log.element_edited.changed_fields.price_currency + Precio de la divisa + + + + + log.element_edited.changed_fields.partname_hint + Sugerencia de nombre de componente + + + + + log.element_edited.changed_fields.partname_regex + Filtro de nombre + + + + + log.element_edited.changed_fields.disable_footprints + Desactivar footprints + + + + + log.element_edited.changed_fields.disable_manufacturers + Desactivar fabricantes + + + + + log.element_edited.changed_fields.disable_autodatasheets + Desactivar links automáticos a datasheets + + + + + log.element_edited.changed_fields.disable_properties + Desactivar propiedades + + + + + log.element_edited.changed_fields.default_description + Descripción predeterminada + + + + + log.element_edited.changed_fields.default_comment + Comentario predeterminado + + + + + log.element_edited.changed_fields.filetype_filter + Extensiones de archivo permitidas + + + + + log.element_edited.changed_fields.not_selectable + No seleccionado + + + + + log.element_edited.changed_fields.parent + Elemento padre + + + + + log.element_edited.changed_fields.shipping_costs + Costes de envío + + + + + log.element_edited.changed_fields.default_currency + Divisa predeterminada + + + + + log.element_edited.changed_fields.address + Dirección + + + + + log.element_edited.changed_fields.phone_number + Número de teléfono + + + + + log.element_edited.changed_fields.fax_number + Número fax + + + + + log.element_edited.changed_fields.email_address + E-mail + + + + + log.element_edited.changed_fields.website + Sitio web + + + + + log.element_edited.changed_fields.auto_product_url + URL de producto + + + + + log.element_edited.changed_fields.is_full + Ubicación de almacenaje llena + + + + + log.element_edited.changed_fields.limit_to_existing_parts + Límite a componentes existentes + + + + + log.element_edited.changed_fields.only_single_part + Solo componentes individuales + + + + + log.element_edited.changed_fields.storage_type + Tipo de almacenaje + + + + + log.element_edited.changed_fields.footprint_3d + Modelo 3D + + + + + log.element_edited.changed_fields.master_picture_attachment + Previsualización de imagen + + + + + log.element_edited.changed_fields.exchange_rate + Tasa de cambio + + + + + log.element_edited.changed_fields.iso_code + Código ISO + + + + + log.element_edited.changed_fields.unit + Símbolo de unidad + + + + + log.element_edited.changed_fields.is_integer + Es entero + + + + + log.element_edited.changed_fields.use_si_prefix + Usar prefijo SI + + + + + log.element_edited.changed_fields.options.width + Anchura + + + + + log.element_edited.changed_fields.options.height + Altura + + + + + log.element_edited.changed_fields.options.supported_element + Tipo de elemento + + + + + log.element_edited.changed_fields.options.additional_css + Estilos adicionales (CSS) + + + + + log.element_edited.changed_fields.options.lines + Contenido + + + + + log.element_edited.changed_fields.permissions.data + Permisos + + + + + log.element_edited.changed_fields.disabled + Deshabilitado + + + + + log.element_edited.changed_fields.theme + Tema + + + + + log.element_edited.changed_fields.timezone + Zona horaria + + + + + log.element_edited.changed_fields.language + Idioma + + + + + log.element_edited.changed_fields.email + E-mail + + + + + log.element_edited.changed_fields.department + Departamento + + + + + log.element_edited.changed_fields.last_name + Apellidos + + + + + log.element_edited.changed_fields.first_name + Nombre + + + + + log.element_edited.changed_fields.group + Grupo + + + + + log.element_edited.changed_fields.currency + Divisa preferida + + + + + log.element_edited.changed_fields.enforce2FA + Forzar 2FA + + + + + log.element_edited.changed_fields.symbol + Símbolo + + + + + log.element_edited.changed_fields.value_min + Valor mín. + + + + + log.element_edited.changed_fields.value_max + Valor máx. + + + + + log.element_edited.changed_fields.value_text + Valor de texto + + + + + log.element_edited.changed_fields.show_in_table + Mostrar en tabla + + + + + log.element_edited.changed_fields.attachment_type + Tipo de archivo + + + + + log.element_edited.changed_fields.needs_review + Necesita revisión + + + + + log.element_edited.changed_fields.tags + Etiquetas + + + + + log.element_edited.changed_fields.mass + Masa + + + + + log.element_edited.changed_fields.ipn + IPN + + + + + log.element_edited.changed_fields.favorite + Favorito + + + + + log.element_edited.changed_fields.minamount + Stock mínimo + + + + + log.element_edited.changed_fields.manufacturer_product_url + Link a página del producto + + + + + log.element_edited.changed_fields.manufacturer_product_number + MPN + + + + + log.element_edited.changed_fields.partUnit + Unidad de medida + + + + + log.element_edited.changed_fields.expiration_date + Fecha de expiración + + + + + log.element_edited.changed_fields.amount + Cantidad + + + + + log.element_edited.changed_fields.storage_location + Ubicación + + + + + attachment.max_file_size + Tamaño máximo de archivo + + + + + user.saml_user + usuario SSO / SAML + + + + + user.saml_user.pw_change_hint + Tu usuario usa Single Sign-On (SSO). No puedes cambiar la contraseña ni los ajustes 2FA. ¡Configúralos en tu proveedor central SSO! + + + + + login.sso_saml_login + Single Sign-On Login (SSO) + + + + + login.local_login_hint + El formulario de debajo solo sirve para registrarte con un usuario local. Si quieres registrarte mediante sign-on, pulsa el botón de arriba. + + + + + part_list.action.action.export + Exportar componentes + + + + + part_list.action.export_json + Exportar a JSON + + + + + part_list.action.export_csv + Exportar a CSV + + + + + part_list.action.export_yaml + Exportar a YAML + + + + + part_list.action.export_xml + Exportar a XML + + + + + parts.import.title + Importar componentes + + + + + parts.import.errors.title + Problemas con las importaciones + + + + + parts.import.flash.error + Error en importe. Probablemente esto haya sido causado por datos inválidos. + + + + + parts.import.format.auto + Automático (basado en extensión de archivo) + + + + + parts.import.flash.error.unknown_format + ¡No se pudo determinar el formato del archivo dado! + + + + + parts.import.flash.error.invalid_file + Archivo inválido. ¡Por favor comprueba que has seleccionado el formato adecuado! + + + + + parts.import.part_category.label + Sobreescribir categoría + + + + + parts.import.part_category.help + Si seleccionas un valor aquí, todos los componentes importados serán asignados a esta categoría. Sin importar que fuera seleccionado en los datos. + + + + + import.create_unknown_datastructures + Crear estructuras de datos desconocidas + + + + + import.create_unknown_datastructures.help + Si esto es seleccionado, las estructuras de datos (como categorías, footprints, etc.) que no existan en la base de datos serán creadas automáticamente. Si no es seleccionado, solo se crearan estructuras de datos existentes, y si no se encuentra ninguna estructura de datos compatible, el componente no será asignado a nada. + + + + + import.path_delimiter + Delimitador de camino + + + + + import.path_delimiter.help + Delimitador usado para marcar diferentes niveles en caminos de estructuras de datos como categoría, footprint, etc. + + + + + parts.import.help_documentation + Vea la <a href="%link%">documentación</a> para más información sobre el formato de archivo. + + + + + parts.import.help + Puedes importar componentes desde archivos con esta herramienta. Los componentes serán añadidos directamente a la base de datos, así que por favor comprueba que el archivo es correcto antes de subirlo aquí. + + + + + parts.import.flash.success + ¡Componente importado correctamente! + + + + + parts.import.errors.imported_entities + Componentes importados + + + + + perm.import + Importar datos + + + + + parts.import.part_needs_review.label + Marcar todos los componentes importados como "Necesita revisión" + + + + + parts.import.part_needs_review.help + Si se selecciona esta opción, todos los componentes serán marcados como "Necesita revisión", independientemente de lo que indiquen los datos. + + + + + project.bom_import.flash.success + Se han importado %count% entradas BOM correctamente. + + + + + project.bom_import.type + Tipo + + + + + project.bom_import.type.kicad_pcbnew + KiCAD Pcbnew BOM (archivo CSV) + + + + + project.bom_import.clear_existing_bom + Eliminar entradas BOM existentes antes de importar + + + + + project.bom_import.clear_existing_bom.help + ¡Seleccionar esta opción eliminará todas las entradas BOM existentes en el proyecto y las sobreescribirá con el archivo BOM importado! + + + + + project.bom_import.flash.invalid_file + No se pudo importar el archivo. Por favor, comprueba que has seleccionado el tipo de archivo adecuado. Mensaje de error: %message% + + + + + project.bom_import.flash.invalid_entries + ¡Error de validación! ¡Por favor comprueba tus datos! + + + + + project.import_bom + Importar BOM para el proyecto + + + + + project.edit.bom.import_bom + Importar BOM + + + + + measurement_unit.new + Nueva Unidad de Medida + + + + + measurement_unit.edit + Editar Unidad de Medida + + + + + user.aboutMe.label + Sobre mí + + + + + storelocation.owner.label + Propietario + + + + + storelocation.part_owner_must_match.label + Lote de Componente debe coincidir con el propietario de la ubicación de almacenaje + + + + + part_lot.owner + Propietario + + + + + part_lot.owner.help + Solo el propietario puede quitar o añadir stock a este lote. + + + + + log.element_edited.changed_fields.owner + Propietario + + + + + log.element_edited.changed_fields.instock_unknown + Cantidad desconocida + + + + + log.element_edited.changed_fields.needs_refill + Necesita relleno + + + + + part.withdraw.access_denied + No se permite realizar la acción deseada. Por favor comprueba tus permisos y el propietario de los lotes de la parte. + + + + + part.info.amount.less_than_desired + Menos de lo deseado + + + + + log.cli_user + Usuario CLI + + + + + log.element_edited.changed_fields.part_owner_must_match + El propietario del componente debe coincidir con el propietario de la ubicación de almacenaje + + + + + part.filter.lessThanDesired + Stock menor de lo deseado (cantidad total < cantidad mínima) + + + + + part.filter.lotOwner + Propietario del lote + + + + + user.show_email_on_profile.label + Mostrar email en la página de perfil pública + + + + + log.details.title + Detalles del log + + + + + log.user_login.login_from_ip + Iniciar sesión con dirección IP + + + + + log.user_login.ip_anonymize_hint + Si los últimos dígitos de la dirección IP no aparecen, entonces se activa el modo RGPD, en el cual las direcciones IP son anonimizadas. + + + + + log.user_not_allowed.unauthorized_access_attempt_to + Acceso no autorizado a la página + + + + + log.user_not_allowed.hint + La petición fue bloqueada. Ninguna acción debería ser requerida. + + + + + log.no_comment + Sin comentarios + + + + + log.element_changed.field + Campo + + + + + log.element_changed.data_before + Datos previos al cambio + + + + + error_table.error + Un error ha ocurrido durante tu petición. + + + + + part.table.invalid_regex + Expresión regular inválida (regex) + + + + + log.element_changed.data_after + Datos después del cambio + + + + + log.element_changed.diff + Diferencia + + + + + log.undo.undo.short + Deshacer + + + + + log.undo.revert.short + Revertir a esta versión + + + + + log.view_version + Ver versión + + + + + log.undo.undelete.short + Restaurar + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + Propietario + + + + + log.element_edited.changed_fields.parent_id + Padre + + + + + log.details.delete_entry + Cancelar la entrada de registro + + + + + log.delete.message.title + ¿Realmente quieres borrar la entrada de registro? + + + + + log.delete.message + Si esto es una entrada de historia de elemento, ¡esto rompe la historia del elemento! Esto puede conllevar resultados inesperados cuando se use la función de viaje en el tiempo. + + + + + log.collection_deleted.on_collection + En colección + + + + + log.element_edited.changed_fields.attachments + Adjuntos + + + + + tfa_u2f.add_key.registration_error + Hubo un error durante el registro de la clave de seguridad. ¡Vuelve a intentarlo o usa otra clave de seguridad! + + + + + log.target_type.none + Ninguno + + + + + ui.darkmode.light + Luminoso + + + + + ui.darkmode.dark + Oscuro + + + + + ui.darkmode.auto + Automático (decidir según los ajustes del sistema) + + + + + label_generator.no_lines_given + ¡No se ha dado contenido de texto! Las etiquetas permanecerán vacías. + + + + + user.password_strength.very_weak + Muy débil + + + + + user.password_strength.weak + Débil + + + + + user.password_strength.medium + Medio + + + + + user.password_strength.strong + Fuerte + + + + + user.password_strength.very_strong + Muy fuerte + + + + + perm.users.impersonate + Personificar otros usuarios + + + + + user.impersonated_by.label + Personificado por + + + + + user.stop_impersonation + Detener la personificación + + + + + user.impersonate.btn + Personificar + + + + + user.impersonate.confirm.title + ¿Realmente quieres personificar a este usuario? + + + + + user.impersonate.confirm.message + Esto será registrado. Haz esto solo por un buen motivo. + +Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. Si lo intentas obtendrás un mensaje de "Acceso denegado". + + + + + log.type.security.user_impersonated + Usuario personificado + + + + + info_providers.providers_list.title + Proveedor de información + + + + + info_providers.providers_list.active + Activo + + + + + info_providers.providers_list.disabled + Desactivado + + + + + info_providers.capabilities.basic + Básico + + + + + info_providers.capabilities.footprint + Footprint + + + + + info_providers.capabilities.picture + Imagen + + + + + info_providers.capabilities.datasheet + Ficha técnica + + + + + info_providers.capabilities.price + Precios + + + + + part.info_provider_reference.badge + El proveedor de información usada para crear este componente. + + + + + part.info_provider_reference + Creado por proveedor de información + + + + + oauth_client.connect.btn + Conectar OAuth + + + + + info_providers.table.provider.label + Proveedor + + + + + info_providers.search.keyword + Palabra clave + + + + + info_providers.search.submit + Buscar + + + + + info_providers.search.providers.help + Seleccionar los proveedores que deban ser buscados. + + + + + info_providers.search.providers + Proveedores + + + + + info_providers.search.info_providers_list + Mostrar todos los proveedores de información disponibles + + + + + info_providers.search.title + Crear componentes de proveedor de información + + + + + oauth_client.flash.connection_successful + Conectado a la aplicación OAuth con éxito + + + + + perm.part.info_providers + Proveedores de información + + + + + perm.part.info_providers.create_parts + Crear componentes a partir de proveedor de información + + + + + entity.edit.alternative_names.label + Nombres alternativos + + + + + entity.edit.alternative_names.help + Los nombres alternativos dados aquí son usados para encontrar este elemento basados en los resultados de los proveedores de información. + + + + + info_providers.form.help_prefix + Proveedor + + + + + update_manager.new_version_available.title + Nueva versión disponible + + + + + update_manager.new_version_available.text + Nueva versión de Part-DB disponible. Más información aquí. + + + + + update_manager.new_version_available.only_administrators_can_see + Solo los administradores pueden ver este mensaje. + + + + + perm.system.show_available_updates + Mostrar actualizaciones de Part-DB disponibles. + + + + + user.settings.api_tokens + Tokens API + + + + + user.settings.api_tokens.description + Utilizando un token API, otras aplicaciones pueden acceder a Part-DB con tus derechos de usuario, para realizar las diversas acciones usando el Part-DB REST API. Si borras un token API aquí, la aplicación que use ese token ya no podrá usar Part-DB por ti. + + + + + api_tokens.name + Nombre + + + + + api_tokens.access_level + Nivel de acceso + + + + + api_tokens.expiration_date + Fecha de caducidad + + + + + api_tokens.added_date + Añadido a + + + + + api_tokens.last_time_used + Última vez usado + + + + + datetime.never + Nunca + + + + + api_token.valid + Válido + + + + + api_token.expired + Caducado + + + + + user.settings.show_api_documentation + Mostrar documentación API + + + + + api_token.create_new + Crear nuevo token API + + + + + api_token.level.read_only + Solo lectura + + + + + api_token.level.edit + Editar + + + + + api_token.level.admin + Admin + + + + + api_token.level.full + Completo + + + + + api_tokens.access_level.help + Puedes restringir a qué accede el token API. El acceso siempre estará limitado por los permisos de tu usuario. + + + + + api_tokens.expiration_date.help + Después de esta fecha, el token ya no será utilizable. Déjalo en blanco si el token no expira nunca. + + + + + api_tokens.your_token_is + Tu token API es + + + + + api_tokens.please_save_it + Por favor guárdalo. ¡No serás capaz de volver a verlo! + + + + + api_tokens.create_new.back_to_user_settings + Volver a ajustes de usuario + + + + + project.build.dont_check_quantity + No comprobar cantidades + + + + + project.build.dont_check_quantity.help + Si se selecciona esta opción, las cantidades retiradas serán usadas tal y como se han dado, sin importar que más o menos componentes realmente sean necesarios para construir este proyecto. + + + + + part_list.action.invert_selection + Invertir selección + + + + + perm.api + API + + + + + perm.api.access_api + Acceder API + + + + + perm.api.manage_tokens + Gestionar tokens API + + + + + user.settings.api_tokens.delete.title + ¿Realmente quieres borrar este token API? + + + + + user.settings.api_tokens.delete + Borrar + + + + + user.settings.api_tokens.delete.message + La aplicación que usa este token API ya no tendrá acceso a Part-DB. ¡Esta acción no puede ser deshecha! + + + + + api_tokens.deleted + ¡Token API borrado con éxito! + + + + + user.settings.api_tokens.no_api_tokens_yet + No se ha configurado ningún token API aún. + + + + + api_token.ends_with + Termina con + + + + + entity.select.creating_new_entities_not_allowed + ¡No tienes permisos para crear nuevas entidades de este tipo! Por favor usa una entidad preexistente. + + + + + scan_dialog.mode + Tipo de código de barras + + + + + scan_dialog.mode.auto + Detectar automáticamente + + + + + scan_dialog.mode.ipn + Código de barras IPN + + + + + scan_dialog.mode.internal + Código de barras Part-DB + + + + + part_association.label + Asociación de componente + + + + + part.edit.tab.associations + Componentes asociados. + + + + + part_association.edit.other_part + Componente asociado + + + + + part_association.edit.type + Tipo de relación + + + + + part_association.edit.comment + Notas + + + + + part_association.edit.type.help + Puedes seleccionar aquí como la parte seleccionada se relaciona con el componente. + + + + + part_association.table.from_this_part + Asociaciones de este componente a otros. + + + + + part_association.table.from + Desde + + + + + part_association.table.type + Relación + + + + + part_association.table.to + A + + + + + part_association.type.compatible + Es compatible con + + + + + part_association.table.to_this_part + Asociaciones de este componente de otros + + + + + part_association.type.other + Otros (valor personalizable) + + + + + part_association.type.supersedes + Reemplaza + + + + + part_association.edit.other_type + Tipo personalizable + + + + + part_association.edit.delete.confirm + ¿De verdad quieres borrar esta asociación? Esto no puede ser deshecho + + + + + part_lot.edit.advanced + Expandir opciones avanzadas + + + + + part_lot.edit.vendor_barcode + Código de barras de vendedor + + + + + part_lot.edit.vendor_barcode.help + Si este lote ya tiene un código de barras (por ejemplo, puesto por el vendedor), puedes escribir aquí sus contenidos, para escanearlos fácilmente. + + + + + scan_dialog.mode.vendor + Código de barras de vendedor (configurados en el lote del componente) + + + + + project.bom.instockAmount + Cantidad en stock + + + + + collection_type.new_element.tooltip + Este elemento fue creado recientemente y no ha sido guardado aún a la base de datos. + + + + + part.merge.title + Combinar componentes + + + + + part.merge.title.into + en + + + + + part.merge.confirm.title + ¿De verdad quieres juntar <b>%other%</b> into <b>%target%</b>? + + + + + part.merge.confirm.message + <b>%other%</b> será borrado, y el componente será guardado con la información mostrada. + + + + + part.info.merge_modal.title + Combinar componentes + + + + + part.info.merge_modal.other_part + Otro componente + + + + + part.info.merge_modal.other_into_this + Combinar otro componente a este (borrar el otro componente, guardar este) + + + + + part.info.merge_modal.this_into_other + Combinar este componente a otro (borrar este componente, guardar el otro) + + + + + part.info.merge_btn + Combinar componentes + + + + + part.update_part_from_info_provider.btn + Actualizar componente con la información de los proveedores + + + + + info_providers.update_part.title + Actualizar componente existente con la información del proveedor + + + + + part.merge.flash.please_review + Datos no guardados aún. Repasa los cambios y haz click en guardar para guardar los datos nuevos. + + + + + user.edit.flash.permissions_fixed + Faltan permisos requeridos por otros permisos. Esto ha sido corregido. Por favor comprueba si los permisos están como quieres. + + + + + permission.legend.dependency_note + Por favor ten en cuenta que algunos de los permisos de operaciones dependen de otros. Si te encuentras con un aviso diciendo que los permisos restantes han sido corregidos y un permiso ha sido configurado a permitir otra vez, tienes que configurar también la operación dependiente a prohibido. Las dependencias se suelen encontrar a la derecha de una operación. + + + + + log.part_stock_changed.timestamp + Versión + + + + + part.info.withdraw_modal.timestamp + Versión de la acción + + + + + part.info.withdraw_modal.timestamp.hint + Este campo te permite especificar la fecha real, cuando la operación de stock fue realizada, no cuando fue registrada. Este valor se guarda en el campo extra del registro de entrada. + + + + + part.info.withdraw_modal.delete_lot_if_empty + Eliminar este lote, si se vacía + + + + + info_providers.search.error.client_exception + Un error ha ocurrido mientras se comunicaba con el proveedor de información. Comprueba la configuración de este proveedor y recarga el token OAuth si es posible. + + + + + eda_info.reference_prefix.placeholder + p. ej. R + + + + + eda_info.reference_prefix + Prefijo de referencia + + + + + eda_info.kicad_section.title + Configuraciones específicas de KiCad + + + + + eda_info.value + Valor + + + + + eda_info.value.placeholder + p. ej. 100n + + + + + eda_info.exclude_from_bom + Excluir componentes de BOM + + + + + eda_info.exclude_from_board + Excluir componente de PCB/Tablero + + + + + eda_info.exclude_from_sim + Excluir componentes de simulación + + + + + eda_info.kicad_symbol + KiCad símbolos esquemáticos + + + + + eda_info.kicad_symbol.placeholder + p. ej. Transistor_BJT:BC547 + + + + + eda_info.kicad_footprint + KiCad footprint + + + + + eda_info.kicad_footprint.placeholder + p. ej. Package_TO_SOT_THT:TO-92 + + + + + part.edit.tab.eda + Información EDA + + + + + api.api_endpoints.title + Punto final API + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + KiCad API root URL + + + + + eda_info.visibility + Forzar visibilidad + + + + + eda_info.visibility.help + Por defecto, la visibilidad al software EDA está automáticamente determinada. Con esta casilla puedes forzar a que sea visible o invisible. + + + + + part.withdraw.zero_amount + ¡Has intentado retirar una cantidad nula! No se ha realizado ninguna acción. + + + + + login.flash.access_denied_please_login + ¡Acceso denegado! Por favor inicia sesión para continuar. + + + + + attachment.upload_multiple_files + Subir archivos + + + + + entity.mass_creation_flash + Creadas %COUNT% entidades con éxito. + + + + + info_providers.search.number_of_results + %number% Resultados + + + + + info_providers.search.no_results + ¡No se encontraron resultados en las fuentes proporcionadas! Verifique su término de búsqueda o intente seleccionar más fuentes de información. + + + + + tfa.check.code.confirmation + Código generado + + + + + info_providers.search.show_existing_part + Mostrar componente existente + + + + + info_providers.search.edit_existing_part + Editar componente existente + + + + + info_providers.search.existing_part_found.short + Componente ya existente + + + + + info_providers.search.existing_part_found + Este componente (o uno muy similar) ya existe en la base de datos. Por favor, comprueba si es el mismo y si quieres volver a crearlo. + + + + + info_providers.search.update_existing_part + Actualizar componente existente con la información del proveedor + + + + + part.create_from_info_provider.no_category_yet + No se ha podido determinar automáticamente la categoría mediante la información del proveedor. Por favor, revisa la información y selecciona la categoría manualmente. + + + + + part_lot.edit.user_barcode + Utilizar código de barras + + + + + scan_dialog.mode.user + Código de barras definido por el usuario (configurado como parte de un lote) + + + + + scan_dialog.mode.eigp + Código de barras EIGP114 (p.e. los códigos de matriz en pedidos de Digikey y Mouser) + + + + + scan_dialog.info_mode + Modo Informativo (Lee el código de barras y muestra su contenido, pero no redirecciona al componente) + + + + + label_scanner.decoded_info.title + Información decodificada + + + + + label_generator.edit_profiles + Modificar perfiles + + + + + label_generator.profile_name_empty + El nombre del perfil no debe estar vacío + + + + + label_generator.save_profile_name + Nombre de perfil + + + + + label_generator.save_profile + Guardar como nuevo perfil + + + + + label_generator.profile_saved + Perfil guardado + + + + + entity.export.flash.error.no_entities + No hay entidades para exportar + + + + + attachment.table.internal_file + Archivo interno + + + + + attachment.table.external_link + Enlace externo + + + + + attachment.view_external.view_at + Ver en %host% + + + + + attachment.view_external + Ver versión externa + + + + diff --git a/translations/messages.fr.xlf b/translations/messages.fr.xlf index 140c7d08..120fee51 100644 --- a/translations/messages.fr.xlf +++ b/translations/messages.fr.xlf @@ -1455,28 +1455,6 @@ Show/Hide sidebar Forum - - - Part-DB1\templates\homepage.html.twig:36 - Part-DB1\templates\homepage.html.twig:36 - templates\homepage.html.twig:33 - - - homepage.basedOn - Basé sur le Part-DB original par - - - - - Part-DB1\templates\homepage.html.twig:39 - Part-DB1\templates\homepage.html.twig:39 - templates\homepage.html.twig:36 - - - homepage.others - et autres - - Part-DB1\templates\homepage.html.twig:45 @@ -6334,52 +6312,6 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia Thème du serveur - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - M - M - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - k - k - - - - - - - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - m - m - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - µ - µ - - Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 @@ -6607,16 +6539,6 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia Le parent ne peut pas être un de ses propres enfants. - - - obsolete - obsolete - - - validator.isSelectable - L'élément doit être sélectionnable ! - - obsolete @@ -7471,17 +7393,6 @@ exemple de ville Rejeter les modifications - - - templates\Parts\show_part_info.html.twig:161 - obsolete - obsolete - - - part.withdraw.caption: - Retrait de composants : - - templates\Parts\show_part_info.html.twig:166 @@ -7592,26 +7503,6 @@ exemple de ville Ex. 10 - - - obsolete - obsolete - - - homepage.basedOn - Basé sur le travail de - - - - - obsolete - obsolete - - - homepage.others - et autres - - obsolete @@ -8708,15 +8599,6 @@ exemple de ville Afficher les anciennes versions des éléments (Time travel) - - - obsolete - - - log.type. - Type. - - obsolete diff --git a/translations/messages.it.xlf b/translations/messages.it.xlf new file mode 100644 index 00000000..7e05a6f2 --- /dev/null +++ b/translations/messages.it.xlf @@ -0,0 +1,12374 @@ + + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + + + attachment_type.caption + Tipi di file per allegati + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 + new + + + attachment_type.edit + Modifica tipo di file + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 + new + + + attachment_type.new + Nuovo tipo di file + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + templates\AdminPages\CategoryAdmin.html.twig:4 + templates\base.html.twig:163 + templates\base.html.twig:170 + templates\base.html.twig:197 + templates\base.html.twig:225 + + + category.labelp + Categorie + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:11 + templates\AdminPages\CategoryAdmin.html.twig:8 + + + admin.options + Opzioni + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + templates\AdminPages\CategoryAdmin.html.twig:9 + + + admin.advanced + Avanzato + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 + new + + + category.edit + Modificare la categoria + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 + new + + + category.new + Nuova categoria + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + + + currency.caption + Valuta + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + + + currency.iso_code.caption + Codice ISO + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + + + currency.symbol.caption + Simbolo della valuta + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 + new + + + currency.edit + Modificare la valuta + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 + new + + + currency.new + Nuova valuta + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + templates\AdminPages\DeviceAdmin.html.twig:4 + + + project.caption + Progetto + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 + new + + + project.edit + Modificare il progetto + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 + new + + + project.new + Nuovo progetto + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:67 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + templates\AdminPages\EntityAdminBase.html.twig:9 + templates\base.html.twig:80 + templates\base.html.twig:179 + templates\base.html.twig:206 + templates\base.html.twig:237 + + + search.placeholder + Ricerca + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + templates\AdminPages\EntityAdminBase.html.twig:13 + templates\base.html.twig:166 + templates\base.html.twig:193 + templates\base.html.twig:221 + + + expandAll + Espande tutto + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + templates\AdminPages\EntityAdminBase.html.twig:17 + templates\base.html.twig:167 + templates\base.html.twig:194 + templates\base.html.twig:222 + + + reduceAll + Ridurre tutto + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + + + part.info.timetravel_hint + Così appare il componente prima di %timestamp%. <i>Si prega di notare che questa funzione è sperimentale, quindi le informazioni potrebbero non essere corrette.</i> + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + templates\AdminPages\EntityAdminBase.html.twig:42 + + + standard.label + Proprietà + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + templates\AdminPages\EntityAdminBase.html.twig:43 + + + infos.label + Informazioni + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + new + + + history.label + Storico + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + templates\AdminPages\EntityAdminBase.html.twig:45 + + + export.label + Esportare + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + templates\AdminPages\EntityAdminBase.html.twig:47 + + + import_export.label + Importare / Esportare + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + + + mass_creation.label + Creazione di massa + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + templates\AdminPages\EntityAdminBase.html.twig:59 + + + admin.common + Comune + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + + + admin.attachments + Allegati + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 + + + admin.parameters + Parametri + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 + templates\AdminPages\EntityAdminBase.html.twig:142 + + + export_all.label + Esportare tutti gli elementi + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 + + + mass_creation.help + Ogni riga sarà interpretata come il nome di un elemento, che verrà creato. Si possono creare strutture nidificate tramite indentazione. + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + templates\AdminPages\EntityAdminBase.html.twig:35 + + + edit.caption + Modificare l'elemento "%name" + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + templates\AdminPages\EntityAdminBase.html.twig:37 + + + new.caption + Nuovo elemento + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + templates\base.html.twig:172 + templates\base.html.twig:199 + templates\base.html.twig:227 + + + footprint.labelp + Footprints + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 + new + + + footprint.edit + Modificare footprint + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 + new + + + footprint.new + Nuovo footprint + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + + + group.edit.caption + Gruppi + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + + + user.edit.permissions + Permessi + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 + new + + + group.edit + Modificare il gruppo + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 + new + + + group.new + Nuovo gruppo + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 + + + label_profile.caption + Profili di etichette + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 + + + label_profile.advanced + Avanzate + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 + + + label_profile.comment + Note + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 + new + + + label_profile.edit + Modificare il profilo di etichetta + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 + new + + + label_profile.new + Nuovo profilo di etichetta + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + templates\AdminPages\ManufacturerAdmin.html.twig:4 + + + manufacturer.caption + Produttori + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 + new + + + manufacturer.edit + Modificare produttore + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 + new + + + manufacturer.new + Nuovo produttore + + + + + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + + + measurement_unit.caption + Unità di misura + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 + Part-DB1\templates\_sidebar.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:8 + templates\base.html.twig:171 + templates\base.html.twig:198 + templates\base.html.twig:226 + + + storelocation.labelp + Ubicazioni + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 + new + + + storelocation.edit + Modificare l'ubicazione + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 + new + + + storelocation.new + Nuova ubicazione + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + templates\AdminPages\SupplierAdmin.html.twig:4 + + + supplier.caption + Fornitori + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 + new + + + supplier.edit + Modificare il fornitore + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 + new + + + supplier.new + Nuovo fornitore + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + + + user.edit.caption + Utenti + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + + + user.edit.configuration + Configurazione + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + + + user.edit.password + Password + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + + + user.edit.tfa.caption + Autenticazione a due fattori + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + + + user.edit.tfa.google_active + App di autenticazione attiva + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + + + tfa_backup.remaining_tokens + Conteggio dei codici di backup restanti + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + + + tfa_backup.generation_date + Data di creazione dei codici di backup + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + + + user.edit.tfa.disabled + Metodo disabilitato + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + + + user.edit.tfa.u2f_keys_count + Chiavi di sicurezza attive + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_title + Si vuole veramente procedere? + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_message + Questo disabiliterà <b>tutti i metodi di autenticazione a due fattori attivi dell'utente</b> ed eliminerà i <b>codici di backup</b>! +<br> +L'utente dovrà configurare nuovamente tutti i metodi di autenticazione a due fattori e stampare nuovi codici di backup! <br><br> +<b> Fare ciò solo se assolutamente sicuri dell'identità dell'utente, altrimenti l'account potrebbe essere compromesso da un utente malintenzionato! </b> + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + + + user.edit.tfa.disable_tfa.btn + Disabilitare tutti i metodi di autenticazione a due fattori + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 + new + + + user.edit + Modificare l'utente + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 + new + + + user.new + Nuovo utente + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:63 + + + attachment.delete + Eliminare + + + + + attachment.external_only + Solo allegato esterno + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:49 + Part-DB1\templates\Parts\edit\_attachments.html.twig:47 + Part-DB1\templates\AdminPages\_attachments.html.twig:47 + Part-DB1\templates\Parts\edit\_attachments.html.twig:45 + + + attachment.preview.alt + Miniatura dell'allegato + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:52 + Part-DB1\templates\Parts\edit\_attachments.html.twig:50 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + Part-DB1\templates\AdminPages\_attachments.html.twig:50 + Part-DB1\templates\Parts\edit\_attachments.html.twig:48 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:45 + + + attachment.view_local + Visualizza la copia locale + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:58 + Part-DB1\templates\Parts\edit\_attachments.html.twig:56 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:43 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + Part-DB1\templates\AdminPages\_attachments.html.twig:56 + Part-DB1\templates\Parts\edit\_attachments.html.twig:54 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + + + attachment.file_not_found + File non trovato + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:66 + Part-DB1\templates\Parts\edit\_attachments.html.twig:64 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:48 + Part-DB1\templates\Parts\edit\_attachments.html.twig:62 + + + attachment.secure + Allegato privato + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:79 + Part-DB1\templates\Parts\edit\_attachments.html.twig:77 + Part-DB1\templates\AdminPages\_attachments.html.twig:77 + Part-DB1\templates\Parts\edit\_attachments.html.twig:75 + + + attachment.create + Aggiungere un allegato + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:84 + Part-DB1\templates\Parts\edit\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + Part-DB1\templates\AdminPages\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_attachments.html.twig:80 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + + + part_lot.edit.delete.confirm + Si vuole davvero cancellare questo stock? Questa azione non potrà essere annullata! + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + templates\AdminPages\_delete_form.html.twig:2 + + + entity.delete.confirm_title + Si vuole veramente eliminare %name%? + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + templates\AdminPages\_delete_form.html.twig:3 + + + entity.delete.message + Questo non può essere annullato! +<br> +I sub elementi saranno spostati verso l'alto. + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + templates\AdminPages\_delete_form.html.twig:9 + + + entity.delete + Cancella elemento + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:45 + Part-DB1\src\Form\Part\PartBaseType.php:286 + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:43 + Part-DB1\src\Form\Part\PartBaseType.php:267 + new + + + edit.log_comment + Cambiare il commento + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + templates\AdminPages\_delete_form.html.twig:12 + + + entity.delete.recursive + Elimina ricorsivo (tutti i sub elementi) + + + + + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 + + + entity.duplicate + Duplicare l'elemento + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + templates\AdminPages\_export_form.html.twig:4 + src\Form\ImportType.php:67 + + + export.format + Formato del file + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + templates\AdminPages\_export_form.html.twig:16 + + + export.level + Livello di verbosità + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + templates\AdminPages\_export_form.html.twig:19 + + + export.level.simple + Semplice + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + templates\AdminPages\_export_form.html.twig:20 + + + export.level.extended + Esteso + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + templates\AdminPages\_export_form.html.twig:21 + + + export.level.full + Completo + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + templates\AdminPages\_export_form.html.twig:31 + + + export.include_children + Includi i sub elementi nell'esportazione + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + templates\AdminPages\_export_form.html.twig:39 + + + export.btn + Esportare + + + + + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + templates\AdminPages\EntityAdminBase.html.twig:94 + templates\Parts\edit_part_info.html.twig:12 + templates\Parts\show_part_info.html.twig:11 + + + id.label + ID + + + + + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:77 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:77 + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:59 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:60 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:53 + templates\AdminPages\EntityAdminBase.html.twig:101 + templates\Parts\show_part_info.html.twig:248 + + + createdAt + Creato il + + + + + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:73 + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:49 + templates\AdminPages\EntityAdminBase.html.twig:114 + templates\Parts\show_part_info.html.twig:263 + + + lastModified + Ultima modifica + + + + + Part-DB1\templates\AdminPages\_info.html.twig:38 + Part-DB1\templates\AdminPages\_info.html.twig:38 + + + entity.info.parts_count + Numero di componenti con questo elemento + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:6 + Part-DB1\templates\helper.twig:125 + Part-DB1\templates\Parts\edit\_specifications.html.twig:6 + + + specifications.property + Parametro + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:7 + Part-DB1\templates\Parts\edit\_specifications.html.twig:7 + + + specifications.symbol + Simbolo + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:8 + Part-DB1\templates\Parts\edit\_specifications.html.twig:8 + + + specifications.value_min + Min. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:9 + Part-DB1\templates\Parts\edit\_specifications.html.twig:9 + + + specifications.value_typ + Typ. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:10 + Part-DB1\templates\Parts\edit\_specifications.html.twig:10 + + + specifications.value_max + Max. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:11 + Part-DB1\templates\Parts\edit\_specifications.html.twig:11 + + + specifications.unit + Unità + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:12 + Part-DB1\templates\Parts\edit\_specifications.html.twig:12 + + + specifications.text + Testo + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:13 + Part-DB1\templates\Parts\edit\_specifications.html.twig:13 + + + specifications.group + Gruppo + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:26 + Part-DB1\templates\Parts\edit\_specifications.html.twig:26 + + + specification.create + Nuovo parametro + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:31 + Part-DB1\templates\Parts\edit\_specifications.html.twig:31 + + + parameter.delete.confirm + Si vuole davvero eliminare questo parametro? + + + + + Part-DB1\templates\attachment_list.html.twig:3 + Part-DB1\templates\attachment_list.html.twig:3 + + + attachment.list.title + Lista allegati + + + + + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + + + part_list.loading.caption + Caricamento + + + + + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + + + part_list.loading.message + Questo può richiedere un attimo. Se questo messaggio non scompare, provare a ricaricare la pagina. + + + + + Part-DB1\templates\base.html.twig:68 + Part-DB1\templates\base.html.twig:68 + templates\base.html.twig:246 + + + vendor.base.javascript_hint + Attivare Javascript per utilizzare tutte le funzionalità! + + + + + Part-DB1\templates\base.html.twig:73 + Part-DB1\templates\base.html.twig:73 + + + sidebar.big.toggle + Mostra/Nascondi la barra laterale + + + + + Part-DB1\templates\base.html.twig:95 + Part-DB1\templates\base.html.twig:95 + templates\base.html.twig:271 + + + loading.caption + Caricamento: + + + + + Part-DB1\templates\base.html.twig:96 + Part-DB1\templates\base.html.twig:96 + templates\base.html.twig:272 + + + loading.message + Questo può richiedere un attimo. Se questo messaggio non scompare, provare a ricaricare la pagina. + + + + + Part-DB1\templates\base.html.twig:101 + Part-DB1\templates\base.html.twig:101 + templates\base.html.twig:277 + + + loading.bar + Caricamento... + + + + + Part-DB1\templates\base.html.twig:112 + Part-DB1\templates\base.html.twig:112 + templates\base.html.twig:288 + + + back_to_top + Torna all'inizio della pagina + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:35 + Part-DB1\templates\Form\permissionLayout.html.twig:35 + + + permission.edit.permission + Autorizzazioni + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:36 + Part-DB1\templates\Form\permissionLayout.html.twig:36 + + + permission.edit.value + Valore + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:53 + Part-DB1\templates\Form\permissionLayout.html.twig:53 + + + permission.legend.title + Spiegazione degli stati: + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:57 + Part-DB1\templates\Form\permissionLayout.html.twig:57 + + + permission.legend.disallow + Vietato + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:61 + Part-DB1\templates\Form\permissionLayout.html.twig:61 + + + permission.legend.allow + Consentito + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:65 + Part-DB1\templates\Form\permissionLayout.html.twig:65 + + + permission.legend.inherit + Ereditare dal gruppo (genitore) + + + + + Part-DB1\templates\helper.twig:3 + Part-DB1\templates\helper.twig:3 + + + bool.true + Vero + + + + + Part-DB1\templates\helper.twig:5 + Part-DB1\templates\helper.twig:5 + + + bool.false + Falso + + + + + Part-DB1\templates\helper.twig:92 + Part-DB1\templates\helper.twig:87 + + + Yes + Si + + + + + Part-DB1\templates\helper.twig:94 + Part-DB1\templates\helper.twig:89 + + + No + No + + + + + Part-DB1\templates\helper.twig:126 + + + specifications.value + Valore + + + + + Part-DB1\templates\homepage.html.twig:7 + Part-DB1\templates\homepage.html.twig:7 + templates\homepage.html.twig:7 + + + version.caption + Versione + + + + + Part-DB1\templates\homepage.html.twig:22 + Part-DB1\templates\homepage.html.twig:22 + templates\homepage.html.twig:19 + + + homepage.license + Informazioni di licenza + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.caption + Pagina del progetto + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.text + Sorgenti, download, segnalazioni di bug, to-do-list ecc. possono essere trovati su <a href="%href%" class="link-external" target="_blank">GitHub project page</a> + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.caption + Aiuto + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.text + Aiuto e suggerimenti possono essere trovati in Wiki di <a href="%href%" class="link-external" target="_blank">GitHub page</a> + + + + + Part-DB1\templates\homepage.html.twig:33 + Part-DB1\templates\homepage.html.twig:33 + templates\homepage.html.twig:30 + + + homepage.forum.caption + Forum + + + + + Part-DB1\templates\homepage.html.twig:45 + Part-DB1\templates\homepage.html.twig:45 + new + + + homepage.last_activity + Ultime attività + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:3 + Part-DB1\templates\LabelSystem\dialog.html.twig:6 + + + label_generator.title + Generatore di etichette + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:16 + + + label_generator.common + Comune + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:20 + + + label_generator.advanced + Avanzate + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:24 + + + label_generator.profiles + Profili + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:58 + + + label_generator.selected_profile + Profilo attualmente selezionato + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:62 + + + label_generator.edit_profile + Modificare il profilo + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:75 + + + label_generator.load_profile + Caricare il profilo + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:102 + + + label_generator.download + Download + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 + + + label_generator.label_btn + Generare una etichetta + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 + + + label_generator.label_empty + Nuova etichetta vuota + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 + + + label_scanner.title + Lettore di etichette + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.title + Webcam non trovata + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.text + E' necessaria una webcam e bisogna dare il permesso di usare la funzione scanner. Si può inserire manualmente il codice a barre qui sotto. + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 + + + label_scanner.source_select + Selezionare una sorgente + + + + + Part-DB1\templates\LogSystem\log_list.html.twig:3 + Part-DB1\templates\LogSystem\log_list.html.twig:3 + + + log.list.title + Registro di sistema + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + new + + + log.undo.confirm_title + Annullare davvero la modifica/ripristinare il timestamp? + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + new + + + log.undo.confirm_message + Vuoi annullare la modifica / reimpostare l'elemento a una determinata data? + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.email_sent_by + Questa e-mail è stata inviata automaticamente da + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.dont_reply + Non rispondere a questa e-mail. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:6 + Part-DB1\templates\mail\pw_reset.html.twig:6 + + + email.hi %name% + Buongiorno %name% + + + + + Part-DB1\templates\mail\pw_reset.html.twig:7 + Part-DB1\templates\mail\pw_reset.html.twig:7 + + + email.pw_reset.message + Qualcuno (si spera tu) ha richiesto una reimpostazione della tua password. Se questa richiesta non è stata fatta da te, ignora questa mail. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:9 + Part-DB1\templates\mail\pw_reset.html.twig:9 + + + email.pw_reset.button + Clicca qui per reimpostare la password + + + + + Part-DB1\templates\mail\pw_reset.html.twig:11 + Part-DB1\templates\mail\pw_reset.html.twig:11 + + + email.pw_reset.fallback + Se questo non funziona, vai su <a href="%url%">%url%</a> e inserisci le seguenti informazioni + + + + + Part-DB1\templates\mail\pw_reset.html.twig:16 + Part-DB1\templates\mail\pw_reset.html.twig:16 + + + email.pw_reset.username + Nome utente + + + + + Part-DB1\templates\mail\pw_reset.html.twig:19 + Part-DB1\templates\mail\pw_reset.html.twig:19 + + + email.pw_reset.token + Token + + + + + Part-DB1\templates\mail\pw_reset.html.twig:24 + Part-DB1\templates\mail\pw_reset.html.twig:24 + + + email.pw_reset.valid_unit %date% + Il token di reset sarà valido fino al <i>%date%</i>. + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:78 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + + + orderdetail.delete + Cancellare + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + + + pricedetails.edit.min_qty + Quantità minima d'ordine + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + + + pricedetails.edit.price + Prezzo + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + + + pricedetails.edit.price_qty + per la quantità + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + + + pricedetail.create + Aggiungi prezzo + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + templates\Parts\edit_part_info.html.twig:4 + + + part.edit.title + Modifica il componente %name% + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + templates\Parts\edit_part_info.html.twig:9 + + + part.edit.card_title + Modificare le informazioni sui componenti di + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + + + part.edit.tab.common + In generale + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + + + part.edit.tab.manufacturer + Produttore + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + + + part.edit.tab.advanced + Avanzate + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + + + part.edit.tab.part_lots + Stock + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + + + part.edit.tab.attachments + Allegati + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + + + part.edit.tab.orderdetails + Informazioni di acquisto + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.specifications + Parametri + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.comment + Note + + + + + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + templates\Parts\new_part.html.twig:8 + + + part.new.card_title + Creare nuovo componente + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + + + part_lot.delete + Cancellare + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + + + part_lot.create + Aggiungere stock + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + + + orderdetail.create + Aggiungere un distributore + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + + + pricedetails.edit.delete.confirm + Vuoi davvero cancellare questo prezzo? Questo non può essere annullato! + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 + + + orderdetails.edit.delete.confirm + Vuoi davvero cancellare questo fornitore? Questo non può essere annullato! + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + templates\Parts\show_part_info.html.twig:4 + templates\Parts\show_part_info.html.twig:9 + + + part.info.title + Informazioni dettagliate per + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + + + part.part_lots.label + Stock + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 + Part-DB1\templates\Parts\lists\_info_card.html.twig:43 + Part-DB1\templates\_navbar_search.html.twig:31 + Part-DB1\templates\_navbar_search.html.twig:26 + templates\base.html.twig:62 + templates\Parts\show_part_info.html.twig:74 + src\Form\PartType.php:86 + + + comment.label + Note + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + + + part.info.specifications + Parametri + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + templates\Parts\show_part_info.html.twig:82 + + + attachment.labelp + Allegati + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 + Part-DB1\templates\Parts\info\show_part_info.html.twig:71 + templates\Parts\show_part_info.html.twig:88 + + + vendor.partinfo.shopping_infos + Informazioni di acquisto + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 + Part-DB1\templates\Parts\info\show_part_info.html.twig:78 + templates\Parts\show_part_info.html.twig:94 + + + vendor.partinfo.history + Storico + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + Part-DB1\templates\Parts\info\show_part_info.html.twig:84 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + templates\base.html.twig:176 + templates\base.html.twig:203 + templates\base.html.twig:217 + templates\base.html.twig:231 + templates\Parts\show_part_info.html.twig:100 + + + tools.label + Utilità + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 + Part-DB1\templates\Parts\info\show_part_info.html.twig:90 + + + extended_info.label + Informazioni estese + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + + + attachment.name + Nome + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + + + attachment.attachment_type + Tipo di allegato + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + + + attachment.file_name + Nome del file + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + + + attachment.file_size + Dimensioni del file + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 + + + attachment.preview + Immagine di anteprima + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 + + + attachment.download_local + Scarica la copia in locale + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + new + + + user.creating_user + Utente che ha creato questo componente + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + + + Unknown + Sconosciuto + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + new + + + accessDenied + Accesso negato + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + new + + + user.last_editing_user + Utente che ha modificato questo componente + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + + + part.isFavorite + Preferito + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + + + part.minOrderAmount + Importo minimo dell'ordine + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:46 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:41 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + templates\base.html.twig:70 + templates\Parts\show_part_info.html.twig:24 + src\Form\PartType.php:80 + + + manufacturer.label + Produttore + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 + Part-DB1\templates\_navbar_search.html.twig:11 + templates\base.html.twig:54 + src\Form\PartType.php:62 + + + name.label + Nome + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + new + + + part.back_to_info + Ritorno alla versione attuale + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:19 + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:18 + templates\base.html.twig:58 + templates\Parts\show_part_info.html.twig:31 + src\Form\PartType.php:65 + + + description.label + Descrizione + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:15 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:14 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + templates\base.html.twig:56 + templates\Parts\show_part_info.html.twig:32 + src\Form\PartType.php:74 + + + category.label + Categoria + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + templates\Parts\show_part_info.html.twig:42 + src\Form\PartType.php:69 + + + instock.label + A stock + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + templates\Parts\show_part_info.html.twig:44 + src\Form\PartType.php:72 + + + mininstock.label + Scorta minima + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:52 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:47 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + templates\base.html.twig:73 + templates\Parts\show_part_info.html.twig:47 + + + footprint.label + Footprint + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 + Part-DB1\templates\Parts\info\_main_infos.html.twig:59 + Part-DB1\templates\Parts\info\_main_infos.html.twig:57 + Part-DB1\templates\Parts\info\_main_infos.html.twig:60 + templates\Parts\show_part_info.html.twig:51 + + + part.avg_price.label + Prezzo medio + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + + + part.supplier.name + Nome + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + + + part.supplier.partnr + Codice + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + + + part.order.minamount + Importo minimo + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + + + part.order.price + Prezzo + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + + + part.order.single_price + Prezzo unitario + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + + + edit.caption_short + Modificare + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + + + delete.caption + Eliminare + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + Part-DB1\templates\Parts\info\_part_lots.html.twig:6 + + + part_lots.description + Descrizione + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + + + part_lots.storage_location + Ubicazione + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + + + part_lots.amount + Quantità + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 + Part-DB1\templates\Parts\info\_part_lots.html.twig:22 + + + part_lots.location_unknown + Ubicazione sconosciuta + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 + Part-DB1\templates\Parts\info\_part_lots.html.twig:29 + + + part_lots.instock_unknown + Quantità sconosciuta + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 + Part-DB1\templates\Parts\info\_part_lots.html.twig:38 + + + part_lots.expiration_date + Data di scadenza + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 + Part-DB1\templates\Parts\info\_part_lots.html.twig:46 + + + part_lots.is_expired + Scaduto + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 + Part-DB1\templates\Parts\info\_part_lots.html.twig:53 + + + part_lots.need_refill + Necessita di rifornimento + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:15 + Part-DB1\templates\Parts\info\_picture.html.twig:15 + + + part.info.prev_picture + Immagine precedente + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:19 + Part-DB1\templates\Parts\info\_picture.html.twig:19 + + + part.info.next_picture + Immagine successiva + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + + + part.mass.tooltip + Peso + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + + + part.needs_review.badge + Necessita revisione + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + + + part.favorite.badge + Preferito + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + + + part.obsolete.badge + Non più disponibile + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:10 + + + parameters.extracted_from_description + Estratto automaticamente dalla descrizione + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:15 + + + parameters.auto_extracted_from_comment + Estratto automaticamente dalle note + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:6 + Part-DB1\templates\Parts\info\_tools.html.twig:4 + templates\Parts\show_part_info.html.twig:125 + + + part.edit.btn + Modifica componente + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:14 + templates\Parts\show_part_info.html.twig:135 + + + part.clone.btn + Clona il componente + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:24 + Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 + templates\Parts\show_part_info.html.twig:143 + + + part.create.btn + Creare un nuovo componente + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:31 + Part-DB1\templates\Parts\info\_tools.html.twig:29 + + + part.delete.confirm_title + Si vuole cancellare questo componente? + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:32 + Part-DB1\templates\Parts\info\_tools.html.twig:30 + + + part.delete.message + Il componente e tutte le informazioni associate (stock, file allegati, ecc.) verranno cancellati. Questa azione non può essere annullata. + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:39 + Part-DB1\templates\Parts\info\_tools.html.twig:37 + + + part.delete + Cancellare il componente + + + + + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + + + parts_list.all.title + Tutti i componenti + + + + + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + + + parts_list.category.title + Componenti con categoria + + + + + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + + + parts_list.footprint.title + Componenti con footprint + + + + + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + + + parts_list.manufacturer.title + Componenti con produttore + + + + + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + + + parts_list.search.title + Ricerca componenti + + + + + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + + + parts_list.storelocation.title + Componenti con ubicazione + + + + + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + + + parts_list.supplier.title + Componenti con fornitore + + + + + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + + + parts_list.tags.title + Componenti con tag + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 + Part-DB1\templates\Parts\lists\_info_card.html.twig:17 + + + entity.info.common.tab + Di base + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 + Part-DB1\templates\Parts\lists\_info_card.html.twig:20 + + + entity.info.statistics.tab + Statistiche + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 + + + entity.info.attachments.tab + Allegati + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 + + + entity.info.parameters.tab + Parametri + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 + Part-DB1\templates\Parts\lists\_info_card.html.twig:30 + + + entity.info.name + Nome + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 + Part-DB1\templates\Parts\lists\_info_card.html.twig:96 + Part-DB1\templates\Parts\lists\_info_card.html.twig:34 + Part-DB1\templates\Parts\lists\_info_card.html.twig:67 + + + entity.info.parent + Elemento padre + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 + Part-DB1\templates\Parts\lists\_info_card.html.twig:46 + + + entity.edit.btn + Modificare + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 + Part-DB1\templates\Parts\lists\_info_card.html.twig:63 + + + entity.info.children_count + Conteggio degli elementi figli + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + + + tfa.check.title + E' richiesta l'autenticazione a due fattori + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:39 + Part-DB1\templates\security\2fa_base_form.html.twig:39 + + + tfa.code.trusted_pc + Questo è un computer attendibile (se questo è abilitato, non vengono eseguite altre query a due fattori su questo computer) + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + + + login.btn + Login + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:42 + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:40 + + + user.logout + Logout + + + + + Part-DB1\templates\security\2fa_form.html.twig:6 + Part-DB1\templates\security\2fa_form.html.twig:6 + + + tfa.check.code.label + Codice Authenticator app + + + + + Part-DB1\templates\security\2fa_form.html.twig:10 + Part-DB1\templates\security\2fa_form.html.twig:10 + + + tfa.check.code.help + Inserire il codice a 6 cifre dall'app Authenticator o uno dei codici di backup se l'Authenticator app non è disponibile. + + + + + Part-DB1\templates\security\login.html.twig:3 + Part-DB1\templates\security\login.html.twig:3 + templates\security\login.html.twig:3 + + + login.title + Login + + + + + Part-DB1\templates\security\login.html.twig:7 + Part-DB1\templates\security\login.html.twig:7 + templates\security\login.html.twig:7 + + + login.card_title + Login + + + + + Part-DB1\templates\security\login.html.twig:31 + Part-DB1\templates\security\login.html.twig:31 + templates\security\login.html.twig:31 + + + login.username.label + Nome utente + + + + + Part-DB1\templates\security\login.html.twig:34 + Part-DB1\templates\security\login.html.twig:34 + templates\security\login.html.twig:34 + + + login.username.placeholder + Nome utente + + + + + Part-DB1\templates\security\login.html.twig:38 + Part-DB1\templates\security\login.html.twig:38 + templates\security\login.html.twig:38 + + + login.password.label + Password + + + + + Part-DB1\templates\security\login.html.twig:40 + Part-DB1\templates\security\login.html.twig:40 + templates\security\login.html.twig:40 + + + login.password.placeholder + Password + + + + + Part-DB1\templates\security\login.html.twig:50 + Part-DB1\templates\security\login.html.twig:50 + templates\security\login.html.twig:50 + + + login.rememberme + Ricordami (non deve essere utilizzato su computer pubblici) + + + + + Part-DB1\templates\security\login.html.twig:64 + Part-DB1\templates\security\login.html.twig:64 + + + pw_reset.password_forget + Hai dimenticato il nome utente o la password? + + + + + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + + + pw_reset.new_pw.header.title + Digitare una nuova password + + + + + Part-DB1\templates\security\pw_reset_request.html.twig:5 + Part-DB1\templates\security\pw_reset_request.html.twig:5 + + + pw_reset.request.header.title + Richiedere una nuova password + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + + + tfa_u2f.http_warning + Si sta accedendo a questa pagina utilizzando il metodo HTTP non sicuro, quindi molto probabilmente U2F non funzionerà (messaggio di errore "Bad Request"). Chiedi a un amministratore di impostare il metodo HTTPS sicuro se si vogliono usare le chiavi di sicurezza. + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + + + r_u2f_two_factor.pressbutton + Si prega di inserire la chiave di sicurezza e premere il bottone! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + + + tfa_u2f.add_key.title + Aggiungere una chiave di sicurezza + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + + + tfa_u2f.explanation + Con l'aiuto di una chiave di sicurezza compatibile con U2F/FIDO (ad es. YubiKey o NitroKey), è possibile ottenere un'autenticazione a due fattori user-friendly e sicura. Le chiavi di sicurezza possono essere registrate qui e, se è richiesta la verifica a due fattori, la chiave deve essere inserita solo tramite USB o digitata sul dispositivo tramite NFC. + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + + + tfa_u2f.add_key.backup_hint + Per garantire l'accesso anche se la chiave viene persa, si consiglia di registrare una seconda chiave come backup e conservarla in un luogo sicuro! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + + + r_u2f_two_factor.name + Nome della chiave visualizzato (ad es. Backup) + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + + + tfa_u2f.add_key.add_button + Aggiungere chiave di sicurezza + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + + + tfa_u2f.add_key.back_to_settings + Ritornare ai parametri + + + + + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + new + + + statistics.title + Statistiche + + + + + Part-DB1\templates\Statistics\statistics.html.twig:14 + Part-DB1\templates\Statistics\statistics.html.twig:14 + new + + + statistics.parts + Componenti + + + + + Part-DB1\templates\Statistics\statistics.html.twig:19 + Part-DB1\templates\Statistics\statistics.html.twig:19 + new + + + statistics.data_structures + Struttura dei dati + + + + + Part-DB1\templates\Statistics\statistics.html.twig:24 + Part-DB1\templates\Statistics\statistics.html.twig:24 + new + + + statistics.attachments + Allegati + + + + + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + new + + + statistics.property + Proprietà + + + + + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + new + + + statistics.value + Valore + + + + + Part-DB1\templates\Statistics\statistics.html.twig:40 + Part-DB1\templates\Statistics\statistics.html.twig:40 + new + + + statistics.distinct_parts_count + Numero di componenti distinti + + + + + Part-DB1\templates\Statistics\statistics.html.twig:44 + Part-DB1\templates\Statistics\statistics.html.twig:44 + new + + + statistics.parts_instock_sum + Somma di tutti i componenti a stock + + + + + Part-DB1\templates\Statistics\statistics.html.twig:48 + Part-DB1\templates\Statistics\statistics.html.twig:48 + new + + + statistics.parts_with_price + Numero di componenti con informazioni di prezzo + + + + + Part-DB1\templates\Statistics\statistics.html.twig:65 + Part-DB1\templates\Statistics\statistics.html.twig:65 + new + + + statistics.categories_count + Numero di categorie + + + + + Part-DB1\templates\Statistics\statistics.html.twig:69 + Part-DB1\templates\Statistics\statistics.html.twig:69 + new + + + statistics.footprints_count + Numero di footprint + + + + + Part-DB1\templates\Statistics\statistics.html.twig:73 + Part-DB1\templates\Statistics\statistics.html.twig:73 + new + + + statistics.manufacturers_count + Numero di fabbricanti + + + + + Part-DB1\templates\Statistics\statistics.html.twig:77 + Part-DB1\templates\Statistics\statistics.html.twig:77 + new + + + statistics.storelocations_count + Numero di ubicazioni + + + + + Part-DB1\templates\Statistics\statistics.html.twig:81 + Part-DB1\templates\Statistics\statistics.html.twig:81 + new + + + statistics.suppliers_count + Numero di fornitori + + + + + Part-DB1\templates\Statistics\statistics.html.twig:85 + Part-DB1\templates\Statistics\statistics.html.twig:85 + new + + + statistics.currencies_count + Numero di valute + + + + + Part-DB1\templates\Statistics\statistics.html.twig:89 + Part-DB1\templates\Statistics\statistics.html.twig:89 + new + + + statistics.measurement_units_count + Numero di unità di misura + + + + + Part-DB1\templates\Statistics\statistics.html.twig:93 + Part-DB1\templates\Statistics\statistics.html.twig:93 + new + + + statistics.devices_count + Numero di progetti + + + + + Part-DB1\templates\Statistics\statistics.html.twig:110 + Part-DB1\templates\Statistics\statistics.html.twig:110 + new + + + statistics.attachment_types_count + Numero di tipi di allegati + + + + + Part-DB1\templates\Statistics\statistics.html.twig:114 + Part-DB1\templates\Statistics\statistics.html.twig:114 + new + + + statistics.all_attachments_count + Numero di allegati + + + + + Part-DB1\templates\Statistics\statistics.html.twig:118 + Part-DB1\templates\Statistics\statistics.html.twig:118 + new + + + statistics.user_uploaded_attachments_count + Numero di allegati caricati dall'utente + + + + + Part-DB1\templates\Statistics\statistics.html.twig:122 + Part-DB1\templates\Statistics\statistics.html.twig:122 + new + + + statistics.private_attachments_count + Numero di allegati privati + + + + + Part-DB1\templates\Statistics\statistics.html.twig:126 + Part-DB1\templates\Statistics\statistics.html.twig:126 + new + + + statistics.external_attachments_count + Numero di allegati esterni (URL) + + + + + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + + + tfa_backup.codes.title + Codici di backup + + + + + Part-DB1\templates\Users\backup_codes.html.twig:12 + Part-DB1\templates\Users\backup_codes.html.twig:12 + + + tfa_backup.codes.explanation + Stampare questi codici e conservare in luogo sicuro! + + + + + Part-DB1\templates\Users\backup_codes.html.twig:13 + Part-DB1\templates\Users\backup_codes.html.twig:13 + + + tfa_backup.codes.help + Se non si ha più accesso al proprio dispositivo con l'app Authenticator (smartphone perso, perdita di dati, ecc.) si può utilizzare uno di questi codici per accedere all'account ed eventualmente impostare una nuova app Authenticator. Ciascuno di questi codici può essere utilizzato una volta, si consiglia di eliminare i codici già utilizzati. Chiunque abbia accesso a questi codici può potenzialmente accedere al suo account, quindi sono da tenere in un luogo sicuro. + + + + + Part-DB1\templates\Users\backup_codes.html.twig:16 + Part-DB1\templates\Users\backup_codes.html.twig:16 + + + tfa_backup.username + Nome utente + + + + + Part-DB1\templates\Users\backup_codes.html.twig:29 + Part-DB1\templates\Users\backup_codes.html.twig:29 + + + tfa_backup.codes.page_generated_on + Pagina generata il %date% + + + + + Part-DB1\templates\Users\backup_codes.html.twig:32 + Part-DB1\templates\Users\backup_codes.html.twig:32 + + + tfa_backup.codes.print + Stampa + + + + + Part-DB1\templates\Users\backup_codes.html.twig:35 + Part-DB1\templates\Users\backup_codes.html.twig:35 + + + tfa_backup.codes.copy_clipboard + Copiare negli appunti + + + + + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:40 + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:38 + templates\base.html.twig:99 + templates\Users\user_info.html.twig:3 + templates\Users\user_info.html.twig:6 + + + user.info.label + Informazioni utente + + + + + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + templates\Users\user_info.html.twig:18 + src\Form\UserSettingsType.php:32 + + + user.firstName.label + Nome + + + + + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + templates\Users\user_info.html.twig:24 + src\Form\UserSettingsType.php:35 + + + user.lastName.label + Cognome + + + + + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + templates\Users\user_info.html.twig:30 + src\Form\UserSettingsType.php:41 + + + user.email.label + Indirizzo email + + + + + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + templates\Users\user_info.html.twig:37 + src\Form\UserSettingsType.php:38 + + + user.department.label + Dipartimento + + + + + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + templates\Users\user_info.html.twig:47 + src\Form\UserSettingsType.php:30 + + + user.username.label + Nome utente + + + + + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + templates\Users\user_info.html.twig:53 + + + group.label + Gruppo: + + + + + Part-DB1\templates\Users\user_info.html.twig:67 + Part-DB1\templates\Users\user_info.html.twig:67 + + + user.permissions + Permessi + + + + + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:39 + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:37 + templates\base.html.twig:98 + templates\Users\user_settings.html.twig:3 + templates\Users\user_settings.html.twig:6 + + + user.settings.label + Impostazioni utente + + + + + Part-DB1\templates\Users\user_settings.html.twig:18 + Part-DB1\templates\Users\user_settings.html.twig:18 + templates\Users\user_settings.html.twig:14 + + + user_settings.data.label + Dati personali + + + + + Part-DB1\templates\Users\user_settings.html.twig:22 + Part-DB1\templates\Users\user_settings.html.twig:22 + templates\Users\user_settings.html.twig:18 + + + user_settings.configuration.label + Configurazione + + + + + Part-DB1\templates\Users\user_settings.html.twig:55 + Part-DB1\templates\Users\user_settings.html.twig:55 + templates\Users\user_settings.html.twig:48 + + + user.settings.change_pw + Cambio password + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + + + user.settings.2fa_settings + Autenticazione a due fattori + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + + + tfa.settings.google.tab + App di autenticazione + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + + + tfa.settings.bakup.tab + Codici di backup + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + + + tfa.settings.u2f.tab + Chiavi di sicurezza (U2F) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + + + tfa.settings.trustedDevices.tab + Dispositivi attendibili + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_title + Si vuole veramente disabilitare l'App di autenticazione? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_message + Se si disabilita l'app di autenticazione, tutti i codici di backup verranno eliminati, quindi potrebbe essere necessario ristamparli.<br> +Attenzione: senza l'autenticazione a due fattori il suo account non è ben protetto da eventuali aggressori! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + + + tfa_google.disabled_message + App di autenticazione disattivata! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + + + tfa_google.step.download + Scaricare un'App di autenticazione (ad esempio <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> oder <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details? id=org.fedorahosted.freeotp">FreeOTP Authenticator</a>) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + + + tfa_google.step.scan + Scansionare con l'app il codice QR adiacente o inserire i dati manualmente + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + + + tfa_google.step.input_code + Digitare il codice generato nel campo sotto e confermare + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + + + tfa_google.step.download_backup + Stampare i propri codici di backup e conservarli in un luogo sicuro + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + + + tfa_google.manual_setup + Configurazione manuale + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + + + tfa_google.manual_setup.type + Tipo + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + + + tfa_google.manual_setup.username + Nome utente + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + + + tfa_google.manual_setup.secret + Secret + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + + + tfa_google.manual_setup.digit_count + Numero di caratteri + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + + + tfa_google.enabled_message + App Authenticator abilitata + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + + + tfa_backup.disabled + Codici di backup disattivati. Impostare l'app Authenticator per attivare i codici di backup. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + + + tfa_backup.explanation + Questi codici di backup permettono di accedere al tuo account anche se si perde il dispositivo con l'app Authenticator. Stampare i codici e conservarli in un luogo sicuro. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_title + Reimpostare davvero i codici? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_message + Questo cancellerà tutti i codici precedenti e genererà un set di nuovi codici. Questa azione non può essere annullata. Stampare i nuovi codici e depositarli in un luogo sicuro! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + + + tfa_backup.enabled + Codici di backup attivati + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + + + tfa_backup.show_codes + Visualizzare i codici di backup + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + + + tfa_u2f.table_caption + Chiavi di sicurezza registrate + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + + + tfa_u2f.delete_u2f.confirm_title + Rimuovere questa chiave di sicurezza? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + + + tfa_u2f.delete_u2f.confirm_message + Se si rimuove questa chiave, non sarà più possibile effettuare il login con essa. Se non ci sono chiavi di sicurezza, l'autenticazione a due fattori è disabilitata. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + + + tfa_u2f.keys.name + Nome della chiave + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + + + tfa_u2f.keys.added_date + Data di registrazione + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + + + tfa_u2f.key_delete + Cancellare la chiave + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + + + tfa_u2f.no_keys_registered + Nessuna chiave di sicurezza registrata + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + + + tfa_u2f.add_new_key + Registrare una nuova chiave di sicurezza + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + + + tfa_trustedDevices.explanation + Quando si controlla il secondo fattore, il computer corrente può essere contrassegnato come affidabile, quindi non sono più necessari controlli a due fattori su questo computer. + +Se è stato fatto in modo errato o se un computer non è più attendibile, puoi reimpostare lo stato di <i>tutti i </i>computer qui. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + + + tfa_trustedDevices.invalidate.confirm_title + Rimuovere tutti i computer attendibili? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + + + tfa_trustedDevices.invalidate.confirm_message + Bisognerà eseguire di nuovo l'autenticazione a due fattori su tutti i computer. Assicurati di avere il tuo dispositivo a due fattori a portata di mano. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + + + tfa_trustedDevices.invalidate.btn + Rimuovere tutti i dispositivi attendibili + + + + + Part-DB1\templates\_navbar.html.twig:4 + Part-DB1\templates\_navbar.html.twig:4 + templates\base.html.twig:29 + + + sidebar.toggle + Commuta la barra laterale + + + + + Part-DB1\templates\_navbar.html.twig:22 + + + navbar.scanner.link + Scanner + + + + + Part-DB1\templates\_navbar.html.twig:38 + Part-DB1\templates\_navbar.html.twig:36 + templates\base.html.twig:97 + + + user.loggedin.label + Loggato come + + + + + Part-DB1\templates\_navbar.html.twig:44 + Part-DB1\templates\_navbar.html.twig:42 + templates\base.html.twig:103 + + + user.login + Login + + + + + Part-DB1\templates\_navbar.html.twig:50 + Part-DB1\templates\_navbar.html.twig:48 + + + ui.toggle_darkmode + Modalità scura + + + + + Part-DB1\templates\_navbar.html.twig:54 + Part-DB1\src\Form\UserSettingsType.php:97 + Part-DB1\templates\_navbar.html.twig:52 + Part-DB1\src\Form\UserSettingsType.php:97 + templates\base.html.twig:106 + src\Form\UserSettingsType.php:44 + + + user.language_select + Cambia lingua + + + + + Part-DB1\templates\_navbar_search.html.twig:4 + Part-DB1\templates\_navbar_search.html.twig:4 + templates\base.html.twig:49 + + + search.options.label + Opzioni di ricerca + + + + + Part-DB1\templates\_navbar_search.html.twig:23 + + + tags.label + Tags + + + + + Part-DB1\templates\_navbar_search.html.twig:27 + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + templates\base.html.twig:60 + templates\Parts\show_part_info.html.twig:36 + src\Form\PartType.php:77 + + + storelocation.label + Ubicazione + + + + + Part-DB1\templates\_navbar_search.html.twig:36 + Part-DB1\templates\_navbar_search.html.twig:31 + templates\base.html.twig:65 + + + ordernumber.label.short + Codice del fornitore + + + + + Part-DB1\templates\_navbar_search.html.twig:40 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + Part-DB1\templates\_navbar_search.html.twig:35 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + templates\base.html.twig:67 + + + supplier.label + Fornitore + + + + + Part-DB1\templates\_navbar_search.html.twig:57 + Part-DB1\templates\_navbar_search.html.twig:52 + templates\base.html.twig:75 + + + search.deactivateBarcode + Disatt. Barcode + + + + + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_navbar_search.html.twig:56 + templates\base.html.twig:77 + + + search.regexmatching + Corrispondenza Reg.Ex. + + + + + Part-DB1\templates\_navbar_search.html.twig:68 + Part-DB1\templates\_navbar_search.html.twig:62 + + + search.submit + Cerca! + + + + + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + templates\base.html.twig:175 + templates\base.html.twig:189 + templates\base.html.twig:202 + templates\base.html.twig:230 + + + project.labelp + Progetti + + + + + Part-DB1\templates\_sidebar.html.twig:2 + Part-DB1\templates\_sidebar.html.twig:2 + templates\base.html.twig:165 + templates\base.html.twig:192 + templates\base.html.twig:220 + + + actions + Azioni + + + + + Part-DB1\templates\_sidebar.html.twig:6 + Part-DB1\templates\_sidebar.html.twig:6 + templates\base.html.twig:169 + templates\base.html.twig:196 + templates\base.html.twig:224 + + + datasource + Fonte dati + + + + + Part-DB1\templates\_sidebar.html.twig:10 + Part-DB1\templates\_sidebar.html.twig:10 + templates\base.html.twig:173 + templates\base.html.twig:200 + templates\base.html.twig:228 + + + manufacturer.labelp + Produttori + + + + + Part-DB1\templates\_sidebar.html.twig:11 + Part-DB1\templates\_sidebar.html.twig:11 + templates\base.html.twig:174 + templates\base.html.twig:201 + templates\base.html.twig:229 + + + supplier.labelp + Fornitori + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:293 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:181 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:243 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:268 + + + attachment.download_failed + Il download dell'allegato è fallito. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 + + + entity.edit_flash + Modifiche salvate con successo. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 + + + entity.edit_flash.invalid + Non è stato possibile salvare le modifiche! Controllare i dati! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 + + + entity.created_flash + Elemento creato. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 + + + entity.created_flash.invalid + Non è stato possibile creare l'elemento. Controllare i dati! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 + src\Controller\BaseAdminController.php:154 + + + attachment_type.deleted + Elemento cancellato! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 + Part-DB1\src\Controller\UserController.php:109 + Part-DB1\src\Controller\UserSettingsController.php:159 + Part-DB1\src\Controller\UserSettingsController.php:193 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:354 + Part-DB1\src\Controller\UserController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:150 + Part-DB1\src\Controller\UserSettingsController.php:182 + + + csfr_invalid + Token CSFR non valido! Ricaricare questa pagina o contattare un amministratore se il problema persiste! + + + + + Part-DB1\src\Controller\LabelController.php:125 + + + label_generator.no_entities_found + Nessun elemento trovato + + + + + Part-DB1\src\Controller\LogController.php:149 + Part-DB1\src\Controller\LogController.php:154 + new + + + log.undo.target_not_found + Elemento di destinazione non trovato nel database! + + + + + Part-DB1\src\Controller\LogController.php:156 + Part-DB1\src\Controller\LogController.php:160 + new + + + log.undo.revert_success + Componente resettato con successo. + + + + + Part-DB1\src\Controller\LogController.php:176 + Part-DB1\src\Controller\LogController.php:180 + new + + + log.undo.element_undelete_success + Elemento recuperato con successo. + + + + + Part-DB1\src\Controller\LogController.php:178 + Part-DB1\src\Controller\LogController.php:182 + new + + + log.undo.element_element_already_undeleted + L'elemento è stato già recuperato! + + + + + Part-DB1\src\Controller\LogController.php:185 + Part-DB1\src\Controller\LogController.php:189 + new + + + log.undo.element_delete_success + Elemento eliminato con successo. + + + + + Part-DB1\src\Controller\LogController.php:187 + Part-DB1\src\Controller\LogController.php:191 + new + + + log.undo.element.element_already_delted + L'elemento è stato già eliminato! + + + + + Part-DB1\src\Controller\LogController.php:194 + Part-DB1\src\Controller\LogController.php:198 + new + + + log.undo.element_change_undone + Annullamento della modifica avvenuto con successo! + + + + + Part-DB1\src\Controller\LogController.php:196 + Part-DB1\src\Controller\LogController.php:200 + new + + + log.undo.do_undelete_before + Bisogna recuperare l'elemento eliminato prima di poter effettuare questa modifica! + + + + + Part-DB1\src\Controller\LogController.php:199 + Part-DB1\src\Controller\LogController.php:203 + new + + + log.undo.log_type_invalid + Questa riga del log non può essere annullata! + + + + + Part-DB1\src\Controller\PartController.php:182 + Part-DB1\src\Controller\PartController.php:182 + src\Controller\PartController.php:80 + + + part.edited_flash + Modifiche salvate! + + + + + Part-DB1\src\Controller\PartController.php:186 + Part-DB1\src\Controller\PartController.php:186 + + + part.edited_flash.invalid + Errore durante il salvataggio: per favore controllare i dati immessi! + + + + + Part-DB1\src\Controller\PartController.php:216 + Part-DB1\src\Controller\PartController.php:219 + + + part.deleted + Componente eliminato con successo. + + + + + Part-DB1\src\Controller\PartController.php:302 + Part-DB1\src\Controller\PartController.php:277 + Part-DB1\src\Controller\PartController.php:317 + src\Controller\PartController.php:113 + src\Controller\PartController.php:142 + + + part.created_flash + Componente creato! + + + + + Part-DB1\src\Controller\PartController.php:308 + Part-DB1\src\Controller\PartController.php:283 + + + part.created_flash.invalid + Errore durante la creazione: per favore verificare i dati immessi! + + + + + Part-DB1\src\Controller\ScanController.php:68 + Part-DB1\src\Controller\ScanController.php:90 + + + scan.qr_not_found + Nessun elemento corrispondente al dato codice a barre. + + + + + Part-DB1\src\Controller\ScanController.php:71 + + + scan.format_unknown + Formato sconosciuto! + + + + + Part-DB1\src\Controller\ScanController.php:86 + + + scan.qr_success + Elemento trovato. + + + + + Part-DB1\src\Controller\SecurityController.php:114 + Part-DB1\src\Controller\SecurityController.php:109 + + + pw_reset.user_or_email + Nome utente / Email + + + + + Part-DB1\src\Controller\SecurityController.php:131 + Part-DB1\src\Controller\SecurityController.php:126 + + + pw_reset.request.success + La richiesta di reimpostazione della password è andata a buon fine! Controllare la casella e-mail per ulteriori istruzioni. + + + + + Part-DB1\src\Controller\SecurityController.php:162 + Part-DB1\src\Controller\SecurityController.php:160 + + + pw_reset.username + Nome utente + + + + + Part-DB1\src\Controller\SecurityController.php:165 + Part-DB1\src\Controller\SecurityController.php:163 + + + pw_reset.token + Token + + + + + Part-DB1\src\Controller\SecurityController.php:194 + Part-DB1\src\Controller\SecurityController.php:192 + + + pw_reset.new_pw.error + Nome utente o Token non valido! Per favore controllare i dati immessi. + + + + + Part-DB1\src\Controller\SecurityController.php:196 + Part-DB1\src\Controller\SecurityController.php:194 + + + pw_reset.new_pw.success + La password è stata reimpostata correttamente. Ora puoi accedere con la tua nuova password. + + + + + Part-DB1\src\Controller\UserController.php:107 + Part-DB1\src\Controller\UserController.php:99 + + + user.edit.reset_success + Tutti i metodi di autenticazione a due fattori sono stati disabilitati con successo. + + + + + Part-DB1\src\Controller\UserSettingsController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:92 + + + tfa_backup.no_codes_enabled + Nessun codice di backup abilitato! + + + + + Part-DB1\src\Controller\UserSettingsController.php:138 + Part-DB1\src\Controller\UserSettingsController.php:132 + + + tfa_u2f.u2f_delete.not_existing + Non esiste una chiave di sicurezza con questo ID. + + + + + Part-DB1\src\Controller\UserSettingsController.php:145 + Part-DB1\src\Controller\UserSettingsController.php:139 + + + tfa_u2f.u2f_delete.access_denied + Non puoi eliminare le chiavi di sicurezza di altri utenti! + + + + + Part-DB1\src\Controller\UserSettingsController.php:153 + Part-DB1\src\Controller\UserSettingsController.php:147 + + + tfa.u2f.u2f_delete.success + Chiave di sicurezza rimossa correttamente. + + + + + Part-DB1\src\Controller\UserSettingsController.php:188 + Part-DB1\src\Controller\UserSettingsController.php:180 + + + tfa_trustedDevice.invalidate.success + Dispositivi attendibili reimpostati correttamente. + + + + + Part-DB1\src\Controller\UserSettingsController.php:235 + Part-DB1\src\Controller\UserSettingsController.php:226 + src\Controller\UserController.php:98 + + + user.settings.saved_flash + Impostazioni salvate! + + + + + Part-DB1\src\Controller\UserSettingsController.php:297 + Part-DB1\src\Controller\UserSettingsController.php:288 + src\Controller\UserController.php:130 + + + user.settings.pw_changed_flash + Password cambiata! + + + + + Part-DB1\src\Controller\UserSettingsController.php:317 + Part-DB1\src\Controller\UserSettingsController.php:306 + + + user.settings.2fa.google.activated + App di autenticazione attivata con successo. + + + + + Part-DB1\src\Controller\UserSettingsController.php:328 + Part-DB1\src\Controller\UserSettingsController.php:315 + + + user.settings.2fa.google.disabled + App di autenticazione disattivata con successo. + + + + + Part-DB1\src\Controller\UserSettingsController.php:346 + Part-DB1\src\Controller\UserSettingsController.php:332 + + + user.settings.2fa.backup_codes.regenerated + I nuovi codici di backup sono stati generati. + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + + + attachment.table.filename + Nome del file + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + + + attachment.table.filesize + Dimensioni del file + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:245 + Part-DB1\src\DataTables\PartsDataTable.php:252 + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:193 + Part-DB1\src\DataTables\PartsDataTable.php:200 + + + true + vero + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:246 + Part-DB1\src\DataTables\PartsDataTable.php:253 + Part-DB1\src\Form\Type\SIUnitType.php:139 + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:201 + Part-DB1\src\Form\Type\SIUnitType.php:139 + + + false + falso + + + + + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 + + + log.target_deleted + eliminato + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 + new + + + log.undo.undelete + Recupera elemento + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 + new + + + log.undo.undo + Annulla le modifiche + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 + new + + + log.undo.revert + Ripristinare l'elemento a questa data + + + + + Part-DB1\src\DataTables\LogDataTable.php:173 + Part-DB1\src\DataTables\LogDataTable.php:161 + + + log.id + ID + + + + + Part-DB1\src\DataTables\LogDataTable.php:178 + Part-DB1\src\DataTables\LogDataTable.php:166 + + + log.timestamp + Data + + + + + Part-DB1\src\DataTables\LogDataTable.php:183 + Part-DB1\src\DataTables\LogDataTable.php:171 + + + log.type + Evento + + + + + Part-DB1\src\DataTables\LogDataTable.php:191 + Part-DB1\src\DataTables\LogDataTable.php:179 + + + log.level + Livello + + + + + Part-DB1\src\DataTables\LogDataTable.php:200 + Part-DB1\src\DataTables\LogDataTable.php:188 + + + log.user + Utente + + + + + Part-DB1\src\DataTables\LogDataTable.php:213 + Part-DB1\src\DataTables\LogDataTable.php:201 + + + log.target_type + Tipo di target + + + + + Part-DB1\src\DataTables\LogDataTable.php:226 + Part-DB1\src\DataTables\LogDataTable.php:214 + + + log.target + Target + + + + + Part-DB1\src\DataTables\LogDataTable.php:231 + Part-DB1\src\DataTables\LogDataTable.php:218 + new + + + log.extra + Extra + + + + + Part-DB1\src\DataTables\PartsDataTable.php:168 + Part-DB1\src\DataTables\PartsDataTable.php:116 + + + part.table.name + Nome + + + + + Part-DB1\src\DataTables\PartsDataTable.php:178 + Part-DB1\src\DataTables\PartsDataTable.php:126 + + + part.table.id + ID + + + + + Part-DB1\src\DataTables\PartsDataTable.php:182 + Part-DB1\src\DataTables\PartsDataTable.php:130 + + + part.table.description + Descrizione + + + + + Part-DB1\src\DataTables\PartsDataTable.php:185 + Part-DB1\src\DataTables\PartsDataTable.php:133 + + + part.table.category + Categoria + + + + + Part-DB1\src\DataTables\PartsDataTable.php:190 + Part-DB1\src\DataTables\PartsDataTable.php:138 + + + part.table.footprint + Footprint + + + + + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:142 + + + part.table.manufacturer + Produttore + + + + + Part-DB1\src\DataTables\PartsDataTable.php:197 + Part-DB1\src\DataTables\PartsDataTable.php:145 + + + part.table.storeLocations + Ubicazione + + + + + Part-DB1\src\DataTables\PartsDataTable.php:216 + Part-DB1\src\DataTables\PartsDataTable.php:164 + + + part.table.amount + Quantità + + + + + Part-DB1\src\DataTables\PartsDataTable.php:224 + Part-DB1\src\DataTables\PartsDataTable.php:172 + + + part.table.minamount + Scorta minima + + + + + Part-DB1\src\DataTables\PartsDataTable.php:232 + Part-DB1\src\DataTables\PartsDataTable.php:180 + + + part.table.partUnit + Unità di misura + + + + + Part-DB1\src\DataTables\PartsDataTable.php:236 + Part-DB1\src\DataTables\PartsDataTable.php:184 + + + part.table.addedDate + Creato il + + + + + Part-DB1\src\DataTables\PartsDataTable.php:240 + Part-DB1\src\DataTables\PartsDataTable.php:188 + + + part.table.lastModified + Ultima modifica + + + + + Part-DB1\src\DataTables\PartsDataTable.php:244 + Part-DB1\src\DataTables\PartsDataTable.php:192 + + + part.table.needsReview + Necessita revisione + + + + + Part-DB1\src\DataTables\PartsDataTable.php:251 + Part-DB1\src\DataTables\PartsDataTable.php:199 + + + part.table.favorite + Favorito + + + + + Part-DB1\src\DataTables\PartsDataTable.php:258 + Part-DB1\src\DataTables\PartsDataTable.php:206 + + + part.table.manufacturingStatus + Stato + + + + + Part-DB1\src\DataTables\PartsDataTable.php:260 + Part-DB1\src\DataTables\PartsDataTable.php:262 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:208 + Part-DB1\src\DataTables\PartsDataTable.php:210 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.unknown + Sconosciuto + + + + + Part-DB1\src\DataTables\PartsDataTable.php:263 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:211 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.announced + Annunciato + + + + + Part-DB1\src\DataTables\PartsDataTable.php:264 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.active + Attivo + + + + + Part-DB1\src\DataTables\PartsDataTable.php:265 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:213 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.nrfnd + Non raccomandato per nuovi progetti + + + + + Part-DB1\src\DataTables\PartsDataTable.php:266 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:214 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.eol + Fine vita + + + + + Part-DB1\src\DataTables\PartsDataTable.php:267 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:215 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.discontinued + Fuori produzione + + + + + Part-DB1\src\DataTables\PartsDataTable.php:271 + Part-DB1\src\DataTables\PartsDataTable.php:219 + + + part.table.mpn + MPN + + + + + Part-DB1\src\DataTables\PartsDataTable.php:275 + Part-DB1\src\DataTables\PartsDataTable.php:223 + + + part.table.mass + Peso + + + + + Part-DB1\src\DataTables\PartsDataTable.php:279 + Part-DB1\src\DataTables\PartsDataTable.php:227 + + + part.table.tags + Tags + + + + + Part-DB1\src\DataTables\PartsDataTable.php:283 + Part-DB1\src\DataTables\PartsDataTable.php:231 + + + part.table.attachments + Allegati + + + + + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 + Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 + + + flash.login_successful + Accesso riuscito + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + JSON + JSON + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + XML + XML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + CSV + CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + YAML + YAML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:124 + Part-DB1\src\Form\AdminPages\ImportType.php:124 + + + import.abort_on_validation.help + Quando questa opzione è abilitata, il rilevamento di dati non validi annullerà l'intero processo. Se questa opzione non è attiva, le voci non valide vengono ignorate e si continuerà a tentare l'importazione delle altre voci. + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:86 + Part-DB1\src\Form\AdminPages\ImportType.php:86 + src\Form\ImportType.php:70 + + + import.csv_separator + Separatore CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:93 + Part-DB1\src\Form\AdminPages\ImportType.php:93 + src\Form\ImportType.php:72 + + + parent.label + Elemento padre + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:101 + Part-DB1\src\Form\AdminPages\ImportType.php:101 + src\Form\ImportType.php:75 + + + import.file + File + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:111 + Part-DB1\src\Form\AdminPages\ImportType.php:111 + src\Form\ImportType.php:78 + + + import.preserve_children + Importa anche sottoelementi + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:120 + Part-DB1\src\Form\AdminPages\ImportType.php:120 + src\Form\ImportType.php:80 + + + import.abort_on_validation + Interrompi in caso di dati non validi + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:132 + Part-DB1\src\Form\AdminPages\ImportType.php:132 + src\Form\ImportType.php:85 + + + import.btn + Importare + + + + + Part-DB1\src\Form\AttachmentFormType.php:113 + Part-DB1\src\Form\AttachmentFormType.php:109 + + + attachment.edit.secure_file.help + Un allegato contrassegnato come privato è accessibile solo da un utente loggato che ha l'autorizzazione corrispondente. Quando questa opzione è attiva, non vengono generate miniature e l'accesso al file è più lento. + + + + + Part-DB1\src\Form\AttachmentFormType.php:127 + Part-DB1\src\Form\AttachmentFormType.php:123 + + + attachment.edit.url.help + Qui è possibile inserire un URL per un file esterno o cercare inserendo una parola chiave nelle risorse incorporate (ad es. footprints). + + + + + Part-DB1\src\Form\AttachmentFormType.php:82 + Part-DB1\src\Form\AttachmentFormType.php:79 + + + attachment.edit.name + Nome + + + + + Part-DB1\src\Form\AttachmentFormType.php:85 + Part-DB1\src\Form\AttachmentFormType.php:82 + + + attachment.edit.attachment_type + Tipo di allegato + + + + + Part-DB1\src\Form\AttachmentFormType.php:94 + Part-DB1\src\Form\AttachmentFormType.php:91 + + + attachment.edit.show_in_table + Mostrare in tabella + + + + + Part-DB1\src\Form\AttachmentFormType.php:105 + Part-DB1\src\Form\AttachmentFormType.php:102 + + + attachment.edit.secure_file + Allegato privato + + + + + Part-DB1\src\Form\AttachmentFormType.php:119 + Part-DB1\src\Form\AttachmentFormType.php:115 + + + attachment.edit.url + URL + + + + + Part-DB1\src\Form\AttachmentFormType.php:133 + Part-DB1\src\Form\AttachmentFormType.php:129 + + + attachment.edit.download_url + Download di un file esterno + + + + + Part-DB1\src\Form\AttachmentFormType.php:146 + Part-DB1\src\Form\AttachmentFormType.php:142 + + + attachment.edit.file + Upload del file + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:86 + + + part.label + Componente + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:87 + + + part_lot.label + Lotto di componenti + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.none + Nessuno + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.qr + QR Code (raccomandato) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code128 + Code 128 (raccomandato) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code39 + Code 39 (raccomandato) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code93 + Code 93 + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.datamatrix + Datamatrix + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label_options.lines_mode.html + Segnaposto + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label.options.lines_mode.twig + Twig + + + + + Part-DB1\src\Form\LabelOptionsType.php:126 + + + label_options.lines_mode.help + Se si seleziona Twig qui, il campo del contenuto viene interpretato come modello Twig. Vedere <a href="https://twig.symfony.com/doc/3.x/templates.html">Twig documentation</a> e <a href="https://docs.part-db.de/usage/labels.html#twig-mode">Wiki</a> per ulteriori informazioni. + + + + + Part-DB1\src\Form\LabelOptionsType.php:47 + + + label_options.page_size.label + Misure dell'etichetta + + + + + Part-DB1\src\Form\LabelOptionsType.php:66 + + + label_options.supported_elements.label + Tipo di target + + + + + Part-DB1\src\Form\LabelOptionsType.php:75 + + + label_options.barcode_type.label + Codice a barre + + + + + Part-DB1\src\Form\LabelOptionsType.php:102 + + + label_profile.lines.label + Contenuto + + + + + Part-DB1\src\Form\LabelOptionsType.php:111 + + + label_options.additional_css.label + Stile aggiuntivo (CSS) + + + + + Part-DB1\src\Form\LabelOptionsType.php:120 + + + label_options.lines_mode.label + Parser mode + + + + + Part-DB1\src\Form\LabelOptionsType.php:51 + + + label_options.width.placeholder + Larghezza + + + + + Part-DB1\src\Form\LabelOptionsType.php:60 + + + label_options.height.placeholder + Altezza + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 + + + label_generator.target_id.range_hint + Qui puoi specificare più ID (ad esempio 1,2,3) e/o un intervallo (1-3) per generare etichette per più elementi contemporaneamente. + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 + + + label_generator.target_id.label + Target ID + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 + + + label_generator.update + Aggiornamento + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 + + + scan_dialog.input + Input + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 + + + scan_dialog.submit + Invia + + + + + Part-DB1\src\Form\ParameterType.php:41 + + + parameters.name.placeholder + es. DC Current Gain + + + + + Part-DB1\src\Form\ParameterType.php:50 + + + parameters.symbol.placeholder + es. h_{FE} + + + + + Part-DB1\src\Form\ParameterType.php:60 + + + parameters.text.placeholder + es. Test conditions + + + + + Part-DB1\src\Form\ParameterType.php:71 + + + parameters.max.placeholder + es. 350 + + + + + Part-DB1\src\Form\ParameterType.php:82 + + + parameters.min.placeholder + es. 100 + + + + + Part-DB1\src\Form\ParameterType.php:93 + + + parameters.typical.placeholder + es. 200 + + + + + Part-DB1\src\Form\ParameterType.php:103 + + + parameters.unit.placeholder + es. V + + + + + Part-DB1\src\Form\ParameterType.php:114 + + + parameter.group.placeholder + es. Specifiche tecniche + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:72 + Part-DB1\src\Form\Part\OrderdetailType.php:75 + + + orderdetails.edit.supplierpartnr + Codice componente del fornitore + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:81 + Part-DB1\src\Form\Part\OrderdetailType.php:84 + + + orderdetails.edit.supplier + Fornitore + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:87 + Part-DB1\src\Form\Part\OrderdetailType.php:90 + + + orderdetails.edit.url + Link all'offerta + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:93 + Part-DB1\src\Form\Part\OrderdetailType.php:96 + + + orderdetails.edit.obsolete + Non più disponibile + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:75 + Part-DB1\src\Form\Part\OrderdetailType.php:78 + + + orderdetails.edit.supplierpartnr.placeholder + es. BC 547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:101 + Part-DB1\src\Form\Part\PartBaseType.php:99 + + + part.edit.name + Nome + + + + + Part-DB1\src\Form\Part\PartBaseType.php:109 + Part-DB1\src\Form\Part\PartBaseType.php:107 + + + part.edit.description + Descrizione + + + + + Part-DB1\src\Form\Part\PartBaseType.php:120 + Part-DB1\src\Form\Part\PartBaseType.php:118 + + + part.edit.mininstock + Scorta minima + + + + + Part-DB1\src\Form\Part\PartBaseType.php:129 + Part-DB1\src\Form\Part\PartBaseType.php:127 + + + part.edit.category + Categoria + + + + + Part-DB1\src\Form\Part\PartBaseType.php:135 + Part-DB1\src\Form\Part\PartBaseType.php:133 + + + part.edit.footprint + Footprint + + + + + Part-DB1\src\Form\Part\PartBaseType.php:142 + Part-DB1\src\Form\Part\PartBaseType.php:140 + + + part.edit.tags + Tags + + + + + Part-DB1\src\Form\Part\PartBaseType.php:154 + Part-DB1\src\Form\Part\PartBaseType.php:152 + + + part.edit.manufacturer.label + Produttore + + + + + Part-DB1\src\Form\Part\PartBaseType.php:161 + Part-DB1\src\Form\Part\PartBaseType.php:159 + + + part.edit.manufacturer_url.label + Link alla pagina del prodotto + + + + + Part-DB1\src\Form\Part\PartBaseType.php:167 + Part-DB1\src\Form\Part\PartBaseType.php:165 + + + part.edit.mpn + Codice componente del produttore + + + + + Part-DB1\src\Form\Part\PartBaseType.php:173 + Part-DB1\src\Form\Part\PartBaseType.php:171 + + + part.edit.manufacturing_status + Stato di produzione + + + + + Part-DB1\src\Form\Part\PartBaseType.php:181 + Part-DB1\src\Form\Part\PartBaseType.php:179 + + + part.edit.needs_review + Necessita revisione + + + + + Part-DB1\src\Form\Part\PartBaseType.php:189 + Part-DB1\src\Form\Part\PartBaseType.php:187 + + + part.edit.is_favorite + Preferito + + + + + Part-DB1\src\Form\Part\PartBaseType.php:197 + Part-DB1\src\Form\Part\PartBaseType.php:195 + + + part.edit.mass + Peso + + + + + Part-DB1\src\Form\Part\PartBaseType.php:203 + Part-DB1\src\Form\Part\PartBaseType.php:201 + + + part.edit.partUnit + Unità di misura + + + + + Part-DB1\src\Form\Part\PartBaseType.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:210 + + + part.edit.comment + Note + + + + + Part-DB1\src\Form\Part\PartBaseType.php:250 + Part-DB1\src\Form\Part\PartBaseType.php:246 + + + part.edit.master_attachment + Immagine di anteprima + + + + + Part-DB1\src\Form\Part\PartBaseType.php:295 + Part-DB1\src\Form\Part\PartBaseType.php:276 + src\Form\PartType.php:91 + + + part.edit.save + Salvare le modifiche + + + + + Part-DB1\src\Form\Part\PartBaseType.php:296 + Part-DB1\src\Form\Part\PartBaseType.php:277 + src\Form\PartType.php:92 + + + part.edit.reset + Annullare le modifiche + + + + + Part-DB1\src\Form\Part\PartBaseType.php:105 + Part-DB1\src\Form\Part\PartBaseType.php:103 + + + part.edit.name.placeholder + es. BC547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:115 + Part-DB1\src\Form\Part\PartBaseType.php:113 + + + part.edit.description.placeholder + es. NPN 45V, 0,1A, 0,5W + + + + + Part-DB1\src\Form\Part\PartBaseType.php:123 + Part-DB1\src\Form\Part\PartBaseType.php:121 + + + part.editmininstock.placeholder + es. 1 + + + + + Part-DB1\src\Form\Part\PartLotType.php:69 + Part-DB1\src\Form\Part\PartLotType.php:69 + + + part_lot.edit.description + Descrizione + + + + + Part-DB1\src\Form\Part\PartLotType.php:78 + Part-DB1\src\Form\Part\PartLotType.php:78 + + + part_lot.edit.location + Ubicazione + + + + + Part-DB1\src\Form\Part\PartLotType.php:89 + Part-DB1\src\Form\Part\PartLotType.php:89 + + + part_lot.edit.amount + Quantità + + + + + Part-DB1\src\Form\Part\PartLotType.php:98 + Part-DB1\src\Form\Part\PartLotType.php:97 + + + part_lot.edit.instock_unknown + Quantità sconosciuta + + + + + Part-DB1\src\Form\Part\PartLotType.php:109 + Part-DB1\src\Form\Part\PartLotType.php:108 + + + part_lot.edit.needs_refill + Deve essere rifornito + + + + + Part-DB1\src\Form\Part\PartLotType.php:120 + Part-DB1\src\Form\Part\PartLotType.php:119 + + + part_lot.edit.expiration_date + Data di scadenza + + + + + Part-DB1\src\Form\Part\PartLotType.php:128 + Part-DB1\src\Form\Part\PartLotType.php:125 + + + part_lot.edit.comment + Note + + + + + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + + + perm.group.other + Varie + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + + + tfa_google.enable + Attivare l'app Authenticator + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + + + tfa_google.disable + Disattivare l'app Authenticator + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + + + google_confirmation + Codice di conferma + + + + + Part-DB1\src\Form\UserSettingsType.php:108 + Part-DB1\src\Form\UserSettingsType.php:108 + src\Form\UserSettingsType.php:46 + + + user.timezone.label + Fuso orario + + + + + Part-DB1\src\Form\UserSettingsType.php:133 + Part-DB1\src\Form\UserSettingsType.php:132 + + + user.currency.label + Valuta preferita + + + + + Part-DB1\src\Form\UserSettingsType.php:140 + Part-DB1\src\Form\UserSettingsType.php:139 + src\Form\UserSettingsType.php:53 + + + save + Applicare le modifiche + + + + + Part-DB1\src\Form\UserSettingsType.php:141 + Part-DB1\src\Form\UserSettingsType.php:140 + src\Form\UserSettingsType.php:54 + + + reset + Scartare le modifiche + + + + + Part-DB1\src\Form\UserSettingsType.php:104 + Part-DB1\src\Form\UserSettingsType.php:104 + src\Form\UserSettingsType.php:45 + + + user_settings.language.placeholder + Lingua del server + + + + + Part-DB1\src\Form\UserSettingsType.php:115 + Part-DB1\src\Form\UserSettingsType.php:115 + src\Form\UserSettingsType.php:48 + + + user_settings.timezone.placeholder + Fuso orario del server + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + + + attachment.label + Allegato + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + + + attachment_type.label + Tipo di allegato + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + + + project.label + Progetto + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + + + measurement_unit.label + Unità di misura + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + + + currency.label + Valuta + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + + + orderdetail.label + Dettagli dell'ordine + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + + + pricedetail.label + Informazioni sul prezzo + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + + + user.label + Utente + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 + + + parameter.label + Parametro + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 + + + label_profile.label + Profilo di etichetta + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 + new + + + log.element_deleted.old_name.unknown + Sconosciuto + + + + + Part-DB1\src\Services\MarkdownParser.php:73 + Part-DB1\src\Services\MarkdownParser.php:73 + + + markdown.loading + Markdown in caricamento. Se questo dura troppo a lungo, provare a ricaricare la pagina! + + + + + Part-DB1\src\Services\PasswordResetManager.php:98 + Part-DB1\src\Services\PasswordResetManager.php:98 + + + pw_reset.email.subject + Reimpostazione della password per l'account Part-DB + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + + + tree.tools.tools + Utilità + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 + src\Services\ToolsTreeBuilder.php:74 + + + tree.tools.edit + Modificare + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + src\Services\ToolsTreeBuilder.php:81 + + + tree.tools.show + Visualizzare + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + + + tree.tools.system + Sistema + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 + + + tree.tools.tools.label_dialog + Generatore di etichette + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 + + + tree.tools.tools.label_scanner + Scanner + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 + src\Services\ToolsTreeBuilder.php:62 + + + tree.tools.edit.attachment_types + Tipo di allegato + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 + src\Services\ToolsTreeBuilder.php:64 + + + tree.tools.edit.categories + Categorie + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 + src\Services\ToolsTreeBuilder.php:66 + + + tree.tools.edit.projects + Progetti + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 + src\Services\ToolsTreeBuilder.php:68 + + + tree.tools.edit.suppliers + Fornitori + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 + src\Services\ToolsTreeBuilder.php:70 + + + tree.tools.edit.manufacturer + Produttori + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 + + + tree.tools.edit.storelocation + Ubicazioni + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 + + + tree.tools.edit.footprint + Footprints + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 + + + tree.tools.edit.currency + Valute + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 + + + tree.tools.edit.measurement_unit + Unità di misura + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.edit.label_profile + Profili di etichette + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 + + + tree.tools.edit.part + Nuovo componente + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + src\Services\ToolsTreeBuilder.php:77 + + + tree.tools.show.all_parts + Tutti i componenti + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.show.all_attachments + Allegati + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 + new + + + tree.tools.show.statistics + Statistiche + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 + + + tree.tools.system.users + Utenti + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 + + + tree.tools.system.groups + Gruppi + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 + new + + + tree.tools.system.event_log + Log degli eventi + + + + + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + src\Services\TreeBuilder.php:124 + + + entity.tree.new + Nuovo elemento + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 + obsolete + + + attachment.external_file + File esterno + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + obsolete + + + attachment.edit + Modificare + + + + + Part-DB1\templates\_navbar.html.twig:27 + templates\base.html.twig:88 + obsolete + + + barcode.scan + Scansione del codice a barre + + + + + Part-DB1\src\Form\UserSettingsType.php:119 + src\Form\UserSettingsType.php:49 + obsolete + + + user.theme.label + Tema + + + + + Part-DB1\src\Form\UserSettingsType.php:129 + src\Form\UserSettingsType.php:50 + obsolete + + + user_settings.theme.placeholder + Tema del server + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 + new + obsolete + + + log.user_login.ip + IP + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:169 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:207 + new + obsolete + + + log.undo_mode.undo + Modifiche annullate + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:171 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:209 + new + obsolete + + + log.undo_mode.revert + Elemento ripristinato + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 + new + obsolete + + + log.element_created.original_instock + Vecchio stock + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 + new + obsolete + + + log.element_deleted.old_name + Vecchio nome + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 + new + obsolete + + + log.element_edited.changed_fields + Campi modificati + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 + new + obsolete + + + log.instock_changed.comment + Commento + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 + new + obsolete + + + log.collection_deleted.deleted + Elemento eliminato + + + + + templates\base.html.twig:81 + obsolete + obsolete + + + go.exclamation + Vai! + + + + + templates\base.html.twig:109 + obsolete + obsolete + + + language.english + Inglese + + + + + templates\base.html.twig:112 + obsolete + obsolete + + + language.german + Tedesco + + + + + obsolete + obsolete + + + flash.password_change_needed + È necessaria la modifica della password! + + + + + obsolete + obsolete + + + attachment.table.type + Tipo di allegato + + + + + obsolete + obsolete + + + attachment.table.element + Elemento associato + + + + + obsolete + obsolete + + + attachment.edit.isPicture + Immagine? + + + + + obsolete + obsolete + + + attachment.edit.is3DModel + Modello 3D? + + + + + obsolete + obsolete + + + attachment.edit.isBuiltin + Incorporato? + + + + + obsolete + obsolete + + + category.edit.default_comment.placeholder + es. utile per il cambio + + + + + obsolete + obsolete + + + tfa_backup.regenerate_codes + Generare nuovi codici di backup + + + + + obsolete + obsolete + + + validator.noneofitschild.self + Un elemento non può essere il proprio elemento padre! + + + + + obsolete + obsolete + + + validator.noneofitschild.children + Un elemento figlio non può essere anche elemento padre! + + + + + obsolete + obsolete + + + validator.part_lot.location_full.no_increasment + L'ubicazione è stata contrassegnata come piena, quindi non è possibile aumentare la quantità di scorte. (Nuova q.tà max. {{ old_amount }}) + + + + + obsolete + obsolete + + + validator.part_lot.location_full + Questa ubicazione è piena, non è possibile aggiungere nuovi componenti. + + + + + obsolete + obsolete + + + validator.part_lot.only_existing + Questa ubicazione è stata contrassegnata come "solo parti esistenti", quindi non è possibile aggiungere nuove parti. + + + + + obsolete + obsolete + + + validator.part_lot.single_part + L'ubicazione è stata contrassegnata come "singolo componente", quindi non vi si possono aggiungere componenti. + + + + + obsolete + obsolete + + + m_status.active.help + Il componente è attualmente e nel prossimo futuro in produzione + + + + + obsolete + obsolete + + + m_status.announced.help + Il componente è stata annunciato ma non è ancora disponibile. + + + + + obsolete + obsolete + + + m_status.discontinued.help + Il componente è fuori produzione e non sarà più prodotto. + + + + + obsolete + obsolete + + + m_status.eol.help + Il prodotto ha raggiunto la fine vita e la produzione sarà interrotta presto. + + + + + obsolete + obsolete + + + m_status.nrfnd.help + Il componente è attualmente in produzione ma non è raccomandato per i nuovi progetti. + + + + + obsolete + obsolete + + + m_status.unknown.help + Lo stato di produzione del componente non è noto. + + + + + obsolete + obsolete + + + flash.success + Successo + + + + + obsolete + obsolete + + + flash.error + Errore + + + + + obsolete + obsolete + + + flash.warning + Attenzione + + + + + obsolete + obsolete + + + flash.notice + Avviso + + + + + obsolete + obsolete + + + flash.info + Info + + + + + obsolete + obsolete + + + validator.noLockout + Non si può ritirare il permesso di "cambiare i permessi", ciò bloccherebbe la sua utenza. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter + Estensioni di file consentite + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.help + È possibile specificare un elenco separato da virgole di estensioni file o tipi mime che un file caricato deve avere quando assegnato a questo tipo di allegato. Per consentire tutti i file immagine supportati è possibile utilizzare image/*. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.placeholder + ad es. .txt, application/pdf, image/* + + + + + src\Form\PartType.php:63 + obsolete + obsolete + + + part.name.placeholder + ad es. BC547 + + + + + obsolete + obsolete + + + entity.edit.not_selectable + Non selezionabile + + + + + obsolete + obsolete + + + entity.edit.not_selectable.help + Se questa opzione è attivata, allora questo elemento non può essere assegnato a un componente come proprietà. Utile, per esempio, se questo elemento deve servire solo per il raggruppamento. + + + + + obsolete + obsolete + + + bbcode.hint + Qui si può usare BBCode (ad es. [b]Bold[/b]) + + + + + obsolete + obsolete + + + entity.create + Creare un elemento + + + + + obsolete + obsolete + + + entity.edit.save + Salva + + + + + obsolete + obsolete + + + category.edit.disable_footprints + Disabilitare i footprint + + + + + obsolete + obsolete + + + category.edit.disable_footprints.help + Se questa opzione è attivata, la proprietà "footprint" è disabilitata per tutti i componenti con questa categoria. + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers + Disabilitare i produttori + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers.help + Se questa opzione è attivata, la proprietà "Produttori" è disabilitata per tutti componenti con questa categoria. + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets + Disabilitare i collegamenti automatici dei datasheet + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets.help + Se questa opzione è attivata, non vengono creati collegamenti automatici ai datasheet per i componenti con questa categoria. + + + + + obsolete + obsolete + + + category.edit.disable_properties + Disattivare le proprietà + + + + + obsolete + obsolete + + + category.edit.disable_properties.help + Se questa opzione è attivata, le proprietà dei componenti sono disabilitate per tutti i componenti con questa categoria. + + + + + obsolete + obsolete + + + category.edit.partname_hint + Suggerimento sul nome del componente + + + + + obsolete + obsolete + + + category.edit.partname_hint.placeholder + ad es. 100nF + + + + + obsolete + obsolete + + + category.edit.partname_regex + Filtro nome + + + + + obsolete + obsolete + + + category.edit.default_description + Descrizione di default + + + + + obsolete + obsolete + + + category.edit.default_description.placeholder + ad es. Condensatore, 10mm x 10mm, SMD + + + + + obsolete + obsolete + + + category.edit.default_comment + Note di default + + + + + obsolete + obsolete + + + company.edit.address + Indirizzo + + + + + obsolete + obsolete + + + company.edit.address.placeholder + ad es. Via del Colosseo 43 +ROMA + + + + + obsolete + obsolete + + + company.edit.phone_number + Numero di telefono + + + + + obsolete + obsolete + + + company.edit.phone_number.placeholder + ad es. +39 0612345678 + + + + + obsolete + obsolete + + + company.edit.fax_number + Numero di fax + + + + + obsolete + obsolete + + + company.edit.email + Email + + + + + obsolete + obsolete + + + company.edit.email.placeholder + es. contatti@societa.it + + + + + obsolete + obsolete + + + company.edit.website + Sito web + + + + + obsolete + obsolete + + + company.edit.website.placeholder + https://www.societa.it + + + + + obsolete + obsolete + + + company.edit.auto_product_url + URL del prodotto + + + + + obsolete + obsolete + + + company.edit.auto_product_url.help + Questo campo viene utilizzato per determinare un collegamento al componente nel sito web del produttore. %PARTNUMBER% verrà sostituito con il numero d'ordine. + + + + + obsolete + obsolete + + + company.edit.auto_product_url.placeholder + es. https://societa.it/product/%PARTNUMBER% + + + + + obsolete + obsolete + + + currency.edit.iso_code + Codice ISO + + + + + obsolete + obsolete + + + currency.edit.exchange_rate + Tasso di cambio + + + + + obsolete + obsolete + + + footprint.edit.3d_model + Modello 3D + + + + + obsolete + obsolete + + + mass_creation.lines + Inserimento + + + + + obsolete + obsolete + + + mass_creation.lines.placeholder + Element 1 + Element 1.1 + Element 1.1.1 + Element 1.2 +Element 2 +Element 3 + + + + + obsolete + obsolete + + + entity.mass_creation.btn + Creare + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer + Numero intero + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer.help + Quando questa opzione è abilitata, tutti i valori con questa unità sono arrotondati a numeri interi. + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix + Utilizzare il prefisso SI + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix.help + Se questa opzione è attivata, i prefissi SI vengono utilizzati per la formattazione dei numeri (ad es. 1,2kg invece di 1200g) + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol + Simbolo dell'unità + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol.placeholder + es. m + + + + + obsolete + obsolete + + + storelocation.edit.is_full.label + Ubicazione piena + + + + + obsolete + obsolete + + + storelocation.edit.is_full.help + Se questa opzione è attivata, non è possibile aggiungere nuovi componenti in questa ubicazione, né aumentare il numero di componenti esistenti. + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.label + Solo componenti esistenti + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.help + Se questa opzione è attiva, non è possibile aggiungere nuovi componenti in questa ubicazione, ma è possibile aumentare il numero di componenti già esistenti. + + + + + obsolete + obsolete + + + storelocation.only_single_part.label + Solo un componente + + + + + obsolete + obsolete + + + storelocation.only_single_part.help + Se questa opzione è attivata, questa ubicazione può contenere solo un singolo componente, ma in qualsiasi quantità. Utile per piccoli scomparti per SMD o alimentatori automatici. + + + + + obsolete + obsolete + + + storelocation.storage_type.label + Tipo di magazzino + + + + + obsolete + obsolete + + + storelocation.storage_type.help + Qui si può scegliere un'unità di misura che un componente deve avere per poter essere immagazzinato in questa ubicazione. + + + + + obsolete + obsolete + + + supplier.edit.default_currency + Valuta di default + + + + + obsolete + obsolete + + + supplier.shipping_costs.label + Costo di spedizione + + + + + obsolete + obsolete + + + user.username.placeholder + es. mario + + + + + obsolete + obsolete + + + user.firstName.placeholder + es. Mario + + + + + obsolete + obsolete + + + user.lastName.placeholder + es. Rossi + + + + + obsolete + obsolete + + + user.email.placeholder + m.rossi@itcorp.com + + + + + obsolete + obsolete + + + user.department.placeholder + es. Produzione + + + + + obsolete + obsolete + + + user.settings.pw_new.label + Nuova password + + + + + obsolete + obsolete + + + user.settings.pw_confirm.label + Confermare la nuova password + + + + + obsolete + obsolete + + + user.edit.needs_pw_change + L'utente deve cambiare la password + + + + + obsolete + obsolete + + + user.edit.user_disabled + Utente disabilitato (non può fare il login) + + + + + obsolete + obsolete + + + user.create + Creare l'utente + + + + + obsolete + obsolete + + + user.edit.save + Salvare + + + + + obsolete + obsolete + + + entity.edit.reset + Scartare le modifiche + + + + + templates\Parts\show_part_info.html.twig:166 + obsolete + obsolete + + + part.withdraw.btn + Prelevare + + + + + templates\Parts\show_part_info.html.twig:171 + obsolete + obsolete + + + part.withdraw.comment: + Commento/Oggetto + + + + + templates\Parts\show_part_info.html.twig:189 + obsolete + obsolete + + + part.add.caption + Aggiungere componenti + + + + + templates\Parts\show_part_info.html.twig:194 + obsolete + obsolete + + + part.add.btn + Aggiungere + + + + + templates\Parts\show_part_info.html.twig:199 + obsolete + obsolete + + + part.add.comment + Commento/Oggetto + + + + + templates\AdminPages\CompanyAdminBase.html.twig:15 + obsolete + obsolete + + + admin.comment + Note + + + + + src\Form\PartType.php:83 + obsolete + obsolete + + + manufacturer_url.label + Link al sito del produttore + + + + + src\Form\PartType.php:66 + obsolete + obsolete + + + part.description.placeholder + es. NPN 45V 0,1A 0,5W + + + + + src\Form\PartType.php:69 + obsolete + obsolete + + + part.instock.placeholder + es. 10 + + + + + src\Form\PartType.php:72 + obsolete + obsolete + + + part.mininstock.placeholder + es. 5 + + + + + obsolete + obsolete + + + part.order.price_per + Prezzo per + + + + + obsolete + obsolete + + + part.withdraw.caption + Prelevare componenti + + + + + obsolete + obsolete + + + datatable.datatable.lengthMenu + _MENU_ + + + + + obsolete + obsolete + + + perm.group.parts + Componenti + + + + + obsolete + obsolete + + + perm.group.structures + Struttura dei dati + + + + + obsolete + obsolete + + + perm.group.system + Sistema + + + + + obsolete + obsolete + + + perm.parts + Componenti + + + + + obsolete + obsolete + + + perm.read + Visualizzare + + + + + obsolete + obsolete + + + perm.edit + Modificare + + + + + obsolete + obsolete + + + perm.create + Creare + + + + + obsolete + obsolete + + + perm.part.move + Cambiare categoria + + + + + obsolete + obsolete + + + perm.delete + Cancellare + + + + + obsolete + obsolete + + + perm.part.search + Cercare + + + + + obsolete + obsolete + + + perm.part.all_parts + Lista di tutti i componenti + + + + + obsolete + obsolete + + + perm.part.no_price_parts + Lista dei componenti senza prezzo + + + + + obsolete + obsolete + + + perm.part.obsolete_parts + Lista dei componenti obsoleti + + + + + obsolete + obsolete + + + perm.part.unknown_instock_parts + Lista dei componenti con stock sconosciuto + + + + + obsolete + obsolete + + + perm.part.change_favorite + Cambia lo stato dei preferiti + + + + + obsolete + obsolete + + + perm.part.show_favorite + Visualizza i preferiti + + + + + obsolete + obsolete + + + perm.part.show_last_edit_parts + Mostra i componenti modificati/aggiunti di recente + + + + + obsolete + obsolete + + + perm.part.show_users + Mostra l'ultimo utente che ha modificato + + + + + obsolete + obsolete + + + perm.part.show_history + Visualizza la storia + + + + + obsolete + obsolete + + + perm.part.name + Nome + + + + + obsolete + obsolete + + + perm.part.description + Descrizione + + + + + obsolete + obsolete + + + perm.part.instock + In stock + + + + + obsolete + obsolete + + + perm.part.mininstock + Scorta minima + + + + + obsolete + obsolete + + + perm.part.comment + Note + + + + + obsolete + obsolete + + + perm.part.storelocation + Ubicazione + + + + + obsolete + obsolete + + + perm.part.manufacturer + Produttore + + + + + obsolete + obsolete + + + perm.part.orderdetails + Informazioni per l'ordine + + + + + obsolete + obsolete + + + perm.part.prices + Prezzi + + + + + obsolete + obsolete + + + perm.part.attachments + File allegati + + + + + obsolete + obsolete + + + perm.part.order + Ordini + + + + + obsolete + obsolete + + + perm.storelocations + Ubicazioni + + + + + obsolete + obsolete + + + perm.move + Spostare + + + + + obsolete + obsolete + + + perm.list_parts + Lista dei componenti + + + + + obsolete + obsolete + + + perm.part.footprints + Footprint + + + + + obsolete + obsolete + + + perm.part.categories + Categorie + + + + + obsolete + obsolete + + + perm.part.supplier + Fornitori + + + + + obsolete + obsolete + + + perm.part.manufacturers + Produttori + + + + + obsolete + obsolete + + + perm.projects + Progetti + + + + + obsolete + obsolete + + + perm.part.attachment_types + Tipi di allegati + + + + + obsolete + obsolete + + + perm.tools.import + Importare + + + + + obsolete + obsolete + + + perm.tools.labels + Etichette + + + + + obsolete + obsolete + + + perm.tools.calculator + Calcolatore di resistenza + + + + + obsolete + obsolete + + + perm.tools.footprints + Footprint + + + + + obsolete + obsolete + + + perm.tools.ic_logos + Loghi IC + + + + + obsolete + obsolete + + + perm.tools.statistics + Statistiche + + + + + obsolete + obsolete + + + perm.edit_permissions + Modificare i permessi + + + + + obsolete + obsolete + + + perm.users.edit_user_name + Modificare il nome utente + + + + + obsolete + obsolete + + + perm.users.edit_change_group + Modificare il gruppo + + + + + obsolete + obsolete + + + perm.users.edit_infos + Modificare le informazioni + + + + + obsolete + obsolete + + + perm.users.edit_permissions + Modificare i permessi + + + + + obsolete + obsolete + + + perm.users.set_password + Cambiare password + + + + + obsolete + obsolete + + + perm.users.change_user_settings + Cambiare i parametri dell'utente + + + + + obsolete + obsolete + + + perm.database.see_status + Mostra stato + + + + + obsolete + obsolete + + + perm.database.update_db + Aggiornare il database + + + + + obsolete + obsolete + + + perm.database.read_db_settings + Visualizza impostazioni DB + + + + + obsolete + obsolete + + + perm.database.write_db_settings + Modificare impostazioni DB + + + + + obsolete + obsolete + + + perm.config.read_config + Leggere la configurazione + + + + + obsolete + obsolete + + + perm.config.edit_config + Modificare la configurazione + + + + + obsolete + obsolete + + + perm.config.server_info + Informazioni sul server + + + + + obsolete + obsolete + + + perm.config.use_debug + Utilizzare gli strumenti di debug + + + + + obsolete + obsolete + + + perm.show_logs + Visualizza i log + + + + + obsolete + obsolete + + + perm.delete_logs + Cancellare i log + + + + + obsolete + obsolete + + + perm.self.edit_infos + Modificare le informazioni + + + + + obsolete + obsolete + + + perm.self.edit_username + Modificare il nome utente + + + + + obsolete + obsolete + + + perm.self.show_permissions + Visualizzare i permessi + + + + + obsolete + obsolete + + + perm.self.show_logs + Visualizzare i propri log + + + + + obsolete + obsolete + + + perm.self.create_labels + Creare etichette + + + + + obsolete + obsolete + + + perm.self.edit_options + Modificare le opzioni + + + + + obsolete + obsolete + + + perm.self.delete_profiles + Cancellare i profili + + + + + obsolete + obsolete + + + perm.self.edit_profiles + Modificare i profili + + + + + obsolete + obsolete + + + perm.part.tools + Utilità + + + + + obsolete + obsolete + + + perm.groups + Gruppi + + + + + obsolete + obsolete + + + perm.users + Utenti + + + + + obsolete + obsolete + + + perm.database + Database + + + + + obsolete + obsolete + + + perm.config + Configurazione + + + + + obsolete + obsolete + + + perm.system + Sistema + + + + + obsolete + obsolete + + + perm.self + Modificare il proprio utente + + + + + obsolete + obsolete + + + perm.labels + Etichette + + + + + obsolete + obsolete + + + perm.part.category + Categorie + + + + + obsolete + obsolete + + + perm.part.minamount + Quantità minima + + + + + obsolete + obsolete + + + perm.part.footprint + Footprint + + + + + obsolete + obsolete + + + perm.part.mpn + MPN + + + + + obsolete + obsolete + + + perm.part.status + Stato di produzione + + + + + obsolete + obsolete + + + perm.part.tags + Tags + + + + + obsolete + obsolete + + + perm.part.unit + Unità di misura + + + + + obsolete + obsolete + + + perm.part.mass + Peso + + + + + obsolete + obsolete + + + perm.part.lots + Lotto di componenti + + + + + obsolete + obsolete + + + perm.show_users + Mostra l'ultimo utente che ha modificato + + + + + obsolete + obsolete + + + perm.currencies + Valute + + + + + obsolete + obsolete + + + perm.measurement_units + Unità di misura + + + + + obsolete + obsolete + + + user.settings.pw_old.label + Password precedente + + + + + obsolete + obsolete + + + pw_reset.submit + Reimpostare la password + + + + + obsolete + obsolete + + + u2f_two_factor + Chiave di sicurezza (U2F) + + + + + obsolete + obsolete + + + google + Google + + + + + tfa.provider.webauthn_two_factor_provider + Chiave di sicurezza + + + + + obsolete + obsolete + + + tfa.provider.google + Authenticator app + + + + + obsolete + obsolete + + + Login successful + Accesso riuscito! + + + + + obsolete + obsolete + + + log.type.exception + Eccezione non gestita (obsoleta) + + + + + obsolete + obsolete + + + log.type.user_login + Utente loggato + + + + + obsolete + obsolete + + + log.type.user_logout + Utente disconnesso + + + + + obsolete + obsolete + + + log.type.unknown + Sconosciuto + + + + + obsolete + obsolete + + + log.type.element_created + Elemento creato + + + + + obsolete + obsolete + + + log.type.element_edited + Elemento modificato + + + + + obsolete + obsolete + + + log.type.element_deleted + Elemento cancellato + + + + + obsolete + obsolete + + + log.type.database_updated + Database aggiornato + + + + + obsolete + + + perm.revert_elements + Ripristinare elemento + + + + + obsolete + + + perm.show_history + Visualizzare la storia + + + + + obsolete + + + perm.tools.lastActivity + Visualizzare l'ultima attività + + + + + obsolete + + + perm.tools.timeTravel + Mostra gli stati delle vecchie versioni (viaggio nel tempo) + + + + + obsolete + + + tfa_u2f.key_added_successful + Chiave di sicurezza aggiunta con successo. + + + + + obsolete + + + Username + Nome utente + + + + + obsolete + + + log.type.security.google_disabled + Authenticator App disabilitata + + + + + obsolete + + + log.type.security.u2f_removed + Chiave di sicurezza rimossa + + + + + obsolete + + + log.type.security.u2f_added + Chiave di sicurezza aggiunta + + + + + obsolete + + + log.type.security.backup_keys_reset + Chiavi di backup rigenerate + + + + + obsolete + + + log.type.security.google_enabled + Authenticator App abilitata + + + + + obsolete + + + log.type.security.password_changed + Password cambiata + + + + + obsolete + + + log.type.security.trusted_device_reset + Apparecchi affidabili resettati + + + + + obsolete + + + log.type.collection_element_deleted + Elemento di raccolta eliminato + + + + + obsolete + + + log.type.security.password_reset + Password reimpostata + + + + + obsolete + + + log.type.security.2fa_admin_reset + Autenticazione a due fattori ripristinata dall'amministratore + + + + + obsolete + + + log.type.user_not_allowed + Tentativo di accesso non autorizzato + + + + + obsolete + + + log.database_updated.success + Successo + + + + + obsolete + + + label_options.barcode_type.2D + 2D + + + + + obsolete + + + label_options.barcode_type.1D + 1D + + + + + obsolete + + + perm.part.parameters + Parametri + + + + + obsolete + + + perm.attachment_show_private + Visualizza allegati privati + + + + + obsolete + + + perm.tools.label_scanner + Scanner di etichette + + + + + obsolete + + + perm.self.read_profiles + Leggere i profili + + + + + obsolete + + + perm.self.create_profiles + Creare i profili + + + + + obsolete + + + perm.labels.use_twig + Usare la sintassi Twig + + + + + label_profile.showInDropdown + Mostra selezione rapida nel codice a barre + + + + + group.edit.enforce_2fa + Forza l'autenticazione a due fattori (2FA) + + + + + group.edit.enforce_2fa.help + Quando questa opzione è attiva, ogni membro diretto del gruppo deve impostare almeno un secondo fattore per l'autenticazione. Raccomandato ad es. per gruppi amministrativi con autorizzazioni di vasta portata. + + + + + selectpicker.empty + Niente selezionato + + + + + selectpicker.nothing_selected + Niente selezionato + + + + + entity.delete.must_not_contain_parts + L'elemento "%PATH%" contiene ancora componenti. Modifica i componenti per poter cancellare questo elemento. + + + + + entity.delete.must_not_contain_attachments + Per questo tipo di file sono presenti ancora degli allegati. Cambia il loro tipo di file per poter cancellare questo tipo di file. + + + + + entity.delete.must_not_contain_prices + Per questa valuta sono ancora presenti dettagli di prezzo. Cambia la loro valuta per poterla cancellare. + + + + + entity.delete.must_not_contain_users + Alcuni utenti fanno ancora parte di questo gruppo. Cambia il gruppo a questi utenti per poter cancellare questo gruppo. + + + + + part.table.edit + Modificare + + + + + part.table.edit.title + Modificare componente + + + + + part_list.action.action.title + Selezionare una azione + + + + + part_list.action.action.group.favorite + Preferito + + + + + part_list.action.action.favorite + Componente favorito + + + + + part_list.action.action.unfavorite + Annulla indicazione di favorito + + + + + part_list.action.action.group.change_field + Modificare il campo + + + + + part_list.action.action.change_category + Modificare la categoria + + + + + part_list.action.action.change_footprint + Modificare footprint + + + + + part_list.action.action.change_manufacturer + Modificare il produttore + + + + + part_list.action.action.change_unit + Modificare l'unità di misura + + + + + part_list.action.action.delete + Cancellare + + + + + part_list.action.submit + Ok + + + + + part_list.action.part_count + %count% componenti selezionati ! + + + + + company.edit.quick.website + Aprire il sito web + + + + + company.edit.quick.email + Inviare una email + + + + + company.edit.quick.phone + Telefono + + + + + company.edit.quick.fax + Fax + + + + + company.fax_number.placeholder + es. +39 12345678 + + + + + part.edit.save_and_clone + Salvare e duplicare + + + + + validator.file_ext_not_allowed + Estensione del file non consentita per questo tipo di allegato. + + + + + tools.reel_calc.title + Calcolatore per nastratura SMD + + + + + tools.reel_calc.inner_dia + Diametro interno + + + + + tools.reel_calc.outer_dia + Diametro esterno + + + + + tools.reel_calc.tape_thick + Spessore del nastro + + + + + tools.reel_calc.part_distance + Distanza tra i componenti + + + + + tools.reel_calc.update + Aggiornare + + + + + tools.reel_calc.parts_per_meter + Componenti per metro + + + + + tools.reel_calc.result_length + Lunghezza del nastro + + + + + tools.reel_calc.result_amount + Numero approssimativo di componenti + + + + + tools.reel_calc.outer_greater_inner_error + Errore: il diametro esterno deve essere più grande del diametro interno! + + + + + tools.reel_calc.missing_values.error + Indicare tutti i valori! + + + + + tools.reel_calc.load_preset + Caricamento del preset + + + + + tools.reel_calc.explanation + Questo calcolatore permette di stimare quanti componenti sono ancora presenti su un nastro SMD (Reel). Misurare le dimensioni specificate del nastro (o usare le specifiche) e premere "Aggiornare" per ottenere il risultato. + + + + + perm.tools.reel_calculator + Calcolatore per nastratura SMD + + + + + tree.tools.tools.reel_calculator + Calcolatore per nastratura SMD + + + + + user.pw_change_needed.flash + È necessario cambiare la password! Impostare una nuova password. + + + + + tree.root_node.text + Radice + + + + + part_list.action.select_null + Elemento vuoto + + + + + part_list.action.delete-title + Si vuole davvero cancellare questi componenti? + + + + + part_list.action.delete-message + Questi componenti e tutte le informazioni collegate (allegati, informazioni sui prezzi, ecc.) saranno cancellati. Questa azione non può essere annullata! + + + + + part.table.actions.success + Azione completata con successo. + + + + + attachment.edit.delete.confirm + Si vuole davvero eliminare questo allegato? + + + + + filter.text_constraint.value.operator.EQ + E' + + + + + filter.text_constraint.value.operator.NEQ + Non è + + + + + filter.text_constraint.value.operator.STARTS + Comincia con + + + + + filter.text_constraint.value.operator.CONTAINS + Contiene + + + + + filter.text_constraint.value.operator.ENDS + Finisce con + + + + + filter.text_constraint.value.operator.LIKE + Modello LIKE + + + + + filter.text_constraint.value.operator.REGEX + Espressione regolare + + + + + filter.number_constraint.value.operator.BETWEEN + fra + + + + + filter.number_constraint.AND + e + + + + + filter.entity_constraint.operator.EQ + E' (senza elementi figli) + + + + + filter.entity_constraint.operator.NEQ + Non è (senza elementi figli) + + + + + filter.entity_constraint.operator.INCLUDING_CHILDREN + E' (incluso elementi figli) + + + + + filter.entity_constraint.operator.EXCLUDING_CHILDREN + Non è (incluso elementi figli) + + + + + part.filter.dbId + Database ID + + + + + filter.tags_constraint.operator.ANY + Uno qualsiasi dei Tag + + + + + filter.tags_constraint.operator.ALL + Tutti i Tag + + + + + filter.tags_constraint.operator.NONE + Nessuno dei Tag + + + + + part.filter.lot_count + Numero di lotti + + + + + part.filter.attachments_count + Numero di allegati + + + + + part.filter.orderdetails_count + Numero di dettagli ordine + + + + + part.filter.lotExpirationDate + Data di scadenza del lotto + + + + + part.filter.lotNeedsRefill + I lotti necessitano di rifornimento + + + + + part.filter.lotUnknwonAmount + I lotti hanno quantità sconosciuta + + + + + part.filter.attachmentName + Nome dell'allegato + + + + + filter.choice_constraint.operator.ANY + Qualsiasi + + + + + filter.choice_constraint.operator.NONE + Nessuno di + + + + + part.filter.amount_sum + Quantità totale + + + + + filter.submit + Aggiornare + + + + + filter.discard + Scartare le modifiche + + + + + filter.clear_filters + Ripristina tutti i filtri + + + + + filter.title + Filtro + + + + + filter.parameter_value_constraint.operator.= + Valore tip. = + + + + + filter.parameter_value_constraint.operator.!= + Valore tip. != + + + + + filter.parameter_value_constraint.operator.< + Valore tip. < + + + + + filter.parameter_value_constraint.operator.> + Valore tip. > + + + + + filter.parameter_value_constraint.operator.<= + Valore tip. <= + + + + + filter.parameter_value_constraint.operator.>= + Valore tip. >= + + + + + filter.parameter_value_constraint.operator.BETWEEN + Valore tip. è fra + + + + + filter.parameter_value_constraint.operator.IN_RANGE + Nell'intervallo di valori + + + + + filter.parameter_value_constraint.operator.NOT_IN_RANGE + Non nell'intervallo di valori + + + + + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE + Più grande dell'intervallo di valori + + + + + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE + Maggiore o uguale all'intervallo di valori + + + + + filter.parameter_value_constraint.operator.LESS_THAN_RANGE + Minore dell'intervallo di valori + + + + + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE + Minore o uguale all'intervallo di valori + + + + + filter.parameter_value_constraint.operator.RANGE_IN_RANGE + L'intervallo è completamente nell'intervallo di valori + + + + + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE + L'intervallo interseca l'intervallo di valori + + + + + filter.text_constraint.value + Nessun valore impostato + + + + + filter.number_constraint.value1 + Nessun valore impostato + + + + + filter.number_constraint.value2 + Valore massimo + + + + + filter.datetime_constraint.value1 + Nessuna data/ora impostata + + + + + filter.datetime_constraint.value2 + Massima data/ora + + + + + filter.constraint.add + Aggiungi filtro + + + + + part.filter.parameters_count + Numero di parametri + + + + + part.filter.lotDescription + Descrizione del lotto di componenti + + + + + parts_list.search.searching_for + Cerca parti con la parola chiave <b>%keyword%</b> + + + + + parts_list.search_options.caption + Opzioni di ricerca abilitate + + + + + attachment.table.element_type + Tipo di elemento collegato + + + + + log.level.debug + Debug + + + + + log.level.info + Info + + + + + log.level.notice + Avviso + + + + + log.level.warning + Avvertimento + + + + + log.level.error + Errore + + + + + log.level.critical + Critico + + + + + log.level.alert + Allarme + + + + + log.level.emergency + Emergenza + + + + + log.type.security + Evento di sicurezza + + + + + log.type.instock_changed + [ALT] Stock modificato + + + + + log.target_id + ID dell'elemento di destinazione + + + + + entity.info.parts_count_recursive + Componenti con questo elemento o i suoi elementi figli + + + + + tools.server_infos.title + Informazioni sul server + + + + + permission.preset.read_only + Sola lettura + + + + + permission.preset.read_only.desc + Consentire solo operazioni di lettura sui dati + + + + + permission.preset.all_inherit + Ereditare tutto + + + + + permission.preset.all_inherit.desc + Impostare tutte le autorizzazioni su Ereditare + + + + + permission.preset.all_forbid + Vietare tutto + + + + + permission.preset.all_forbid.desc + Impostare tutte le autorizzazioni su Vietare + + + + + permission.preset.all_allow + Consentire tutto + + + + + permission.preset.all_allow.desc + Impostare tutte le autorizzazioni su Consentire + + + + + perm.server_infos + Informazioni server + + + + + permission.preset.editor + Editor + + + + + permission.preset.editor.desc + Consentire la modifica di componenti e strutture dati + + + + + permission.preset.admin + Admin + + + + + permission.preset.admin.desc + Consentire azioni amministrative + + + + + permission.preset.button + Applicare il preset + + + + + perm.attachments.show_private + Mostrare allegati privati + + + + + perm.attachments.list_attachments + Mostrare la lista degli allegati + + + + + user.edit.permission_success + Impostazione delle autorizzazioni applicata correttamente. Controllare se le nuove autorizzazioni si adattano alle esigenze. + + + + + perm.group.data + Dati + + + + + part_list.action.action.group.needs_review + Necessita di revisione + + + + + part_list.action.action.set_needs_review + Imposta lo stato "Necessita di revisione" + + + + + part_list.action.action.unset_needs_review + Cancella lo stato "Necessita di revisione" + + + + + part.edit.ipn + Codice interno (IPN) + + + + + part.ipn.not_defined + Non definito + + + + + part.table.ipn + IPN + + + + + currency.edit.update_rate + Recupera il tasso di cambio + + + + + currency.edit.exchange_rate_update.unsupported_currency + Questa valuta non è supportata dal provider dei tassi di cambio. Controllare la configurazione del provider dei tassi di cambio. + + + + + currency.edit.exchange_rate_update.generic_error + Impossibile recuperare il tasso di cambio. Controllare la configurazione del provider dei tassi di cambio. + + + + + currency.edit.exchange_rate_updated.success + Recuperato il tasso di cambio. + + + + + project.bom.quantity + Quantità BOM + + + + + project.bom.mountnames + Nome dell'attrezzatura + + + + + project.bom.name + Nome + + + + + project.bom.comment + Note + + + + + project.bom.part + Componente + + + + + project.bom.add_entry + Aggiungere voce + + + + + part_list.action.group.projects + Progetti + + + + + part_list.action.projects.add_to_project + Aggiungere componenti al progetto + + + + + project.bom.delete.confirm + Sicuro di vole eliminare questa voce della BOM? + + + + + project.add_parts_to_project + Aggiungere componenti al progetto BOM + + + + + part.info.add_part_to_project + Aggiungere questo componente al progetto + + + + + project_bom_entry.label + Voce della BOM + + + + + project.edit.status + Stato del progetto + + + + + project.status.draft + Bozza + + + + + project.status.planning + In pianificazione + + + + + project.status.in_production + In produzione + + + + + project.status.finished + Completato + + + + + project.status.archived + Archiviato + + + + + part.new_build_part.error.build_part_already_exists + Questo progetto ha già un componente collegato. + + + + + project.edit.associated_build_part + Componente di produzione collegato + + + + + project.edit.associated_build_part.add + Aggiungere il componente di produzione + + + + + project.edit.associated_build.hint + Questo componente rappresenta le istanze costruite del progetto che vengono immagazzinate da qualche parte + + + + + part.info.projectBuildPart.hint + Questo componente rappresenta le istanze costruite di questo progetto ed è collegato ad esso + + + + + part.is_build_part + Componente di produzione + + + + + project.info.title + Informazioni progetto + + + + + project.info.bom_entries_count + Voci BOM + + + + + project.info.sub_projects_count + Sottoprogetto + + + + + project.info.bom_add_parts + Aggiungere voci BOM + + + + + project.info.info.label + Info + + + + + project.info.sub_projects.label + Sottoprogetti + + + + + project.bom.price + Prezzo + + + + + part.info.withdraw_modal.title.withdraw + Rimuovere i componenti dal lotto + + + + + part.info.withdraw_modal.title.add + Aggiungere componenti al lotto + + + + + part.info.withdraw_modal.title.move + Spostare i componenti in un altro lotto + + + + + part.info.withdraw_modal.amount + Quantità + + + + + part.info.withdraw_modal.move_to + Spostare in + + + + + part.info.withdraw_modal.comment + Commento + + + + + part.info.withdraw_modal.comment.hint + Puoi inserire qui un commento che descrive perché questa azione è stata eseguita (ad es. per cosa è stato necessario questo componente). Questa informazione viene memorizzata nel registro. + + + + + modal.close + Chiudere + + + + + modal.submit + Inviare + + + + + part.withdraw.success + Componenti rimossi/aggiunti/spostati con successo. + + + + + perm.parts_stock + Stock di componenti + + + + + perm.parts_stock.withdraw + Rimuovere componenti dallo stock + + + + + perm.parts_stock.add + Aggiungere componenti allo stock + + + + + perm.parts_stock.move + Spostare componenti tra gli stock + + + + + user.permissions_schema_updated + Lo schema di autorizzazione del tuo account utente è stato aggiornato all'ultima versione. + + + + + log.type.part_stock_changed + Modificato lo stock di componenti + + + + + log.part_stock_changed.withdraw + Componenti prelevati + + + + + log.part_stock_changed.add + Stock aggiunto + + + + + log.part_stock_changed.move + Stock spostato + + + + + log.part_stock_changed.comment + Commento + + + + + log.part_stock_changed.change + Cambiare + + + + + log.part_stock_changed.move_target + Spostato in + + + + + tools.builtin_footprints_viewer.title + Immagini del footprint incluse + + + + + tools.builtin_footprints_viewer.hint + Questa galleria elenca tutte le immagini dei footprint incluse. Se vuoi usarli in un allegato, digita il nome (o una parola chiave) nel campo URL dell'allegato e seleziona l'immagine desiderata dal menu a discesa. + + + + + tools.ic_logos.title + Loghi IC + + + + + part_list.action.group.labels + Etichette + + + + + part_list.action.projects.generate_label + Generare etichette (per componenti) + + + + + part_list.action.projects.generate_label_lot + Generare etichette (per le scorte di componenti) + + + + + part_list.action.generate_label.empty + Etichetta vuota + + + + + project.info.builds.label + Costruire + + + + + project.builds.build_not_possible + Impossibile costruire: non sono disponibili abbastanza componenti + + + + + project.builds.following_bom_entries_miss_instock + I seguenti componenti non sono sufficienti per costruire questo progetto almeno una volta: + + + + + project.builds.stocked + a magazzino + + + + + project.builds.needed + necessario + + + + + project.builds.build_possible + Costruzione possibile + + + + + project.builds.number_of_builds_possible + Ci sono abbastanza componenti in magazzino per costruire <b>%max_builds%</b> copie di questo progetto. + + + + + project.builds.check_project_status + Lo stato attuale del progetto è <b>"%project_status%"</b>. Controllare se si vuole davvero costruire il progetto con questo stato! + + + + + project.builds.following_bom_entries_miss_instock_n + Non ci sono abbastanza componenti disponibili per costruire questo progetto %number_of_builds% volte. In magazzino mancano i seguenti componenti: + + + + + project.build.flash.invalid_input + Il progetto non può essere costruito. Si prega di controllare le voci! + + + + + project.build.required_qty + Quantità richiesta + + + + + project.build.btn_build + Costruire + + + + + project.build.help + Seleziona da quali lotti devono essere prelevati i componenti necessari per la costruzione (e in quale quantità). Quando sarà completato il prelievo dei componenti, seleziona ogni voce BOM o usa la casella di controllo superiore per mettere tutti i segni di spunta in una volta sola. + + + + + project.build.buildsPartLot.new_lot + Creare un nuovo lotto + + + + + project.build.add_builds_to_builds_part + Add builds to project builds part + + + + + project.build.builds_part_lot + Lotto target + + + + + project.builds.number_of_builds + Quantità da costruire + + + + + project.builds.no_stocked_builds + Numero di build stock + + + + + user.change_avatar.label + Cambia immagine del profilo + + + + + user_settings.change_avatar.label + Cambia immagine del profilo + + + + + user_settings.remove_avatar.label + Rimuovi immagine del profilo + + + + + part.edit.name.category_hint + Suggerimento dalla categoria + + + + + category.edit.partname_regex.placeholder + es. "/Condensatore \d+ nF/i" + + + + + category.edit.partname_regex.help + Un'espressione regolare compatibile con PCRE che il nome del componente deve soddisfare. + + + + + entity.select.add_hint + Usare -> per creare strutture annidate, ad es. "Element 1->Element 1.1" + + + + + entity.select.group.new_not_added_to_DB + Nuovo (non ancora aggiunto al DB) + + + + + part.edit.save_and_new + Salvare e creare un nuovo componente + + + + + homepage.first_steps.title + Primi passi + + + + + homepage.first_steps.introduction + Il database è attualmente ancora vuoto. Leggere la <a href="%url%">documentazione</a> o iniziare a creare le seguenti strutture di dati. + + + + + homepage.first_steps.create_part + Oppure creare direttamente un <a href="%url%">nuovo componente</a>. + + + + + homepage.first_steps.hide_hint + Questo avviso sarà nascosto una volta creato il primo componente. + + + + + homepage.forum.text + Per domande su Part-DB utilizzare il <a href="%href%" class="link-external" target="_blank">discussion forum</a> + + + + + log.element_edited.changed_fields.category + Categoria + + + + + log.element_edited.changed_fields.footprint + Footprint + + + + + log.element_edited.changed_fields.manufacturer + Produttore + + + + + log.element_edited.changed_fields.value_typical + valore tip. + + + + + log.element_edited.changed_fields.pw_reset_expires + Reimpostazione password + + + + + log.element_edited.changed_fields.comment + Note + + + + + log.element_edited.changed_fields.supplierpartnr + Codice del fornitore + + + + + log.element_edited.changed_fields.supplier_product_url + URL del prodotto + + + + + log.element_edited.changed_fields.price + Prezzo + + + + + log.element_edited.changed_fields.min_discount_quantity + Quantità minima d'ordine + + + + + log.element_edited.changed_fields.original_filename + Nome file originale + + + + + log.element_edited.changed_fields.path + Percorso file + + + + + log.element_edited.changed_fields.description + Descrizione + + + + + log.element_edited.changed_fields.manufacturing_status + Stato di produzione + + + + + log.element_edited.changed_fields.options.barcode_type + Tipo di codice a barre + + + + + log.element_edited.changed_fields.status + Stato + + + + + log.element_edited.changed_fields.quantity + Quantità BOM + + + + + log.element_edited.changed_fields.mountnames + Nome dell'attrezzatura + + + + + log.element_edited.changed_fields.name + Nome + + + + + log.element_edited.changed_fields.part + Componente + + + + + log.element_edited.changed_fields.price_currency + Valuta del prezzo + + + + + log.element_edited.changed_fields.partname_hint + Suggerimento sul nome del componente + + + + + log.element_edited.changed_fields.partname_regex + Filtro nome + + + + + log.element_edited.changed_fields.disable_footprints + Disabilita footprint + + + + + log.element_edited.changed_fields.disable_manufacturers + Disabilita produttori + + + + + log.element_edited.changed_fields.disable_autodatasheets + Disattivare il datasheet automatico + + + + + log.element_edited.changed_fields.disable_properties + Disabilita le proprietà + + + + + log.element_edited.changed_fields.default_description + Descrizione standard + + + + + log.element_edited.changed_fields.default_comment + Commento standard + + + + + log.element_edited.changed_fields.filetype_filter + Estensioni di file consentite + + + + + log.element_edited.changed_fields.not_selectable + Non selezionato + + + + + log.element_edited.changed_fields.parent + Elemento padre di livello + + + + + log.element_edited.changed_fields.shipping_costs + Spese di spedizione + + + + + log.element_edited.changed_fields.default_currency + Valuta di default + + + + + log.element_edited.changed_fields.address + Indirizzo + + + + + log.element_edited.changed_fields.phone_number + Numero di telefono + + + + + log.element_edited.changed_fields.fax_number + Numero di fax + + + + + log.element_edited.changed_fields.email_address + Email + + + + + log.element_edited.changed_fields.website + Sito web + + + + + log.element_edited.changed_fields.auto_product_url + URL del prodotto + + + + + log.element_edited.changed_fields.is_full + Ubicazione piena + + + + + log.element_edited.changed_fields.limit_to_existing_parts + Solo componenti esistenti + + + + + log.element_edited.changed_fields.only_single_part + Solo componenti singoli + + + + + log.element_edited.changed_fields.storage_type + Tipo di ubicazione + + + + + log.element_edited.changed_fields.footprint_3d + Modello 3D + + + + + log.element_edited.changed_fields.master_picture_attachment + Immagine di anteprima + + + + + log.element_edited.changed_fields.exchange_rate + Tasso di cambio + + + + + log.element_edited.changed_fields.iso_code + Codice ISO + + + + + log.element_edited.changed_fields.unit + Simbolo dell'unità + + + + + log.element_edited.changed_fields.is_integer + Numero intero + + + + + log.element_edited.changed_fields.use_si_prefix + Usa i prefissi SI + + + + + log.element_edited.changed_fields.options.width + Larghezza + + + + + log.element_edited.changed_fields.options.height + Altezza + + + + + log.element_edited.changed_fields.options.supported_element + Tipo di elemento + + + + + log.element_edited.changed_fields.options.additional_css + Sile CSS aggiuntivo + + + + + log.element_edited.changed_fields.options.lines + Contenuto + + + + + log.element_edited.changed_fields.permissions.data + Autorizzazioni + + + + + log.element_edited.changed_fields.disabled + Disabilitato + + + + + log.element_edited.changed_fields.theme + Tema + + + + + log.element_edited.changed_fields.timezone + Fuso orario + + + + + log.element_edited.changed_fields.language + Lingua + + + + + log.element_edited.changed_fields.email + Email + + + + + log.element_edited.changed_fields.department + Dipartimento + + + + + log.element_edited.changed_fields.last_name + Cognome + + + + + log.element_edited.changed_fields.first_name + Nome + + + + + log.element_edited.changed_fields.group + Gruppo + + + + + log.element_edited.changed_fields.currency + Valuta preferita + + + + + log.element_edited.changed_fields.enforce2FA + Forza 2FA + + + + + log.element_edited.changed_fields.symbol + Simbolo + + + + + log.element_edited.changed_fields.value_min + Valore min. + + + + + log.element_edited.changed_fields.value_max + Valore max. + + + + + log.element_edited.changed_fields.value_text + Valore di testo + + + + + log.element_edited.changed_fields.show_in_table + Mostrare in tabella + + + + + log.element_edited.changed_fields.attachment_type + Tipo di file + + + + + log.element_edited.changed_fields.needs_review + Revisione necessaria + + + + + log.element_edited.changed_fields.tags + Tags + + + + + log.element_edited.changed_fields.mass + Massa + + + + + log.element_edited.changed_fields.ipn + IPN + + + + + log.element_edited.changed_fields.favorite + Preferito + + + + + log.element_edited.changed_fields.minamount + Scorta minima + + + + + log.element_edited.changed_fields.manufacturer_product_url + Link alla pagina del prodotto + + + + + log.element_edited.changed_fields.manufacturer_product_number + MPN + + + + + log.element_edited.changed_fields.partUnit + Unità di misura + + + + + log.element_edited.changed_fields.expiration_date + Data di scadenza + + + + + log.element_edited.changed_fields.amount + Quantità + + + + + log.element_edited.changed_fields.storage_location + Ubicazione + + + + + attachment.max_file_size + Dimensione massima del file + + + + + user.saml_user + Utente SSO / SAML + + + + + user.saml_user.pw_change_hint + Si sta usando Single Sign-On (SSO) per accedere. Pertanto non è possibile configurare la password e l'autenticazione a due fattori. Utilizzare gli strumenti del fornitore SSO per modificarle! + + + + + login.sso_saml_login + Single Sign-On Login (SSO) + + + + + login.local_login_hint + Il modulo qui sotto può essere utilizzato solo per accedere con un utente locale. Se invece si vuole accedere tramite Single Sign-On, usare il pulsante qui sopra. + + + + + part_list.action.action.export + Esportare componenti + + + + + part_list.action.export_json + Esportare come JSON + + + + + part_list.action.export_csv + Esportare come CSV + + + + + part_list.action.export_yaml + Esportare in YAML + + + + + part_list.action.export_xml + Esportare come XML + + + + + parts.import.title + Importare componenti + + + + + parts.import.errors.title + Problemi con l'importazione + + + + + parts.import.flash.error + Si sono verificati errori durante l'esportazione, probabilmente causati da dati errati. + + + + + parts.import.format.auto + Automatico (in base all'estensione del file) + + + + + parts.import.flash.error.unknown_format + Non è stato possibile determinare automaticamente il formato. Selezionare il formato corretto manualmente! + + + + + parts.import.flash.error.invalid_file + File errato o formattato in modo errato. Verificare di aver scelto il formato corretto. + + + + + parts.import.part_category.label + Sovrascrivere categoria + + + + + parts.import.part_category.help + Se si seleziona una categoria qui, tutti i componenti importati saranno assegnati a questa categoria, indipendentemente da ciò che è scritto nei dati da importare. + + + + + import.create_unknown_datastructures + Crea strutture di dati sconosciute + + + + + import.create_unknown_datastructures.help + Se questa opzione è selezionata, le strutture di dati (ad es. categorie, footprint, ecc.) che non esistono ancora nel database vengono create automaticamente. Se questa opzione non è selezionata, vengono utilizzate solo le strutture di dati che già esistono nel database, e se non esiste una struttura corrispondente, il campo corrispondente del componente viene lasciato vuoto. + + + + + import.path_delimiter + Limitatore di percorso + + + + + import.path_delimiter.help + Il delimitatore utilizzato per contrassegnare diversi livelli nella struttura dei dati come le categorie, i footprint, ecc. + + + + + parts.import.help_documentation + Consultare la <a href="%link%">Documentazione</a> per ulteriori informazioni sul formato del file. + + + + + parts.import.help + Con questo strumento è possibile importare componenti da file esistenti. I componenti sono memorizzati direttamente nel database (senza la possibilità di controllarli prima). Pertanto, controllate il vostro file di importazione prima di caricarlo qui! + + + + + parts.import.flash.success + Importazione di componenti avvenuta con successo! + + + + + parts.import.errors.imported_entities + Componenti importati + + + + + perm.import + Importare dati + + + + + parts.import.part_needs_review.label + Contrassegnare tutti i componenti come "Necessaria revisione" + + + + + parts.import.part_needs_review.help + Quando questa opzione è selezionata, tutti i componenti importati sono contrassegnati come "Necessaria revisione", indipendentemente da ciò che è scritto nei dati di importazione. + + + + + project.bom_import.flash.success + %Count% voci BOM importate con successo. + + + + + project.bom_import.type + Tipo + + + + + project.bom_import.type.kicad_pcbnew + KiCAD Pcbnew BOM (CSV file) + + + + + project.bom_import.clear_existing_bom + Cancellare le voci della BOM (lista dei materiali) esistenti prima dell'importazione + + + + + project.bom_import.clear_existing_bom.help + Quando questa opzione è selezionata, tutte le voci BOM (lista dei materiali) già esistenti nel progetto vengono cancellate e sovrascritte con i dati BOM importati. + + + + + project.bom_import.flash.invalid_file + Impossibile importare il file. Verificare di aver scelto il tipo di file corretto. Messaggio di errore: %message% + + + + + project.bom_import.flash.invalid_entries + Errore di convalida! Controllare il file importato! + + + + + project.import_bom + Importa BOM per il progetto + + + + + project.edit.bom.import_bom + Importare BOM + + + + + measurement_unit.new + Nuova unità di misura + + + + + measurement_unit.edit + Modificare l'unità di misura + + + + + user.aboutMe.label + About Me + + + + + storelocation.owner.label + Proprietario + + + + + storelocation.part_owner_must_match.label + Il proprietario del componente deve corrispondere al proprietario dell'ubicazione + + + + + part_lot.owner + Proprietario + + + + + part_lot.owner.help + Solo il proprietario può prelevare o aggiungere componenti da questo lotto. + + + + + log.element_edited.changed_fields.owner + Proprietario + + + + + log.element_edited.changed_fields.instock_unknown + Quantità sconosciuta + + + + + log.element_edited.changed_fields.needs_refill + Necessita di rifornimento + + + + + part.withdraw.access_denied + Non si è autorizzati a eseguire l'azione richiesta! Si prega di controllare le proprie autorizzazioni e il proprietario dello stock di componenti. + + + + + part.info.amount.less_than_desired + Meno di quanto desiderato + + + + + log.cli_user + Utente CLI + + + + + log.element_edited.changed_fields.part_owner_must_match + Il proprietario del componente deve corrispondere al proprietario dell'ubicazione! + + + + + part.filter.lessThanDesired + La giacenza è inferiore a quanto richiesto (quantità totale < quantità minima) + + + + + part.filter.lotOwner + Proprietario del lotto + + + + + user.show_email_on_profile.label + Visualizza l'indirizzo e-mail sulla pagina del profilo pubblico + + + + + log.details.title + Dettagli del log + + + + + log.user_login.login_from_ip + Login dall'indirizzo IP + + + + + log.user_login.ip_anonymize_hint + Se mancano le ultime cifre dell'indirizzo IP, allora è attivata la modalità GDPR, in cui gli indirizzi IP sono resi anonimi. + + + + + log.user_not_allowed.unauthorized_access_attempt_to + Tentativo di accesso non autorizzato alla pagina + + + + + log.user_not_allowed.hint + La richiesta è stata bloccata. Non dovrebbe essere necessaria alcuna altra azione. + + + + + log.no_comment + Nessun commento + + + + + log.element_changed.field + Campo + + + + + log.element_changed.data_before + Dati prima della modifica + + + + + error_table.error + Durante la richiesta si è verificato un errore. + + + + + part.table.invalid_regex + Espressione regolare non valida (regex) + + + + + log.element_changed.data_after + Dati dopo la modifica + + + + + log.element_changed.diff + Differenza + + + + + log.undo.undo.short + Annullare + + + + + log.undo.revert.short + Ripristina alla versione + + + + + log.view_version + Visualizza la versione + + + + + log.undo.undelete.short + Ripristinare + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + Proprietario + + + + + log.element_edited.changed_fields.parent_id + Elemento di livello superiore + + + + + log.details.delete_entry + Cancellare la voce di log + + + + + log.delete.message.title + Si vuole davvero cancellare questa voce di log? + + + + + log.delete.message + Se questa è una voce di storia per un elemento, allora la cancellazione porterà alla perdita di dati della storia! Questo può dare risultati inaspettati quando si usa la funzione di viaggio nel tempo. + + + + + log.collection_deleted.on_collection + in collezione + + + + + log.element_edited.changed_fields.attachments + Allegati + + + + + tfa_u2f.add_key.registration_error + Si è verificato un errore durante la registrazione della chiave di sicurezza. Riprovare o usare un'altra chiave! + + + + + log.target_type.none + Nessuno + + + + + ui.darkmode.light + Luminoso + + + + + ui.darkmode.dark + Scuro + + + + + ui.darkmode.auto + Auto (in base alle impostazioni di sistema) + + + + + label_generator.no_lines_given + Nessun contenuto di testo specificato! Le etichette generate saranno vuote. + + + + + user.password_strength.very_weak + Molto debole + + + + + user.password_strength.weak + Debole + + + + + user.password_strength.medium + Media + + + + + user.password_strength.strong + Forte + + + + + user.password_strength.very_strong + Molto forte + + + + + perm.users.impersonate + Impersonare altri utenti + + + + + user.impersonated_by.label + Come utente + + + + + user.stop_impersonation + Termina di impersonare + + + + + user.impersonate.btn + Impersonare + + + + + user.impersonate.confirm.title + Vuoi davvero fingere di essere questo utente? + + + + + user.impersonate.confirm.message + Questo utente è loggato. Dovrebbe essere fatto solo per un buon motivo. + +Notare che non è possibile impersonare un utente disattivato. Quando si prova a farlo, si riceverà un messaggio "Accesso negato". + + + + + log.type.security.user_impersonated + Utente impersonato + + + + + info_providers.providers_list.title + Fornitore di informazioni + + + + + info_providers.providers_list.active + Attivo + + + + + info_providers.providers_list.disabled + Disabilitato + + + + + info_providers.capabilities.basic + Di base + + + + + info_providers.capabilities.footprint + Footprint + + + + + info_providers.capabilities.picture + Immagini + + + + + info_providers.capabilities.datasheet + Datasheets + + + + + info_providers.capabilities.price + Prezzi + + + + + part.info_provider_reference.badge + Il fornitore di informazioni utilizzato per creare questo componente + + + + + part.info_provider_reference + Creato tramite il fornitore di informazioni + + + + + oauth_client.connect.btn + OAuth di connessione + + + + + info_providers.table.provider.label + Fornitore + + + + + info_providers.search.keyword + Parola chiave + + + + + info_providers.search.submit + Ricerca + + + + + info_providers.search.providers.help + Seleziona le fonti di informazioni in cui cercare. + + + + + info_providers.search.providers + Fornitori + + + + + info_providers.search.info_providers_list + Visualizza tutte le fonti di informazioni disponibili + + + + + info_providers.search.title + Creare un componente dalla fonte di informazioni + + + + + oauth_client.flash.connection_successful + Connessione all'applicazione OAuth stabilita con successo! + + + + + perm.part.info_providers + Fornitori di informazioni + + + + + perm.part.info_providers.create_parts + Creare componenti dalla fonte di informazioni + + + + + entity.edit.alternative_names.label + Nomi alternativi + + + + + entity.edit.alternative_names.help + I nomi alternativi qui forniti, sono utilizzati per selezionare automaticamente questo elemento in base ai dati restituiti dalle fonti di informazioni. + + + + + info_providers.form.help_prefix + Fornitore + + + + + update_manager.new_version_available.title + E' disponibile una nuova versione + + + + + update_manager.new_version_available.text + E' disponibile una nuova versione di Part-DB. Controlla qui. + + + + + update_manager.new_version_available.only_administrators_can_see + Solo gli amministratori possono vedere questo messaggio. + + + + + perm.system.show_available_updates + Visualizza gli aggiornamenti di Part-DB disponibili + + + + + user.settings.api_tokens + API Tokens + + + + + user.settings.api_tokens.description + Utilizzando un token API, altre applicazioni possono accedere a Part-DB con i diritti utente per eseguire varie azioni tramite l'API REST di Part-DB. Se si elimina un token API qui, l'applicazione che lo utilizza non sarà più in grado di accedere a Part-DB. + + + + + api_tokens.name + Nome + + + + + api_tokens.access_level + Livello di accesso + + + + + api_tokens.expiration_date + Data di scadenza + + + + + api_tokens.added_date + Aggiunto il + + + + + api_tokens.last_time_used + Ultimo utilizzo + + + + + datetime.never + Mai + + + + + api_token.valid + Valido + + + + + api_token.expired + Scaduto + + + + + user.settings.show_api_documentation + Mostra la documentazione delle API + + + + + api_token.create_new + Creare un nuovo API token + + + + + api_token.level.read_only + Sola lettura + + + + + api_token.level.edit + Modificare + + + + + api_token.level.admin + Admin + + + + + api_token.level.full + Completo + + + + + api_tokens.access_level.help + Si può limitare ciò a cui l'API token può accedere. L'accesso è sempre limitato dai permessi dell'utente. + + + + + api_tokens.expiration_date.help + Dopo questa data, il token non sarà più utilizzabile. Lasciare vuoto se il token non deve mai scadere. + + + + + api_tokens.your_token_is + Il tuo API token è + + + + + api_tokens.please_save_it + Per favore, conserva il token. Non sarà più possibile vederlo! + + + + + api_tokens.create_new.back_to_user_settings + Tornare alle impostazioni utente + + + + + project.build.dont_check_quantity + Non controllare le quantità + + + + + project.build.dont_check_quantity.help + Quando questa opzione è selezionata, le quantità selezionate vengono prelevate, indipendentemente dal fatto che ci siano più o meno componenti di quelli effettivamente necessari per costruire il progetto. + + + + + part_list.action.invert_selection + Invertire la selezione + + + + + perm.api + API + + + + + perm.api.access_api + Accesso API + + + + + perm.api.manage_tokens + Gestire gli API token + + + + + user.settings.api_tokens.delete.title + Si vuole davvero cancellare questo API token? + + + + + user.settings.api_tokens.delete + Cancellare + + + + + user.settings.api_tokens.delete.message + L'applicazione che utilizza questo API token non avrà più accesso a Part-DB. Questa azione non può essere annullata! + + + + + api_tokens.deleted + Il token API è stato cancellato! + + + + + user.settings.api_tokens.no_api_tokens_yet + Ancora nessun API token configurato. + + + + + api_token.ends_with + Finisce con + + + + + entity.select.creating_new_entities_not_allowed + Non è consentito creare nuove entità di questo tipo. Si prega di selezionarne una preesistente. + + + + + scan_dialog.mode + Tipo di codice a barre + + + + + scan_dialog.mode.auto + Rilevamento automatico + + + + + scan_dialog.mode.ipn + Codice a barre IPN + + + + + scan_dialog.mode.internal + Codice a barre Part-DB + + + + + part_association.label + Associazione di componenti + + + + + part.edit.tab.associations + Componenti associati + + + + + part_association.edit.other_part + Componente associato + + + + + part_association.edit.type + Tipo di relazione + + + + + part_association.edit.comment + Note + + + + + part_association.edit.type.help + Qui è possibile indicare come è correlato il componente scelto a questo componente. + + + + + part_association.table.from_this_part + Associazioni da questo componente ad altri + + + + + part_association.table.from + Da + + + + + part_association.table.type + Relazione + + + + + part_association.table.to + A + + + + + part_association.type.compatible + È compatibile con + + + + + part_association.table.to_this_part + Associazioni a questo componente da altri + + + + + part_association.type.other + Altro (valore personalizzato) + + + + + part_association.type.supersedes + Sostituisce + + + + + part_association.edit.other_type + Tipo personalizzato + + + + + part_association.edit.delete.confirm + Si vuole davvero eliminare questa associazione? Questa azione non può essere annullata. + + + + + part_lot.edit.advanced + Espandere le opzioni avanzate + + + + + part_lot.edit.vendor_barcode + Codice a barre del fornitore + + + + + part_lot.edit.vendor_barcode.help + Se il lotto ha già un codice a barre (ad esempio inserito dal fornitore), è possibile inserirne il contenuto qui per facilitare la scansione. + + + + + scan_dialog.mode.vendor + Codice a barre del fornitore (configurato nel lotto della parte) + + + + + project.bom.instockAmount + Quantità a magazzino + + + + + collection_type.new_element.tooltip + Questo elemento è stato creato di recente e non è stato ancora reso permanente nel database. + + + + + part.merge.title + Unire componente + + + + + part.merge.title.into + in + + + + + part.merge.confirm.title + Si vuole davvero unire <b>%other%</b> in <b>%target%</b>? + + + + + part.merge.confirm.message + <b>%other%</b> verrà eliminato, e il componente verrà salvato con le informazioni mostrate. + + + + + part.info.merge_modal.title + Unire componenti + + + + + part.info.merge_modal.other_part + Altro componente + + + + + part.info.merge_modal.other_into_this + Unire l'altro componente in questo (elimina l'altro componente e mantiene questo) + + + + + part.info.merge_modal.this_into_other + Unire questo componente in un altro (elimina questo componente e mantiene l'altro) + + + + + part.info.merge_btn + Unire componente + + + + + part.update_part_from_info_provider.btn + Aggiornare componente dalle fonti d'informazioni + + + + + info_providers.update_part.title + Aggiornare componente esistente dalle fonti d'informazioni + + + + + part.merge.flash.please_review + Dati non ancora salvati. Rivedere le modifiche e fare clic su Salva per rendere persistenti i nuovi dati. + + + + + user.edit.flash.permissions_fixed + Mancavano le autorizzazioni richieste da altre autorizzazioni. Questo è stato corretto. Controllare se le autorizzazioni sono quelle previste. + + + + + permission.legend.dependency_note + Si prega di notare che alcune operazioni di autorizzazione dipendono l'una dall'altra. Se si riceve un avviso che le autorizzazioni mancanti sono state corrette e un'autorizzazione è stata impostata nuovamente su "consenti", è necessario impostare anche l'operazione dipendente su "proibisci". Le dipendenze sono normalmente indicate a destra di un'operazione. + + + + + log.part_stock_changed.timestamp + Timestamp + + + + + part.info.withdraw_modal.timestamp + Timestamp dell'azione + + + + + part.info.withdraw_modal.timestamp.hint + Questo campo consente di specificare la data effettiva in cui l'operazione di magazzino è stata effettivamente eseguita, e non solo quando è stata registrata. Questo valore viene salvato nel campo extra dell'entry di registro. + + + + + part.info.withdraw_modal.delete_lot_if_empty + Eliminare questo lotto se diventa vuoto + + + + + info_providers.search.error.client_exception + Si è verificato un errore durante la comunicazione con la fonte di informazioni. Verificare la configurazione per questa fonte e, se possibile, aggiornare i token OAuth. + + + + + eda_info.reference_prefix.placeholder + e.s. R + + + + + eda_info.reference_prefix + Prefisso di riferimento + + + + + eda_info.kicad_section.title + Impostazioni specifiche di KiCad + + + + + eda_info.value + Valore + + + + + eda_info.value.placeholder + e.s. 100n + + + + + eda_info.exclude_from_bom + Escludere componente dalla BOM + + + + + eda_info.exclude_from_board + Escludere componente dalla PCB/Board + + + + + eda_info.exclude_from_sim + Escludere componente dalla simulazione + + + + + eda_info.kicad_symbol + Simbolo schematico di KiCad + + + + + eda_info.kicad_symbol.placeholder + e.s. Transistor_BJT:BC547 + + + + + eda_info.kicad_footprint + Footprint di KiCad + + + + + eda_info.kicad_footprint.placeholder + e.s. Package_TO_SOT_THT:TO-92 + + + + + part.edit.tab.eda + Informazioni EDA + + + + + api.api_endpoints.title + Punti di accesso dell'API + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + URL radice dell'API di KiCad + + + + + eda_info.visibility + Rendere visibile + + + + + eda_info.visibility.help + Per impostazione predefinita, la visibilità del software EDA viene determinata automaticamente. Con questa casella di controllo, si può forzare il componente ad essere visibile o invisibile. + + + + + part.withdraw.zero_amount + Si è provato a prelevare/aggiungere una quantità pari a zero! Non è stata eseguita alcuna azione. + + + + + login.flash.access_denied_please_login + Accesso negato! Si prega di effettuare l'accesso per continuare. + + + + + attachment.upload_multiple_files + Carica file + + + + + entity.mass_creation_flash + %COUNT% elementi creati correttamente. + + + + + info_providers.search.number_of_results + %number% risultati + + + + + info_providers.search.no_results + Nessun risultato trovato per i provider selezionati! Controlla il termine di ricerca o prova a scegliere provider aggiuntivi. + + + + + tfa.check.code.confirmation + Codice generato + + + + + info_providers.search.show_existing_part + Mostra i componenti esistenti + + + + + info_providers.search.edit_existing_part + Modifica i componenti esistenti + + + + + info_providers.search.existing_part_found.short + Componenti già presenti + + + + + info_providers.search.existing_part_found + Questo componente (o uno molto simile) è già stato trovato nel database. Controlla se è lo stesso e se vuoi crearlo di nuovo! + + + + + info_providers.search.update_existing_part + Aggiorna il componente esistente con le informazioni del provider + + + + + part.create_from_info_provider.no_category_yet + La categoria non può essere determinata automaticamente dal provider di informazioni. Verifica i dati e seleziona manualmente la categoria. + + + + + part_lot.edit.user_barcode + Codice a barre utente + + + + + scan_dialog.mode.user + Codice a barre definito dall'utente (configurato sul lotto del componente) + + + + + scan_dialog.mode.eigp + Codice a barre EIGP 114 (ad esempio, i codici datamatrix sugli ordini di Digikey e Mouser) + + + + + scan_dialog.info_mode + Modalità "info" (Decodifica il codice a barre e mostra il suo contenuto, senza reindirizzare alla parte) + + + + + label_scanner.decoded_info.title + Informazioni decodificate + + + + + label_generator.edit_profiles + Modifica profili + + + + + label_generator.profile_name_empty + Il nome del profilo non può essere vuoto + + + + + label_generator.save_profile_name + Nome profilo + + + + + label_generator.save_profile + Salva come nuovo profilo + + + + + label_generator.profile_saved + Profilo salvato! + + + + + entity.export.flash.error.no_entities + Non ci sono entità da esportare! + + + + + attachment.table.internal_file + File interno + + + + + attachment.table.external_link + Link esterno + + + + + attachment.view_external.view_at + Visualizza da %host% + + + + + attachment.view_external + Visualizza la versione esterna + + + + + part.table.actions.error + Si sono verificati %count% errori durante l'esecuzione dell'azione: + + + + + part.table.actions.error_detail + %part_name% (ID: %part_id%): %message% + + + + + part_list.action.action.change_location + Cambia posizione (solo per componenti con stock singolo) + + + + + parts.table.action_handler.error.part_lots_multiple + Questo componente contiene più di uno stock. Cambia manualmente la posizione per selezionare quale stock scegliere. + + + + diff --git a/translations/messages.ja.xlf b/translations/messages.ja.xlf index c6e199da..4becc319 100644 --- a/translations/messages.ja.xlf +++ b/translations/messages.ja.xlf @@ -1455,28 +1455,6 @@ フォーラム - - - Part-DB1\templates\homepage.html.twig:36 - Part-DB1\templates\homepage.html.twig:36 - templates\homepage.html.twig:33 - - - homepage.basedOn - オリジナルのPart-DBの作者 - - - - - Part-DB1\templates\homepage.html.twig:39 - Part-DB1\templates\homepage.html.twig:39 - templates\homepage.html.twig:36 - - - homepage.others - - - Part-DB1\templates\homepage.html.twig:45 @@ -6334,52 +6312,6 @@ サーバー全体のテーマ - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - M - M - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - k - k - - - - - - - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - m - m - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - µ - µ - - Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 @@ -6607,16 +6539,6 @@ 要素は自身の子とすることはできません。 - - - obsolete - obsolete - - - validator.isSelectable - 要素は選択可能である必要があります。 - - obsolete @@ -7472,17 +7394,6 @@ Exampletown 変更を破棄 - - - templates\Parts\show_part_info.html.twig:161 - obsolete - obsolete - - - part.withdraw.caption: - 部品を撤回する - - templates\Parts\show_part_info.html.twig:166 @@ -7593,26 +7504,6 @@ Exampletown 例: 5 - - - obsolete - obsolete - - - homepage.basedOn - オリジナルのPart-DBの作者: - - - - - obsolete - obsolete - - - homepage.others - - - obsolete @@ -8709,15 +8600,6 @@ Exampletown 要素の以前のバージョンを表示する (タイムトラベル) - - - obsolete - - - log.type. - __log.type. - - obsolete diff --git a/translations/messages.nl.xlf b/translations/messages.nl.xlf new file mode 100644 index 00000000..760533d7 --- /dev/null +++ b/translations/messages.nl.xlf @@ -0,0 +1,728 @@ + + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + + + attachment_type.caption + Bijlage bestandstypen + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 + new + + + attachment_type.edit + Bewerk bestandstype + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 + new + + + attachment_type.new + Nieuw bestandstype + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + templates\AdminPages\CategoryAdmin.html.twig:4 + templates\base.html.twig:163 + templates\base.html.twig:170 + templates\base.html.twig:197 + templates\base.html.twig:225 + + + category.labelp + Categorieën + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:11 + templates\AdminPages\CategoryAdmin.html.twig:8 + + + admin.options + Opties + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + templates\AdminPages\CategoryAdmin.html.twig:9 + + + admin.advanced + Geavanceerd + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 + new + + + category.edit + Categorie bewerken + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 + new + + + category.new + Nieuwe categorie + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + + + currency.caption + Valuta + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + + + currency.iso_code.caption + ISO-code + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + + + currency.symbol.caption + Valutateken + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 + new + + + currency.edit + Valuta bewerken + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 + new + + + currency.new + Nieuwe valuta + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + templates\AdminPages\DeviceAdmin.html.twig:4 + + + project.caption + Project + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 + new + + + project.edit + Project bewerken + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 + new + + + project.new + Nieuw project + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:67 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + templates\AdminPages\EntityAdminBase.html.twig:9 + templates\base.html.twig:80 + templates\base.html.twig:179 + templates\base.html.twig:206 + templates\base.html.twig:237 + + + search.placeholder + Zoeken + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + templates\AdminPages\EntityAdminBase.html.twig:13 + templates\base.html.twig:166 + templates\base.html.twig:193 + templates\base.html.twig:221 + + + expandAll + Alles uitvouwen + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + templates\AdminPages\EntityAdminBase.html.twig:17 + templates\base.html.twig:167 + templates\base.html.twig:194 + templates\base.html.twig:222 + + + reduceAll + Alles inklappen + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + + + part.info.timetravel_hint + Dit is hoe het component er uitzag voor %timestamp%. <i>Let op: deze feature is nog experimenteel, de getoonde informatie kan onnauwkeurig zijn.</i> + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + templates\AdminPages\EntityAdminBase.html.twig:42 + + + standard.label + Eigenschappen + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + templates\AdminPages\EntityAdminBase.html.twig:43 + + + infos.label + Informatie + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + new + + + history.label + Geschiedenis + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + templates\AdminPages\EntityAdminBase.html.twig:45 + + + export.label + Exporteren + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + templates\AdminPages\EntityAdminBase.html.twig:47 + + + import_export.label + Importeren/Exporteren + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + + + mass_creation.label + Bulk toevoegen + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + templates\AdminPages\EntityAdminBase.html.twig:59 + + + admin.common + Algemeen + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + + + admin.attachments + Bijlagen + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 + + + admin.parameters + Parameters + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 + templates\AdminPages\EntityAdminBase.html.twig:142 + + + export_all.label + Exporteer alle elementen + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 + + + mass_creation.help + Elke regel wordt geïnterpreteerd als de naam van een element, dat aangemaakt zal worden. Je kunt geneste structuren maken d.m.v. indentatie. + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + templates\AdminPages\EntityAdminBase.html.twig:35 + + + edit.caption + Bewerk element "%name" + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + templates\AdminPages\EntityAdminBase.html.twig:37 + + + new.caption + Nieuw element + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + templates\base.html.twig:172 + templates\base.html.twig:199 + templates\base.html.twig:227 + + + footprint.labelp + Voetafdruk + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 + new + + + footprint.edit + Voetafdruk bewerken + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 + new + + + footprint.new + Nieuwe voetafdruk + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + + + group.edit.caption + Groepen + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + + + user.edit.permissions + Rechten + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 + new + + + group.edit + Groep bewerken + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 + new + + + group.new + Nieuwe groep + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 + + + label_profile.caption + Label profiel + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 + + + label_profile.advanced + Geavanceerd + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 + + + label_profile.comment + Notities + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 + new + + + label_profile.edit + Bewerk label profiel + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 + new + + + label_profile.new + Nieuw label profiel + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + templates\AdminPages\ManufacturerAdmin.html.twig:4 + + + manufacturer.caption + Fabrikanten + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 + new + + + manufacturer.edit + Bewerk fabrikant + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 + new + + + manufacturer.new + Nieuwe fabrikant + + + + + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + + + measurement_unit.caption + Meeteenheden + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 + Part-DB1\templates\_sidebar.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:8 + templates\base.html.twig:171 + templates\base.html.twig:198 + templates\base.html.twig:226 + + + storelocation.labelp + Opslag locaties + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 + new + + + storelocation.edit + Bewerk opslag locatie + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 + new + + + storelocation.new + Nieuwe opslag locatie + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + templates\AdminPages\SupplierAdmin.html.twig:4 + + + supplier.caption + Leveranciers + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 + new + + + supplier.edit + Bewerk leverancier + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 + new + + + supplier.new + Nieuwe leverancier + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + + + user.edit.caption + Gebruikers + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + + + user.edit.configuration + Instellingen + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + + + user.edit.password + Wachtwoord + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + + + user.edit.tfa.caption + Tweefactorauthenticatie + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + + + user.edit.tfa.google_active + Tweefactorauthenticatie-app actief + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + + + tfa_backup.remaining_tokens + Aantal resterende back-up codes + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + + + tfa_backup.generation_date + Datum waarop de back-up codes gegenereerd zijn + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + + + user.edit.tfa.disabled + Methode niet geactiveerd + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + + + user.edit.tfa.u2f_keys_count + Actieve beveiligingssleutels + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_title + Weet u zeker dat u wilt doorgaan? + + + + diff --git a/translations/messages.pl.xlf b/translations/messages.pl.xlf new file mode 100644 index 00000000..5b038410 --- /dev/null +++ b/translations/messages.pl.xlf @@ -0,0 +1,12227 @@ + + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + + + attachment_type.caption + Typy plików dla załącznikówRegPrzerwanie w przypadku nieprawidłowych danychport + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 + new + + + attachment_type.edit + Edytuj typ pliku + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 + new + + + attachment_type.new + Nowy typ pliku + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + templates\AdminPages\CategoryAdmin.html.twig:4 + templates\base.html.twig:163 + templates\base.html.twig:170 + templates\base.html.twig:197 + templates\base.html.twig:225 + + + category.labelp + Kategorie + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:11 + templates\AdminPages\CategoryAdmin.html.twig:8 + + + admin.options + Opcje + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + templates\AdminPages\CategoryAdmin.html.twig:9 + + + admin.advanced + Zaawansowane + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 + new + + + category.edit + Edycja kategorii + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 + new + + + category.new + Nowa kategoria + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + + + currency.caption + Waluta + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + + + currency.iso_code.caption + Kod ISO waluty + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + + + currency.symbol.caption + Symbol waluty + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 + new + + + currency.edit + Edycja waluty + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 + new + + + currency.new + Nowa waluta + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + templates\AdminPages\DeviceAdmin.html.twig:4 + + + project.caption + Projekt + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 + new + + + project.edit + Edycja projektu + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 + new + + + project.new + Nowy projekt + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:67 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + templates\AdminPages\EntityAdminBase.html.twig:9 + templates\base.html.twig:80 + templates\base.html.twig:179 + templates\base.html.twig:206 + templates\base.html.twig:237 + + + search.placeholder + Szukaj + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + templates\AdminPages\EntityAdminBase.html.twig:13 + templates\base.html.twig:166 + templates\base.html.twig:193 + templates\base.html.twig:221 + + + expandAll + Rozwiń wszystko + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + templates\AdminPages\EntityAdminBase.html.twig:17 + templates\base.html.twig:167 + templates\base.html.twig:194 + templates\base.html.twig:222 + + + reduceAll + Zwiń wszystko + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + + + part.info.timetravel_hint + Tak wyglądał komponent przed %timestamp%. <i>Należy pamiętać, że ta funkcja jest eksperymentalna, a wyświetlane informacje niekoniecznie są poprawne.</i>or + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + templates\AdminPages\EntityAdminBase.html.twig:42 + + + standard.label + Właściwości + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + templates\AdminPages\EntityAdminBase.html.twig:43 + + + infos.label + Informacje + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + new + + + history.label + Historia + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + templates\AdminPages\EntityAdminBase.html.twig:45 + + + export.label + Eksport + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + templates\AdminPages\EntityAdminBase.html.twig:47 + + + import_export.label + Importuj / Eksportuj + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + + + mass_creation.label + Masowe tworzenie + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + templates\AdminPages\EntityAdminBase.html.twig:59 + + + admin.common + Ogólne + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + + + admin.attachments + Załączniki + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 + + + admin.parameters + Parametry + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 + templates\AdminPages\EntityAdminBase.html.twig:142 + + + export_all.label + Eksportuj wszystkie elementy + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 + + + mass_creation.help + Każda linia będzie interpretowana jako nazwa elementu, który zostanie utworzony. Struktury zagnieżdżone można tworzyć poprzez wcięcia. + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + templates\AdminPages\EntityAdminBase.html.twig:35 + + + edit.caption + Edytuj element + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + templates\AdminPages\EntityAdminBase.html.twig:37 + + + new.caption + Nowy element + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + templates\base.html.twig:172 + templates\base.html.twig:199 + templates\base.html.twig:227 + + + footprint.labelp + Pola lutownicze + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 + new + + + footprint.edit + Edytuj układ padów (footprint) + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 + new + + + footprint.new + Utwórz układ padów (footprint) + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + + + group.edit.caption + Grupy + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + + + user.edit.permissions + Uprawnienia + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 + new + + + group.edit + Edytuj grupę + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 + new + + + group.new + Nowa grupa + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 + + + label_profile.caption + Profile etykiet + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 + + + label_profile.advanced + Zaawansowane + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 + + + label_profile.comment + Komentarz + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 + new + + + label_profile.edit + Edytuj profil etykiety + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 + new + + + label_profile.new + Nowy profil etykiety + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + templates\AdminPages\ManufacturerAdmin.html.twig:4 + + + manufacturer.caption + Producenci + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 + new + + + manufacturer.edit + Edytuj producenta + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 + new + + + manufacturer.new + Nowy producent + + + + + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + + + measurement_unit.caption + Jednostka miary + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 + Part-DB1\templates\_sidebar.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:8 + templates\base.html.twig:171 + templates\base.html.twig:198 + templates\base.html.twig:226 + + + storelocation.labelp + Lokalizacja miejsca przechowywania + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 + new + + + storelocation.edit + Edytuj lokalizację miejsca przechowywania + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 + new + + + storelocation.new + Nowa lokalizacja miejsca przechowywania + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + templates\AdminPages\SupplierAdmin.html.twig:4 + + + supplier.caption + Dostawcy + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 + new + + + supplier.edit + Edytuj dostawcę + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 + new + + + supplier.new + Nowy dostawca + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + + + user.edit.caption + Użytkownicy + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + + + user.edit.configuration + Konfiguracja + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + + + user.edit.password + Hasło + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + + + user.edit.tfa.caption + Uwierzytelnianie dwuskładnikowe + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + + + user.edit.tfa.google_active + Aplikacja uwierzytelniająca aktywna + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + + + tfa_backup.remaining_tokens + Pozostałe kody zapasowe + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + + + tfa_backup.generation_date + Data utworzenia kodów zapasowych + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + + + user.edit.tfa.disabled + Metoda wyłączona + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + + + user.edit.tfa.u2f_keys_count + Aktywne klucze bezpieczeństwa + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_title + Czy na pewno chcesz kontynuować? + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_message + Dostęp zabroniony! Zaloguj się, aby kontynuować. + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + + + user.edit.tfa.disable_tfa.btn + Wyłącz wszystkie metody uwierzytelniania dwuskładnikowego + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 + new + + + user.edit + Edytuj użytkownika + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 + new + + + user.new + Nowy użytkownik + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:63 + + + attachment.delete + Usuń + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:41 + Part-DB1\templates\Parts\edit\_attachments.html.twig:38 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:35 + Part-DB1\src\DataTables\AttachmentDataTable.php:159 + Part-DB1\templates\Parts\edit\_attachments.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:159 + + + attachment.external + Zewnętrzny + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:49 + Part-DB1\templates\Parts\edit\_attachments.html.twig:47 + Part-DB1\templates\AdminPages\_attachments.html.twig:47 + Part-DB1\templates\Parts\edit\_attachments.html.twig:45 + + + attachment.preview.alt + Miniaturka załącznika + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:52 + Part-DB1\templates\Parts\edit\_attachments.html.twig:50 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + Part-DB1\templates\AdminPages\_attachments.html.twig:50 + Part-DB1\templates\Parts\edit\_attachments.html.twig:48 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:45 + + + attachment.view + Widok + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:58 + Part-DB1\templates\Parts\edit\_attachments.html.twig:56 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:43 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + Part-DB1\templates\AdminPages\_attachments.html.twig:56 + Part-DB1\templates\Parts\edit\_attachments.html.twig:54 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + + + attachment.file_not_found + Plik nie znaleziony + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:66 + Part-DB1\templates\Parts\edit\_attachments.html.twig:64 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:48 + Part-DB1\templates\Parts\edit\_attachments.html.twig:62 + + + attachment.secure + Załącznik prywatny + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:79 + Part-DB1\templates\Parts\edit\_attachments.html.twig:77 + Part-DB1\templates\AdminPages\_attachments.html.twig:77 + Part-DB1\templates\Parts\edit\_attachments.html.twig:75 + + + attachment.create + Utwórz załącznik + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:84 + Part-DB1\templates\Parts\edit\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + Part-DB1\templates\AdminPages\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_attachments.html.twig:80 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + + + part_lot.edit.delete.confirm + Czy na pewno chcesz usunąć magazyn? Tej operacji nie można cofnąć. + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + templates\AdminPages\_delete_form.html.twig:2 + + + entity.delete.confirm_title + Czy na pewno chcesz usunąć %name%? + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + templates\AdminPages\_delete_form.html.twig:3 + + + entity.delete.message + Tego działania nie można cofnąć! + +Po usunięciu pod elementy zostaną przeniesione na górę. + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + templates\AdminPages\_delete_form.html.twig:9 + + + entity.delete + Usuń element + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:45 + Part-DB1\src\Form\Part\PartBaseType.php:286 + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:43 + Part-DB1\src\Form\Part\PartBaseType.php:267 + new + + + edit.log_comment + Edycja komentarza + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + templates\AdminPages\_delete_form.html.twig:12 + + + entity.delete.recursive + Usuń rekursywnie (wszystkie elementy podrzędne) + + + + + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 + + + entity.duplicate + Duplikat + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + templates\AdminPages\_export_form.html.twig:4 + src\Form\ImportType.php:67 + + + export.format + Typ pliku + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + templates\AdminPages\_export_form.html.twig:16 + + + export.level + Szczegółowość + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + templates\AdminPages\_export_form.html.twig:19 + + + export.level.simple + Podstawowa + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + templates\AdminPages\_export_form.html.twig:20 + + + export.level.extended + Rozszerzona + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + templates\AdminPages\_export_form.html.twig:21 + + + export.level.full + Pełna + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + templates\AdminPages\_export_form.html.twig:31 + + + export.include_children + Eksportuj również podelementy + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + templates\AdminPages\_export_form.html.twig:39 + + + export.btn + Eksport + + + + + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + templates\AdminPages\EntityAdminBase.html.twig:94 + templates\Parts\edit_part_info.html.twig:12 + templates\Parts\show_part_info.html.twig:11 + + + id.label + ID + + + + + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:77 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:77 + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:59 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:60 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:53 + templates\AdminPages\EntityAdminBase.html.twig:101 + templates\Parts\show_part_info.html.twig:248 + + + createdAt + Utworzony o + + + + + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:73 + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:49 + templates\AdminPages\EntityAdminBase.html.twig:114 + templates\Parts\show_part_info.html.twig:263 + + + lastModified + Ostatnio zmodyfikowany + + + + + Part-DB1\templates\AdminPages\_info.html.twig:38 + Part-DB1\templates\AdminPages\_info.html.twig:38 + + + entity.info.parts_count + Liczba komponentów z tym elementem + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:6 + Part-DB1\templates\helper.twig:125 + Part-DB1\templates\Parts\edit\_specifications.html.twig:6 + + + specifications.property + Parametr + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:7 + Part-DB1\templates\Parts\edit\_specifications.html.twig:7 + + + specifications.symbol + Symbol + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:8 + Part-DB1\templates\Parts\edit\_specifications.html.twig:8 + + + specifications.value_min + Wartość minimalna + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:9 + Part-DB1\templates\Parts\edit\_specifications.html.twig:9 + + + specifications.value_typ + Wartość nominalna + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:10 + Part-DB1\templates\Parts\edit\_specifications.html.twig:10 + + + specifications.value_max + Wartość maksymalna + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:11 + Part-DB1\templates\Parts\edit\_specifications.html.twig:11 + + + specifications.unit + Jednostka + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:12 + Part-DB1\templates\Parts\edit\_specifications.html.twig:12 + + + specifications.text + Tekst + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:13 + Part-DB1\templates\Parts\edit\_specifications.html.twig:13 + + + specifications.group + Grupa + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:26 + Part-DB1\templates\Parts\edit\_specifications.html.twig:26 + + + specification.create + Nowy parametr + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:31 + Part-DB1\templates\Parts\edit\_specifications.html.twig:31 + + + parameter.delete.confirm + Czy na pewno chcesz usunąć ten parametr? + + + + + Part-DB1\templates\attachment_list.html.twig:3 + Part-DB1\templates\attachment_list.html.twig:3 + + + attachment.list.title + Lista załączników + + + + + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + + + part_list.loading.caption + Ładowanie + + + + + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + + + part_list.loading.message + To może chwilę potrwać. Jeśli ten komunikat nie zniknie, spróbuj ponownie załadować stronę. + + + + + Part-DB1\templates\base.html.twig:68 + Part-DB1\templates\base.html.twig:68 + templates\base.html.twig:246 + + + vendor.base.javascript_hint + Aktywuj JavaScript, aby móc korzystać ze wszystkich funkcji! + + + + + Part-DB1\templates\base.html.twig:73 + Part-DB1\templates\base.html.twig:73 + + + sidebar.big.toggle + Pokaż/ukryj pasek boczny + + + + + Part-DB1\templates\base.html.twig:95 + Part-DB1\templates\base.html.twig:95 + templates\base.html.twig:271 + + + loading.caption + Ładowanie: + + + + + Part-DB1\templates\base.html.twig:96 + Part-DB1\templates\base.html.twig:96 + templates\base.html.twig:272 + + + loading.message + To może chwilę potrwać. Jeśli ten komunikat będzie się wyświetlał przez dłuższy czas, spróbuj ponownie załadować stronę. + + + + + Part-DB1\templates\base.html.twig:101 + Part-DB1\templates\base.html.twig:101 + templates\base.html.twig:277 + + + loading.bar + Ładowanie... + + + + + Part-DB1\templates\base.html.twig:112 + Part-DB1\templates\base.html.twig:112 + templates\base.html.twig:288 + + + back_to_top + Powrót na górę strony + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:35 + Part-DB1\templates\Form\permissionLayout.html.twig:35 + + + permission.edit.permission + Uprawnienia + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:36 + Part-DB1\templates\Form\permissionLayout.html.twig:36 + + + permission.edit.value + Wartość + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:53 + Part-DB1\templates\Form\permissionLayout.html.twig:53 + + + permission.legend.title + Wyjaśnienie stanów + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:57 + Part-DB1\templates\Form\permissionLayout.html.twig:57 + + + permission.legend.disallow + Nie zezwalaj + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:61 + Part-DB1\templates\Form\permissionLayout.html.twig:61 + + + permission.legend.allow + Zezwalaj + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:65 + Part-DB1\templates\Form\permissionLayout.html.twig:65 + + + permission.legend.inherit + Dziedziczenie z grupy (nadrzędnej) + + + + + Part-DB1\templates\helper.twig:3 + Part-DB1\templates\helper.twig:3 + + + bool.true + Prawda + + + + + Part-DB1\templates\helper.twig:5 + Part-DB1\templates\helper.twig:5 + + + bool.false + Fałsz + + + + + Part-DB1\templates\helper.twig:92 + Part-DB1\templates\helper.twig:87 + + + Yes + Tak + + + + + Part-DB1\templates\helper.twig:94 + Part-DB1\templates\helper.twig:89 + + + No + Nie + + + + + Part-DB1\templates\helper.twig:126 + + + specifications.value + Wartość + + + + + Part-DB1\templates\homepage.html.twig:7 + Part-DB1\templates\homepage.html.twig:7 + templates\homepage.html.twig:7 + + + version.caption + Wersja + + + + + Part-DB1\templates\homepage.html.twig:22 + Part-DB1\templates\homepage.html.twig:22 + templates\homepage.html.twig:19 + + + homepage.license + Licencja + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.caption + Strona projektu + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.text + Kod źródłowy, pliki do pobrania, raporty o błędach, listę rzeczy do zrobienia itp. można znaleźć na stronie projektu <a class=„link-external” target=„_blank” href=„%href%”>GitHub</a> + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.caption + Pomoc + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.text + Pomoc i wskazówki można znaleźć w Wiki na stronie <a href=„%href%” class=„link-external” target=„_blank”>GitHub</a>. + + + + + Part-DB1\templates\homepage.html.twig:33 + Part-DB1\templates\homepage.html.twig:33 + templates\homepage.html.twig:30 + + + homepage.forum.caption + Forum + + + + + Part-DB1\templates\homepage.html.twig:45 + Part-DB1\templates\homepage.html.twig:45 + new + + + homepage.last_activity + Ostatnia aktywność + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:3 + Part-DB1\templates\LabelSystem\dialog.html.twig:6 + + + label_generator.title + Generator etykiet + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:16 + + + label_generator.common + Ogólne + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:20 + + + label_generator.advanced + Zaawansowane + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:24 + + + label_generator.profiles + Profile + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:58 + + + label_generator.selected_profile + Aktualnie wybrany profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:62 + + + label_generator.edit_profile + Edytuj profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:75 + + + label_generator.load_profile + Załaduj profil + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:102 + + + label_generator.download + Pobierz + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 + + + label_generator.label_btn + Wygeneruj etykietę + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 + + + label_generator.label_empty + Utwórz pustą etykietę + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 + + + label_scanner.title + Skaner etykiet + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.title + Nie znaleziono kamery + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.text + Potrzebujesz kamery internetowej oraz wyrazić zgodę na korzystanie z funkcji skanera. Poniżej możesz ręcznie wprowadzić kod kreskowy. + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 + + + label_scanner.source_select + Wybierz źródło + + + + + Part-DB1\templates\LogSystem\log_list.html.twig:3 + Part-DB1\templates\LogSystem\log_list.html.twig:3 + + + log.list.title + Dziennik systemowy + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + new + + + log.undo.confirm_title + Czy naprawdę cofnąć zmiany/powrócić do sygnatury czasowej? + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + new + + + log.undo.confirm_message + Czy na pewno chcesz cofnąć daną zmianę/zresetować element do podanego znacznika czasu? + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.email_sent_by + Ten e-mail został wysłany automatycznie przez + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.dont_reply + Nie odpowiadaj na tego e-maila. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:6 + Part-DB1\templates\mail\pw_reset.html.twig:6 + + + email.hi %name% + Cześć %name% + + + + + Part-DB1\templates\mail\pw_reset.html.twig:7 + Part-DB1\templates\mail\pw_reset.html.twig:7 + + + email.pw_reset.message + ktoś (miejmy nadzieję, że Ty) poprosił o zresetowanie Twojego hasła. Jeżeli to nie Ty wysłałeś tę prośbę, zignoruj tę wiadomość. + + + + + Part-DB1\templates\mail\pw_reset.html.twig:9 + Part-DB1\templates\mail\pw_reset.html.twig:9 + + + email.pw_reset.button + Kliknij tutaj, aby zresetować hasło + + + + + Part-DB1\templates\mail\pw_reset.html.twig:11 + Part-DB1\templates\mail\pw_reset.html.twig:11 + + + email.pw_reset.fallback + Jeśli to nie zadziała, zadzwoń pod numer <a href="%url%">%url%</a> i wprowadź następujące informacje + + + + + Part-DB1\templates\mail\pw_reset.html.twig:16 + Part-DB1\templates\mail\pw_reset.html.twig:16 + + + email.pw_reset.username + Nazwa użytkownika + + + + + Part-DB1\templates\mail\pw_reset.html.twig:19 + Part-DB1\templates\mail\pw_reset.html.twig:19 + + + email.pw_reset.token + Token + + + + + Part-DB1\templates\mail\pw_reset.html.twig:24 + Part-DB1\templates\mail\pw_reset.html.twig:24 + + + email.pw_reset.valid_unit %date% + Token resetowania jest ważny do <i>%date%</i> + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:78 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + + + orderdetail.delete + Usuń + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + + + pricedetails.edit.min_qty + Minimalna ilość zamówienia + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + + + pricedetails.edit.price + Cena + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + + + pricedetails.edit.price_qty + dla ilości + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + + + pricedetail.create + Utwórz cenę + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + templates\Parts\edit_part_info.html.twig:4 + + + part.edit.title + Edytuj komponent %name% + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + templates\Parts\edit_part_info.html.twig:9 + + + part.edit.card_title + Edytuj komponent + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + + + part.edit.tab.common + Ogólne + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + + + part.edit.tab.manufacturer + Producent + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + + + part.edit.tab.advanced + Zaawansowane + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + + + part.edit.tab.part_lots + Zapasy + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + + + part.edit.tab.attachments + Załączniki + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + + + part.edit.tab.orderdetails + Informacja o zakupie + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.specifications + Parametry + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.comment + Notatki + + + + + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + templates\Parts\new_part.html.twig:8 + + + part.new.card_title + Utwórz nowy komponent + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + + + part_lot.delete + Usuń + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + + + part_lot.create + Dodaj zapas + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + + + orderdetail.create + Dodaj dystrybutora + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + + + pricedetails.edit.delete.confirm + Czy na pewno chcesz usunąć tę cenę? Tego nie da się cofnąć! + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 + + + orderdetails.edit.delete.confirm + Czy naprawdę chcesz usunąć tego dostawcę? Nie można tego cofnąć! + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + templates\Parts\show_part_info.html.twig:4 + templates\Parts\show_part_info.html.twig:9 + + + part.info.title + Informacje szczegółowe dla komponentu + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + + + part.part_lots.label + Zapasy + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 + Part-DB1\templates\Parts\lists\_info_card.html.twig:43 + Part-DB1\templates\_navbar_search.html.twig:31 + Part-DB1\templates\_navbar_search.html.twig:26 + templates\base.html.twig:62 + templates\Parts\show_part_info.html.twig:74 + src\Form\PartType.php:86 + + + comment.label + Notatki + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + + + part.info.specifications + Parametry + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + templates\Parts\show_part_info.html.twig:82 + + + attachment.labelp + Załączniki + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 + Part-DB1\templates\Parts\info\show_part_info.html.twig:71 + templates\Parts\show_part_info.html.twig:88 + + + vendor.partinfo.shopping_infos + Informacje o zakupach + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 + Part-DB1\templates\Parts\info\show_part_info.html.twig:78 + templates\Parts\show_part_info.html.twig:94 + + + vendor.partinfo.history + Historia + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + Part-DB1\templates\Parts\info\show_part_info.html.twig:84 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + templates\base.html.twig:176 + templates\base.html.twig:203 + templates\base.html.twig:217 + templates\base.html.twig:231 + templates\Parts\show_part_info.html.twig:100 + + + tools.label + Narzędzia + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 + Part-DB1\templates\Parts\info\show_part_info.html.twig:90 + + + extended_info.label + Informacje rozszerzone + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + + + attachment.name + Nazwa + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + + + attachment.attachment_type + Typ załącznika + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + + + attachment.file_name + Nazwa pliku + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + + + attachment.file_size + Rozmiar pliku + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 + + + attachment.preview + Podgląd obrazu + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 + + + attachment.download + Pobierz + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + new + + + user.creating_user + Użytkownik, który utworzył ten komponent + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + + + Unknown + Nieznany + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + new + + + accessDenied + Odmowa dostępu + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + new + + + user.last_editing_user + Użytkownik, który ostatnio edytował ten komponent + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + + + part.isFavorite + Ulubiony + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + + + part.minOrderAmount + Minimalna ilość zamawiana + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:46 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:41 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + templates\base.html.twig:70 + templates\Parts\show_part_info.html.twig:24 + src\Form\PartType.php:80 + + + manufacturer.label + Producent + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 + Part-DB1\templates\_navbar_search.html.twig:11 + templates\base.html.twig:54 + src\Form\PartType.php:62 + + + name.label + Nazwa + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + new + + + part.back_to_info + Powrót do aktualnej wersji + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:19 + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:18 + templates\base.html.twig:58 + templates\Parts\show_part_info.html.twig:31 + src\Form\PartType.php:65 + + + description.label + Opis + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:15 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:14 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + templates\base.html.twig:56 + templates\Parts\show_part_info.html.twig:32 + src\Form\PartType.php:74 + + + category.label + Kategoria + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + templates\Parts\show_part_info.html.twig:42 + src\Form\PartType.php:69 + + + instock.label + Na stanie + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + templates\Parts\show_part_info.html.twig:44 + src\Form\PartType.php:72 + + + mininstock.label + Minimalny stan zapasów + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:52 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:47 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + templates\base.html.twig:73 + templates\Parts\show_part_info.html.twig:47 + + + footprint.label + Układ padów (footprint) + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 + Part-DB1\templates\Parts\info\_main_infos.html.twig:59 + Part-DB1\templates\Parts\info\_main_infos.html.twig:57 + Part-DB1\templates\Parts\info\_main_infos.html.twig:60 + templates\Parts\show_part_info.html.twig:51 + + + part.avg_price.label + Średnia cena + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + + + part.supplier.name + Nazwa + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + + + part.supplier.partnr + Nr zamówienia + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + + + part.order.minamount + Ilość minimalna + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + + + part.order.price + Cena + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + + + part.order.single_price + Cena jednostkowa + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + + + edit.caption_short + Edycja + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + + + delete.caption + Usuń + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + Part-DB1\templates\Parts\info\_part_lots.html.twig:6 + + + part_lots.description + Opis + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + + + part_lots.storage_location + Miejsce przechowywania + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + + + part_lots.amount + Ilość + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 + Part-DB1\templates\Parts\info\_part_lots.html.twig:22 + + + part_lots.location_unknown + Miejsce przechowywania nieznane + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 + Part-DB1\templates\Parts\info\_part_lots.html.twig:29 + + + part_lots.instock_unknown + Ilość nieznana + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 + Part-DB1\templates\Parts\info\_part_lots.html.twig:38 + + + part_lots.expiration_date + Data ważności + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 + Part-DB1\templates\Parts\info\_part_lots.html.twig:46 + + + part_lots.is_expired + Wygasł + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 + Part-DB1\templates\Parts\info\_part_lots.html.twig:53 + + + part_lots.need_refill + Wymaga uzupełnienia + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:15 + Part-DB1\templates\Parts\info\_picture.html.twig:15 + + + part.info.prev_picture + Poprzedni obraz + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:19 + Part-DB1\templates\Parts\info\_picture.html.twig:19 + + + part.info.next_picture + Następny obraz + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + + + part.mass.tooltip + Masa + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + + + part.needs_review.badge + Wymaga recenzji + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + + + part.favorite.badge + Ulubiony + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + + + part.obsolete.badge + Nie dostępny + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:10 + + + parameters.extracted_from_description + Automatycznie pobrane z opisu + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:15 + + + parameters.auto_extracted_from_comment + Automatycznie pobierane z notatek + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:6 + Part-DB1\templates\Parts\info\_tools.html.twig:4 + templates\Parts\show_part_info.html.twig:125 + + + part.edit.btn + Edytuj komponent + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:14 + templates\Parts\show_part_info.html.twig:135 + + + part.clone.btn + Sklonuj komponent + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:24 + Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 + templates\Parts\show_part_info.html.twig:143 + + + part.create.btn + Utwórz nowy komponent + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:31 + Part-DB1\templates\Parts\info\_tools.html.twig:29 + + + part.delete.confirm_title + Na pewno chcesz usunąć ten komponent? + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:32 + Part-DB1\templates\Parts\info\_tools.html.twig:30 + + + part.delete.message + Ta część i wszelkie powiązane z nią informacje (takie jak załączniki, informacje o cenie itp.) zostaną usunięte. Nie można tego cofnąć! + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:39 + Part-DB1\templates\Parts\info\_tools.html.twig:37 + + + part.delete + Usuń komponent + + + + + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + + + parts_list.all.title + Wszystkie komponenty + + + + + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + + + parts_list.category.title + Komponenty z kategorią + + + + + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + + + parts_list.footprint.title + Komponenty z polami lutowniczymi + + + + + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + + + parts_list.manufacturer.title + Komponenty z producentem + + + + + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + + + parts_list.search.title + Wyszukiwarka komponentów + + + + + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + + + parts_list.storelocation.title + Komponenty z lokalizacją magazynu + + + + + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + + + parts_list.supplier.title + Komponenty z dostawcą + + + + + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + + + parts_list.tags.title + Komponenty z etykietą + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 + Part-DB1\templates\Parts\lists\_info_card.html.twig:17 + + + entity.info.common.tab + Ogólne + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 + Part-DB1\templates\Parts\lists\_info_card.html.twig:20 + + + entity.info.statistics.tab + Statystyki + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 + + + entity.info.attachments.tab + Załączniki + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 + + + entity.info.parameters.tab + Parametry + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 + Part-DB1\templates\Parts\lists\_info_card.html.twig:30 + + + entity.info.name + Nazwa + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 + Part-DB1\templates\Parts\lists\_info_card.html.twig:96 + Part-DB1\templates\Parts\lists\_info_card.html.twig:34 + Part-DB1\templates\Parts\lists\_info_card.html.twig:67 + + + entity.info.parent + Nadrzędny + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 + Part-DB1\templates\Parts\lists\_info_card.html.twig:46 + + + entity.edit.btn + Edycja + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 + Part-DB1\templates\Parts\lists\_info_card.html.twig:63 + + + entity.info.children_count + Ilość elementów podrzędnych + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + + + tfa.check.title + Wymagane uwierzytelnianie dwuskładnikowe + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:39 + Part-DB1\templates\security\2fa_base_form.html.twig:39 + + + tfa.code.trusted_pc + To jest zaufany komputer (jeśli ta opcja jest włączona, na tym komputerze nie są wykonywane żadne dalsze zapytania dwuskładnikowe). + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + + + login.btn + Zaloguj + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:42 + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:40 + + + user.logout + Wyloguj + + + + + Part-DB1\templates\security\2fa_form.html.twig:6 + Part-DB1\templates\security\2fa_form.html.twig:6 + + + tfa.check.code.label + Kod z aplikacji Authenticator + + + + + Part-DB1\templates\security\2fa_form.html.twig:10 + Part-DB1\templates\security\2fa_form.html.twig:10 + + + tfa.check.code.help + Wprowadź 6-cyfrowy kod z aplikacji Authenticator lub jeden z kodów zapasowych, jeśli Authenticator nie jest dostępny. + + + + + Part-DB1\templates\security\login.html.twig:3 + Part-DB1\templates\security\login.html.twig:3 + templates\security\login.html.twig:3 + + + login.title + Login + + + + + Part-DB1\templates\security\login.html.twig:7 + Part-DB1\templates\security\login.html.twig:7 + templates\security\login.html.twig:7 + + + login.card_title + Login + + + + + Part-DB1\templates\security\login.html.twig:31 + Part-DB1\templates\security\login.html.twig:31 + templates\security\login.html.twig:31 + + + login.username.label + Nazwa użytkownika + + + + + Part-DB1\templates\security\login.html.twig:34 + Part-DB1\templates\security\login.html.twig:34 + templates\security\login.html.twig:34 + + + login.username.placeholder + Nazwa użytkownika + + + + + Part-DB1\templates\security\login.html.twig:38 + Part-DB1\templates\security\login.html.twig:38 + templates\security\login.html.twig:38 + + + login.password.label + Hasło + + + + + Part-DB1\templates\security\login.html.twig:40 + Part-DB1\templates\security\login.html.twig:40 + templates\security\login.html.twig:40 + + + login.password.placeholder + Hasło + + + + + Part-DB1\templates\security\login.html.twig:50 + Part-DB1\templates\security\login.html.twig:50 + templates\security\login.html.twig:50 + + + login.rememberme + Zapamiętaj mnie (nie należy używać na współdzielonych komputerach) + + + + + Part-DB1\templates\security\login.html.twig:64 + Part-DB1\templates\security\login.html.twig:64 + + + pw_reset.password_forget + Nie pamiętasz nazwy użytkownika/hasła? + + + + + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + + + pw_reset.new_pw.header.title + Ustaw nowe hasło + + + + + Part-DB1\templates\security\pw_reset_request.html.twig:5 + Part-DB1\templates\security\pw_reset_request.html.twig:5 + + + pw_reset.request.header.title + Poproś o nowe hasło + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + + + tfa_u2f.http_warning + Uzyskujesz dostęp do tej strony przy użyciu niezabezpieczonej metody HTTP, więc KLUCZ U2F najprawdopodobniej nie będzie działać (komunikat o błędzie Bad Request). Jeśli chcesz używać kluczy bezpieczeństwa, poproś administratora o skonfigurowanie bezpiecznej metody HTTPS. + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + + + r_u2f_two_factor.pressbutton + Podłącz klucz bezpieczeństwa i naciśnij jego przycisk! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + + + tfa_u2f.add_key.title + Dodaj klucz bezpieczeństwa + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + + + tfa_u2f.explanation + Za pomocą klucza bezpieczeństwa kompatybilnego z U2F/FIDO (np. YubiKey lub NitroKey) można osiągnąć przyjazne dla użytkownika i bezpieczne uwierzytelnianie dwuskładnikowe. Klucze bezpieczeństwa można tutaj zarejestrować, a jeśli wymagana jest weryfikacja dwuskładnikowa, wystarczy włożyć klucz przez USB lub wpisać go na urządzeniu za pomocą NFC. + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + + + tfa_u2f.add_key.backup_hint + Aby mieć pewność dostępu nawet w przypadku zgubienia klucza, zaleca się zarejestrowanie drugiego klucza jako kopię zapasową i przechowywanie go w bezpiecznym miejscu! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + + + r_u2f_two_factor.name + Wyświetlana nazwa klucza (np. Backup) + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + + + tfa_u2f.add_key.add_button + Dodaj klucz + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + + + tfa_u2f.add_key.back_to_settings + Powrót do ustawień + + + + + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + new + + + statistics.title + Statystyki + + + + + Part-DB1\templates\Statistics\statistics.html.twig:14 + Part-DB1\templates\Statistics\statistics.html.twig:14 + new + + + statistics.parts + Komponenty + + + + + Part-DB1\templates\Statistics\statistics.html.twig:19 + Part-DB1\templates\Statistics\statistics.html.twig:19 + new + + + statistics.data_structures + Struktury danych + + + + + Part-DB1\templates\Statistics\statistics.html.twig:24 + Part-DB1\templates\Statistics\statistics.html.twig:24 + new + + + statistics.attachments + Załączniki + + + + + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + new + + + statistics.property + Właściwość + + + + + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + new + + + statistics.value + Wartość + + + + + Part-DB1\templates\Statistics\statistics.html.twig:40 + Part-DB1\templates\Statistics\statistics.html.twig:40 + new + + + statistics.distinct_parts_count + Liczba różnych komponentów + + + + + Part-DB1\templates\Statistics\statistics.html.twig:44 + Part-DB1\templates\Statistics\statistics.html.twig:44 + new + + + statistics.parts_instock_sum + Suma wszystkich istniejących zapasów komponentów + + + + + Part-DB1\templates\Statistics\statistics.html.twig:48 + Part-DB1\templates\Statistics\statistics.html.twig:48 + new + + + statistics.parts_with_price + Komponenty z informacją o cenie + + + + + Part-DB1\templates\Statistics\statistics.html.twig:65 + Part-DB1\templates\Statistics\statistics.html.twig:65 + new + + + statistics.categories_count + Liczba kategorii + + + + + Part-DB1\templates\Statistics\statistics.html.twig:69 + Part-DB1\templates\Statistics\statistics.html.twig:69 + new + + + statistics.footprints_count + Liczba pól lutowniczych + + + + + Part-DB1\templates\Statistics\statistics.html.twig:73 + Part-DB1\templates\Statistics\statistics.html.twig:73 + new + + + statistics.manufacturers_count + Liczba producentów + + + + + Part-DB1\templates\Statistics\statistics.html.twig:77 + Part-DB1\templates\Statistics\statistics.html.twig:77 + new + + + statistics.storelocations_count + Liczba miejsc przechowywania + + + + + Part-DB1\templates\Statistics\statistics.html.twig:81 + Part-DB1\templates\Statistics\statistics.html.twig:81 + new + + + statistics.suppliers_count + Liczba dostawców + + + + + Part-DB1\templates\Statistics\statistics.html.twig:85 + Part-DB1\templates\Statistics\statistics.html.twig:85 + new + + + statistics.currencies_count + Liczba walut + + + + + Part-DB1\templates\Statistics\statistics.html.twig:89 + Part-DB1\templates\Statistics\statistics.html.twig:89 + new + + + statistics.measurement_units_count + Liczba jednostek miar + + + + + Part-DB1\templates\Statistics\statistics.html.twig:93 + Part-DB1\templates\Statistics\statistics.html.twig:93 + new + + + statistics.devices_count + Liczba projektów + + + + + Part-DB1\templates\Statistics\statistics.html.twig:110 + Part-DB1\templates\Statistics\statistics.html.twig:110 + new + + + statistics.attachment_types_count + Liczba typów załączników + + + + + Part-DB1\templates\Statistics\statistics.html.twig:114 + Part-DB1\templates\Statistics\statistics.html.twig:114 + new + + + statistics.all_attachments_count + Liczba wszystkich załączników + + + + + Part-DB1\templates\Statistics\statistics.html.twig:118 + Part-DB1\templates\Statistics\statistics.html.twig:118 + new + + + statistics.user_uploaded_attachments_count + Liczba załączników przesłanych przez użytkownika + + + + + Part-DB1\templates\Statistics\statistics.html.twig:122 + Part-DB1\templates\Statistics\statistics.html.twig:122 + new + + + statistics.private_attachments_count + Liczba prywatnych załączników + + + + + Part-DB1\templates\Statistics\statistics.html.twig:126 + Part-DB1\templates\Statistics\statistics.html.twig:126 + new + + + statistics.external_attachments_count + Liczba załączników zewnętrznych (URL) + + + + + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + + + tfa_backup.codes.title + Kody zapasowe + + + + + Part-DB1\templates\Users\backup_codes.html.twig:12 + Part-DB1\templates\Users\backup_codes.html.twig:12 + + + tfa_backup.codes.explanation + Wydrukuj te kody i przechowuj je w bezpiecznym miejscu! + + + + + Part-DB1\templates\Users\backup_codes.html.twig:13 + Part-DB1\templates\Users\backup_codes.html.twig:13 + + + tfa_backup.codes.help + Jeśli nie masz już dostępu do urządzenia z aplikacją Authenticator (zgubiony smartfon, utrata danych itp.), możesz użyć jednego z tych kodów, aby uzyskać dostęp do swojego konta i ewentualnie skonfigurować nową aplikację Authenticator. Każdy z tych kodów można użyć tylko raz, zaleca się usunięcie wykorzystanych kodów. Każdy, kto ma dostęp do tych kodów, może potencjalnie uzyskać dostęp do Twojego konta, dlatego przechowuj je w bezpiecznym miejscu. + + + + + Part-DB1\templates\Users\backup_codes.html.twig:16 + Part-DB1\templates\Users\backup_codes.html.twig:16 + + + tfa_backup.username + Nazwa użytkownika + + + + + Part-DB1\templates\Users\backup_codes.html.twig:29 + Part-DB1\templates\Users\backup_codes.html.twig:29 + + + tfa_backup.codes.page_generated_on + Strona wygenerowana w dniu %date% + + + + + Part-DB1\templates\Users\backup_codes.html.twig:32 + Part-DB1\templates\Users\backup_codes.html.twig:32 + + + tfa_backup.codes.print + Drukuj + + + + + Part-DB1\templates\Users\backup_codes.html.twig:35 + Part-DB1\templates\Users\backup_codes.html.twig:35 + + + tfa_backup.codes.copy_clipboard + Kopiuj do schowka + + + + + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:40 + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:38 + templates\base.html.twig:99 + templates\Users\user_info.html.twig:3 + templates\Users\user_info.html.twig:6 + + + user.info.label + Informacje o użytkowniku + + + + + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + templates\Users\user_info.html.twig:18 + src\Form\UserSettingsType.php:32 + + + user.firstName.label + Imię + + + + + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + templates\Users\user_info.html.twig:24 + src\Form\UserSettingsType.php:35 + + + user.lastName.label + Nazwisko + + + + + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + templates\Users\user_info.html.twig:30 + src\Form\UserSettingsType.php:41 + + + user.email.label + E-mail + + + + + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + templates\Users\user_info.html.twig:37 + src\Form\UserSettingsType.php:38 + + + user.department.label + Dział + + + + + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + templates\Users\user_info.html.twig:47 + src\Form\UserSettingsType.php:30 + + + user.username.label + Nazwa użytkownika + + + + + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + templates\Users\user_info.html.twig:53 + + + group.label + Grupa + + + + + Part-DB1\templates\Users\user_info.html.twig:67 + Part-DB1\templates\Users\user_info.html.twig:67 + + + user.permissions + Uprawnienia + + + + + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:39 + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:37 + templates\base.html.twig:98 + templates\Users\user_settings.html.twig:3 + templates\Users\user_settings.html.twig:6 + + + user.settings.label + Ustawienia użytkownika + + + + + Part-DB1\templates\Users\user_settings.html.twig:18 + Part-DB1\templates\Users\user_settings.html.twig:18 + templates\Users\user_settings.html.twig:14 + + + user_settings.data.label + Dane personalne + + + + + Part-DB1\templates\Users\user_settings.html.twig:22 + Part-DB1\templates\Users\user_settings.html.twig:22 + templates\Users\user_settings.html.twig:18 + + + user_settings.configuration.label + Konfiguracja + + + + + Part-DB1\templates\Users\user_settings.html.twig:55 + Part-DB1\templates\Users\user_settings.html.twig:55 + templates\Users\user_settings.html.twig:48 + + + user.settings.change_pw + Zmień hasło + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + + + user.settings.2fa_settings + Uwierzytelnianie dwuskładnikowe + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + + + tfa.settings.google.tab + Aplikacja Authenticator + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + + + tfa.settings.bakup.tab + Kody zapasowe + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + + + tfa.settings.u2f.tab + Klucze bezpieczeństwa (U2F) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + + + tfa.settings.trustedDevices.tab + Zaufane urządzenia + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_title + Czy naprawdę chcesz wyłączyć aplikację Authenticator? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_message + Jeśli wyłączysz aplikację Authenticator, wszystkie kody zapasowe zostaną usunięte, więc możesz potrzebować ich ponownego wydrukowania.<br> Również pamiętaj, że bez weryfikacji dwuetapowej Twoje konto nie jest już tak dobrze chronione przed atakami! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + + + tfa_google.disabled_message + Aplikacja Authenticator wyłączona! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + + + tfa_google.step.download + Pobierz aplikację uwierzytelniającą (np. <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> oder <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp">FreeOTP Authenticator</a>) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + + + tfa_google.step.scan + Zeskanuj sąsiedni kod QR za pomocą aplikacji lub wprowadź dane ręcznie. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + + + tfa_google.step.input_code + Zeskanuj sąsiadujący kod QR za pomocą aplikacji lub wprowadź dane ręcznie. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + + + tfa_google.step.download_backup + Wydrukuj kody zapasowe i przechowuj je w bezpiecznym miejscu. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + + + tfa_google.manual_setup + Konfiguracja ręczna + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + + + tfa_google.manual_setup.type + Typ + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + + + tfa_google.manual_setup.username + Nazwa użytkownika + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + + + tfa_google.manual_setup.secret + Sekret + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + + + tfa_google.manual_setup.digit_count + Liczba cyfr + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + + + tfa_google.enabled_message + Włączona aplikacja Authenticator + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + + + tfa_backup.disabled + Kody zapasowe wyłączone. Skonfiguruj aplikację uwierzytelniającą, aby włączyć kody zapasowe. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + + + tfa_backup.explanation + Możesz użyć tych kodów zapasowych, aby uzyskać dostęp do konta, nawet jeśli zgubisz urządzenie z aplikacją Authenticator. Wydrukuj kody i przechowuj je w bezpiecznym miejscu. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_title + Naprawdę zresetować kody? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_message + Spowoduje to usunięcie wszystkich poprzednich kodów i wygenerowanie zestawu nowych kodów. Nie można tego cofnąć. Pamiętaj, aby wydrukować nowe kody i przechowywać je w bezpiecznym miejscu! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + + + tfa_backup.enabled + Kody zapasowe włączone + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + + + tfa_backup.show_codes + Pokaż kody zapasowe + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + + + tfa_u2f.table_caption + Zarejestrowane klucze bezpieczeństwa + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + + + tfa_u2f.delete_u2f.confirm_title + Naprawdę usunąć ten klucz bezpieczeństwa? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + + + tfa_u2f.delete_u2f.confirm_message + Jeśli usuniesz ten klucz, logowanie za jego pomocą nie będzie już możliwe. Jeśli nie pozostaną żadne klucze zabezpieczeń, uwierzytelnianie dwuskładnikowe zostanie wyłączone. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + + + tfa_u2f.keys.name + Nazwa klucza + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + + + tfa_u2f.keys.added_date + Data dodania + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + + + tfa_u2f.key_delete + Usuń klucz + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + + + tfa_u2f.no_keys_registered + Brak zarejestrowanych kluczy + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + + + tfa_u2f.add_new_key + Dodaj nowy klucz + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + + + tfa_trustedDevices.explanation + Podczas sprawdzania drugiego czynnika bieżący komputer może zostać oznaczony jako godny zaufania, więc nie są już wymagane żadne kontrole dwuskładnikowe na tym komputerze. +Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, możesz zresetować status wszystkich komputerów tutaj. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + + + tfa_trustedDevices.invalidate.confirm_title + Naprawdę usunąć wszystkie zaufane komputery? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + + + tfa_trustedDevices.invalidate.confirm_message + Konieczne będzie ponowne przeprowadzenie uwierzytelniania dwuskładnikowego na wszystkich komputerach. Upewnij się, że masz pod ręką swoje urządzenie dwuskładnikowe. + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + + + tfa_trustedDevices.invalidate.btn + Resetuj zaufane urządzenia + + + + + Part-DB1\templates\_navbar.html.twig:4 + Part-DB1\templates\_navbar.html.twig:4 + templates\base.html.twig:29 + + + sidebar.toggle + Przełącz pasek boczny + + + + + Part-DB1\templates\_navbar.html.twig:22 + + + navbar.scanner.link + Skaner + + + + + Part-DB1\templates\_navbar.html.twig:38 + Part-DB1\templates\_navbar.html.twig:36 + templates\base.html.twig:97 + + + user.loggedin.label + Zalogowany jako + + + + + Part-DB1\templates\_navbar.html.twig:44 + Part-DB1\templates\_navbar.html.twig:42 + templates\base.html.twig:103 + + + user.login + Zaloguj + + + + + Part-DB1\templates\_navbar.html.twig:50 + Part-DB1\templates\_navbar.html.twig:48 + + + ui.toggle_darkmode + Tryb ciemny + + + + + Part-DB1\templates\_navbar.html.twig:54 + Part-DB1\src\Form\UserSettingsType.php:97 + Part-DB1\templates\_navbar.html.twig:52 + Part-DB1\src\Form\UserSettingsType.php:97 + templates\base.html.twig:106 + src\Form\UserSettingsType.php:44 + + + user.language_select + Język + + + + + Part-DB1\templates\_navbar_search.html.twig:4 + Part-DB1\templates\_navbar_search.html.twig:4 + templates\base.html.twig:49 + + + search.options.label + Opcje wyszukiwania + + + + + Part-DB1\templates\_navbar_search.html.twig:23 + + + tags.label + Tagi + + + + + Part-DB1\templates\_navbar_search.html.twig:27 + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + templates\base.html.twig:60 + templates\Parts\show_part_info.html.twig:36 + src\Form\PartType.php:77 + + + storelocation.label + Miejsce przechowywania + + + + + Part-DB1\templates\_navbar_search.html.twig:36 + Part-DB1\templates\_navbar_search.html.twig:31 + templates\base.html.twig:65 + + + ordernumber.label.short + Nr zamówienia. + + + + + Part-DB1\templates\_navbar_search.html.twig:40 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + Part-DB1\templates\_navbar_search.html.twig:35 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + templates\base.html.twig:67 + + + supplier.label + Dostawca + + + + + Part-DB1\templates\_navbar_search.html.twig:57 + Part-DB1\templates\_navbar_search.html.twig:52 + templates\base.html.twig:75 + + + search.deactivateBarcode + Dezaktywuj kod kreskowy + + + + + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_navbar_search.html.twig:56 + templates\base.html.twig:77 + + + search.regexmatching + Dopasowywanie Reg.Ex. + + + + + Part-DB1\templates\_navbar_search.html.twig:68 + Part-DB1\templates\_navbar_search.html.twig:62 + + + search.submit + Idź! + + + + + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + templates\base.html.twig:175 + templates\base.html.twig:189 + templates\base.html.twig:202 + templates\base.html.twig:230 + + + project.labelp + Projekty + + + + + Part-DB1\templates\_sidebar.html.twig:2 + Part-DB1\templates\_sidebar.html.twig:2 + templates\base.html.twig:165 + templates\base.html.twig:192 + templates\base.html.twig:220 + + + actions + Akcje + + + + + Part-DB1\templates\_sidebar.html.twig:6 + Part-DB1\templates\_sidebar.html.twig:6 + templates\base.html.twig:169 + templates\base.html.twig:196 + templates\base.html.twig:224 + + + datasource + Źródło danych + + + + + Part-DB1\templates\_sidebar.html.twig:10 + Part-DB1\templates\_sidebar.html.twig:10 + templates\base.html.twig:173 + templates\base.html.twig:200 + templates\base.html.twig:228 + + + manufacturer.labelp + Producenci + + + + + Part-DB1\templates\_sidebar.html.twig:11 + Part-DB1\templates\_sidebar.html.twig:11 + templates\base.html.twig:174 + templates\base.html.twig:201 + templates\base.html.twig:229 + + + supplier.labelp + Dostawcy + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:293 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:181 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:243 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:268 + + + attachment.download_failed + Pobieranie zewnętrznego załącznika nie powiodło się. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 + + + entity.edit_flash + Zmiany zostały pomyślnie zapisane. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 + + + entity.edit_flash.invalid + Nie można zapisać zmienionych danych. Sprawdź wprowadzone dane! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 + + + entity.created_flash + Utworzony element. + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 + + + entity.created_flash.invalid + Nie można utworzyć elementu. Sprawdź wprowadzone dane! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 + src\Controller\BaseAdminController.php:154 + + + attachment_type.deleted + Element usunięty! + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 + Part-DB1\src\Controller\UserController.php:109 + Part-DB1\src\Controller\UserSettingsController.php:159 + Part-DB1\src\Controller\UserSettingsController.php:193 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:354 + Part-DB1\src\Controller\UserController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:150 + Part-DB1\src\Controller\UserSettingsController.php:182 + + + csfr_invalid + Token CSRF jest nieprawidłowy. Załaduj ponownie tę stronę lub skontaktuj się z administratorem, jeśli ten komunikat będzie się powtarzał. + + + + + Part-DB1\src\Controller\LabelController.php:125 + + + label_generator.no_entities_found + Nie znaleziono żadnych elementów + + + + + Part-DB1\src\Controller\LogController.php:149 + Part-DB1\src\Controller\LogController.php:154 + new + + + log.undo.target_not_found + Element docelowy nie został znaleziony w bazie danych! + + + + + Part-DB1\src\Controller\LogController.php:156 + Part-DB1\src\Controller\LogController.php:160 + new + + + log.undo.revert_success + Pomyślnie zresetowano komponent. + + + + + Part-DB1\src\Controller\LogController.php:176 + Part-DB1\src\Controller\LogController.php:180 + new + + + log.undo.element_undelete_success + Komponent został pomyślnie przywrócony. + + + + + Part-DB1\src\Controller\LogController.php:178 + Part-DB1\src\Controller\LogController.php:182 + new + + + log.undo.element_element_already_undeleted + Komponenty zostały już przywrócone! + + + + + Part-DB1\src\Controller\LogController.php:185 + Part-DB1\src\Controller\LogController.php:189 + new + + + log.undo.element_delete_success + Komponent został pomyślnie usunięty. + + + + + Part-DB1\src\Controller\LogController.php:187 + Part-DB1\src\Controller\LogController.php:191 + new + + + log.undo.element.element_already_delted + Komponent został już usunięty + + + + + Part-DB1\src\Controller\LogController.php:194 + Part-DB1\src\Controller\LogController.php:198 + new + + + log.undo.element_change_undone + Zmiana została pomyślnie anulowana. + + + + + Part-DB1\src\Controller\LogController.php:196 + Part-DB1\src\Controller\LogController.php:200 + new + + + log.undo.do_undelete_before + Przed cofnięciem tej zmiany należy najpierw przywrócić element! + + + + + Part-DB1\src\Controller\LogController.php:199 + Part-DB1\src\Controller\LogController.php:203 + new + + + log.undo.log_type_invalid + Tego wpisu w dzienniku nie można cofnąć! + + + + + Part-DB1\src\Controller\PartController.php:182 + Part-DB1\src\Controller\PartController.php:182 + src\Controller\PartController.php:80 + + + part.edited_flash + Zmiany zapisane! + + + + + Part-DB1\src\Controller\PartController.php:186 + Part-DB1\src\Controller\PartController.php:186 + + + part.edited_flash.invalid + Błąd podczas zapisywania: Sprawdź wprowadzone dane! + + + + + Part-DB1\src\Controller\PartController.php:216 + Part-DB1\src\Controller\PartController.php:219 + + + part.deleted + Komponent został pomyślnie usunięty. + + + + + Part-DB1\src\Controller\PartController.php:302 + Part-DB1\src\Controller\PartController.php:277 + Part-DB1\src\Controller\PartController.php:317 + src\Controller\PartController.php:113 + src\Controller\PartController.php:142 + + + part.created_flash + Komponenty zostały utworzone pomyślnie! + + + + + Part-DB1\src\Controller\PartController.php:308 + Part-DB1\src\Controller\PartController.php:283 + + + part.created_flash.invalid + Błąd podczas tworzenia: Proszę sprawdzić swoje dane wejściowe! + + + + + Part-DB1\src\Controller\ScanController.php:68 + Part-DB1\src\Controller\ScanController.php:90 + + + scan.qr_not_found + Nie znaleziono elementu dla podanego kodu kreskowego. + + + + + Part-DB1\src\Controller\ScanController.php:71 + + + scan.format_unknown + Format nieznany + + + + + Part-DB1\src\Controller\ScanController.php:86 + + + scan.qr_success + Element znaleziono + + + + + Part-DB1\src\Controller\SecurityController.php:114 + Part-DB1\src\Controller\SecurityController.php:109 + + + pw_reset.user_or_email + Nazwa użytkownika / adres e-mail + + + + + Part-DB1\src\Controller\SecurityController.php:131 + Part-DB1\src\Controller\SecurityController.php:126 + + + pw_reset.request.success + Prośba o zresetowanie została pomyślnie wykonana! Aby uzyskać dalsze instrukcje, sprawdź swoją pocztę e-mail. + + + + + Part-DB1\src\Controller\SecurityController.php:162 + Part-DB1\src\Controller\SecurityController.php:160 + + + pw_reset.username + Nazwa użytkownika + + + + + Part-DB1\src\Controller\SecurityController.php:165 + Part-DB1\src\Controller\SecurityController.php:163 + + + pw_reset.token + Token + + + + + Part-DB1\src\Controller\SecurityController.php:194 + Part-DB1\src\Controller\SecurityController.php:192 + + + pw_reset.new_pw.error + Nazwa użytkownika lub token są nieprawidłowe! Sprawdź wprowadzone dane. + + + + + Part-DB1\src\Controller\SecurityController.php:196 + Part-DB1\src\Controller\SecurityController.php:194 + + + pw_reset.new_pw.success + Hasło zostało pomyślnie zresetowane. Możesz teraz zalogować się przy użyciu nowego hasła. + + + + + Part-DB1\src\Controller\UserController.php:107 + Part-DB1\src\Controller\UserController.php:99 + + + user.edit.reset_success + Wszystkie metody uwierzytelniania dwuskładnikowego zostały pomyślnie wyłączone. + + + + + Part-DB1\src\Controller\UserSettingsController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:92 + + + tfa_backup.no_codes_enabled + Nie włączono żadnych kodów zapasowych! + + + + + Part-DB1\src\Controller\UserSettingsController.php:138 + Part-DB1\src\Controller\UserSettingsController.php:132 + + + tfa_u2f.u2f_delete.not_existing + Żaden klucz bezpieczeństwa o tym identyfikatorze nie istnieje. + + + + + Part-DB1\src\Controller\UserSettingsController.php:145 + Part-DB1\src\Controller\UserSettingsController.php:139 + + + tfa_u2f.u2f_delete.access_denied + Nie możesz usuwać kluczy bezpieczeństwa innych użytkowników! + + + + + Part-DB1\src\Controller\UserSettingsController.php:153 + Part-DB1\src\Controller\UserSettingsController.php:147 + + + tfa.u2f.u2f_delete.success + Klucz bezpieczeństwa został pomyślnie usunięty. + + + + + Part-DB1\src\Controller\UserSettingsController.php:188 + Part-DB1\src\Controller\UserSettingsController.php:180 + + + tfa_trustedDevice.invalidate.success + Zaufane urządzenia zostały pomyślnie zresetowane. + + + + + Part-DB1\src\Controller\UserSettingsController.php:235 + Part-DB1\src\Controller\UserSettingsController.php:226 + src\Controller\UserController.php:98 + + + user.settings.saved_flash + Ustawienia zapisane! + + + + + Part-DB1\src\Controller\UserSettingsController.php:297 + Part-DB1\src\Controller\UserSettingsController.php:288 + src\Controller\UserController.php:130 + + + user.settings.pw_changed_flash + Hasło zostało zmienione! + + + + + Part-DB1\src\Controller\UserSettingsController.php:317 + Part-DB1\src\Controller\UserSettingsController.php:306 + + + user.settings.2fa.google.activated + Aplikacja uwierzytelniająca została pomyślnie aktywowana. + + + + + Part-DB1\src\Controller\UserSettingsController.php:328 + Part-DB1\src\Controller\UserSettingsController.php:315 + + + user.settings.2fa.google.disabled + Aplikacja uwierzytelniająca została pomyślnie dezaktywowana. + + + + + Part-DB1\src\Controller\UserSettingsController.php:346 + Part-DB1\src\Controller\UserSettingsController.php:332 + + + user.settings.2fa.backup_codes.regenerated + Pomyślnie wygenerowano nowe kody zapasowe. + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + + + attachment.table.filename + Nazwa pliku + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + + + attachment.table.filesize + Rozmiar pliku + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:245 + Part-DB1\src\DataTables\PartsDataTable.php:252 + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:193 + Part-DB1\src\DataTables\PartsDataTable.php:200 + + + true + prawda + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:246 + Part-DB1\src\DataTables\PartsDataTable.php:253 + Part-DB1\src\Form\Type\SIUnitType.php:139 + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:201 + Part-DB1\src\Form\Type\SIUnitType.php:139 + + + false + fałsz + + + + + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 + + + log.target_deleted + usunięto + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 + new + + + log.undo.undelete + Przywróć element + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 + new + + + log.undo.undo + Cofnij zmianę + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 + new + + + log.undo.revert + Zresetuj element do stanu z tego punktu w czasie! + + + + + Part-DB1\src\DataTables\LogDataTable.php:173 + Part-DB1\src\DataTables\LogDataTable.php:161 + + + log.id + ID + + + + + Part-DB1\src\DataTables\LogDataTable.php:178 + Part-DB1\src\DataTables\LogDataTable.php:166 + + + log.timestamp + Znacznik czasu + + + + + Part-DB1\src\DataTables\LogDataTable.php:183 + Part-DB1\src\DataTables\LogDataTable.php:171 + + + log.type + Zdarzenie + + + + + Part-DB1\src\DataTables\LogDataTable.php:191 + Part-DB1\src\DataTables\LogDataTable.php:179 + + + log.level + Poziom + + + + + Part-DB1\src\DataTables\LogDataTable.php:200 + Part-DB1\src\DataTables\LogDataTable.php:188 + + + log.user + Użytkownik + + + + + Part-DB1\src\DataTables\LogDataTable.php:213 + Part-DB1\src\DataTables\LogDataTable.php:201 + + + log.target_type + Typ docelowy + + + + + Part-DB1\src\DataTables\LogDataTable.php:226 + Part-DB1\src\DataTables\LogDataTable.php:214 + + + log.target + Target + + + + + Part-DB1\src\DataTables\LogDataTable.php:231 + Part-DB1\src\DataTables\LogDataTable.php:218 + new + + + log.extra + Extra + + + + + Part-DB1\src\DataTables\PartsDataTable.php:168 + Part-DB1\src\DataTables\PartsDataTable.php:116 + + + part.table.name + Nazwa + + + + + Part-DB1\src\DataTables\PartsDataTable.php:178 + Part-DB1\src\DataTables\PartsDataTable.php:126 + + + part.table.id + ID + + + + + Part-DB1\src\DataTables\PartsDataTable.php:182 + Part-DB1\src\DataTables\PartsDataTable.php:130 + + + part.table.description + Opis + + + + + Part-DB1\src\DataTables\PartsDataTable.php:185 + Part-DB1\src\DataTables\PartsDataTable.php:133 + + + part.table.category + Kategoria + + + + + Part-DB1\src\DataTables\PartsDataTable.php:190 + Part-DB1\src\DataTables\PartsDataTable.php:138 + + + part.table.footprint + Pola lutownicze + + + + + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:142 + + + part.table.manufacturer + Producent + + + + + Part-DB1\src\DataTables\PartsDataTable.php:197 + Part-DB1\src\DataTables\PartsDataTable.php:145 + + + part.table.storeLocations + Miejsca przechowywania + + + + + Part-DB1\src\DataTables\PartsDataTable.php:216 + Part-DB1\src\DataTables\PartsDataTable.php:164 + + + part.table.amount + Ilość + + + + + Part-DB1\src\DataTables\PartsDataTable.php:224 + Part-DB1\src\DataTables\PartsDataTable.php:172 + + + part.table.minamount + Ilość minimalna + + + + + Part-DB1\src\DataTables\PartsDataTable.php:232 + Part-DB1\src\DataTables\PartsDataTable.php:180 + + + part.table.partUnit + Jednostka pomiarowa + + + + + Part-DB1\src\DataTables\PartsDataTable.php:236 + Part-DB1\src\DataTables\PartsDataTable.php:184 + + + part.table.addedDate + Data dodania + + + + + Part-DB1\src\DataTables\PartsDataTable.php:240 + Part-DB1\src\DataTables\PartsDataTable.php:188 + + + part.table.lastModified + Ostatnio zmodyfikowany + + + + + Part-DB1\src\DataTables\PartsDataTable.php:244 + Part-DB1\src\DataTables\PartsDataTable.php:192 + + + part.table.needsReview + Wymaga sprawdzenia + + + + + Part-DB1\src\DataTables\PartsDataTable.php:251 + Part-DB1\src\DataTables\PartsDataTable.php:199 + + + part.table.favorite + Ulubiony + + + + + Part-DB1\src\DataTables\PartsDataTable.php:258 + Part-DB1\src\DataTables\PartsDataTable.php:206 + + + part.table.manufacturingStatus + Status + + + + + Part-DB1\src\DataTables\PartsDataTable.php:260 + Part-DB1\src\DataTables\PartsDataTable.php:262 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:208 + Part-DB1\src\DataTables\PartsDataTable.php:210 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.unknown + Nieznany + + + + + Part-DB1\src\DataTables\PartsDataTable.php:263 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:211 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.announced + Ogłoszono + + + + + Part-DB1\src\DataTables\PartsDataTable.php:264 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.active + Aktywny + + + + + Part-DB1\src\DataTables\PartsDataTable.php:265 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:213 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.nrfnd + Nie zalecane dla nowych projektów + + + + + Part-DB1\src\DataTables\PartsDataTable.php:266 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:214 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.eol + End of life + + + + + Part-DB1\src\DataTables\PartsDataTable.php:267 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:215 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.discontinued + Wycofany + + + + + Part-DB1\src\DataTables\PartsDataTable.php:271 + Part-DB1\src\DataTables\PartsDataTable.php:219 + + + part.table.mpn + MPN + + + + + Part-DB1\src\DataTables\PartsDataTable.php:275 + Part-DB1\src\DataTables\PartsDataTable.php:223 + + + part.table.mass + Masa + + + + + Part-DB1\src\DataTables\PartsDataTable.php:279 + Part-DB1\src\DataTables\PartsDataTable.php:227 + + + part.table.tags + Tagi + + + + + Part-DB1\src\DataTables\PartsDataTable.php:283 + Part-DB1\src\DataTables\PartsDataTable.php:231 + + + part.table.attachments + Załączniki + + + + + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 + Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 + + + flash.login_successful + Zalogowano poprawnie + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + JSON + JSON + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + XML + XML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + CSV + CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + YAML + YAML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:124 + Part-DB1\src\Form\AdminPages\ImportType.php:124 + + + import.abort_on_validation.help + Gdy ta opcja jest aktywna, cały proces importu jest przerywany w przypadku wykrycia nieprawidłowych danych. Jeśli opcja ta nie zostanie wybrana, nieprawidłowe dane zostaną zignorowane, a importer spróbuje zaimportować pozostałe elementy. + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:86 + Part-DB1\src\Form\AdminPages\ImportType.php:86 + src\Form\ImportType.php:70 + + + import.csv_separator + Separator CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:93 + Part-DB1\src\Form\AdminPages\ImportType.php:93 + src\Form\ImportType.php:72 + + + parent.label + Element nadrzędny + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:101 + Part-DB1\src\Form\AdminPages\ImportType.php:101 + src\Form\ImportType.php:75 + + + import.file + Plik + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:111 + Part-DB1\src\Form\AdminPages\ImportType.php:111 + src\Form\ImportType.php:78 + + + import.preserve_children + Zachowaj elementy podrzędne podczas importu + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:120 + Part-DB1\src\Form\AdminPages\ImportType.php:120 + src\Form\ImportType.php:80 + + + import.abort_on_validation + Przerwij w przypadku nieprawidłowych danych + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:132 + Part-DB1\src\Form\AdminPages\ImportType.php:132 + src\Form\ImportType.php:85 + + + import.btn + Importuj + + + + + Part-DB1\src\Form\AttachmentFormType.php:113 + Part-DB1\src\Form\AttachmentFormType.php:109 + + + attachment.edit.secure_file.help + Załącznik oznaczony jako prywatny może być dostępny tylko dla uwierzytelnionych użytkowników z odpowiednimi uprawnieniami. Jeśli ta opcja jest włączona, nie są generowane miniatury, a dostęp do pliku jest mniej wydajny. + + + + + Part-DB1\src\Form\AttachmentFormType.php:127 + Part-DB1\src\Form\AttachmentFormType.php:123 + + + attachment.edit.url.help + W tym miejscu można wprowadzić adres URL do pliku zewnętrznego lub słowo kluczowe w celu wyszukiwania we wbudowanych zasobach (np. footprints). + + + + + Part-DB1\src\Form\AttachmentFormType.php:82 + Part-DB1\src\Form\AttachmentFormType.php:79 + + + attachment.edit.name + Nazwa + + + + + Part-DB1\src\Form\AttachmentFormType.php:85 + Part-DB1\src\Form\AttachmentFormType.php:82 + + + attachment.edit.attachment_type + Typ załącznika + + + + + Part-DB1\src\Form\AttachmentFormType.php:94 + Part-DB1\src\Form\AttachmentFormType.php:91 + + + attachment.edit.show_in_table + Pokaż w tabeli + + + + + Part-DB1\src\Form\AttachmentFormType.php:105 + Part-DB1\src\Form\AttachmentFormType.php:102 + + + attachment.edit.secure_file + Załącznik prywatny + + + + + Part-DB1\src\Form\AttachmentFormType.php:119 + Part-DB1\src\Form\AttachmentFormType.php:115 + + + attachment.edit.url + Adres URL + + + + + Part-DB1\src\Form\AttachmentFormType.php:133 + Part-DB1\src\Form\AttachmentFormType.php:129 + + + attachment.edit.download_url + Pobierz plik zewnętrzny + + + + + Part-DB1\src\Form\AttachmentFormType.php:146 + Part-DB1\src\Form\AttachmentFormType.php:142 + + + attachment.edit.file + Wyślij plik + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:86 + + + part.label + Komponent + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:87 + + + part_lot.label + Spis komponentów + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.none + Brak + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.qr + QR Code (zalecane) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code128 + Code 128 (zalecane) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code39 + Code 39 (zalecane) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code93 + Code 93 + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.datamatrix + Datamatrix + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label_options.lines_mode.html + Placeholders (symbole zastępcze) + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label.options.lines_mode.twig + Twig + + + + + Part-DB1\src\Form\LabelOptionsType.php:126 + + + label_options.lines_mode.help + Jeśli wybierzesz Twig tutaj, pole treści będzie interpretowane jako szablon Twig. Zobacz <a href="https://twig.symfony.com/doc/3.x/templates.html">dokumentację Twig</a> oraz <a href="https://docs.part-db.de/usage/labels.html#twig-mode">Wiki</a>, aby uzyskać więcej informacji. + + + + + Part-DB1\src\Form\LabelOptionsType.php:47 + + + label_options.page_size.label + Rozmiar etykiety + + + + + Part-DB1\src\Form\LabelOptionsType.php:66 + + + label_options.supported_elements.label + Typ docelowy + + + + + Part-DB1\src\Form\LabelOptionsType.php:75 + + + label_options.barcode_type.label + Kod kreskowy + + + + + Part-DB1\src\Form\LabelOptionsType.php:102 + + + label_profile.lines.label + Treść + + + + + Part-DB1\src\Form\LabelOptionsType.php:111 + + + label_options.additional_css.label + Style dodatkowe (CSS) + + + + + Part-DB1\src\Form\LabelOptionsType.php:120 + + + label_options.lines_mode.label + Tryb parsera + + + + + Part-DB1\src\Form\LabelOptionsType.php:51 + + + label_options.width.placeholder + Szerokość + + + + + Part-DB1\src\Form\LabelOptionsType.php:60 + + + label_options.height.placeholder + Wysokość + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 + + + label_generator.target_id.range_hint + W tym miejscu można określić wiele identyfikatorów ID (np. 1,2,3) i/lub zakres (1-3), aby wygenerować etykiety dla wielu elementów jednocześnie. + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 + + + label_generator.target_id.label + ID obiektu + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 + + + label_generator.update + Aktualizacja + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 + + + scan_dialog.input + Wejście + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 + + + scan_dialog.submit + Prześlij + + + + + Part-DB1\src\Form\ParameterType.php:41 + + + parameters.name.placeholder + np. DC Current Gain + + + + + Part-DB1\src\Form\ParameterType.php:50 + + + parameters.symbol.placeholder + np. h_{FE} + + + + + Part-DB1\src\Form\ParameterType.php:60 + + + parameters.text.placeholder + np. Warunki testowe + + + + + Part-DB1\src\Form\ParameterType.php:71 + + + parameters.max.placeholder + np. 350 + + + + + Part-DB1\src\Form\ParameterType.php:82 + + + parameters.min.placeholder + np. 100 + + + + + Part-DB1\src\Form\ParameterType.php:93 + + + parameters.typical.placeholder + np. 200 + + + + + Part-DB1\src\Form\ParameterType.php:103 + + + parameters.unit.placeholder + np. V + + + + + Part-DB1\src\Form\ParameterType.php:114 + + + parameter.group.placeholder + np. Specyfikacje techniczne + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:72 + Part-DB1\src\Form\Part\OrderdetailType.php:75 + + + orderdetails.edit.supplierpartnr + Numer katalogowy dostawcy + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:81 + Part-DB1\src\Form\Part\OrderdetailType.php:84 + + + orderdetails.edit.supplier + Dostawca + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:87 + Part-DB1\src\Form\Part\OrderdetailType.php:90 + + + orderdetails.edit.url + Link do oferty + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:93 + Part-DB1\src\Form\Part\OrderdetailType.php:96 + + + orderdetails.edit.obsolete + Już niedostępne + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:75 + Part-DB1\src\Form\Part\OrderdetailType.php:78 + + + orderdetails.edit.supplierpartnr.placeholder + np. BC 547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:101 + Part-DB1\src\Form\Part\PartBaseType.php:99 + + + part.edit.name + Nazwa + + + + + Part-DB1\src\Form\Part\PartBaseType.php:109 + Part-DB1\src\Form\Part\PartBaseType.php:107 + + + part.edit.description + Opis + + + + + Part-DB1\src\Form\Part\PartBaseType.php:120 + Part-DB1\src\Form\Part\PartBaseType.php:118 + + + part.edit.mininstock + Minimalny stan magazynowy + + + + + Part-DB1\src\Form\Part\PartBaseType.php:129 + Part-DB1\src\Form\Part\PartBaseType.php:127 + + + part.edit.category + Kategoria + + + + + Part-DB1\src\Form\Part\PartBaseType.php:135 + Part-DB1\src\Form\Part\PartBaseType.php:133 + + + part.edit.footprint + Pola lutownicze + + + + + Part-DB1\src\Form\Part\PartBaseType.php:142 + Part-DB1\src\Form\Part\PartBaseType.php:140 + + + part.edit.tags + Tagi + + + + + Part-DB1\src\Form\Part\PartBaseType.php:154 + Part-DB1\src\Form\Part\PartBaseType.php:152 + + + part.edit.manufacturer.label + Producent + + + + + Part-DB1\src\Form\Part\PartBaseType.php:161 + Part-DB1\src\Form\Part\PartBaseType.php:159 + + + part.edit.manufacturer_url.label + Link do strony produktu + + + + + Part-DB1\src\Form\Part\PartBaseType.php:167 + Part-DB1\src\Form\Part\PartBaseType.php:165 + + + part.edit.mpn + Numer katalogowy producenta + + + + + Part-DB1\src\Form\Part\PartBaseType.php:173 + Part-DB1\src\Form\Part\PartBaseType.php:171 + + + part.edit.manufacturing_status + Status produkcji + + + + + Part-DB1\src\Form\Part\PartBaseType.php:181 + Part-DB1\src\Form\Part\PartBaseType.php:179 + + + part.edit.needs_review + Wymaga sprawdzenia + + + + + Part-DB1\src\Form\Part\PartBaseType.php:189 + Part-DB1\src\Form\Part\PartBaseType.php:187 + + + part.edit.is_favorite + Ulubiony + + + + + Part-DB1\src\Form\Part\PartBaseType.php:197 + Part-DB1\src\Form\Part\PartBaseType.php:195 + + + part.edit.mass + Masa + + + + + Part-DB1\src\Form\Part\PartBaseType.php:203 + Part-DB1\src\Form\Part\PartBaseType.php:201 + + + part.edit.partUnit + Jednostka pomiarowa + + + + + Part-DB1\src\Form\Part\PartBaseType.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:210 + + + part.edit.comment + Komentarz + + + + + Part-DB1\src\Form\Part\PartBaseType.php:250 + Part-DB1\src\Form\Part\PartBaseType.php:246 + + + part.edit.master_attachment + Miniaturka + + + + + Part-DB1\src\Form\Part\PartBaseType.php:295 + Part-DB1\src\Form\Part\PartBaseType.php:276 + src\Form\PartType.php:91 + + + part.edit.save + Zapisz zmiany + + + + + Part-DB1\src\Form\Part\PartBaseType.php:296 + Part-DB1\src\Form\Part\PartBaseType.php:277 + src\Form\PartType.php:92 + + + part.edit.reset + Zresetuj zmiany + + + + + Part-DB1\src\Form\Part\PartBaseType.php:105 + Part-DB1\src\Form\Part\PartBaseType.php:103 + + + part.edit.name.placeholder + np. BC547 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:115 + Part-DB1\src\Form\Part\PartBaseType.php:113 + + + part.edit.description.placeholder + np. NPN 45V, 0,1A, 0,5W + + + + + Part-DB1\src\Form\Part\PartBaseType.php:123 + Part-DB1\src\Form\Part\PartBaseType.php:121 + + + part.editmininstock.placeholder + np. 1 + + + + + Part-DB1\src\Form\Part\PartLotType.php:69 + Part-DB1\src\Form\Part\PartLotType.php:69 + + + part_lot.edit.description + Opis + + + + + Part-DB1\src\Form\Part\PartLotType.php:78 + Part-DB1\src\Form\Part\PartLotType.php:78 + + + part_lot.edit.location + Miejsce przechowywania + + + + + Part-DB1\src\Form\Part\PartLotType.php:89 + Part-DB1\src\Form\Part\PartLotType.php:89 + + + part_lot.edit.amount + Ilość + + + + + Part-DB1\src\Form\Part\PartLotType.php:98 + Part-DB1\src\Form\Part\PartLotType.php:97 + + + part_lot.edit.instock_unknown + Ilość nieznana + + + + + Part-DB1\src\Form\Part\PartLotType.php:109 + Part-DB1\src\Form\Part\PartLotType.php:108 + + + part_lot.edit.needs_refill + Wymaga uzupełnienia + + + + + Part-DB1\src\Form\Part\PartLotType.php:120 + Part-DB1\src\Form\Part\PartLotType.php:119 + + + part_lot.edit.expiration_date + Data wygaśnięcia + + + + + Part-DB1\src\Form\Part\PartLotType.php:128 + Part-DB1\src\Form\Part\PartLotType.php:125 + + + part_lot.edit.comment + Komentarz + + + + + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + + + perm.group.other + Różne + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + + + tfa_google.enable + Włącz aplikację uwierzytelniającą + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + + + tfa_google.disable + Dezaktywacja aplikacji uwierzytelniającej + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + + + google_confirmation + Kod potwierdzenia + + + + + Part-DB1\src\Form\UserSettingsType.php:108 + Part-DB1\src\Form\UserSettingsType.php:108 + src\Form\UserSettingsType.php:46 + + + user.timezone.label + Strefa czasowa + + + + + Part-DB1\src\Form\UserSettingsType.php:133 + Part-DB1\src\Form\UserSettingsType.php:132 + + + user.currency.label + Preferowana waluta + + + + + Part-DB1\src\Form\UserSettingsType.php:140 + Part-DB1\src\Form\UserSettingsType.php:139 + src\Form\UserSettingsType.php:53 + + + save + Zapisz zmiany + + + + + Part-DB1\src\Form\UserSettingsType.php:141 + Part-DB1\src\Form\UserSettingsType.php:140 + src\Form\UserSettingsType.php:54 + + + reset + Porzuć zmiany + + + + + Part-DB1\src\Form\UserSettingsType.php:104 + Part-DB1\src\Form\UserSettingsType.php:104 + src\Form\UserSettingsType.php:45 + + + user_settings.language.placeholder + Język obowiązujący na serwerze + + + + + Part-DB1\src\Form\UserSettingsType.php:115 + Part-DB1\src\Form\UserSettingsType.php:115 + src\Form\UserSettingsType.php:48 + + + user_settings.timezone.placeholder + Strefa czasowa obejmująca cały serwer + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + + + attachment.label + Załącznik + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + + + attachment_type.label + Typ załącznika + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + + + project.label + Projekt + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + + + measurement_unit.label + Jednostka pomiarowa + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + + + currency.label + Waluta + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + + + orderdetail.label + Szczegóły zamówienia + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + + + pricedetail.label + Szczegóły ceny + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + + + user.label + Użytkownik + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 + + + parameter.label + Parametr + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 + + + label_profile.label + Profil etykiety + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 + new + + + log.element_deleted.old_name.unknown + Nieznany + + + + + Part-DB1\src\Services\MarkdownParser.php:73 + Part-DB1\src\Services\MarkdownParser.php:73 + + + markdown.loading + Ładowanie Markdown. Jeśli ta wiadomość nie zniknie, spróbuj odświeżyć stronę. + + + + + Part-DB1\src\Services\PasswordResetManager.php:98 + Part-DB1\src\Services\PasswordResetManager.php:98 + + + pw_reset.email.subject + Resetowanie hasła do konta Part-DB + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + + + tree.tools.tools + Narzędzia + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 + src\Services\ToolsTreeBuilder.php:74 + + + tree.tools.edit + Edycja + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + src\Services\ToolsTreeBuilder.php:81 + + + tree.tools.show + Pokaż + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + + + tree.tools.system + System + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 + + + tree.tools.tools.label_dialog + Generator etykiet + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 + + + tree.tools.tools.label_scanner + Skaner + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 + src\Services\ToolsTreeBuilder.php:62 + + + tree.tools.edit.attachment_types + Typy załączników + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 + src\Services\ToolsTreeBuilder.php:64 + + + tree.tools.edit.categories + Kategorie + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 + src\Services\ToolsTreeBuilder.php:66 + + + tree.tools.edit.projects + Projekty + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 + src\Services\ToolsTreeBuilder.php:68 + + + tree.tools.edit.suppliers + Dostawcy + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 + src\Services\ToolsTreeBuilder.php:70 + + + tree.tools.edit.manufacturer + Producenci + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 + + + tree.tools.edit.storelocation + Miejsca przechowywania + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 + + + tree.tools.edit.footprint + Pola lutownicze + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 + + + tree.tools.edit.currency + Waluty + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 + + + tree.tools.edit.measurement_unit + Jednostka miary + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.edit.label_profile + Profile etykiet + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 + + + tree.tools.edit.part + Nowy komponent + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + src\Services\ToolsTreeBuilder.php:77 + + + tree.tools.show.all_parts + Pokaż wszystkie części + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.show.all_attachments + Załączniki + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 + new + + + tree.tools.show.statistics + Statystyki + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 + + + tree.tools.system.users + Użytkownicy + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 + + + tree.tools.system.groups + Grupy + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 + new + + + tree.tools.system.event_log + Dziennik zdarzeń + + + + + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + src\Services\TreeBuilder.php:124 + + + entity.tree.new + Nowy element + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 + obsolete + + + attachment.external_file + Plik zewnętrzny + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + obsolete + + + attachment.edit + Edycja + + + + + Part-DB1\templates\_navbar.html.twig:27 + templates\base.html.twig:88 + obsolete + + + barcode.scan + Skanuj kod kreskowy + + + + + Part-DB1\src\Form\UserSettingsType.php:119 + src\Form\UserSettingsType.php:49 + obsolete + + + user.theme.label + Motyw + + + + + Part-DB1\src\Form\UserSettingsType.php:129 + src\Form\UserSettingsType.php:50 + obsolete + + + user_settings.theme.placeholder + Motyw dla całego serwera + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 + new + obsolete + + + log.user_login.ip + IP + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:169 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:207 + new + obsolete + + + log.undo_mode.undo + Zmiana cofnięta + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:171 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:209 + new + obsolete + + + log.undo_mode.revert + Element przywrócony + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 + new + obsolete + + + log.element_created.original_instock + Stary stan magazynowy + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 + new + obsolete + + + log.element_deleted.old_name + Stara nazwa + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 + new + obsolete + + + log.element_edited.changed_fields + Zmienione pola + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 + new + obsolete + + + log.instock_changed.comment + Komentarz + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 + new + obsolete + + + log.collection_deleted.deleted + Usunięty element: + + + + + templates\base.html.twig:81 + obsolete + obsolete + + + go.exclamation + Idź! + + + + + templates\base.html.twig:109 + obsolete + obsolete + + + language.english + Angielski + + + + + templates\base.html.twig:112 + obsolete + obsolete + + + language.german + Niemiecki + + + + + obsolete + obsolete + + + flash.password_change_needed + Wymagana zmiana hasła! + + + + + obsolete + obsolete + + + attachment.table.type + Typ załącznika + + + + + obsolete + obsolete + + + attachment.table.element + Element powiązany + + + + + obsolete + obsolete + + + attachment.edit.isPicture + Obraz? + + + + + obsolete + obsolete + + + attachment.edit.is3DModel + Model 3D? + + + + + obsolete + obsolete + + + attachment.edit.isBuiltin + Wbudowany zasób? + + + + + obsolete + obsolete + + + category.edit.default_comment.placeholder + Np. przydatne do przełączania + + + + + obsolete + obsolete + + + tfa_backup.regenerate_codes + Wygeneruj nowe kody kopii zapasowych + + + + + obsolete + obsolete + + + validator.noneofitschild.self + Element nie może być swoim własnym elementem nadrzędnym! + + + + + obsolete + obsolete + + + validator.noneofitschild.children + Nie możesz przypisać elementu podrzędnego jako elementu nadrzędnego (spowodowałoby to pętle)! + + + + + obsolete + obsolete + + + validator.part_lot.location_full.no_increasment + Miejsce przechowywania zostało oznaczone jako pełne, więc nie można zwiększyć ilości w magazynie. (Nowa ilość maksymalna {{ old_amount }}) + + + + + obsolete + obsolete + + + validator.part_lot.location_full + Lokalizacja jest pełna. Nie można do niego dodawać nowych części. + + + + + obsolete + obsolete + + + validator.part_lot.only_existing + Nie można dodać nowych części do tej lokalizacji, ponieważ jest ona oznaczona jako „Tylko istniejące” + + + + + obsolete + obsolete + + + validator.part_lot.single_part + Miejsce przechowywania zostało oznaczone jako „Tylko jeden komponent”, więc nie można dodać nowego komponentu. + + + + + obsolete + obsolete + + + m_status.active.help + Komponent jest obecnie i w najbliższej przyszłości w produkcji + + + + + obsolete + obsolete + + + m_status.announced.help + Komponent został zgłoszony, ale nie jest jeszcze dostępny. + + + + + obsolete + obsolete + + + m_status.discontinued.help + Komponent nie jest już produkowany. + + + + + obsolete + obsolete + + + m_status.eol.help + Produkcja komponentu wkrótce zostanie zakończona. + + + + + obsolete + obsolete + + + m_status.nrfnd.help + Komponent jest nadal produkowany, ale nie zaleca się już jego stosowania w nowych konstrukcjach. + + + + + obsolete + obsolete + + + m_status.unknown.help + Status produkcji nie jest znany. + + + + + obsolete + obsolete + + + flash.success + Sukces + + + + + obsolete + obsolete + + + flash.error + Błąd + + + + + obsolete + obsolete + + + flash.warning + Ostrzeżenie + + + + + obsolete + obsolete + + + flash.notice + Powiadomienie + + + + + obsolete + obsolete + + + flash.info + Informacja + + + + + obsolete + obsolete + + + validator.noLockout + Nie można samodzielnie wycofać uprawnienia "zmień uprawnienia", aby zapobiec przypadkowemu zablokowaniu. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter + Dozwolone rozszerzenia plików + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.help + W tym miejscu można określić rozdzielaną przecinkami listę rozszerzeń plików lub typów MIME, które musi mieć przesłany plik z tym typem załącznika. Aby zezwolić na wszystkie obsługiwane pliki obrazów, można użyć image/*. + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.placeholder + np. .txt, application/pdf, image/* + + + + + src\Form\PartType.php:63 + obsolete + obsolete + + + part.name.placeholder + Np. BC547 + + + + + obsolete + obsolete + + + entity.edit.not_selectable + Nie można wybrać + + + + + obsolete + obsolete + + + entity.edit.not_selectable.help + Jeżeli ta opcja jest włączona, element nie może być przypisany do właściwości komponentu. Przydatne, jeśli ten element jest używany tylko do grupowania. + + + + + obsolete + obsolete + + + bbcode.hint + Możesz użyć tutaj BBCode (np. [b]Pogrubienie[/b]). + + + + + obsolete + obsolete + + + entity.create + Utwórz element + + + + + obsolete + obsolete + + + entity.edit.save + Zapisz + + + + + obsolete + obsolete + + + category.edit.disable_footprints + Wyłącz pola lutownicze + + + + + obsolete + obsolete + + + category.edit.disable_footprints.help + Jeśli ta opcja jest aktywna, właściwość "footprint" jest wyłączona dla wszystkich części z tą kategorią. + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers + Wyłącz producentów + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers.help + Jeśli ta opcja jest aktywna, właściwość "producent" jest wyłączona dla wszystkich części z tą kategorią. + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets + Wyłącz automatyczne łącza do kart katalogowych + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets.help + Jeśli ta opcja jest aktywna, dla części z tą kategorią nie są tworzone automatyczne linki do kart katalogowych. + + + + + obsolete + obsolete + + + category.edit.disable_properties + Wyłącz właściwości + + + + + obsolete + obsolete + + + category.edit.disable_properties.help + Jeśli ta opcja jest aktywna, właściwość "komponent" są wyłączone dla części z tą kategorią. + + + + + obsolete + obsolete + + + category.edit.partname_hint + Podpowiedź do nazwy części + + + + + obsolete + obsolete + + + category.edit.partname_hint.placeholder + Np. 100 nF + + + + + obsolete + obsolete + + + category.edit.partname_regex + Filtr nazwy + + + + + obsolete + obsolete + + + category.edit.default_description + Opis domyślny + + + + + obsolete + obsolete + + + category.edit.default_description.placeholder + Np. Kondensator, 10mm x 10mm, SMD + + + + + obsolete + obsolete + + + category.edit.default_comment + Komentarz domyślny + + + + + obsolete + obsolete + + + company.edit.address + Adres + + + + + obsolete + obsolete + + + company.edit.address.placeholder + np. PrzykładowaUlica 314 +PrzykładowaMiejscowość + + + + + obsolete + obsolete + + + company.edit.phone_number + Nr telefonu + + + + + obsolete + obsolete + + + company.edit.phone_number.placeholder + +48 12345 6789 + + + + + obsolete + obsolete + + + company.edit.fax_number + Nr faksu + + + + + obsolete + obsolete + + + company.edit.email + e-mail + + + + + obsolete + obsolete + + + company.edit.email.placeholder + np. kontakt@foo.bar + + + + + obsolete + obsolete + + + company.edit.website + Strona domowa + + + + + obsolete + obsolete + + + company.edit.website.placeholder + np. https://www.foo.bar + + + + + obsolete + obsolete + + + company.edit.auto_product_url + Adres URL produktu + + + + + obsolete + obsolete + + + company.edit.auto_product_url.help + To pole służy do określenia łącza do komponentu na stronie firmy. %PARTNUMBER% Zostanie zastąpiony numerem zamówienia. + + + + + obsolete + obsolete + + + company.edit.auto_product_url.placeholder + https://foo.bar/product/%PARTNUMBER% + + + + + obsolete + obsolete + + + currency.edit.iso_code + Kod ISO + + + + + obsolete + obsolete + + + currency.edit.exchange_rate + Kurs wymiany + + + + + obsolete + obsolete + + + footprint.edit.3d_model + Model 3D + + + + + obsolete + obsolete + + + mass_creation.lines + Dane wejściowe + + + + + obsolete + obsolete + + + mass_creation.lines.placeholder + Element 1 + Element 1.1 + Element 1.1.1 + Element 1.2 +Element 2 +Element 3 + + + + + obsolete + obsolete + + + entity.mass_creation.btn + Utwórz + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer + Czy liczba całkowita + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer.help + If this option is activated, all values with this unit will be rounded to whole numbers. + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix + Jeśli ta opcja jest aktywna, wszystkie wartości w tej jednostce będą zaokrąglane do liczb całkowitych. + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix.help + Jeśli ta opcja jest włączona, wartości są wyprowadzane z przedrostkami SI (np. 1,2 kg zamiast 1200 g). + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol + Symbol jednostki + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol.placeholder + Np. m + + + + + obsolete + obsolete + + + storelocation.edit.is_full.label + Miejsce magazynowania pełne + + + + + obsolete + obsolete + + + storelocation.edit.is_full.help + Jeśli ta opcja jest wybrana, nie jest możliwe dodawanie nowych komponentów do tej lokalizacji ani zwiększanie ilości istniejących części. + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.label + Ograniczenie do istniejących komponentów + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.help + Jeśli ta opcja jest aktywna, nie można dodawać nowych części do tej lokalizacji magazynu, ale można zwiększyć ilość istniejących części. + + + + + obsolete + obsolete + + + storelocation.only_single_part.label + Tylko pojedyncza część + + + + + obsolete + obsolete + + + storelocation.only_single_part.help + Jeśli ta opcja jest aktywna, tylko pojedyncza część (z każdą ilością) może być przypisana do tej lokalizacji przechowywania. Przydatne w przypadku małych skrzynek SMD lub podajników. + + + + + obsolete + obsolete + + + storelocation.storage_type.label + Typ pamięci masowej + + + + + obsolete + obsolete + + + storelocation.storage_type.help + W tym miejscu można wybrać jednostkę miary, którą musi mieć część, aby mogła zostać przypisana do tego miejsca przechowywania + + + + + obsolete + obsolete + + + supplier.edit.default_currency + Domyślna waluta + + + + + obsolete + obsolete + + + supplier.shipping_costs.label + Koszty wysyłki + + + + + obsolete + obsolete + + + user.username.placeholder + np. jan.nowak + + + + + obsolete + obsolete + + + user.firstName.placeholder + Np. Jan + + + + + obsolete + obsolete + + + user.lastName.placeholder + Np. Nowak + + + + + obsolete + obsolete + + + user.email.placeholder + j.doe@ecorp.com + + + + + obsolete + obsolete + + + user.department.placeholder + Np. Rozwój + + + + + obsolete + obsolete + + + user.settings.pw_new.label + Nowe hasło + + + + + obsolete + obsolete + + + user.settings.pw_confirm.label + Potwierdź nowe hasło + + + + + obsolete + obsolete + + + user.edit.needs_pw_change + Użytkownik musi zmienić hasło + + + + + obsolete + obsolete + + + user.edit.user_disabled + Użytkownik wyłączony (brak możliwości logowania) + + + + + obsolete + obsolete + + + user.create + Utwórz użytkownika + + + + + obsolete + obsolete + + + user.edit.save + Zapisz + + + + + obsolete + obsolete + + + entity.edit.reset + Porzuć zmiany + + + + + templates\Parts\show_part_info.html.twig:166 + obsolete + obsolete + + + part.withdraw.btn + Usunąć + + + + + templates\Parts\show_part_info.html.twig:171 + obsolete + obsolete + + + part.withdraw.comment: + Komentarz/Cel + + + + + templates\Parts\show_part_info.html.twig:189 + obsolete + obsolete + + + part.add.caption + Dodaj komponent + + + + + templates\Parts\show_part_info.html.twig:194 + obsolete + obsolete + + + part.add.btn + Dodaj + + + + + templates\Parts\show_part_info.html.twig:199 + obsolete + obsolete + + + part.add.comment + Komentarz/Cel + + + + + templates\AdminPages\CompanyAdminBase.html.twig:15 + obsolete + obsolete + + + admin.comment + Komentarz + + + + + src\Form\PartType.php:83 + obsolete + obsolete + + + manufacturer_url.label + Adres URL producenta + + + + + src\Form\PartType.php:66 + obsolete + obsolete + + + part.description.placeholder + np. NPN 45V 0,1A 0,5W + + + + + src\Form\PartType.php:69 + obsolete + obsolete + + + part.instock.placeholder + np. 10 + + + + + src\Form\PartType.php:72 + obsolete + obsolete + + + part.mininstock.placeholder + np. 5 + + + + + obsolete + obsolete + + + part.order.price_per + Cena za + + + + + obsolete + obsolete + + + part.withdraw.caption + Usuń komponent + + + + + obsolete + obsolete + + + datatable.datatable.lengthMenu + _MENU_ + + + + + obsolete + obsolete + + + perm.group.parts + Części + + + + + obsolete + obsolete + + + perm.group.structures + Struktury danych + + + + + obsolete + obsolete + + + perm.group.system + System + + + + + obsolete + obsolete + + + perm.parts + Części + + + + + obsolete + obsolete + + + perm.read + Podgląd + + + + + obsolete + obsolete + + + perm.edit + Edycja + + + + + obsolete + obsolete + + + perm.create + Utwórz + + + + + obsolete + obsolete + + + perm.part.move + Zmiana kategorii + + + + + obsolete + obsolete + + + perm.delete + Usuń + + + + + obsolete + obsolete + + + perm.part.search + Wyszukiwanie + + + + + obsolete + obsolete + + + perm.part.all_parts + Wszystkie części + + + + + obsolete + obsolete + + + perm.part.no_price_parts + Lista komponentów bez informacji o cenie + + + + + obsolete + obsolete + + + perm.part.obsolete_parts + Części przestarzałe + + + + + obsolete + obsolete + + + perm.part.unknown_instock_parts + Lista komponentów z nieznanym stanem magazynowym + + + + + obsolete + obsolete + + + perm.part.change_favorite + Zmiana statusu ulubiony + + + + + obsolete + obsolete + + + perm.part.show_favorite + Ulubione komponenty + + + + + obsolete + obsolete + + + perm.part.show_last_edit_parts + Pokaż ostatnio edytowane/dodane komponenty + + + + + obsolete + obsolete + + + perm.part.show_users + Pokaż ostatnio edytowanego użytkownika + + + + + obsolete + obsolete + + + perm.part.show_history + Pokaż historię + + + + + obsolete + obsolete + + + perm.part.name + Nazwa + + + + + obsolete + obsolete + + + perm.part.description + Opis + + + + + obsolete + obsolete + + + perm.part.instock + Dostępne + + + + + obsolete + obsolete + + + perm.part.mininstock + Minimalny stan na magazynie + + + + + obsolete + obsolete + + + perm.part.comment + Komentarz + + + + + obsolete + obsolete + + + perm.part.storelocation + Miejsce przechowywania + + + + + obsolete + obsolete + + + perm.part.manufacturer + Producent + + + + + obsolete + obsolete + + + perm.part.orderdetails + Szczegóły zamówienia + + + + + obsolete + obsolete + + + perm.part.prices + Ceny + + + + + obsolete + obsolete + + + perm.part.attachments + Załączniki do plików + + + + + obsolete + obsolete + + + perm.part.order + Zamówienia + + + + + obsolete + obsolete + + + perm.storelocations + Miejsca przechowywania + + + + + obsolete + obsolete + + + perm.move + Przesunięcie + + + + + obsolete + obsolete + + + perm.list_parts + Lista części + + + + + obsolete + obsolete + + + perm.part.footprints + Pola lutownicze + + + + + obsolete + obsolete + + + perm.part.categories + Kategorie + + + + + obsolete + obsolete + + + perm.part.supplier + Dostawcy + + + + + obsolete + obsolete + + + perm.part.manufacturers + Producenci + + + + + obsolete + obsolete + + + perm.projects + Projekty + + + + + obsolete + obsolete + + + perm.part.attachment_types + Typy załączników + + + + + obsolete + obsolete + + + perm.tools.import + Import + + + + + obsolete + obsolete + + + perm.tools.labels + Etykiety + + + + + obsolete + obsolete + + + perm.tools.calculator + Kalkulator dla rezystorów + + + + + obsolete + obsolete + + + perm.tools.footprints + Pola lutownicze + + + + + obsolete + obsolete + + + perm.tools.ic_logos + Loga producentów + + + + + obsolete + obsolete + + + perm.tools.statistics + Statystyki + + + + + obsolete + obsolete + + + perm.edit_permissions + Edytuj uprawnienia + + + + + obsolete + obsolete + + + perm.users.edit_user_name + Edytuj nazwę użytkownika + + + + + obsolete + obsolete + + + perm.users.edit_change_group + Zmień grupę + + + + + obsolete + obsolete + + + perm.users.edit_infos + Edytuj informacje + + + + + obsolete + obsolete + + + perm.users.edit_permissions + Edytuj uprawnienia + + + + + obsolete + obsolete + + + perm.users.set_password + Ustaw hasło + + + + + obsolete + obsolete + + + perm.users.change_user_settings + Zmiana ustawień użytkownika + + + + + obsolete + obsolete + + + perm.database.see_status + Pokaż status + + + + + obsolete + obsolete + + + perm.database.update_db + Aktualizacja bazy danych + + + + + obsolete + obsolete + + + perm.database.read_db_settings + Odczyt ustawień bazy danych + + + + + obsolete + obsolete + + + perm.database.write_db_settings + Zapis ustawień bazy danych + + + + + obsolete + obsolete + + + perm.config.read_config + Czytaj konfigurację + + + + + obsolete + obsolete + + + perm.config.edit_config + Edytuj konfigurację + + + + + obsolete + obsolete + + + perm.config.server_info + Informacje o serwerze + + + + + obsolete + obsolete + + + perm.config.use_debug + Użyj narzędzi do debugowania + + + + + obsolete + obsolete + + + perm.show_logs + Pokaż dzienniki + + + + + obsolete + obsolete + + + perm.delete_logs + Usuń logi + + + + + obsolete + obsolete + + + perm.self.edit_infos + Edytuj informacje + + + + + obsolete + obsolete + + + perm.self.edit_username + Edytuj nazwę użytkownika + + + + + obsolete + obsolete + + + perm.self.show_permissions + Wyświetlanie uprawnień + + + + + obsolete + obsolete + + + perm.self.show_logs + Pokaż własne wpisy dziennika + + + + + obsolete + obsolete + + + perm.self.create_labels + Tworzenie etykiet + + + + + obsolete + obsolete + + + perm.self.edit_options + Opcje edycji + + + + + obsolete + obsolete + + + perm.self.delete_profiles + Usuń profile + + + + + obsolete + obsolete + + + perm.self.edit_profiles + Edytuj profile + + + + + obsolete + obsolete + + + perm.part.tools + Narzędzia + + + + + obsolete + obsolete + + + perm.groups + Grupy + + + + + obsolete + obsolete + + + perm.users + Użytkownicy + + + + + obsolete + obsolete + + + perm.database + Baza danych + + + + + obsolete + obsolete + + + perm.config + Konfiguracja + + + + + obsolete + obsolete + + + perm.system + System + + + + + obsolete + obsolete + + + perm.self + Edytuj własnego użytkownika + + + + + obsolete + obsolete + + + perm.labels + Etykiety + + + + + obsolete + obsolete + + + perm.part.category + Kategoria + + + + + obsolete + obsolete + + + perm.part.minamount + Ilość minimalna + + + + + obsolete + obsolete + + + perm.part.footprint + Układ padów (footprint) + + + + + obsolete + obsolete + + + perm.part.mpn + MPN + + + + + obsolete + obsolete + + + perm.part.status + Status produkcji + + + + + obsolete + obsolete + + + perm.part.tags + Tagi + + + + + obsolete + obsolete + + + perm.part.unit + Jednostka miary + + + + + obsolete + obsolete + + + perm.part.mass + Masa + + + + + obsolete + obsolete + + + perm.part.lots + Miejsca przechowywania + + + + + obsolete + obsolete + + + perm.show_users + Pokaż ostatnio edytowanego użytkownika + + + + + obsolete + obsolete + + + perm.currencies + Waluty + + + + + obsolete + obsolete + + + perm.measurement_units + Jednostka miary + + + + + obsolete + obsolete + + + user.settings.pw_old.label + Stare hasło + + + + + obsolete + obsolete + + + pw_reset.submit + Resetowanie hasła + + + + + obsolete + obsolete + + + u2f_two_factor + Klucz bezpieczeństwa (U2F) + + + + + obsolete + obsolete + + + google + Google + + + + + tfa.provider.webauthn_two_factor_provider + Klucz zabezpieczeń + + + + + obsolete + obsolete + + + tfa.provider.google + Aplikacja Authenticator + + + + + obsolete + obsolete + + + Login successful + Zalogowano poprawnie + + + + + obsolete + obsolete + + + log.type.exception + Nieobsługiwany wyjątek (przestarzały) + + + + + obsolete + obsolete + + + log.type.user_login + Logowanie użytkownika + + + + + obsolete + obsolete + + + log.type.user_logout + Wylogowanie użytkownika + + + + + obsolete + obsolete + + + log.type.unknown + Nieznany + + + + + obsolete + obsolete + + + log.type.element_created + Utworzony element + + + + + obsolete + obsolete + + + log.type.element_edited + Edytowano komponent + + + + + obsolete + obsolete + + + log.type.element_deleted + Element usunięty + + + + + obsolete + obsolete + + + log.type.database_updated + Baza danych zaktualizowana + + + + + obsolete + + + perm.revert_elements + Odwrócenie elementu + + + + + obsolete + + + perm.show_history + Pokaż historię + + + + + obsolete + + + perm.tools.lastActivity + Pokaż ostatnie działanie + + + + + obsolete + + + perm.tools.timeTravel + Pokaż stare wersje elementów (podróż w czasie) + + + + + obsolete + + + tfa_u2f.key_added_successful + Klucz bezpieczeństwa został dodany pomyślnie. + + + + + obsolete + + + Username + Nazwa użytkownika + + + + + obsolete + + + log.type.security.google_disabled + Aplikacja Authenticator wyłączona + + + + + obsolete + + + log.type.security.u2f_removed + Usunięty klucz bezpieczeństwa + + + + + obsolete + + + log.type.security.u2f_added + Dodano klucz bezpieczeństwa + + + + + obsolete + + + log.type.security.backup_keys_reset + Regeneracja kluczy zapasowych + + + + + obsolete + + + log.type.security.google_enabled + Włączona aplikacja Authenticator + + + + + obsolete + + + log.type.security.password_changed + Hasło zostało zmienione + + + + + obsolete + + + log.type.security.trusted_device_reset + Zaufane urządzenia zresetowane + + + + + obsolete + + + log.type.collection_element_deleted + Usunięty element kolekcji + + + + + obsolete + + + log.type.security.password_reset + Resetowanie hasła + + + + + obsolete + + + log.type.security.2fa_admin_reset + Resetowanie dwuskładnikowe przez administratora + + + + + obsolete + + + log.type.user_not_allowed + Próba nieautoryzowanego dostępu + + + + + obsolete + + + log.database_updated.success + Sukces + + + + + obsolete + + + label_options.barcode_type.2D + 2D + + + + + obsolete + + + label_options.barcode_type.1D + 1D + + + + + obsolete + + + perm.part.parameters + Parametry + + + + + obsolete + + + perm.attachment_show_private + Wyświetlanie prywatnych załączników + + + + + obsolete + + + perm.tools.label_scanner + Skaner etykiet + + + + + obsolete + + + perm.self.read_profiles + Wczytaj profile + + + + + obsolete + + + perm.self.create_profiles + Utwórz profile + + + + + obsolete + + + perm.labels.use_twig + Użyj trybu Twig + + + + + label_profile.showInDropdown + Pokaż w szybkim wyborze + + + + + group.edit.enforce_2fa + Wymuś uwierzytelnianie dwuskładnikowe (2FA) + + + + + group.edit.enforce_2fa.help + Jeśli ta opcja jest włączona, każdy bezpośredni członek tej grupy musi skonfigurować co najmniej jeden drugi czynnik uwierzytelniania. Zalecane dla grup administracyjnych z wieloma uprawnieniami. + + + + + selectpicker.empty + Nic nie wybrano + + + + + selectpicker.nothing_selected + Nic nie wybrano + + + + + entity.delete.must_not_contain_parts + Element "%PATH%" nadal zawiera komponenty! Musisz przenieść części, aby móc usunąć ten element. + + + + + entity.delete.must_not_contain_attachments + Typ załącznika nadal zawiera załączniki. Zmień ich typ, aby móc usunąć ten typ załącznika. + + + + + entity.delete.must_not_contain_prices + Waluta nadal zawiera szczegóły cenowe. Musisz zmienić ich walutę, aby móc usunąć ten element. + + + + + entity.delete.must_not_contain_users + Użytkownicy nadal korzystają z tej grupy! Zmień swoją grupę, aby móc usunąć tę grupę. + + + + + part.table.edit + Edytuj + + + + + part.table.edit.title + Edytuj część + + + + + part_list.action.action.title + Wybierz akcję + + + + + part_list.action.action.group.favorite + Ulubione + + + + + part_list.action.action.favorite + Dodaj do ulubionych + + + + + part_list.action.action.unfavorite + Usuń z ulubionych + + + + + part_list.action.action.group.change_field + Zmień pole + + + + + part_list.action.action.change_category + Zmień kategorię + + + + + part_list.action.action.change_footprint + Zmień Układ padów (footprint) + + + + + part_list.action.action.change_manufacturer + Zmień producenta + + + + + part_list.action.action.change_unit + Zmień jednostkę + + + + + part_list.action.action.delete + Usuń + + + + + part_list.action.submit + OK + + + + + part_list.action.part_count + Wybrano %count% części! + + + + + company.edit.quick.website + Otwórz stronę + + + + + company.edit.quick.email + Wyślij e-mail + + + + + company.edit.quick.phone + Zadzwoń + + + + + company.edit.quick.fax + Wyślij FAX + + + + + company.fax_number.placeholder + Np. +48 123 456 789 + + + + + part.edit.save_and_clone + Zapisz i sklonuj + + + + + validator.file_ext_not_allowed + Rozszerzenie pliku nie jest dozwolone dla tego typu załącznika. + + + + + tools.reel_calc.title + Kalkulator szpul SMD + + + + + tools.reel_calc.inner_dia + Średnica wewnętrzna + + + + + tools.reel_calc.outer_dia + Wymiar zewnętrzny + + + + + tools.reel_calc.tape_thick + Grubość taśmy + + + + + tools.reel_calc.part_distance + Odstęp + + + + + tools.reel_calc.update + Aktualizuj + + + + + tools.reel_calc.parts_per_meter + Komponent na metry + + + + + tools.reel_calc.result_length + Długość taśmy + + + + + tools.reel_calc.result_amount + Przybliżona ilość elementów + + + + + tools.reel_calc.outer_greater_inner_error + Błąd: średnica zewnętrzna musi być większa niż średnica wewnętrzna! + + + + + tools.reel_calc.missing_values.error + Proszę uzupełnić wszystkie wartości! + + + + + tools.reel_calc.load_preset + Załaduj ustawienia + + + + + tools.reel_calc.explanation + Ten kalkulator daje Ci oszacowanie, ile części pozostało na szpuli SMD. Zmierz zanotowane wymiary na szpuli (lub użyj któregoś z ustawień wstępnych) i kliknij "Aktualizuj", aby uzyskać wynik. + + + + + perm.tools.reel_calculator + Kalkulator rolek SMD + + + + + tree.tools.tools.reel_calculator + Kalkulator rolek SMD + + + + + user.pw_change_needed.flash + Twoje hasło musi zostać zmienione! Ustaw nowe hasło. + + + + + tree.root_node.text + Węzeł główny + + + + + part_list.action.select_null + Pusty element + + + + + part_list.action.delete-title + Czy na pewno chcesz usunąć te części? + + + + + part_list.action.delete-message + Te komponenty i wszystkie powiązane informacje (załączniki, informacje o cenach itp.) zostaną usunięte. Tego nie da się cofnąć! + + + + + part.table.actions.success + Akcje zakończyły się pomyślnie. + + + + + attachment.edit.delete.confirm + Czy na pewno chcesz usunąć ten załącznik? + + + + + filter.text_constraint.value.operator.EQ + Jest równe + + + + + filter.text_constraint.value.operator.NEQ + Nie jest równe + + + + + filter.text_constraint.value.operator.STARTS + Rozpoczyna się od + + + + + filter.text_constraint.value.operator.CONTAINS + Zawiera + + + + + filter.text_constraint.value.operator.ENDS + Kończy się na + + + + + filter.text_constraint.value.operator.LIKE + LIKE wzór + + + + + filter.text_constraint.value.operator.REGEX + Wyrażenie regularne + + + + + filter.number_constraint.value.operator.BETWEEN + Pomiędzy + + + + + filter.number_constraint.AND + i + + + + + filter.entity_constraint.operator.EQ + Czy (bez elementów podrzędnych) + + + + + filter.entity_constraint.operator.NEQ + Nie jest (bez elementów podrzędnych) + + + + + filter.entity_constraint.operator.INCLUDING_CHILDREN + Czy jest (w tym elementy podrzędne) + + + + + filter.entity_constraint.operator.EXCLUDING_CHILDREN + Nie jest (w tym elementy podrzędne) + + + + + part.filter.dbId + Identyfikator ID bazy danych + + + + + filter.tags_constraint.operator.ANY + Którykolwiek z tagów + + + + + filter.tags_constraint.operator.ALL + Wszystkie tagi + + + + + filter.tags_constraint.operator.NONE + Żaden z tagów + + + + + part.filter.lot_count + Liczba partii + + + + + part.filter.attachments_count + Ilość załączników + + + + + part.filter.orderdetails_count + Liczba informacji o zamówieniu + + + + + part.filter.lotExpirationDate + Data ważności zapasów komponentów + + + + + part.filter.lotNeedsRefill + Każda partia potrzebuje uzupełnienia + + + + + part.filter.lotUnknwonAmount + Zapasy o nieznanej ilości + + + + + part.filter.attachmentName + Nazwa załącznika + + + + + filter.choice_constraint.operator.ANY + Którykolwiek + + + + + filter.choice_constraint.operator.NONE + Żaden z + + + + + part.filter.amount_sum + Ilość całkowita + + + + + filter.submit + Aktualizuj + + + + + filter.discard + Porzuć zmiany + + + + + filter.clear_filters + Wyczyść filtry + + + + + filter.title + Filtr + + + + + filter.parameter_value_constraint.operator.= + Typ. Wartość = + + + + + filter.parameter_value_constraint.operator.!= + Typ. Wartość != + + + + + filter.parameter_value_constraint.operator.< + Typ. Wartość < + + + + + filter.parameter_value_constraint.operator.> + Typ. Wartość > + + + + + filter.parameter_value_constraint.operator.<= + Typ. Wartość <= + + + + + filter.parameter_value_constraint.operator.>= + Typ. Wartość >= + + + + + filter.parameter_value_constraint.operator.BETWEEN + Wartość typowa mieści się pomiędzy + + + + + filter.parameter_value_constraint.operator.IN_RANGE + W zakresie wartości + + + + + filter.parameter_value_constraint.operator.NOT_IN_RANGE + Poza zakresem wartości + + + + + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE + Większy niż zakres wartości + + + + + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE + Większy lub równy niż zakres wartości + + + + + filter.parameter_value_constraint.operator.LESS_THAN_RANGE + Mniejszy niż zakres wartości + + + + + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE + Mniejszy lub równy niż zakres wartości + + + + + filter.parameter_value_constraint.operator.RANGE_IN_RANGE + Zakres mieści się w przedziale wartości + + + + + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE + Zakres wychodzi poza przedział wartości + + + + + filter.text_constraint.value + Nie ustawiono żadnej wartości + + + + + filter.number_constraint.value1 + Nie ustawiono żadnej wartości + + + + + filter.number_constraint.value2 + Maksymalna wartość + + + + + filter.datetime_constraint.value1 + Brak ustawionej daty i godziny + + + + + filter.datetime_constraint.value2 + Maksymalna data/godzina + + + + + filter.constraint.add + Dodaj ograniczenie + + + + + part.filter.parameters_count + Liczba parametrów + + + + + part.filter.lotDescription + Opis partii + + + + + parts_list.search.searching_for + Wyszukiwanie części ze słowem kluczowym <b>%keyword%</b> + + + + + parts_list.search_options.caption + Włączono opcje wyszukiwania + + + + + attachment.table.element_type + Powiązany typ elementu + + + + + log.level.debug + Debug + + + + + log.level.info + Info + + + + + log.level.notice + Ogłoszenie + + + + + log.level.warning + Ostrzeżenie + + + + + log.level.error + Błąd + + + + + log.level.critical + Krytyczny + + + + + log.level.alert + Alarm + + + + + log.level.emergency + Awaryjny + + + + + log.type.security + Zdarzenie związane z bezpieczeństwem + + + + + log.type.instock_changed + [STARY] Zmieniono stan magazynowy + + + + + log.target_id + Identyfikator ID elementu docelowego + + + + + entity.info.parts_count_recursive + Liczba części z tym elementem lub jego pod elementami + + + + + tools.server_infos.title + Informacje o serwerze + + + + + permission.preset.read_only + Tylko do odczytu + + + + + permission.preset.read_only.desc + Zezwalaj tylko na operacje odczytu danych + + + + + permission.preset.all_inherit + Dziedzicz wszystko + + + + + permission.preset.all_inherit.desc + Ustaw wszystkie uprawnienia na "Dziedzicz" + + + + + permission.preset.all_forbid + Zabroń wszystkiego + + + + + permission.preset.all_forbid.desc + Ustaw wszystkie uprawnienia na "Zabroń" + + + + + permission.preset.all_allow + Zezwól na wszystko + + + + + permission.preset.all_allow.desc + Ustaw wszystkie uprawnienia na dozwolone + + + + + perm.server_infos + Informacje o serwerze + + + + + permission.preset.editor + Edytor + + + + + permission.preset.editor.desc + Zezwalaj na edycję komponentów i struktur danych + + + + + permission.preset.admin + Administrator + + + + + permission.preset.admin.desc + Zezwalaj na działania administracyjne + + + + + permission.preset.button + Zastosuj ustawienia wstępne + + + + + perm.attachments.show_private + Pokaż prywatne załączniki + + + + + perm.attachments.list_attachments + Pokaż wszystkie załączniki + + + + + user.edit.permission_success + Zestaw uprawnień został pomyślnie zastosowany. Sprawdź, czy nowe uprawnienia odpowiadają Twoim potrzebom. + + + + + perm.group.data + Dane + + + + + part_list.action.action.group.needs_review + Wymaga sprawdzenia + + + + + part_list.action.action.set_needs_review + Ustaw status "Wymaga sprawdzenia" + + + + + part_list.action.action.unset_needs_review + Wyczyść status "Wymaga sprawdzenia" + + + + + part.edit.ipn + Internal Part Number (IPN) + + + + + part.ipn.not_defined + Niezdefiniowany + + + + + part.table.ipn + IPN + + + + + currency.edit.update_rate + Pobieranie kursu wymiany + + + + + currency.edit.exchange_rate_update.unsupported_currency + Waluta nie jest obsługiwana przez dostawcę usług wymiany walut. Sprawdź konfigurację dostawcy kursów wymiany. + + + + + currency.edit.exchange_rate_update.generic_error + Nie można pobrać kursu wymiany. Sprawdź konfigurację dostawcy kursów wymiany walut. + + + + + currency.edit.exchange_rate_updated.success + Pomyślnie pobrano kurs wymiany. + + + + + project.bom.quantity + Ilość BOM + + + + + project.bom.mountnames + Minimalna ilość + + + + + project.bom.name + Nazwa + + + + + project.bom.comment + Komentarz + + + + + project.bom.part + Komponent + + + + + project.bom.add_entry + Dodaj wpis + + + + + part_list.action.group.projects + Projekty + + + + + part_list.action.projects.add_to_project + Dodaj części do projektu + + + + + project.bom.delete.confirm + Czy naprawdę chcesz usunąć ten wpis w BOM? + + + + + project.add_parts_to_project + Dodawanie komponentów do listy BOM projektu + + + + + part.info.add_part_to_project + Dodaj ten komponent do projektu + + + + + project_bom_entry.label + Wpis BOM + + + + + project.edit.status + Status projektu + + + + + project.status.draft + Wersja robocza + + + + + project.status.planning + W planowaniu + + + + + project.status.in_production + W produkcji + + + + + project.status.finished + Zakończony + + + + + project.status.archived + Zarchiwizowany + + + + + part.new_build_part.error.build_part_already_exists + Ten projekt ma już powiązany komponent. + + + + + project.edit.associated_build_part + Powiązany komponent produkcyjny + + + + + project.edit.associated_build_part.add + Dodaj komponent produkcyjny + + + + + project.edit.associated_build.hint + Ten komponent reprezentuje zbudowane instancje projektu, które są gdzieś przechowywane + + + + + part.info.projectBuildPart.hint + Ten komponent reprezentuje zbudowane instancje następującego projektu i jest z nim powiązany + + + + + part.is_build_part + Czy komponent produkcyjny + + + + + project.info.title + Informacje o projekcie + + + + + project.info.bom_entries_count + Wpisy BOM + + + + + project.info.sub_projects_count + Podprojekty + + + + + project.info.bom_add_parts + Dodawanie pozycji w BOM + + + + + project.info.info.label + Info + + + + + project.info.sub_projects.label + Podprojekty + + + + + project.bom.price + Cena + + + + + part.info.withdraw_modal.title.withdraw + Usuwanie części z partii + + + + + part.info.withdraw_modal.title.add + Dodawanie komponentów do partii + + + + + part.info.withdraw_modal.title.move + Przenoszenie komponentów do innej partii + + + + + part.info.withdraw_modal.amount + Ilość + + + + + part.info.withdraw_modal.move_to + Przenieś do + + + + + part.info.withdraw_modal.comment + Komentarz + + + + + part.info.withdraw_modal.comment.hint + Możesz ustawić tutaj komentarz opisujący, dlaczego wykonujesz tę operację (np. do czego potrzebujesz części). Informacje te zostaną zapisane w dzienniku. + + + + + modal.close + Zamknij + + + + + modal.submit + Prześlij + + + + + part.withdraw.success + Komponenty zostały pomyślnie usunięte/dodane/przeniesione. + + + + + perm.parts_stock + Zapas komponentów + + + + + perm.parts_stock.withdraw + Usuwanie komponentów z istniejących zapasów + + + + + perm.parts_stock.add + Dodawanie komponentów do magazynu + + + + + perm.parts_stock.move + Przenoszenie części między partiami + + + + + user.permissions_schema_updated + Schemat uprawnień użytkownika został zaktualizowany do najnowszej wersji. + + + + + log.type.part_stock_changed + Zmiana zapasów komponentów + + + + + log.part_stock_changed.withdraw + Usunięte komponenty + + + + + log.part_stock_changed.add + Dodane komponenty + + + + + log.part_stock_changed.move + Przeniesione komponenty + + + + + log.part_stock_changed.comment + Komentarz + + + + + log.part_stock_changed.change + Zmiana + + + + + log.part_stock_changed.move_target + Przeniesiono do + + + + + tools.builtin_footprints_viewer.title + Wbudowana galeria pól lutowniczych + + + + + tools.builtin_footprints_viewer.hint + a galeria zawiera listę wszystkich dostarczonych obrazów footprintów. Jeśli chcesz użyć ich w załączniku, wpisz nazwę (lub słowo kluczowe) w polu adresu URL załącznika i wybierz żądany obraz z menu rozwijanego. + + + + + tools.ic_logos.title + Loga producentów + + + + + part_list.action.group.labels + Etykiety + + + + + part_list.action.projects.generate_label + Generowanie etykiet (dla komponentów) + + + + + part_list.action.projects.generate_label_lot + Generowanie etykiet (dla partii komponentów) + + + + + part_list.action.generate_label.empty + Pusta etykieta + + + + + project.info.builds.label + Budowa + + + + + project.builds.build_not_possible + Budowa nie jest możliwa: Brak części w magazynie + + + + + project.builds.following_bom_entries_miss_instock + Poniższe części nie są dostępne w wystarczającej ilości, aby zbudować ten projekt przynajmniej raz: + + + + + project.builds.stocked + dostępny + + + + + project.builds.needed + potrzebny + + + + + project.builds.build_possible + Możliwa budowa + + + + + project.builds.number_of_builds_possible + Masz wystarczająco dużo zapasów, aby zbudować <b>%max_builds%</b> egzemplarzy tego projektu. + + + + + project.builds.check_project_status + Bieżący status projektu to <b>„%project_status%”</b>. Powinieneś sprawdzić, czy naprawdę chcesz zbudować projekt z tym statusem! + + + + + project.builds.following_bom_entries_miss_instock_n + Nie masz wystarczającej liczby części, aby zbudować ten projekt %number_of_builds% razy. W magazynie brakuje następujących części: + + + + + project.build.flash.invalid_input + Nie można zbudować projektu. Sprawdź dane wejściowe! + + + + + project.build.required_qty + Ilość wymagana + + + + + project.build.btn_build + Buduj + + + + + project.build.help + Wybierz, z których partii komponentów należy pobrać zapasy do budowy tego projektu (i w jakiej ilości). Zaznacz pole wyboru dla każdego wpisu BOM po zakończeniu pobierania części lub użyj górnego pola wyboru, aby zaznaczyć wszystkie pola jednocześnie. + + + + + project.build.buildsPartLot.new_lot + Utwórz nową partię + + + + + project.build.add_builds_to_builds_part + + + + + + project.build.builds_part_lot + Partia docelowa + + + + + project.builds.number_of_builds + Ilość do zbudowania + + + + + project.builds.no_stocked_builds + Liczba zbudowanych instancji + + + + + user.change_avatar.label + Zmień awatar + + + + + user_settings.change_avatar.label + Zmień awatar + + + + + user_settings.remove_avatar.label + Usuń awatar + + + + + part.edit.name.category_hint + Wskazówka z kategorii + + + + + category.edit.partname_regex.placeholder + np. "/Kondensator \d+ nF/i" + + + + + category.edit.partname_regex.help + Wyrażenie regularne zgodne z PCRE, do którego musi pasować nazwa komponentu. + + + + + entity.select.add_hint + Użyj "->", aby utworzyć zagnieżdżone struktury, np. „Węzeł 1->Węzeł 1.1”. + + + + + entity.select.group.new_not_added_to_DB + Nowość (jeszcze niedodana do DB) + + + + + part.edit.save_and_new + Zapisz i utwórz nowy pusty komponent + + + + + homepage.first_steps.title + Pierwsze kroki + + + + + homepage.first_steps.introduction + Twoja baza danych jest nadal pusta. Może warto przeczytać <a href="%url%">dokumentację</a> lub zacząć tworzyć następujące struktury danych: + + + + + homepage.first_steps.create_part + Możesz też bezpośrednio <a href=„%url%”>utworzyć nową część</a>. + + + + + homepage.first_steps.hide_hint + Informacje te są ukrywane zaraz po utworzeniu pierwszego komponentu. + + + + + homepage.forum.text + W przypadku pytań dotyczących Part-DB skorzystaj z <a href="%href%" class="link-external" target="_blank">GitHub.com</a> + + + + + log.element_edited.changed_fields.category + Kategoria + + + + + log.element_edited.changed_fields.footprint + Układ padów (footprint) + + + + + log.element_edited.changed_fields.manufacturer + Producent + + + + + log.element_edited.changed_fields.value_typical + Wartość nominalna + + + + + log.element_edited.changed_fields.pw_reset_expires + Reset hasła + + + + + log.element_edited.changed_fields.comment + Uwagi + + + + + log.element_edited.changed_fields.supplierpartnr + Numer zamówienia + + + + + log.element_edited.changed_fields.supplier_product_url + Adres URL produktu + + + + + log.element_edited.changed_fields.price + Cena + + + + + log.element_edited.changed_fields.min_discount_quantity + Minimalna ilość zamówienia + + + + + log.element_edited.changed_fields.original_filename + Oryginalna nazwa pliku + + + + + log.element_edited.changed_fields.path + Ścieżka do pliku + + + + + log.element_edited.changed_fields.description + Opis + + + + + log.element_edited.changed_fields.manufacturing_status + Status produkcji + + + + + log.element_edited.changed_fields.options.barcode_type + Typ kodu kreskowego + + + + + log.element_edited.changed_fields.status + Status + + + + + log.element_edited.changed_fields.quantity + Wielkość BOM + + + + + log.element_edited.changed_fields.mountnames + + + + + + log.element_edited.changed_fields.name + Nazwa + + + + + log.element_edited.changed_fields.part + Komponent + + + + + log.element_edited.changed_fields.price_currency + Waluta ceny + + + + + log.element_edited.changed_fields.partname_hint + Nazwa referencyjna + + + + + log.element_edited.changed_fields.partname_regex + Filtr nazwy + + + + + log.element_edited.changed_fields.disable_footprints + Wyłącz pola lutownicze + + + + + log.element_edited.changed_fields.disable_manufacturers + Wyłącz producentów + + + + + log.element_edited.changed_fields.disable_autodatasheets + Wyłącz automatyczne łącza do kart katalogowych + + + + + log.element_edited.changed_fields.disable_properties + Wyłącz właściwości + + + + + log.element_edited.changed_fields.default_description + Opis domyślny + + + + + log.element_edited.changed_fields.default_comment + Domyślny komentarz + + + + + log.element_edited.changed_fields.filetype_filter + Dozwolone rozszerzenia plików + + + + + log.element_edited.changed_fields.not_selectable + Nie wybrano + + + + + log.element_edited.changed_fields.parent + Element nadrzędny + + + + + log.element_edited.changed_fields.shipping_costs + Koszty wysyłki + + + + + log.element_edited.changed_fields.default_currency + Domyślna waluta + + + + + log.element_edited.changed_fields.address + Adres + + + + + log.element_edited.changed_fields.phone_number + Numer telefonu + + + + + log.element_edited.changed_fields.fax_number + Numer FAX + + + + + log.element_edited.changed_fields.email_address + E-mail + + + + + log.element_edited.changed_fields.website + Strona internetowa + + + + + log.element_edited.changed_fields.auto_product_url + Adres URL produktu + + + + + log.element_edited.changed_fields.is_full + Miejsce przechowywania pełne + + + + + log.element_edited.changed_fields.limit_to_existing_parts + Ograniczenie do istniejących części + + + + + log.element_edited.changed_fields.only_single_part + Tylko pojedyncza część + + + + + log.element_edited.changed_fields.storage_type + Typ przechowywania + + + + + log.element_edited.changed_fields.footprint_3d + Model 3D + + + + + log.element_edited.changed_fields.master_picture_attachment + Podgląd obrazu + + + + + log.element_edited.changed_fields.exchange_rate + Kurs wymiany + + + + + log.element_edited.changed_fields.iso_code + Kod ISO + + + + + log.element_edited.changed_fields.unit + Symbol jednostki + + + + + log.element_edited.changed_fields.is_integer + Czy liczba całkowita + + + + + log.element_edited.changed_fields.use_si_prefix + Użyj przedrostka SI + + + + + log.element_edited.changed_fields.options.width + Szerokość + + + + + log.element_edited.changed_fields.options.height + Wysokość + + + + + log.element_edited.changed_fields.options.supported_element + Typ docelowy + + + + + log.element_edited.changed_fields.options.additional_css + Dodatkowe style (CSS) + + + + + log.element_edited.changed_fields.options.lines + Treść + + + + + log.element_edited.changed_fields.permissions.data + Uprawnienia + + + + + log.element_edited.changed_fields.disabled + Wyłączony + + + + + log.element_edited.changed_fields.theme + Motyw + + + + + log.element_edited.changed_fields.timezone + Strefa czasowa + + + + + log.element_edited.changed_fields.language + Język + + + + + log.element_edited.changed_fields.email + E-mail + + + + + log.element_edited.changed_fields.department + Dział + + + + + log.element_edited.changed_fields.last_name + Imię + + + + + log.element_edited.changed_fields.first_name + Nazwisko + + + + + log.element_edited.changed_fields.group + Grupa + + + + + log.element_edited.changed_fields.currency + Preferowana waluta + + + + + log.element_edited.changed_fields.enforce2FA + Wymuszanie 2FA + + + + + log.element_edited.changed_fields.symbol + Symbol + + + + + log.element_edited.changed_fields.value_min + Wartość minimalna + + + + + log.element_edited.changed_fields.value_max + Wartość maksymalna + + + + + log.element_edited.changed_fields.value_text + Wartość tekstowa + + + + + log.element_edited.changed_fields.show_in_table + Pokaż w tabeli + + + + + log.element_edited.changed_fields.attachment_type + Typ pliku + + + + + log.element_edited.changed_fields.needs_review + Wymaga sprawdzenia + + + + + log.element_edited.changed_fields.tags + Tagi + + + + + log.element_edited.changed_fields.mass + Waga + + + + + log.element_edited.changed_fields.ipn + IPN + + + + + log.element_edited.changed_fields.favorite + Ulubiony + + + + + log.element_edited.changed_fields.minamount + Minimalny stan zapasów + + + + + log.element_edited.changed_fields.manufacturer_product_url + Link do strony produktu + + + + + log.element_edited.changed_fields.manufacturer_product_number + MPN + + + + + log.element_edited.changed_fields.partUnit + Jednostka pomiarowa + + + + + log.element_edited.changed_fields.expiration_date + Data wygaśnięcia + + + + + log.element_edited.changed_fields.amount + Ilość + + + + + log.element_edited.changed_fields.storage_location + Miejsce przechowywania + + + + + attachment.max_file_size + Maksymalny rozmiar pliku + + + + + user.saml_user + Użytkownik SSO / SAML + + + + + user.saml_user.pw_change_hint + Do logowania używasz logowania jednokrotnego (SSO). W związku z tym nie można tutaj skonfigurować hasła ani uwierzytelniania dwuskładnikowego. Zamiast tego użyj strony centralnej swojego dostawcy SSO! + + + + + login.sso_saml_login + Single Sign-On Login (SSO) + + + + + login.local_login_hint + Poniższy formularz służy wyłącznie do logowania przy użyciu użytkownika lokalnego. Jeśli chcesz zalogować się za pomocą pojedynczego logowania, naciśnij przycisk powyżej. + + + + + part_list.action.action.export + Eksportuj części + + + + + part_list.action.export_json + Eksportuj do JSON + + + + + part_list.action.export_csv + Eksportuj do CSV + + + + + part_list.action.export_yaml + Eksportuj do YAML + + + + + part_list.action.export_xml + Eksportuj do XML + + + + + parts.import.title + Importuj części + + + + + parts.import.errors.title + Problemy z importem + + + + + parts.import.flash.error + Błędy podczas importu. Jest to najprawdopodobniej spowodowane nieprawidłowymi danymi. + + + + + parts.import.format.auto + Automatycznie (na podstawie rozszerzenia pliku) + + + + + parts.import.flash.error.unknown_format + Nie można określić formatu na podstawie podanego pliku! + + + + + parts.import.flash.error.invalid_file + Plik jest nieprawidłowy. Sprawdź, czy wybrałeś właściwy format! + + + + + parts.import.part_category.label + Nadpisywanie kategorii + + + + + parts.import.part_category.help + Jeśli tutaj wybierzesz wartość, wszystkie importowane części zostaną przypisane do tej kategorii, niezależnie od tego, co zostało ustawione w danych. + + + + + import.create_unknown_datastructures + Utwórz nieznane struktury danych + + + + + import.create_unknown_datastructures.help + Jeśli ta opcja jest wybrana, struktury danych (takie jak kategorie, footprinty itp.), które jeszcze nie istnieją w bazie danych, zostaną automatycznie utworzone. Jeśli ta opcja nie jest wybrana, będą używane tylko istniejące struktury danych, a jeśli nie zostanie znaleziona odpowiadająca struktura danych, część nie otrzyma przypisania. + + + + + import.path_delimiter + Separator ścieżki + + + + + import.path_delimiter.help + Separator używany do oznaczania różnych poziomów w ścieżkach struktury danych, takich jak kategoria, footprint itp. + + + + + parts.import.help_documentation + Zobacz <a href="%link%">documentation</a>, aby uzyskać więcej informacji na temat formatu pliku. + + + + + parts.import.help + Możesz importować części z istniejących plików za pomocą tego narzędzia. Części będą bezpośrednio zapisywane w bazie danych, dlatego przed przesłaniem pliku tutaj upewnij się, że jego zawartość jest poprawna. + + + + + parts.import.flash.success + Import komponentów udany! + + + + + parts.import.errors.imported_entities + Importowane komponenty + + + + + perm.import + Importuj dane + + + + + parts.import.part_needs_review.label + Oznacz wszystkie importowane części jako "Wymaga sprawdzenia". + + + + + parts.import.part_needs_review.help + Jeśli ta opcja zostanie wybrana, wszystkie części zostaną oznaczone jako "Wymaga sprawdzenia", niezależnie od tego, co zostało ustawione w danych. + + + + + project.bom_import.flash.success + Pomyślnie zaimportowano %count% wpis(ów) BOM. + + + + + project.bom_import.type + Typ + + + + + project.bom_import.type.kicad_pcbnew + KiCAD Pcbnew BOM (Plik CSV) + + + + + project.bom_import.clear_existing_bom + Wyczyść istniejące wpisy BOM przed importem + + + + + project.bom_import.clear_existing_bom.help + Wybranie tej opcji spowoduje usunięcie wszystkich istniejących wpisów BOM w projekcie i zastąpienie ich zaimportowanym plikiem BOM! + + + + + project.bom_import.flash.invalid_file + Nie można zaimportować pliku. Sprawdź, czy wybrałeś właściwy typ pliku. Komunikat o błędzie: %message% + + + + + project.bom_import.flash.invalid_entries + Błąd walidacji! Sprawdź swoje dane! + + + + + project.import_bom + Importuj BOM dla projektu + + + + + project.edit.bom.import_bom + Importuj BOM + + + + + measurement_unit.new + Nowa jednostka pomiarowa + + + + + measurement_unit.edit + Edytuj jednostkę miary + + + + + user.aboutMe.label + O mnie + + + + + storelocation.owner.label + Właściciel + + + + + storelocation.part_owner_must_match.label + Właściciel części partii musi być zgodny z właścicielem miejsca przechowywania + + + + + part_lot.owner + Właściciel + + + + + part_lot.owner.help + Tylko właściciel może wycofać lub dodać akcje do tej partii. + + + + + log.element_edited.changed_fields.owner + Właściciel + + + + + log.element_edited.changed_fields.instock_unknown + Ilość nieznana + + + + + log.element_edited.changed_fields.needs_refill + Wymagane uzupełnienie + + + + + part.withdraw.access_denied + Nie można wykonać żądanej akcji. Sprawdź swoje uprawnienia i właściciela partii komponentów. + + + + + part.info.amount.less_than_desired + Mniej niż pożądane + + + + + log.cli_user + Użytkownik CLI + + + + + log.element_edited.changed_fields.part_owner_must_match + Właściciel komponentu musi odpowiadać właścicielowi lokalizacji przechowywania! + + + + + part.filter.lessThanDesired + Dostępne mniej niż wymagane (całkowita ilość < minimalna ilość) + + + + + part.filter.lotOwner + Właściciel partii + + + + + user.show_email_on_profile.label + Pokaż e-mail na publicznej stronie profilu + + + + + log.details.title + Szczegóły dziennika + + + + + log.user_login.login_from_ip + Logowanie z adresu IP + + + + + log.user_login.ip_anonymize_hint + Jeśli brakuje ostatnich cyfr adresu IP, włączony jest tryb GDPR, w którym adresy IP są anonimowe. + + + + + log.user_not_allowed.unauthorized_access_attempt_to + Próba nieautoryzowanego dostępu do strony + + + + + log.user_not_allowed.hint + Żądanie zostało zablokowane. Nie powinno być wymagane żadne działanie. + + + + + log.no_comment + Bez komentarza + + + + + log.element_changed.field + Pole + + + + + log.element_changed.data_before + Dane przed zmianą + + + + + error_table.error + Wystąpił błąd podczas żądania. + + + + + part.table.invalid_regex + Nieprawidłowe wyrażenie regularne (regex) + + + + + log.element_changed.data_after + Dane po zmianie + + + + + log.element_changed.diff + Różnica + + + + + log.undo.undo.short + Cofnij + + + + + log.undo.revert.short + Przywróć ten znacznik czasu + + + + + log.view_version + Zobacz wersję + + + + + log.undo.undelete.short + Cofnij usunięcie + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + Właściciel + + + + + log.element_edited.changed_fields.parent_id + Rodzic + + + + + log.details.delete_entry + Usuń wpis dziennika + + + + + log.delete.message.title + Czy naprawdę chcesz usunąć wpis dziennika? + + + + + log.delete.message + Jeśli to jest wpis w historii elementu, może to naruszyć historię elementu! Może to prowadzić do nieoczekiwanych wyników podczas korzystania z funkcji podróży w czasie. + + + + + log.collection_deleted.on_collection + w kolekcji + + + + + log.element_edited.changed_fields.attachments + Załączniki + + + + + tfa_u2f.add_key.registration_error + Wystąpił błąd podczas rejestracji klucza zabezpieczeń. Spróbuj ponownie lub użyj innego klucza zabezpieczeń! + + + + + log.target_type.none + Żaden + + + + + ui.darkmode.light + Jasny + + + + + ui.darkmode.dark + Ciemny + + + + + ui.darkmode.auto + Automatyczny (na podstawie ustawień systemu) + + + + + label_generator.no_lines_given + Nie podano zawartości tekstowej! Etykiety pozostaną puste. + + + + + user.password_strength.very_weak + Bardzo słabe + + + + + user.password_strength.weak + Słabe + + + + + user.password_strength.medium + Średnie + + + + + user.password_strength.strong + Mocne + + + + + user.password_strength.very_strong + Bardzo mocne + + + + + perm.users.impersonate + Podszywanie się pod innych użytkowników + + + + + user.impersonated_by.label + Podszywanie się pod + + + + + user.stop_impersonation + Zatrzymaj podszywanie się + + + + + user.impersonate.btn + Podszywanie się + + + + + user.impersonate.confirm.title + Czy naprawdę chcesz podszyć się pod tego użytkownika? + + + + + user.impersonate.confirm.message + To zostanie zarejestrowane. Powinieneś to robić tylko z uzasadnionego powodu. + +Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli spróbujesz, otrzymasz komunikat "Odmowa dostępu". + + + + + log.type.security.user_impersonated + Podszywanie się pod użytkownika + + + + + info_providers.providers_list.title + Dostawcy informacji + + + + + info_providers.providers_list.active + Aktywni + + + + + info_providers.providers_list.disabled + Wyłączeni + + + + + info_providers.capabilities.basic + Podstawowy + + + + + info_providers.capabilities.footprint + Układ padów (footprint) + + + + + info_providers.capabilities.picture + Obraz + + + + + info_providers.capabilities.datasheet + Datasheet + + + + + info_providers.capabilities.price + Ceny + + + + + part.info_provider_reference.badge + Źródło informacji użytych do utworzenia tego komponentu. + + + + + part.info_provider_reference + Utworzone przez dostawcę informacji + + + + + oauth_client.connect.btn + Połącz OAuth + + + + + info_providers.table.provider.label + Dostawca + + + + + info_providers.search.keyword + Słowo kluczowe + + + + + info_providers.search.submit + Wyszukiwanie + + + + + info_providers.search.providers.help + Wybierz dostawców, u których ma zostać przeprowadzone wyszukiwanie. + + + + + info_providers.search.providers + Dostawcy + + + + + info_providers.search.info_providers_list + Pokaż wszystkich dostępnych dostawców informacji + + + + + info_providers.search.title + Tworzenie części od dostawcy informacji + + + + + oauth_client.flash.connection_successful + Pomyślnie połączono z aplikacją OAuth! + + + + + perm.part.info_providers + Dostawcy informacji + + + + + perm.part.info_providers.create_parts + Utwórz komponent na podstawie dostarczonych informacji + + + + + entity.edit.alternative_names.label + Alternatywne nazwy + + + + + entity.edit.alternative_names.help + Podane tutaj alternatywne nazwy są używane do znajdowania tego elementu na podstawie dostarczonych informacji + + + + + info_providers.form.help_prefix + Dostawca + + + + + update_manager.new_version_available.title + Dostępna jest nowa wersja Part-DB. Sprawdź ją tutaj + + + + + update_manager.new_version_available.text + Dostępna jest nowa wersja Part-DB. Sprawdź ją tutaj. + + + + + update_manager.new_version_available.only_administrators_can_see + Tylko administratorzy mogą zobaczyć tę wiadomość. + + + + + perm.system.show_available_updates + Pokaż dostępne aktualizacje Part-DB + + + + + user.settings.api_tokens + Tokeny API + + + + + user.settings.api_tokens.description + Korzystając z tokena API, inne aplikacje mogą uzyskać dostęp do Part-DB z Twoimi uprawnieniami, aby wykonywać różne działania za pomocą REST API Part-DB. Jeśli usuniesz tutaj token API, aplikacja korzystająca z tego tokena nie będzie mogła już uzyskać dostępu do Part-DB w Twoim imieniu. + + + + + api_tokens.name + Imię + + + + + api_tokens.access_level + Poziom dostępu + + + + + api_tokens.expiration_date + Data wygaśnięcia + + + + + api_tokens.added_date + Utworzono + + + + + api_tokens.last_time_used + Ostatnio używany + + + + + datetime.never + Nigdy + + + + + api_token.valid + Ważny + + + + + api_token.expired + Wygasł + + + + + user.settings.show_api_documentation + Pokaż dokumentację API + + + + + api_token.create_new + Utwórz nowy token API + + + + + api_token.level.read_only + Tylko do odczytu + + + + + api_token.level.edit + Edycja + + + + + api_token.level.admin + Admin + + + + + api_token.level.full + Pełny + + + + + api_tokens.access_level.help + Możesz ograniczyć, do czego token API może uzyskać dostęp. Dostęp zawsze jest ograniczony przez uprawnienia Twojego użytkownika. + + + + + api_tokens.expiration_date.help + Po tej dacie token nie będzie już użyteczny. Pozostaw puste, jeśli token ma nigdy nie wygasnąć. + + + + + api_tokens.your_token_is + Twój token API to + + + + + api_tokens.please_save_it + Proszę to zapisać. Nie będziesz mógł tego zobaczyć ponownie! + + + + + api_tokens.create_new.back_to_user_settings + Powrót do ustawień użytkownika + + + + + project.build.dont_check_quantity + Nie sprawdzaj ilości + + + + + project.build.dont_check_quantity.help + Jeśli ta opcja jest wybrana, wybrane ilości są usuwane z magazynu, niezależnie od tego, czy jest więcej czy mniej komponentów niż jest to faktycznie wymagane do zbudowania projektu. + + + + + part_list.action.invert_selection + Odwróć zaznaczenie + + + + + perm.api + API + + + + + perm.api.access_api + Dostęp API + + + + + perm.api.manage_tokens + Zarządzaj tokenami API + + + + + user.settings.api_tokens.delete.title + Czy na pewno chcesz usunąć ten token API? + + + + + user.settings.api_tokens.delete + Usuń + + + + + user.settings.api_tokens.delete.message + Aplikacja korzystająca z tego tokena API nie będzie miała już dostępu do Part-DB. Tej operacji nie można cofnąć! + + + + + api_tokens.deleted + Token API został pomyślnie usunięty! + + + + + user.settings.api_tokens.no_api_tokens_yet + Nie skonfigurowano jeszcze tokenów API. + + + + + api_token.ends_with + Kończy się na + + + + + entity.select.creating_new_entities_not_allowed + Nie masz uprawnień do tworzenia nowych jednostek tego typu! Proszę, wybierz istniejącą. + + + + + scan_dialog.mode + Typ kodu kreskowego + + + + + scan_dialog.mode.auto + Automatycznie + + + + + scan_dialog.mode.ipn + Kod kreskowy IPN + + + + + scan_dialog.mode.internal + Kod kreskowy Part-DB + + + + + part_association.label + Powiązanie części + + + + + part.edit.tab.associations + Części powiązane + + + + + part_association.edit.other_part + Część powiązana + + + + + part_association.edit.type + Typ relacji + + + + + part_association.edit.comment + Komentarz + + + + + part_association.edit.type.help + Możesz tutaj wybrać, w jaki sposób wybrana część jest związana z tą częścią + + + + + part_association.table.from_this_part + Powiązania z tej części do innych + + + + + part_association.table.from + Z + + + + + part_association.table.type + Relacja + + + + + part_association.table.to + Do + + + + + part_association.type.compatible + Jest kompatybilny z + + + + + part_association.table.to_this_part + Powiązania tej części z innymi + + + + + part_association.type.other + Inne (wartość niestandardowa) + + + + + part_association.type.supersedes + Zastępuje + + + + + part_association.edit.other_type + Niestandardowy typ + + + + + part_association.edit.delete.confirm + Czy na pewno chcesz usunąć to powiązanie? Nie można tego cofnąć. + + + + + part_lot.edit.advanced + Rozwiń opcje zaawansowane + + + + + part_lot.edit.vendor_barcode + Kod kreskowy producenta + + + + + part_lot.edit.vendor_barcode.help + Jeśli ta partia ma już kod kreskowy (np. umieszczony przez dostawcę), możesz wprowadzić jego treść tutaj, aby łatwiej go zeskanować. + + + + + scan_dialog.mode.vendor + Kod kreskowy dostawcy (skonfigurowany w partii części) + + + + + project.bom.instockAmount + Ilość w magazynie + + + + + collection_type.new_element.tooltip + Ten element został nowo utworzony i nie został jeszcze zapisany w bazie danych. + + + + + part.merge.title + Połącz część + + + + + part.merge.title.into + w + + + + + part.merge.confirm.title + Czy na pewno chcesz scalić <b>%other%</b> z <b>%target%</b>? + + + + + part.merge.confirm.message + <b>%other%</b> Zostanie usunięta, a część będzie zapisana z widocznymi informacjami. + + + + + part.info.merge_modal.title + Scal części + + + + + part.info.merge_modal.other_part + Inna część + + + + + part.info.merge_modal.other_into_this + Scal inną część z tą (usuń tamtą część, zachowaj tę). + + + + + part.info.merge_modal.this_into_other + Scal tę część z inną (usuń tę część, zachowaj drugą). + + + + + part.info.merge_btn + Połącz część + + + + + part.update_part_from_info_provider.btn + Zaktualizuj komponent na podstawie danych od dostawcy. + + + + + info_providers.update_part.title + Zaktualizuj istniejący komponent na podstawie danych od dostawcy. + + + + + part.merge.flash.please_review + Dane nie zostały jeszcze zapisane. Przejrzyj zmiany i kliknij "zapisz", aby zachować nowe dane. + + + + + user.edit.flash.permissions_fixed + Brakowało uprawnień wymaganych przez inne uprawnienia. Problem został skorygowany. Proszę sprawdzić, czy uprawnienia są zgodne z Twoimi zamierzeniami. + + + + + permission.legend.dependency_note + Należy pamiętać, że niektóre operacje związane z uprawnieniami są od siebie zależne. Jeśli napotkasz ostrzeżenie, że brakujące uprawnienia zostały skorygowane i ustawiono je ponownie na "zezwól", musisz także ustawić zależną operację na "zabroń". Zależne operacje zwykle znajdują się po prawej stronie danej operacji. + + + + + log.part_stock_changed.timestamp + Znacznik czasu + + + + + part.info.withdraw_modal.timestamp + Czas działania + + + + + part.info.withdraw_modal.timestamp.hint + To pole pozwala na wskazanie rzeczywistej daty wykonania operacji magazynowej, a nie tylko jej zarejestrowania. Wartość ta jest zapisywana w dodatkowym polu dziennika. + + + + + part.info.withdraw_modal.delete_lot_if_empty + Usuń ten zasób, gdy stanie się pusty w wyniku operacji + + + + + info_providers.search.error.client_exception + Wystąpił błąd podczas komunikacji z dostawcą informacji. Sprawdź konfigurację tego dostawcy i jeśli to możliwe, odśwież tokeny OAuth. + + + + + eda_info.reference_prefix.placeholder + Np. R + + + + + eda_info.reference_prefix + Prefiks odniesienia + + + + + eda_info.kicad_section.title + Ustawienia specyficzne dla programu KiCad + + + + + eda_info.value + Wartość + + + + + eda_info.value.placeholder + np. 100n + + + + + eda_info.exclude_from_bom + Wyklucz część z BOM + + + + + eda_info.exclude_from_board + Wyklucz część z PCB/Płyty + + + + + eda_info.exclude_from_sim + Wyklucz komponent z symulacji + + + + + eda_info.kicad_symbol + Symbol schematu KiCad + + + + + eda_info.kicad_symbol.placeholder + Np. Transistor_BJT:BC547 + + + + + eda_info.kicad_footprint + Układ padów (footprint) KiCad + + + + + eda_info.kicad_footprint.placeholder + np. Package_TO_SOT_THT:TO-92 + + + + + part.edit.tab.eda + Informacje EDA + + + + + api.api_endpoints.title + Punkty końcowe API + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + Główny adres URL API KiCad + + + + + eda_info.visibility + Wymuś widoczność + + + + + eda_info.visibility.help + Domyślnie widoczność dla oprogramowania EDA jest określana automatycznie. Za pomocą tego pola wyboru można wymusić, aby część była widoczna lub niewidoczna. + + + + + part.withdraw.zero_amount + Próbowano usunąć/dodać ilość równą zero! Nie wykonano żadnej akcji. + + + + + login.flash.access_denied_please_login + Dostęp zabroniony! Zaloguj się, aby kontynuować. + + + + + attachment.upload_multiple_files + Prześlij pliki + + + + + entity.mass_creation_flash + Pomyślnie utworzono %COUNT% elementów. + + + + + info_providers.search.number_of_results + %number% Wyników + + + + + info_providers.search.no_results + Nie znaleziono wyników u wybranych dostawców! Sprawdź wyszukiwany termin lub spróbuj wybrać dodatkowych dostawców. + + + + + tfa.check.code.confirmation + Wygenerowany kod + + + + diff --git a/translations/messages.ru.xlf b/translations/messages.ru.xlf index b21bbc46..2b4a5f70 100644 --- a/translations/messages.ru.xlf +++ b/translations/messages.ru.xlf @@ -1,7 +1,7 @@ - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 @@ -12,7 +12,7 @@ Типы прикрепленных файлов - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 new @@ -22,7 +22,7 @@ Изменить тип файла - + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 new @@ -32,7 +32,7 @@ Новый тип файла - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 Part-DB1\templates\_sidebar.html.twig:22 @@ -51,7 +51,7 @@ Категории - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 @@ -64,7 +64,7 @@ Опции - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 @@ -77,7 +77,7 @@ Расширенные - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 new @@ -87,7 +87,7 @@ Изменить категорию - + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 new @@ -97,7 +97,7 @@ Новая категория - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 @@ -107,7 +107,7 @@ Валюта - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 @@ -117,7 +117,7 @@ Код ISO - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 @@ -127,7 +127,7 @@ Знак валюты - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 new @@ -137,7 +137,7 @@ Изменить валюту - + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 new @@ -147,7 +147,38 @@ Новая валюта - + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + templates\AdminPages\DeviceAdmin.html.twig:4 + + + project.caption + Проект + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 + new + + + project.edit + Редактировать проект + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 + new + + + project.new + Новый проект + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 Part-DB1\templates\_navbar_search.html.twig:67 @@ -170,7 +201,7 @@ Поиск - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 Part-DB1\templates\_sidebar.html.twig:3 @@ -186,7 +217,7 @@ Раскрыть всё - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 Part-DB1\templates\_sidebar.html.twig:4 @@ -202,7 +233,7 @@ Свернуть всё - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 Part-DB1\templates\Parts\info\_sidebar.html.twig:4 @@ -214,7 +245,7 @@ Так этот компонент выглядел до %timestamp%. <i>Это экспериментальная возможность, поэтому информация может быть неверной.</i> - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 @@ -225,7 +256,7 @@ Свойства - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 @@ -236,7 +267,7 @@ Информация - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 @@ -247,7 +278,7 @@ История - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 @@ -258,7 +289,7 @@ Экспорт - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 @@ -269,7 +300,7 @@ Импорт / Экспорт - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 @@ -279,7 +310,7 @@ Создать несколько - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 @@ -290,7 +321,7 @@ Общие - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 @@ -300,7 +331,7 @@ Вложения - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 @@ -309,7 +340,7 @@ Параметры - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 @@ -320,7 +351,7 @@ Экспортировать всё - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 @@ -330,7 +361,7 @@ Каждая строка будет интерпретирована как имя создаваемого элемента - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 @@ -341,7 +372,7 @@ Редактировать элемент "%name" - + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 @@ -352,7 +383,7 @@ Новый элемент - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 Part-DB1\templates\_sidebar.html.twig:9 @@ -367,7 +398,7 @@ Посадочные места - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 new @@ -377,7 +408,7 @@ Изменить посадочное место - + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 new @@ -387,7 +418,7 @@ Новое посадочное место - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 @@ -397,7 +428,7 @@ Группы - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 @@ -409,7 +440,7 @@ Разрешения - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 new @@ -419,7 +450,7 @@ Изменить группу - + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 new @@ -429,7 +460,7 @@ Новая группа - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 @@ -438,7 +469,7 @@ Профили этикеток - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 @@ -447,7 +478,7 @@ Расширенные - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 @@ -456,7 +487,7 @@ Комментарий - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 new @@ -466,7 +497,7 @@ Изменить профиль этикетки - + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 new @@ -476,7 +507,7 @@ Новый профиль этикетки - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 @@ -487,7 +518,7 @@ Производители - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 new @@ -497,7 +528,7 @@ Изменить производителя - + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 new @@ -507,7 +538,7 @@ Новый производитель - + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 @@ -517,7 +548,7 @@ Единица измерения - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 Part-DB1\templates\_sidebar.html.twig:8 @@ -532,7 +563,7 @@ Хранилища - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 new @@ -542,7 +573,7 @@ Изменить место хранения - + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 new @@ -552,7 +583,7 @@ Новое место хранения - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 @@ -563,7 +594,7 @@ Поставщики - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 new @@ -573,7 +604,7 @@ Изменить поставщика - + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 new @@ -583,7 +614,7 @@ Новый поставщик - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 @@ -593,7 +624,7 @@ Пользователь - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 @@ -603,7 +634,7 @@ Конфигурация - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 @@ -613,7 +644,7 @@ Пароль - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 @@ -623,7 +654,7 @@ Двухфакторная аутентификация - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 @@ -633,7 +664,7 @@ Приложение аутентификации активно - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 Part-DB1\templates\Users\backup_codes.html.twig:15 @@ -647,7 +678,7 @@ Количество оставшихся резервных кодов - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 Part-DB1\templates\Users\backup_codes.html.twig:17 @@ -661,7 +692,7 @@ Дата создания резервных кодов - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 @@ -673,7 +704,7 @@ Способ выключен - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 @@ -683,7 +714,7 @@ Активные ключи безопасности - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 @@ -693,7 +724,7 @@ Вы действительно хотите продолжить? - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 @@ -706,7 +737,7 @@ <b>Делайте это только в том случае, если вы абсолютно уверены в личности пользователя (обращающегося за помощью), в противном случае учетная запись может быть взломана злоумышленником!</b> - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 @@ -716,7 +747,7 @@ Запретить все способы двухфакторной аутентификации - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 new @@ -726,7 +757,7 @@ Изменить пользователя - + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 new @@ -736,7 +767,7 @@ Новый пользователь - + Part-DB1\templates\AdminPages\_attachments.html.twig:4 Part-DB1\templates\Parts\edit\_attachments.html.twig:4 @@ -749,7 +780,7 @@ Удалить - + Part-DB1\templates\AdminPages\_attachments.html.twig:41 Part-DB1\templates\Parts\edit\_attachments.html.twig:38 @@ -763,7 +794,7 @@ Внешний - + Part-DB1\templates\AdminPages\_attachments.html.twig:49 Part-DB1\templates\Parts\edit\_attachments.html.twig:47 @@ -775,7 +806,7 @@ Значок вложения - + Part-DB1\templates\AdminPages\_attachments.html.twig:52 Part-DB1\templates\Parts\edit\_attachments.html.twig:50 @@ -789,7 +820,7 @@ Посмотреть - + Part-DB1\templates\AdminPages\_attachments.html.twig:58 Part-DB1\templates\Parts\edit\_attachments.html.twig:56 @@ -805,7 +836,7 @@ Файл не найден - + Part-DB1\templates\AdminPages\_attachments.html.twig:66 Part-DB1\templates\Parts\edit\_attachments.html.twig:64 @@ -817,7 +848,7 @@ Личное - + Part-DB1\templates\AdminPages\_attachments.html.twig:79 Part-DB1\templates\Parts\edit\_attachments.html.twig:77 @@ -829,7 +860,7 @@ Добавить вложение - + Part-DB1\templates\AdminPages\_attachments.html.twig:84 Part-DB1\templates\Parts\edit\_attachments.html.twig:82 @@ -843,7 +874,7 @@ Вы уверены, что хотите удалить этот склад? Это не может быть отменено! - + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 Part-DB1\templates\AdminPages\_delete_form.html.twig:2 @@ -854,7 +885,7 @@ Вы действительно хотите удалить %name%? - + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 Part-DB1\templates\AdminPages\_delete_form.html.twig:3 @@ -867,7 +898,7 @@ Подэлементы будут перенесены наверх. - + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 Part-DB1\templates\AdminPages\_delete_form.html.twig:11 @@ -878,7 +909,7 @@ Удалить элемент - + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 Part-DB1\templates\Parts\info\_tools.html.twig:45 @@ -893,7 +924,7 @@ Изменить комментарий - + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 Part-DB1\templates\AdminPages\_delete_form.html.twig:24 @@ -904,7 +935,7 @@ Удалить рекурсивно (все подэлементы) - + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 @@ -913,7 +944,7 @@ Повторный элемент - + Part-DB1\templates\AdminPages\_export_form.html.twig:4 Part-DB1\src\Form\AdminPages\ImportType.php:76 @@ -927,7 +958,7 @@ Формат файла - + Part-DB1\templates\AdminPages\_export_form.html.twig:16 Part-DB1\templates\AdminPages\_export_form.html.twig:16 @@ -938,7 +969,7 @@ Уровень детализации - + Part-DB1\templates\AdminPages\_export_form.html.twig:19 Part-DB1\templates\AdminPages\_export_form.html.twig:19 @@ -949,7 +980,7 @@ Простой - + Part-DB1\templates\AdminPages\_export_form.html.twig:20 Part-DB1\templates\AdminPages\_export_form.html.twig:20 @@ -960,7 +991,7 @@ Расширенный - + Part-DB1\templates\AdminPages\_export_form.html.twig:21 Part-DB1\templates\AdminPages\_export_form.html.twig:21 @@ -971,7 +1002,7 @@ Полный - + Part-DB1\templates\AdminPages\_export_form.html.twig:31 Part-DB1\templates\AdminPages\_export_form.html.twig:31 @@ -982,7 +1013,7 @@ Включить подэлементы в экспорт - + Part-DB1\templates\AdminPages\_export_form.html.twig:39 Part-DB1\templates\AdminPages\_export_form.html.twig:39 @@ -993,7 +1024,7 @@ Экспорт - + Part-DB1\templates\AdminPages\_info.html.twig:4 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 @@ -1012,7 +1043,7 @@ ID - + Part-DB1\templates\AdminPages\_info.html.twig:11 Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 @@ -1036,7 +1067,7 @@ Создан - + Part-DB1\templates\AdminPages\_info.html.twig:25 Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 @@ -1054,7 +1085,7 @@ Последнее изменение - + Part-DB1\templates\AdminPages\_info.html.twig:38 Part-DB1\templates\AdminPages\_info.html.twig:38 @@ -1064,7 +1095,7 @@ Количество компонентов в этом элементе - + Part-DB1\templates\AdminPages\_parameters.html.twig:6 Part-DB1\templates\helper.twig:125 @@ -1075,7 +1106,7 @@ Параметр - + Part-DB1\templates\AdminPages\_parameters.html.twig:7 Part-DB1\templates\Parts\edit\_specifications.html.twig:7 @@ -1085,7 +1116,7 @@ Символ - + Part-DB1\templates\AdminPages\_parameters.html.twig:8 Part-DB1\templates\Parts\edit\_specifications.html.twig:8 @@ -1095,7 +1126,7 @@ Мин. - + Part-DB1\templates\AdminPages\_parameters.html.twig:9 Part-DB1\templates\Parts\edit\_specifications.html.twig:9 @@ -1105,7 +1136,7 @@ Тип. - + Part-DB1\templates\AdminPages\_parameters.html.twig:10 Part-DB1\templates\Parts\edit\_specifications.html.twig:10 @@ -1115,7 +1146,7 @@ Макс. - + Part-DB1\templates\AdminPages\_parameters.html.twig:11 Part-DB1\templates\Parts\edit\_specifications.html.twig:11 @@ -1125,7 +1156,7 @@ Единица - + Part-DB1\templates\AdminPages\_parameters.html.twig:12 Part-DB1\templates\Parts\edit\_specifications.html.twig:12 @@ -1135,7 +1166,7 @@ Текст - + Part-DB1\templates\AdminPages\_parameters.html.twig:13 Part-DB1\templates\Parts\edit\_specifications.html.twig:13 @@ -1145,7 +1176,7 @@ Группа - + Part-DB1\templates\AdminPages\_parameters.html.twig:26 Part-DB1\templates\Parts\edit\_specifications.html.twig:26 @@ -1155,7 +1186,7 @@ Новый Параметр - + Part-DB1\templates\AdminPages\_parameters.html.twig:31 Part-DB1\templates\Parts\edit\_specifications.html.twig:31 @@ -1165,7 +1196,7 @@ Вы точно уверены что хотите удалить этот параметр ? - + Part-DB1\templates\attachment_list.html.twig:3 Part-DB1\templates\attachment_list.html.twig:3 @@ -1175,7 +1206,7 @@ Список вложений - + Part-DB1\templates\attachment_list.html.twig:10 Part-DB1\templates\LogSystem\_log_table.html.twig:8 @@ -1189,7 +1220,7 @@ Загрузка - + Part-DB1\templates\attachment_list.html.twig:11 Part-DB1\templates\LogSystem\_log_table.html.twig:9 @@ -1203,7 +1234,7 @@ Это может занять время. Если это сообщение не пропадает, попробуйте перегрузить страницу. - + Part-DB1\templates\base.html.twig:68 Part-DB1\templates\base.html.twig:68 @@ -1214,7 +1245,7 @@ Включите Javascript чтобы использовать весь функционал! - + Part-DB1\templates\base.html.twig:73 Part-DB1\templates\base.html.twig:73 @@ -1224,7 +1255,7 @@ Показать/Скрыть боковую панель - + Part-DB1\templates\base.html.twig:95 Part-DB1\templates\base.html.twig:95 @@ -1235,7 +1266,7 @@ Загрузка: - + Part-DB1\templates\base.html.twig:96 Part-DB1\templates\base.html.twig:96 @@ -1246,7 +1277,7 @@ Это может занять время. Если это сообщение показывается слишком долго, попробуйте перегрузить страницу. - + Part-DB1\templates\base.html.twig:101 Part-DB1\templates\base.html.twig:101 @@ -1257,7 +1288,7 @@ Загрузка... - + Part-DB1\templates\base.html.twig:112 Part-DB1\templates\base.html.twig:112 @@ -1268,7 +1299,7 @@ Назад на верх страницы - + Part-DB1\templates\Form\permissionLayout.html.twig:35 Part-DB1\templates\Form\permissionLayout.html.twig:35 @@ -1278,7 +1309,7 @@ Разрешения - + Part-DB1\templates\Form\permissionLayout.html.twig:36 Part-DB1\templates\Form\permissionLayout.html.twig:36 @@ -1288,7 +1319,7 @@ Значение - + Part-DB1\templates\Form\permissionLayout.html.twig:53 Part-DB1\templates\Form\permissionLayout.html.twig:53 @@ -1298,7 +1329,7 @@ Объяснение состояний - + Part-DB1\templates\Form\permissionLayout.html.twig:57 Part-DB1\templates\Form\permissionLayout.html.twig:57 @@ -1308,7 +1339,7 @@ Запрещено - + Part-DB1\templates\Form\permissionLayout.html.twig:61 Part-DB1\templates\Form\permissionLayout.html.twig:61 @@ -1318,7 +1349,7 @@ Разрешено - + Part-DB1\templates\Form\permissionLayout.html.twig:65 Part-DB1\templates\Form\permissionLayout.html.twig:65 @@ -1328,7 +1359,7 @@ Наследовать от родительской группы - + Part-DB1\templates\helper.twig:3 Part-DB1\templates\helper.twig:3 @@ -1338,7 +1369,7 @@ Да - + Part-DB1\templates\helper.twig:5 Part-DB1\templates\helper.twig:5 @@ -1348,7 +1379,7 @@ Нет - + Part-DB1\templates\helper.twig:92 Part-DB1\templates\helper.twig:87 @@ -1358,7 +1389,7 @@ Да - + Part-DB1\templates\helper.twig:94 Part-DB1\templates\helper.twig:89 @@ -1368,7 +1399,7 @@ Нет - + Part-DB1\templates\helper.twig:126 @@ -1377,7 +1408,7 @@ Значение - + Part-DB1\templates\homepage.html.twig:7 Part-DB1\templates\homepage.html.twig:7 @@ -1388,7 +1419,7 @@ Версия - + Part-DB1\templates\homepage.html.twig:22 Part-DB1\templates\homepage.html.twig:22 @@ -1399,7 +1430,7 @@ Информация о лицензии - + Part-DB1\templates\homepage.html.twig:31 Part-DB1\templates\homepage.html.twig:31 @@ -1410,7 +1441,7 @@ Страница проекта - + Part-DB1\templates\homepage.html.twig:31 Part-DB1\templates\homepage.html.twig:31 @@ -1421,7 +1452,7 @@ Загрузки, отчеты об ошибках, список пожеланий и т.д. находятся на <a href="%href%" class="link-external" target="_blank">странице проекта в GitHub</a> - + Part-DB1\templates\homepage.html.twig:32 Part-DB1\templates\homepage.html.twig:32 @@ -1432,7 +1463,7 @@ Помощь - + Part-DB1\templates\homepage.html.twig:32 Part-DB1\templates\homepage.html.twig:32 @@ -1443,7 +1474,7 @@ Помощь и подсказки находятся в документах Wiki <a href="%href%" class="link-external" target="_blank">на странице в GitHub</a> - + Part-DB1\templates\homepage.html.twig:33 Part-DB1\templates\homepage.html.twig:33 @@ -1454,29 +1485,7 @@ Форум - - - Part-DB1\templates\homepage.html.twig:36 - Part-DB1\templates\homepage.html.twig:36 - templates\homepage.html.twig:33 - - - homepage.basedOn - На основе оригинальной версии Part-DB - - - - - Part-DB1\templates\homepage.html.twig:39 - Part-DB1\templates\homepage.html.twig:39 - templates\homepage.html.twig:36 - - - homepage.others - и другие - - - + Part-DB1\templates\homepage.html.twig:45 Part-DB1\templates\homepage.html.twig:45 @@ -1487,7 +1496,7 @@ Последние действия - + Part-DB1\templates\LabelSystem\dialog.html.twig:3 Part-DB1\templates\LabelSystem\dialog.html.twig:6 @@ -1497,7 +1506,7 @@ Генератор этикеток - + Part-DB1\templates\LabelSystem\dialog.html.twig:16 @@ -1506,7 +1515,7 @@ Общее - + Part-DB1\templates\LabelSystem\dialog.html.twig:20 @@ -1515,7 +1524,7 @@ Расширенные - + Part-DB1\templates\LabelSystem\dialog.html.twig:24 @@ -1524,7 +1533,7 @@ Профили - + Part-DB1\templates\LabelSystem\dialog.html.twig:58 @@ -1533,7 +1542,7 @@ Выбранный профиль - + Part-DB1\templates\LabelSystem\dialog.html.twig:62 @@ -1542,7 +1551,7 @@ Изменить профиль - + Part-DB1\templates\LabelSystem\dialog.html.twig:75 @@ -1551,7 +1560,7 @@ Загрузить профиль - + Part-DB1\templates\LabelSystem\dialog.html.twig:102 @@ -1560,7 +1569,7 @@ Скачать - + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 @@ -1570,7 +1579,7 @@ Генерировать этикетку - + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 @@ -1579,7 +1588,7 @@ Новая пустая этикетка - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 @@ -1588,7 +1597,7 @@ Сканер этикеток - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 @@ -1597,7 +1606,7 @@ Веб-камера не найдена - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 @@ -1606,7 +1615,7 @@ Чтобы использовать функцию сканирования вам понадобится веб-камера и разрешение для ее использования. Ниже, вы можете ввести штрихкод вручную. - + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 @@ -1615,7 +1624,7 @@ Выбор источника - + Part-DB1\templates\LogSystem\log_list.html.twig:3 Part-DB1\templates\LogSystem\log_list.html.twig:3 @@ -1625,7 +1634,7 @@ Системный лог - + Part-DB1\templates\LogSystem\_log_table.html.twig:1 Part-DB1\templates\LogSystem\_log_table.html.twig:1 @@ -1636,7 +1645,7 @@ Подтвердите отмену изменения/откат состояния? - + Part-DB1\templates\LogSystem\_log_table.html.twig:2 Part-DB1\templates\LogSystem\_log_table.html.twig:2 @@ -1647,7 +1656,7 @@ Вы действительно хотите отменить данное изменение и вернуть состояние элемента до предыдущей версии? - + Part-DB1\templates\mail\base.html.twig:24 Part-DB1\templates\mail\base.html.twig:24 @@ -1657,7 +1666,7 @@ Это письмо послано автоматически от - + Part-DB1\templates\mail\base.html.twig:24 Part-DB1\templates\mail\base.html.twig:24 @@ -1667,7 +1676,7 @@ Не отвечайте на это письмо. - + Part-DB1\templates\mail\pw_reset.html.twig:6 Part-DB1\templates\mail\pw_reset.html.twig:6 @@ -1677,7 +1686,7 @@ Привет %name% - + Part-DB1\templates\mail\pw_reset.html.twig:7 Part-DB1\templates\mail\pw_reset.html.twig:7 @@ -1687,7 +1696,7 @@ кто-то (надеемся что Вы) запросил сброс пароля. Если это сделали не Вы - игнорируйте это письмо. - + Part-DB1\templates\mail\pw_reset.html.twig:9 Part-DB1\templates\mail\pw_reset.html.twig:9 @@ -1697,7 +1706,7 @@ Нажмите здесь для сброса пароля - + Part-DB1\templates\mail\pw_reset.html.twig:11 Part-DB1\templates\mail\pw_reset.html.twig:11 @@ -1707,7 +1716,7 @@ Если это не помогло, откройте ссылку <a href="%url%">%url%</a> и введите следующую информацию - + Part-DB1\templates\mail\pw_reset.html.twig:16 Part-DB1\templates\mail\pw_reset.html.twig:16 @@ -1717,7 +1726,7 @@ Имя пользователя - + Part-DB1\templates\mail\pw_reset.html.twig:19 Part-DB1\templates\mail\pw_reset.html.twig:19 @@ -1727,7 +1736,7 @@ Жетон - + Part-DB1\templates\mail\pw_reset.html.twig:24 Part-DB1\templates\mail\pw_reset.html.twig:24 @@ -1737,7 +1746,7 @@ Жетон на сброс будет действителен до <i>%date%</i> - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 @@ -1749,7 +1758,7 @@ Удалить - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 @@ -1759,7 +1768,7 @@ Минимальное количество для скидки - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 @@ -1769,7 +1778,7 @@ Цена - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 @@ -1779,7 +1788,7 @@ для количества - + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 @@ -1789,7 +1798,7 @@ Добавить цену - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 @@ -1800,7 +1809,7 @@ Редактировать %name% - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 @@ -1811,7 +1820,7 @@ Редактировать информацию о компоненте - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 @@ -1821,7 +1830,7 @@ Общие - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 @@ -1831,7 +1840,7 @@ Производитель - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 @@ -1841,7 +1850,7 @@ Расширенные - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 @@ -1851,7 +1860,7 @@ Склады - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 @@ -1861,7 +1870,7 @@ Вложения - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 @@ -1871,7 +1880,7 @@ Информация для заказа - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 @@ -1880,7 +1889,7 @@ Параметры - + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 @@ -1890,7 +1899,7 @@ Комментарий - + Part-DB1\templates\Parts\edit\new_part.html.twig:8 Part-DB1\templates\Parts\edit\new_part.html.twig:8 @@ -1901,7 +1910,7 @@ Добавить новый компонент - + Part-DB1\templates\Parts\edit\_lots.html.twig:5 Part-DB1\templates\Parts\edit\_lots.html.twig:5 @@ -1911,7 +1920,7 @@ Удалить - + Part-DB1\templates\Parts\edit\_lots.html.twig:28 Part-DB1\templates\Parts\edit\_lots.html.twig:28 @@ -1921,7 +1930,7 @@ Добавить склад - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 @@ -1931,7 +1940,7 @@ Добавить дистрибьютора - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 @@ -1941,7 +1950,7 @@ Вы действительно хотите удалить эту цену? Это не может быть отменено. - + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 @@ -1951,7 +1960,7 @@ Вы действительно хотите удалить эту информацию о дистрибьюторе? Это не может быть отменено! - + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 Part-DB1\templates\Parts\info\show_part_info.html.twig:19 @@ -1965,7 +1974,7 @@ Детальная информация о компоненте - + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 Part-DB1\templates\Parts\info\show_part_info.html.twig:47 @@ -1975,7 +1984,7 @@ Склады - + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 Part-DB1\templates\Parts\lists\_info_card.html.twig:43 @@ -1990,7 +1999,7 @@ Комментарий - + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 @@ -1999,7 +2008,7 @@ Параметры - + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 Part-DB1\templates\Parts\info\show_part_info.html.twig:64 @@ -2010,7 +2019,7 @@ Вложения - + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 Part-DB1\templates\Parts\info\show_part_info.html.twig:71 @@ -2021,7 +2030,7 @@ Информация о покупках - + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 Part-DB1\templates\Parts\info\show_part_info.html.twig:78 @@ -2032,7 +2041,7 @@ История - + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 Part-DB1\templates\_sidebar.html.twig:54 @@ -2051,7 +2060,7 @@ Инструменты - + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 Part-DB1\templates\Parts\info\show_part_info.html.twig:90 @@ -2061,7 +2070,7 @@ Расширенная информ. - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 @@ -2071,7 +2080,7 @@ Имя - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 @@ -2081,7 +2090,7 @@ Тип вложения - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 @@ -2091,7 +2100,7 @@ Имя файла - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 @@ -2101,7 +2110,7 @@ Размер файла - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 @@ -2110,7 +2119,7 @@ Предпросмотр - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 @@ -2120,7 +2129,7 @@ Скачать - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 @@ -2131,7 +2140,7 @@ Пользователь создавший компонент - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 @@ -2142,10 +2151,10 @@ Unknown - Неизвестное + Неизвестный тип - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 @@ -2158,7 +2167,7 @@ Доступ запрещен - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 @@ -2169,7 +2178,7 @@ Пользователь последний редактировавший этот компонент - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 @@ -2179,7 +2188,7 @@ Избранное - + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 @@ -2189,7 +2198,7 @@ Минимальное количество для заказа - + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 Part-DB1\templates\_navbar_search.html.twig:46 @@ -2206,7 +2215,7 @@ Производитель - + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 Part-DB1\templates\_navbar_search.html.twig:11 @@ -2218,7 +2227,7 @@ Имя - + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 Part-DB1\templates\Parts\info\_main_infos.html.twig:27 @@ -2229,7 +2238,7 @@ Вернуться к текущей версии - + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 Part-DB1\templates\_navbar_search.html.twig:19 @@ -2244,7 +2253,7 @@ Описание - + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 Part-DB1\templates\_navbar_search.html.twig:15 @@ -2261,7 +2270,7 @@ Категория - + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 Part-DB1\templates\Parts\info\_main_infos.html.twig:39 @@ -2273,7 +2282,7 @@ В наличии - + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 Part-DB1\templates\Parts\info\_main_infos.html.twig:41 @@ -2285,7 +2294,7 @@ Минимально в наличии - + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 Part-DB1\templates\_navbar_search.html.twig:52 @@ -2301,7 +2310,7 @@ Посадочное место - + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 Part-DB1\templates\Parts\info\_main_infos.html.twig:59 @@ -2314,7 +2323,7 @@ Средняя цена - + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 Part-DB1\templates\Parts\info\_order_infos.html.twig:5 @@ -2324,7 +2333,7 @@ Название - + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 Part-DB1\templates\Parts\info\_order_infos.html.twig:6 @@ -2334,7 +2343,7 @@ Номер.комп. - + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 Part-DB1\templates\Parts\info\_order_infos.html.twig:28 @@ -2344,7 +2353,7 @@ Миним. количество - + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 Part-DB1\templates\Parts\info\_order_infos.html.twig:29 @@ -2354,7 +2363,7 @@ Цена - + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 Part-DB1\templates\Parts\info\_order_infos.html.twig:31 @@ -2364,7 +2373,7 @@ Цена за единицу - + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 Part-DB1\templates\Parts\info\_order_infos.html.twig:71 @@ -2374,7 +2383,7 @@ Редакт. - + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 Part-DB1\templates\Parts\info\_order_infos.html.twig:72 @@ -2384,7 +2393,7 @@ Удалить - + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 Part-DB1\templates\Parts\info\_part_lots.html.twig:6 @@ -2394,7 +2403,7 @@ Описание - + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 Part-DB1\templates\Parts\info\_part_lots.html.twig:7 @@ -2404,7 +2413,7 @@ Место хранения - + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 Part-DB1\templates\Parts\info\_part_lots.html.twig:8 @@ -2414,7 +2423,7 @@ Количество - + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 Part-DB1\templates\Parts\info\_part_lots.html.twig:22 @@ -2424,7 +2433,7 @@ Место хранения неизвестно - + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 Part-DB1\templates\Parts\info\_part_lots.html.twig:29 @@ -2434,7 +2443,7 @@ Наличие неизвестно - + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 Part-DB1\templates\Parts\info\_part_lots.html.twig:38 @@ -2444,7 +2453,7 @@ Дата истечения - + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 Part-DB1\templates\Parts\info\_part_lots.html.twig:46 @@ -2454,7 +2463,7 @@ Истекло - + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 Part-DB1\templates\Parts\info\_part_lots.html.twig:53 @@ -2464,7 +2473,7 @@ Нужно пополнить - + Part-DB1\templates\Parts\info\_picture.html.twig:15 Part-DB1\templates\Parts\info\_picture.html.twig:15 @@ -2474,7 +2483,7 @@ Предыдущая картинка - + Part-DB1\templates\Parts\info\_picture.html.twig:19 Part-DB1\templates\Parts\info\_picture.html.twig:19 @@ -2484,7 +2493,7 @@ Следующая картинка - + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 Part-DB1\templates\Parts\info\_sidebar.html.twig:21 @@ -2494,7 +2503,7 @@ Вес - + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 Part-DB1\templates\Parts\info\_sidebar.html.twig:30 @@ -2504,7 +2513,7 @@ Нужно рассмотреть - + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 Part-DB1\templates\Parts\info\_sidebar.html.twig:39 @@ -2514,7 +2523,7 @@ Избранное - + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 Part-DB1\templates\Parts\info\_sidebar.html.twig:47 @@ -2524,7 +2533,7 @@ Больше не доступно - + Part-DB1\templates\Parts\info\_specifications.html.twig:10 @@ -2533,7 +2542,7 @@ Автоматически получено из описания - + Part-DB1\templates\Parts\info\_specifications.html.twig:15 @@ -2542,7 +2551,7 @@ Автоматически получено из комментария - + Part-DB1\templates\Parts\info\_tools.html.twig:6 Part-DB1\templates\Parts\info\_tools.html.twig:4 @@ -2553,7 +2562,7 @@ Редактировать компонент - + Part-DB1\templates\Parts\info\_tools.html.twig:16 Part-DB1\templates\Parts\info\_tools.html.twig:14 @@ -2564,7 +2573,7 @@ Клонировать компонент - + Part-DB1\templates\Parts\info\_tools.html.twig:24 Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 @@ -2575,7 +2584,7 @@ Создать новый компонент - + Part-DB1\templates\Parts\info\_tools.html.twig:31 Part-DB1\templates\Parts\info\_tools.html.twig:29 @@ -2585,7 +2594,7 @@ Вы действительно хотите удалить этот компонент? - + Part-DB1\templates\Parts\info\_tools.html.twig:32 Part-DB1\templates\Parts\info\_tools.html.twig:30 @@ -2596,7 +2605,7 @@ Это не может быть отменено! - + Part-DB1\templates\Parts\info\_tools.html.twig:39 Part-DB1\templates\Parts\info\_tools.html.twig:37 @@ -2606,7 +2615,7 @@ Удалить компонент - + Part-DB1\templates\Parts\lists\all_list.html.twig:4 Part-DB1\templates\Parts\lists\all_list.html.twig:4 @@ -2616,7 +2625,7 @@ Все компоненты - + Part-DB1\templates\Parts\lists\category_list.html.twig:4 Part-DB1\templates\Parts\lists\category_list.html.twig:4 @@ -2626,7 +2635,7 @@ Компоненты с категорией - + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 @@ -2636,7 +2645,7 @@ Компоненты с посадочным местом - + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 @@ -2646,7 +2655,7 @@ Компоненты производителя - + Part-DB1\templates\Parts\lists\search_list.html.twig:4 Part-DB1\templates\Parts\lists\search_list.html.twig:4 @@ -2656,7 +2665,7 @@ Поиск компонентов - + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 @@ -2666,7 +2675,7 @@ Компоненты хранилища - + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 @@ -2676,7 +2685,7 @@ Компоненты поставщика - + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 Part-DB1\templates\Parts\lists\tags_list.html.twig:4 @@ -2686,7 +2695,7 @@ Компоненты с меткой - + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 Part-DB1\templates\Parts\lists\_info_card.html.twig:17 @@ -2696,7 +2705,7 @@ Общие - + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 Part-DB1\templates\Parts\lists\_info_card.html.twig:20 @@ -2706,7 +2715,7 @@ Статистика - + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 @@ -2715,7 +2724,7 @@ Вложения - + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 @@ -2724,7 +2733,7 @@ Параметры - + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 Part-DB1\templates\Parts\lists\_info_card.html.twig:30 @@ -2734,7 +2743,7 @@ Имя - + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 Part-DB1\templates\Parts\lists\_info_card.html.twig:96 @@ -2746,7 +2755,7 @@ Родитель - + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 Part-DB1\templates\Parts\lists\_info_card.html.twig:46 @@ -2756,7 +2765,7 @@ Редактировать - + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 Part-DB1\templates\Parts\lists\_info_card.html.twig:63 @@ -2766,7 +2775,7 @@ Количество дочерних элементов - + Part-DB1\templates\security\2fa_base_form.html.twig:3 Part-DB1\templates\security\2fa_base_form.html.twig:5 @@ -2778,7 +2787,7 @@ Нужна двухфакторная аутентификация - + Part-DB1\templates\security\2fa_base_form.html.twig:39 Part-DB1\templates\security\2fa_base_form.html.twig:39 @@ -2788,7 +2797,7 @@ Это доверенный компьютер (если включено то двухфакторная аутентификация на этом компьютере больше не будет запрашиваться) - + Part-DB1\templates\security\2fa_base_form.html.twig:52 Part-DB1\templates\security\login.html.twig:58 @@ -2800,7 +2809,7 @@ Вход - + Part-DB1\templates\security\2fa_base_form.html.twig:53 Part-DB1\templates\security\U2F\u2f_login.html.twig:13 @@ -2814,7 +2823,7 @@ Выход - + Part-DB1\templates\security\2fa_form.html.twig:6 Part-DB1\templates\security\2fa_form.html.twig:6 @@ -2824,7 +2833,7 @@ код приложения Аутентификатор - + Part-DB1\templates\security\2fa_form.html.twig:10 Part-DB1\templates\security\2fa_form.html.twig:10 @@ -2834,7 +2843,7 @@ Введите 6-значный код из вашего приложения Аутентификатор или один из резервных кодов если приложение не доступно. - + Part-DB1\templates\security\login.html.twig:3 Part-DB1\templates\security\login.html.twig:3 @@ -2845,7 +2854,7 @@ Вход - + Part-DB1\templates\security\login.html.twig:7 Part-DB1\templates\security\login.html.twig:7 @@ -2856,7 +2865,7 @@ Вход - + Part-DB1\templates\security\login.html.twig:31 Part-DB1\templates\security\login.html.twig:31 @@ -2867,7 +2876,7 @@ Имя пользователя - + Part-DB1\templates\security\login.html.twig:34 Part-DB1\templates\security\login.html.twig:34 @@ -2878,7 +2887,7 @@ Имя пользователя - + Part-DB1\templates\security\login.html.twig:38 Part-DB1\templates\security\login.html.twig:38 @@ -2889,7 +2898,7 @@ Пароль - + Part-DB1\templates\security\login.html.twig:40 Part-DB1\templates\security\login.html.twig:40 @@ -2900,7 +2909,7 @@ Пароль - + Part-DB1\templates\security\login.html.twig:50 Part-DB1\templates\security\login.html.twig:50 @@ -2911,7 +2920,7 @@ Запомнить меня (включайте это только на личном компьютере) - + Part-DB1\templates\security\login.html.twig:64 Part-DB1\templates\security\login.html.twig:64 @@ -2921,7 +2930,7 @@ Забыл имя пользователя/пароль? - + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 @@ -2931,7 +2940,7 @@ Установить новый пароль - + Part-DB1\templates\security\pw_reset_request.html.twig:5 Part-DB1\templates\security\pw_reset_request.html.twig:5 @@ -2941,7 +2950,7 @@ Запросить новый пароль - + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 Part-DB1\templates\security\U2F\u2f_register.html.twig:10 @@ -2953,7 +2962,7 @@ Вы зашли на эту страницу используя небезопасный протокол HTTP, поэтому U2F скорее всего не будет работать (сообщение об ошибке: Неверный запрос). Попросите администратора настроить HTTPS если Вы хотите использовать ключи безопасности. - + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 Part-DB1\templates\security\U2F\u2f_register.html.twig:22 @@ -2965,7 +2974,7 @@ Пожалуйста вставьте Ваш ключ безопасности и нажмите на нем кнопку! - + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 Part-DB1\templates\security\U2F\u2f_register.html.twig:3 @@ -2975,7 +2984,7 @@ Добавить ключ безопасности - + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 Part-DB1\templates\Users\_2fa_settings.html.twig:111 @@ -2987,7 +2996,7 @@ U2F/FIDO совместимый ключ (например YubiKey или NitroKey) предоставляет удобный и безопасный двухфакторный способ аутентификации. Здесь вы можете зарегистрировать свои ключи безопасности и, если Вас запросят пройти двухфакторную проверку, просто вставьте ключ в USB порт или поднесите его к устройству считывания NFC. - + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 Part-DB1\templates\security\U2F\u2f_register.html.twig:7 @@ -2997,7 +3006,7 @@ Чтобы не утратить доступ в случае потери ключа мы рекомендуем зарегистрировать еще один как резервный и хранить его в безопасном месте! - + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 Part-DB1\templates\security\U2F\u2f_register.html.twig:16 @@ -3007,7 +3016,7 @@ Отображаемое имя ключа (напр. Резервный) - + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 Part-DB1\templates\security\U2F\u2f_register.html.twig:19 @@ -3017,7 +3026,7 @@ Добавить ключ безопасности - + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 Part-DB1\templates\security\U2F\u2f_register.html.twig:27 @@ -3027,7 +3036,7 @@ Вернуться к настройкам - + Part-DB1\templates\Statistics\statistics.html.twig:5 Part-DB1\templates\Statistics\statistics.html.twig:8 @@ -3040,7 +3049,7 @@ Статистика - + Part-DB1\templates\Statistics\statistics.html.twig:14 Part-DB1\templates\Statistics\statistics.html.twig:14 @@ -3051,7 +3060,7 @@ Компоненты - + Part-DB1\templates\Statistics\statistics.html.twig:19 Part-DB1\templates\Statistics\statistics.html.twig:19 @@ -3062,7 +3071,7 @@ Структуры данных - + Part-DB1\templates\Statistics\statistics.html.twig:24 Part-DB1\templates\Statistics\statistics.html.twig:24 @@ -3073,7 +3082,7 @@ Вложения - + Part-DB1\templates\Statistics\statistics.html.twig:34 Part-DB1\templates\Statistics\statistics.html.twig:59 @@ -3088,7 +3097,7 @@ Свойство - + Part-DB1\templates\Statistics\statistics.html.twig:35 Part-DB1\templates\Statistics\statistics.html.twig:60 @@ -3103,7 +3112,7 @@ Значение - + Part-DB1\templates\Statistics\statistics.html.twig:40 Part-DB1\templates\Statistics\statistics.html.twig:40 @@ -3114,7 +3123,7 @@ Количество разных компонентов - + Part-DB1\templates\Statistics\statistics.html.twig:44 Part-DB1\templates\Statistics\statistics.html.twig:44 @@ -3125,7 +3134,7 @@ Сумма всех существующих запасов компонентов - + Part-DB1\templates\Statistics\statistics.html.twig:48 Part-DB1\templates\Statistics\statistics.html.twig:48 @@ -3136,7 +3145,7 @@ Количество компонентов с данными о стоимости - + Part-DB1\templates\Statistics\statistics.html.twig:65 Part-DB1\templates\Statistics\statistics.html.twig:65 @@ -3147,7 +3156,7 @@ Количество категорий - + Part-DB1\templates\Statistics\statistics.html.twig:69 Part-DB1\templates\Statistics\statistics.html.twig:69 @@ -3158,7 +3167,7 @@ Количество посадочных мест - + Part-DB1\templates\Statistics\statistics.html.twig:73 Part-DB1\templates\Statistics\statistics.html.twig:73 @@ -3169,7 +3178,7 @@ Количество производителей - + Part-DB1\templates\Statistics\statistics.html.twig:77 Part-DB1\templates\Statistics\statistics.html.twig:77 @@ -3180,7 +3189,7 @@ Количество хранилищ - + Part-DB1\templates\Statistics\statistics.html.twig:81 Part-DB1\templates\Statistics\statistics.html.twig:81 @@ -3191,7 +3200,7 @@ Количество поставщиков - + Part-DB1\templates\Statistics\statistics.html.twig:85 Part-DB1\templates\Statistics\statistics.html.twig:85 @@ -3202,7 +3211,7 @@ Количество валют - + Part-DB1\templates\Statistics\statistics.html.twig:89 Part-DB1\templates\Statistics\statistics.html.twig:89 @@ -3213,7 +3222,7 @@ Количество единиц измерений - + Part-DB1\templates\Statistics\statistics.html.twig:93 Part-DB1\templates\Statistics\statistics.html.twig:93 @@ -3224,7 +3233,7 @@ Количество устройств - + Part-DB1\templates\Statistics\statistics.html.twig:110 Part-DB1\templates\Statistics\statistics.html.twig:110 @@ -3235,7 +3244,7 @@ Количество типов вложений - + Part-DB1\templates\Statistics\statistics.html.twig:114 Part-DB1\templates\Statistics\statistics.html.twig:114 @@ -3246,7 +3255,7 @@ Общее количество вложений - + Part-DB1\templates\Statistics\statistics.html.twig:118 Part-DB1\templates\Statistics\statistics.html.twig:118 @@ -3257,7 +3266,7 @@ Число пользователей прикрепивших вложения - + Part-DB1\templates\Statistics\statistics.html.twig:122 Part-DB1\templates\Statistics\statistics.html.twig:122 @@ -3268,7 +3277,7 @@ Количество личных вложений - + Part-DB1\templates\Statistics\statistics.html.twig:126 Part-DB1\templates\Statistics\statistics.html.twig:126 @@ -3279,7 +3288,7 @@ Количество вложений доступных по внешним ссылкам (URL) - + Part-DB1\templates\Users\backup_codes.html.twig:3 Part-DB1\templates\Users\backup_codes.html.twig:9 @@ -3291,7 +3300,7 @@ Резервные коды - + Part-DB1\templates\Users\backup_codes.html.twig:12 Part-DB1\templates\Users\backup_codes.html.twig:12 @@ -3301,7 +3310,7 @@ Распечатайте эти коды и храните их в безопасном месте! - + Part-DB1\templates\Users\backup_codes.html.twig:13 Part-DB1\templates\Users\backup_codes.html.twig:13 @@ -3311,7 +3320,7 @@ Если Вы больше не можете использовать приложение Аутентификатор (потеряли смартфон, утратили данные и т.п.) то используйте один из этих кодов чтобы получить доступ к своему аккаунту и возможности заново настроить приложение Аутентификатор. Каждый из этих кодов может быть использован только один раз, после чего мы рекомендуем уничтожить использованные коды. Храните коды в безопасном месте, в противном случае любой кто получит к ним доступ сможет зайти в Ваш аккаунт. - + Part-DB1\templates\Users\backup_codes.html.twig:16 Part-DB1\templates\Users\backup_codes.html.twig:16 @@ -3321,7 +3330,7 @@ Имя пользователя - + Part-DB1\templates\Users\backup_codes.html.twig:29 Part-DB1\templates\Users\backup_codes.html.twig:29 @@ -3331,7 +3340,7 @@ Дата генерации страницы %date% - + Part-DB1\templates\Users\backup_codes.html.twig:32 Part-DB1\templates\Users\backup_codes.html.twig:32 @@ -3341,7 +3350,7 @@ Печать - + Part-DB1\templates\Users\backup_codes.html.twig:35 Part-DB1\templates\Users\backup_codes.html.twig:35 @@ -3351,7 +3360,7 @@ Скопировать в буфер обмена - + Part-DB1\templates\Users\user_info.html.twig:3 Part-DB1\templates\Users\user_info.html.twig:6 @@ -3368,7 +3377,7 @@ Информация о пользователе - + Part-DB1\templates\Users\user_info.html.twig:18 Part-DB1\src\Form\UserSettingsType.php:77 @@ -3382,7 +3391,7 @@ Имя - + Part-DB1\templates\Users\user_info.html.twig:24 Part-DB1\src\Form\UserSettingsType.php:82 @@ -3396,7 +3405,7 @@ Фамилия - + Part-DB1\templates\Users\user_info.html.twig:30 Part-DB1\src\Form\UserSettingsType.php:92 @@ -3410,7 +3419,7 @@ Email - + Part-DB1\templates\Users\user_info.html.twig:37 Part-DB1\src\Form\UserSettingsType.php:87 @@ -3424,7 +3433,7 @@ Отдел - + Part-DB1\templates\Users\user_info.html.twig:47 Part-DB1\src\Form\UserSettingsType.php:73 @@ -3438,7 +3447,7 @@ Имя пользователя - + Part-DB1\templates\Users\user_info.html.twig:53 Part-DB1\src\Services\ElementTypeNameGenerator.php:93 @@ -3451,7 +3460,7 @@ Группа - + Part-DB1\templates\Users\user_info.html.twig:67 Part-DB1\templates\Users\user_info.html.twig:67 @@ -3461,7 +3470,7 @@ Разрешения - + Part-DB1\templates\Users\user_settings.html.twig:3 Part-DB1\templates\Users\user_settings.html.twig:6 @@ -3478,7 +3487,7 @@ Настройки пользователя - + Part-DB1\templates\Users\user_settings.html.twig:18 Part-DB1\templates\Users\user_settings.html.twig:18 @@ -3489,7 +3498,7 @@ Личные данные - + Part-DB1\templates\Users\user_settings.html.twig:22 Part-DB1\templates\Users\user_settings.html.twig:22 @@ -3500,7 +3509,7 @@ Конфигурация - + Part-DB1\templates\Users\user_settings.html.twig:55 Part-DB1\templates\Users\user_settings.html.twig:55 @@ -3511,7 +3520,7 @@ Сменить пароль - + Part-DB1\templates\Users\_2fa_settings.html.twig:6 Part-DB1\templates\Users\_2fa_settings.html.twig:6 @@ -3521,7 +3530,7 @@ Двухфакторная аутентификация - + Part-DB1\templates\Users\_2fa_settings.html.twig:13 Part-DB1\templates\Users\_2fa_settings.html.twig:13 @@ -3531,7 +3540,7 @@ приложение Аутентификатор - + Part-DB1\templates\Users\_2fa_settings.html.twig:17 Part-DB1\templates\Users\_2fa_settings.html.twig:17 @@ -3541,7 +3550,7 @@ Резервные коды - + Part-DB1\templates\Users\_2fa_settings.html.twig:21 Part-DB1\templates\Users\_2fa_settings.html.twig:21 @@ -3551,7 +3560,7 @@ Ключи безопасности (U2F) - + Part-DB1\templates\Users\_2fa_settings.html.twig:25 Part-DB1\templates\Users\_2fa_settings.html.twig:25 @@ -3561,7 +3570,7 @@ Доверенные устройства - + Part-DB1\templates\Users\_2fa_settings.html.twig:33 Part-DB1\templates\Users\_2fa_settings.html.twig:33 @@ -3571,7 +3580,7 @@ Вы действительно хотите отключить приложение Аутентификатор? - + Part-DB1\templates\Users\_2fa_settings.html.twig:33 Part-DB1\templates\Users\_2fa_settings.html.twig:33 @@ -3582,7 +3591,7 @@ Также отметим что без двухфакторной аутентификации Ваш аккаунт хуже защищен от злоумышленников! - + Part-DB1\templates\Users\_2fa_settings.html.twig:39 Part-DB1\templates\Users\_2fa_settings.html.twig:39 @@ -3592,7 +3601,7 @@ приложение Аутентификатор выключено! - + Part-DB1\templates\Users\_2fa_settings.html.twig:48 Part-DB1\templates\Users\_2fa_settings.html.twig:48 @@ -3602,7 +3611,7 @@ Скачать приложение для аутентификации можно по ссылке (e.g. <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> oder <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp">FreeOTP Authenticator</a>) - + Part-DB1\templates\Users\_2fa_settings.html.twig:49 Part-DB1\templates\Users\_2fa_settings.html.twig:49 @@ -3612,7 +3621,7 @@ Считать QR код используя приложение или ввести данные вручную - + Part-DB1\templates\Users\_2fa_settings.html.twig:50 Part-DB1\templates\Users\_2fa_settings.html.twig:50 @@ -3622,7 +3631,7 @@ Введите сгенерированный код в поле внизу и нажмите на "Подтвердить" - + Part-DB1\templates\Users\_2fa_settings.html.twig:51 Part-DB1\templates\Users\_2fa_settings.html.twig:51 @@ -3632,7 +3641,7 @@ Распечатать резервные коды и сохранить из в безопасном месте - + Part-DB1\templates\Users\_2fa_settings.html.twig:58 Part-DB1\templates\Users\_2fa_settings.html.twig:58 @@ -3642,7 +3651,7 @@ Настроить вручную - + Part-DB1\templates\Users\_2fa_settings.html.twig:62 Part-DB1\templates\Users\_2fa_settings.html.twig:62 @@ -3652,7 +3661,7 @@ Тип - + Part-DB1\templates\Users\_2fa_settings.html.twig:63 Part-DB1\templates\Users\_2fa_settings.html.twig:63 @@ -3662,7 +3671,7 @@ Имя пользователя - + Part-DB1\templates\Users\_2fa_settings.html.twig:64 Part-DB1\templates\Users\_2fa_settings.html.twig:64 @@ -3672,7 +3681,7 @@ Секрет - + Part-DB1\templates\Users\_2fa_settings.html.twig:65 Part-DB1\templates\Users\_2fa_settings.html.twig:65 @@ -3682,7 +3691,7 @@ Количество цифр - + Part-DB1\templates\Users\_2fa_settings.html.twig:74 Part-DB1\templates\Users\_2fa_settings.html.twig:74 @@ -3692,7 +3701,7 @@ Приложения Аутентификатор разрешено - + Part-DB1\templates\Users\_2fa_settings.html.twig:83 Part-DB1\templates\Users\_2fa_settings.html.twig:83 @@ -3702,7 +3711,7 @@ Резервные коды запрещены. Установите приложение чтобы разрешить резервные коды. - + Part-DB1\templates\Users\_2fa_settings.html.twig:84 Part-DB1\templates\Users\_2fa_settings.html.twig:92 @@ -3714,7 +3723,7 @@ Вы можете использовать эти резервные коды для доступа в ваш аккаунт если вы потеряли устройство на котором установлено приложение Аутентификатор. Распечатайте коды и храните их в безопасном месте. - + Part-DB1\templates\Users\_2fa_settings.html.twig:88 Part-DB1\templates\Users\_2fa_settings.html.twig:88 @@ -3724,7 +3733,7 @@ Точно хотите сбросить коды? - + Part-DB1\templates\Users\_2fa_settings.html.twig:88 Part-DB1\templates\Users\_2fa_settings.html.twig:88 @@ -3735,7 +3744,7 @@ Не забудьте распечатать новы кода и хранить их в безопасном месте! - + Part-DB1\templates\Users\_2fa_settings.html.twig:91 Part-DB1\templates\Users\_2fa_settings.html.twig:91 @@ -3745,7 +3754,7 @@ Резервные коды разрешены - + Part-DB1\templates\Users\_2fa_settings.html.twig:99 Part-DB1\templates\Users\_2fa_settings.html.twig:99 @@ -3755,7 +3764,7 @@ Показать резервные коды - + Part-DB1\templates\Users\_2fa_settings.html.twig:114 Part-DB1\templates\Users\_2fa_settings.html.twig:114 @@ -3765,7 +3774,7 @@ Зарегистрировать ключи безопасности - + Part-DB1\templates\Users\_2fa_settings.html.twig:115 Part-DB1\templates\Users\_2fa_settings.html.twig:115 @@ -3775,7 +3784,7 @@ Точно хотите удалить этот ключ безопасности? - + Part-DB1\templates\Users\_2fa_settings.html.twig:116 Part-DB1\templates\Users\_2fa_settings.html.twig:116 @@ -3785,7 +3794,7 @@ Если вы удалите этот ключ то вы больше не сможете использовать его для входа. Если не останется зарегистрированных ключей то двухфакторная аутентификация будет запрещена. - + Part-DB1\templates\Users\_2fa_settings.html.twig:123 Part-DB1\templates\Users\_2fa_settings.html.twig:123 @@ -3795,7 +3804,7 @@ Имя ключа - + Part-DB1\templates\Users\_2fa_settings.html.twig:124 Part-DB1\templates\Users\_2fa_settings.html.twig:124 @@ -3805,7 +3814,7 @@ Дата регистрации - + Part-DB1\templates\Users\_2fa_settings.html.twig:134 Part-DB1\templates\Users\_2fa_settings.html.twig:134 @@ -3815,7 +3824,7 @@ Удалить ключ - + Part-DB1\templates\Users\_2fa_settings.html.twig:141 Part-DB1\templates\Users\_2fa_settings.html.twig:141 @@ -3825,7 +3834,7 @@ Еще нет зарегистрированных ключей. - + Part-DB1\templates\Users\_2fa_settings.html.twig:144 Part-DB1\templates\Users\_2fa_settings.html.twig:144 @@ -3835,7 +3844,7 @@ Зарегистрировать новый ключ безопасности - + Part-DB1\templates\Users\_2fa_settings.html.twig:148 Part-DB1\templates\Users\_2fa_settings.html.twig:148 @@ -3846,7 +3855,7 @@ Если что-то пошло не так или этот компьютер перестал быть доверенным, вы можете сбросить здесь статус <i>ВСЕХ</i> компьютеров. - + Part-DB1\templates\Users\_2fa_settings.html.twig:149 Part-DB1\templates\Users\_2fa_settings.html.twig:149 @@ -3856,7 +3865,7 @@ Точно хотите удалить все доверенные компьютеры? - + Part-DB1\templates\Users\_2fa_settings.html.twig:150 Part-DB1\templates\Users\_2fa_settings.html.twig:150 @@ -3866,7 +3875,7 @@ Вам снова нужно будет использовать двухфакторную аутентификацию на всех компьютерах. Убедитесь что у вас есть на руках устройство для двухфакторного подтверждения. - + Part-DB1\templates\Users\_2fa_settings.html.twig:154 Part-DB1\templates\Users\_2fa_settings.html.twig:154 @@ -3876,7 +3885,7 @@ Сбросить доверенные устройства - + Part-DB1\templates\_navbar.html.twig:4 Part-DB1\templates\_navbar.html.twig:4 @@ -3887,7 +3896,7 @@ Переключить боковую панель - + Part-DB1\templates\_navbar.html.twig:22 @@ -3896,7 +3905,7 @@ Сканер - + Part-DB1\templates\_navbar.html.twig:38 Part-DB1\templates\_navbar.html.twig:36 @@ -3907,7 +3916,7 @@ Вошел как - + Part-DB1\templates\_navbar.html.twig:44 Part-DB1\templates\_navbar.html.twig:42 @@ -3918,7 +3927,7 @@ Вход - + Part-DB1\templates\_navbar.html.twig:50 Part-DB1\templates\_navbar.html.twig:48 @@ -3928,7 +3937,7 @@ Темный режим - + Part-DB1\templates\_navbar.html.twig:54 Part-DB1\src\Form\UserSettingsType.php:97 @@ -3942,7 +3951,7 @@ Выбрать язык - + Part-DB1\templates\_navbar_search.html.twig:4 Part-DB1\templates\_navbar_search.html.twig:4 @@ -3953,7 +3962,7 @@ Опции поиска - + Part-DB1\templates\_navbar_search.html.twig:23 @@ -3962,7 +3971,7 @@ Метки - + Part-DB1\templates\_navbar_search.html.twig:27 Part-DB1\src\Form\LabelOptionsType.php:68 @@ -3977,7 +3986,7 @@ Место хранения - + Part-DB1\templates\_navbar_search.html.twig:36 Part-DB1\templates\_navbar_search.html.twig:31 @@ -3988,7 +3997,7 @@ Ном.заказа - + Part-DB1\templates\_navbar_search.html.twig:40 Part-DB1\src\Services\ElementTypeNameGenerator.php:89 @@ -4001,7 +4010,7 @@ Поставщик - + Part-DB1\templates\_navbar_search.html.twig:57 Part-DB1\templates\_navbar_search.html.twig:52 @@ -4009,10 +4018,10 @@ search.deactivateBarcode - Деакт. Штрихкод + Деактивировать штрих-код - + Part-DB1\templates\_navbar_search.html.twig:61 Part-DB1\templates\_navbar_search.html.twig:56 @@ -4023,7 +4032,7 @@ Соответствие рег.выраж. - + Part-DB1\templates\_navbar_search.html.twig:68 Part-DB1\templates\_navbar_search.html.twig:62 @@ -4033,7 +4042,23 @@ Поехали! - + + + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + templates\base.html.twig:175 + templates\base.html.twig:189 + templates\base.html.twig:202 + templates\base.html.twig:230 + + + project.labelp + Проекты + + + Part-DB1\templates\_sidebar.html.twig:2 Part-DB1\templates\_sidebar.html.twig:2 @@ -4046,7 +4071,7 @@ Действия - + Part-DB1\templates\_sidebar.html.twig:6 Part-DB1\templates\_sidebar.html.twig:6 @@ -4059,7 +4084,7 @@ Источник данных - + Part-DB1\templates\_sidebar.html.twig:10 Part-DB1\templates\_sidebar.html.twig:10 @@ -4072,7 +4097,7 @@ Производители - + Part-DB1\templates\_sidebar.html.twig:11 Part-DB1\templates\_sidebar.html.twig:11 @@ -4085,7 +4110,7 @@ Поставщики - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 @@ -4101,7 +4126,7 @@ Не удалось скачать внешнее вложение. - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 @@ -4111,7 +4136,7 @@ Изменения успешно сохранены. - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 @@ -4121,7 +4146,7 @@ Изменения не могут быть сохранены! Проверьте введенные данные! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 @@ -4131,7 +4156,7 @@ Элемент создан. - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 @@ -4141,7 +4166,7 @@ Не могу создать элемент. Проверьте введенные данные! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 @@ -4152,7 +4177,7 @@ Элемент удален! - + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 Part-DB1\src\Controller\UserController.php:109 @@ -4168,7 +4193,7 @@ CSFR жетон неверен. Перегрузите эту страницу или свяжитесь с администратором если это сообщение остаётся на экране. - + Part-DB1\src\Controller\LabelController.php:125 @@ -4177,7 +4202,7 @@ Ничего не найдено - + Part-DB1\src\Controller\LogController.php:149 Part-DB1\src\Controller\LogController.php:154 @@ -4188,7 +4213,7 @@ Этот элемент не найден в базе! - + Part-DB1\src\Controller\LogController.php:156 Part-DB1\src\Controller\LogController.php:160 @@ -4199,7 +4224,7 @@ Состояние успешно возвращено. - + Part-DB1\src\Controller\LogController.php:176 Part-DB1\src\Controller\LogController.php:180 @@ -4210,7 +4235,7 @@ Элемент успешно возвращен. - + Part-DB1\src\Controller\LogController.php:178 Part-DB1\src\Controller\LogController.php:182 @@ -4221,7 +4246,7 @@ Элемент уже был возвращен! - + Part-DB1\src\Controller\LogController.php:185 Part-DB1\src\Controller\LogController.php:189 @@ -4232,7 +4257,7 @@ Элемент успешно удален. - + Part-DB1\src\Controller\LogController.php:187 Part-DB1\src\Controller\LogController.php:191 @@ -4243,7 +4268,7 @@ Элемент уже был удален! - + Part-DB1\src\Controller\LogController.php:194 Part-DB1\src\Controller\LogController.php:198 @@ -4254,7 +4279,7 @@ Откат изменений завершен успешно! - + Part-DB1\src\Controller\LogController.php:196 Part-DB1\src\Controller\LogController.php:200 @@ -4265,7 +4290,7 @@ Чтобы откатить это изменение вам нужно вернуть элемент! - + Part-DB1\src\Controller\LogController.php:199 Part-DB1\src\Controller\LogController.php:203 @@ -4276,7 +4301,7 @@ Эта запись в логах не может быть возвращена! - + Part-DB1\src\Controller\PartController.php:182 Part-DB1\src\Controller\PartController.php:182 @@ -4287,7 +4312,7 @@ Изменения сохранены! - + Part-DB1\src\Controller\PartController.php:186 Part-DB1\src\Controller\PartController.php:186 @@ -4297,7 +4322,7 @@ Ошибка при сохранении: проверьте введенные данные! - + Part-DB1\src\Controller\PartController.php:216 Part-DB1\src\Controller\PartController.php:219 @@ -4307,7 +4332,7 @@ Компонент успешно удален. - + Part-DB1\src\Controller\PartController.php:302 Part-DB1\src\Controller\PartController.php:277 @@ -4317,10 +4342,10 @@ part.created_flash - Деталь создана! + Компонент создан! - + Part-DB1\src\Controller\PartController.php:308 Part-DB1\src\Controller\PartController.php:283 @@ -4330,7 +4355,7 @@ Пожалуйста проверьте введенные данные! - + Part-DB1\src\Controller\ScanController.php:68 Part-DB1\src\Controller\ScanController.php:90 @@ -4340,7 +4365,7 @@ По данному штрихкоду совпадений не найдено. - + Part-DB1\src\Controller\ScanController.php:71 @@ -4349,7 +4374,7 @@ Неизвестный формат! - + Part-DB1\src\Controller\ScanController.php:86 @@ -4358,7 +4383,7 @@ Элемент найден. - + Part-DB1\src\Controller\SecurityController.php:114 Part-DB1\src\Controller\SecurityController.php:109 @@ -4368,7 +4393,7 @@ Имя пользователя / Email - + Part-DB1\src\Controller\SecurityController.php:131 Part-DB1\src\Controller\SecurityController.php:126 @@ -4378,7 +4403,7 @@ Запрос на сброс пароля прошел успешно! Проверьте почтовый ящик для дальнейших инструкций. - + Part-DB1\src\Controller\SecurityController.php:162 Part-DB1\src\Controller\SecurityController.php:160 @@ -4388,7 +4413,7 @@ Имя пользователя - + Part-DB1\src\Controller\SecurityController.php:165 Part-DB1\src\Controller\SecurityController.php:163 @@ -4398,7 +4423,7 @@ Жетон - + Part-DB1\src\Controller\SecurityController.php:194 Part-DB1\src\Controller\SecurityController.php:192 @@ -4408,7 +4433,7 @@ Имя пользователя или Жетон неверные! Проверьте введенные данные. - + Part-DB1\src\Controller\SecurityController.php:196 Part-DB1\src\Controller\SecurityController.php:194 @@ -4418,7 +4443,7 @@ Пароль успешно сброшен. Теперь вы можете зайти используя новый пароль. - + Part-DB1\src\Controller\UserController.php:107 Part-DB1\src\Controller\UserController.php:99 @@ -4428,7 +4453,7 @@ Все двухфакторные способы аутентификации успешно запрещены. - + Part-DB1\src\Controller\UserSettingsController.php:101 Part-DB1\src\Controller\UserSettingsController.php:92 @@ -4438,7 +4463,7 @@ Нет разрешенных резервных кодов! - + Part-DB1\src\Controller\UserSettingsController.php:138 Part-DB1\src\Controller\UserSettingsController.php:132 @@ -4448,7 +4473,7 @@ Для этого ID нет привязанных ключей безопасности. - + Part-DB1\src\Controller\UserSettingsController.php:145 Part-DB1\src\Controller\UserSettingsController.php:139 @@ -4458,7 +4483,7 @@ Вы не можете удалять ключи безопасности других пользователей! - + Part-DB1\src\Controller\UserSettingsController.php:153 Part-DB1\src\Controller\UserSettingsController.php:147 @@ -4468,7 +4493,7 @@ Ключ безопасности успешно удален. - + Part-DB1\src\Controller\UserSettingsController.php:188 Part-DB1\src\Controller\UserSettingsController.php:180 @@ -4478,7 +4503,7 @@ Доверенные устройства успешно сброшены. - + Part-DB1\src\Controller\UserSettingsController.php:235 Part-DB1\src\Controller\UserSettingsController.php:226 @@ -4489,7 +4514,7 @@ Настройки сохранены! - + Part-DB1\src\Controller\UserSettingsController.php:297 Part-DB1\src\Controller\UserSettingsController.php:288 @@ -4500,7 +4525,7 @@ Пароль изменен! - + Part-DB1\src\Controller\UserSettingsController.php:317 Part-DB1\src\Controller\UserSettingsController.php:306 @@ -4510,7 +4535,7 @@ Приложение Аутентификатор успешно активировано! - + Part-DB1\src\Controller\UserSettingsController.php:328 Part-DB1\src\Controller\UserSettingsController.php:315 @@ -4520,7 +4545,7 @@ Приложение Аутентификатор успешно запрещено! - + Part-DB1\src\Controller\UserSettingsController.php:346 Part-DB1\src\Controller\UserSettingsController.php:332 @@ -4530,7 +4555,7 @@ Новые резервные коды успешно сгенерированы. - + Part-DB1\src\DataTables\AttachmentDataTable.php:148 Part-DB1\src\DataTables\AttachmentDataTable.php:148 @@ -4540,7 +4565,7 @@ Имя файла - + Part-DB1\src\DataTables\AttachmentDataTable.php:153 Part-DB1\src\DataTables\AttachmentDataTable.php:153 @@ -4550,7 +4575,7 @@ Размер файла - + Part-DB1\src\DataTables\AttachmentDataTable.php:183 Part-DB1\src\DataTables\AttachmentDataTable.php:191 @@ -4570,7 +4595,7 @@ истина - + Part-DB1\src\DataTables\AttachmentDataTable.php:184 Part-DB1\src\DataTables\AttachmentDataTable.php:192 @@ -4592,7 +4617,7 @@ ложь - + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 @@ -4602,7 +4627,7 @@ Объект удален - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 @@ -4613,7 +4638,7 @@ Вернуть элемент - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 @@ -4624,7 +4649,7 @@ Откатить изменение - + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 @@ -4635,7 +4660,7 @@ Вернуть состояние элемента на данный момент - + Part-DB1\src\DataTables\LogDataTable.php:173 Part-DB1\src\DataTables\LogDataTable.php:161 @@ -4645,7 +4670,7 @@ ID - + Part-DB1\src\DataTables\LogDataTable.php:178 Part-DB1\src\DataTables\LogDataTable.php:166 @@ -4655,7 +4680,7 @@ Время события - + Part-DB1\src\DataTables\LogDataTable.php:183 Part-DB1\src\DataTables\LogDataTable.php:171 @@ -4665,7 +4690,7 @@ Тип - + Part-DB1\src\DataTables\LogDataTable.php:191 Part-DB1\src\DataTables\LogDataTable.php:179 @@ -4675,7 +4700,7 @@ Уровень - + Part-DB1\src\DataTables\LogDataTable.php:200 Part-DB1\src\DataTables\LogDataTable.php:188 @@ -4685,7 +4710,7 @@ Пользователь - + Part-DB1\src\DataTables\LogDataTable.php:213 Part-DB1\src\DataTables\LogDataTable.php:201 @@ -4695,7 +4720,7 @@ Тип объекта - + Part-DB1\src\DataTables\LogDataTable.php:226 Part-DB1\src\DataTables\LogDataTable.php:214 @@ -4705,7 +4730,7 @@ Объект - + Part-DB1\src\DataTables\LogDataTable.php:231 Part-DB1\src\DataTables\LogDataTable.php:218 @@ -4716,7 +4741,7 @@ Доп-но - + Part-DB1\src\DataTables\PartsDataTable.php:168 Part-DB1\src\DataTables\PartsDataTable.php:116 @@ -4726,7 +4751,7 @@ Имя - + Part-DB1\src\DataTables\PartsDataTable.php:178 Part-DB1\src\DataTables\PartsDataTable.php:126 @@ -4736,7 +4761,7 @@ ID - + Part-DB1\src\DataTables\PartsDataTable.php:182 Part-DB1\src\DataTables\PartsDataTable.php:130 @@ -4746,7 +4771,7 @@ Описание - + Part-DB1\src\DataTables\PartsDataTable.php:185 Part-DB1\src\DataTables\PartsDataTable.php:133 @@ -4756,7 +4781,7 @@ Категория - + Part-DB1\src\DataTables\PartsDataTable.php:190 Part-DB1\src\DataTables\PartsDataTable.php:138 @@ -4766,7 +4791,7 @@ Посад.место - + Part-DB1\src\DataTables\PartsDataTable.php:194 Part-DB1\src\DataTables\PartsDataTable.php:142 @@ -4776,7 +4801,7 @@ Производитель - + Part-DB1\src\DataTables\PartsDataTable.php:197 Part-DB1\src\DataTables\PartsDataTable.php:145 @@ -4786,7 +4811,7 @@ Хранилища - + Part-DB1\src\DataTables\PartsDataTable.php:216 Part-DB1\src\DataTables\PartsDataTable.php:164 @@ -4796,7 +4821,7 @@ Количество - + Part-DB1\src\DataTables\PartsDataTable.php:224 Part-DB1\src\DataTables\PartsDataTable.php:172 @@ -4806,7 +4831,7 @@ Мин. Количество - + Part-DB1\src\DataTables\PartsDataTable.php:232 Part-DB1\src\DataTables\PartsDataTable.php:180 @@ -4816,7 +4841,7 @@ Единица измерения - + Part-DB1\src\DataTables\PartsDataTable.php:236 Part-DB1\src\DataTables\PartsDataTable.php:184 @@ -4826,7 +4851,7 @@ Дата добавления - + Part-DB1\src\DataTables\PartsDataTable.php:240 Part-DB1\src\DataTables\PartsDataTable.php:188 @@ -4836,7 +4861,7 @@ Дата последнего изменения - + Part-DB1\src\DataTables\PartsDataTable.php:244 Part-DB1\src\DataTables\PartsDataTable.php:192 @@ -4846,7 +4871,7 @@ Требует рассмотрения - + Part-DB1\src\DataTables\PartsDataTable.php:251 Part-DB1\src\DataTables\PartsDataTable.php:199 @@ -4856,7 +4881,7 @@ Избранное - + Part-DB1\src\DataTables\PartsDataTable.php:258 Part-DB1\src\DataTables\PartsDataTable.php:206 @@ -4866,7 +4891,7 @@ Статус - + Part-DB1\src\DataTables\PartsDataTable.php:260 Part-DB1\src\DataTables\PartsDataTable.php:262 @@ -4880,7 +4905,7 @@ Неизвестно - + Part-DB1\src\DataTables\PartsDataTable.php:263 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4892,7 +4917,7 @@ Заявлено - + Part-DB1\src\DataTables\PartsDataTable.php:264 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4904,7 +4929,7 @@ Активно - + Part-DB1\src\DataTables\PartsDataTable.php:265 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4916,7 +4941,7 @@ Не рекомендовано для новых дизайнов - + Part-DB1\src\DataTables\PartsDataTable.php:266 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4928,7 +4953,7 @@ Окончание срока службы - + Part-DB1\src\DataTables\PartsDataTable.php:267 Part-DB1\src\Form\Part\PartBaseType.php:90 @@ -4940,7 +4965,7 @@ Прекращено - + Part-DB1\src\DataTables\PartsDataTable.php:271 Part-DB1\src\DataTables\PartsDataTable.php:219 @@ -4950,7 +4975,7 @@ MPN - + Part-DB1\src\DataTables\PartsDataTable.php:275 Part-DB1\src\DataTables\PartsDataTable.php:223 @@ -4960,7 +4985,7 @@ Вес - + Part-DB1\src\DataTables\PartsDataTable.php:279 Part-DB1\src\DataTables\PartsDataTable.php:227 @@ -4970,7 +4995,7 @@ Метки - + Part-DB1\src\DataTables\PartsDataTable.php:283 Part-DB1\src\DataTables\PartsDataTable.php:231 @@ -4980,7 +5005,7 @@ Вложения - + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 @@ -4990,7 +5015,7 @@ Успешный вход - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5001,7 +5026,7 @@ JSON - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5012,7 +5037,7 @@ XML - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5023,7 +5048,7 @@ CSV - + Part-DB1\src\Form\AdminPages\ImportType.php:77 Part-DB1\src\Form\AdminPages\ImportType.php:77 @@ -5034,7 +5059,7 @@ YAML - + Part-DB1\src\Form\AdminPages\ImportType.php:124 Part-DB1\src\Form\AdminPages\ImportType.php:124 @@ -5044,7 +5069,7 @@ Когда эта опция включена весь процесс импорта прерывается в случае обнаружения неверных данных в любой записи. Если выключена то записи с неверными данными будут игнорированы а все прочие успешно импортированы. - + Part-DB1\src\Form\AdminPages\ImportType.php:86 Part-DB1\src\Form\AdminPages\ImportType.php:86 @@ -5055,7 +5080,7 @@ Разделитель CSV - + Part-DB1\src\Form\AdminPages\ImportType.php:93 Part-DB1\src\Form\AdminPages\ImportType.php:93 @@ -5066,7 +5091,7 @@ Родительский элемент - + Part-DB1\src\Form\AdminPages\ImportType.php:101 Part-DB1\src\Form\AdminPages\ImportType.php:101 @@ -5077,7 +5102,7 @@ Файл - + Part-DB1\src\Form\AdminPages\ImportType.php:111 Part-DB1\src\Form\AdminPages\ImportType.php:111 @@ -5088,7 +5113,7 @@ Создавать дочерние элементы при импорте - + Part-DB1\src\Form\AdminPages\ImportType.php:120 Part-DB1\src\Form\AdminPages\ImportType.php:120 @@ -5099,7 +5124,7 @@ Прерывать при неверных данных - + Part-DB1\src\Form\AdminPages\ImportType.php:132 Part-DB1\src\Form\AdminPages\ImportType.php:132 @@ -5110,7 +5135,7 @@ Импорт - + Part-DB1\src\Form\AttachmentFormType.php:113 Part-DB1\src\Form\AttachmentFormType.php:109 @@ -5120,7 +5145,7 @@ Вложение отмеченное как личное может быть просмотрено только пользователем с соответствующим разрешением. Если это включено то миниатюра не формируется и доступ к файлу менее производителен. - + Part-DB1\src\Form\AttachmentFormType.php:127 Part-DB1\src\Form\AttachmentFormType.php:123 @@ -5130,7 +5155,7 @@ Здесь вы можете задать url для внешнего файла или указать ключевое слово по которому будет произведен поиск в базе данных (например в базе посадочных мест) - + Part-DB1\src\Form\AttachmentFormType.php:82 Part-DB1\src\Form\AttachmentFormType.php:79 @@ -5140,7 +5165,7 @@ Имя - + Part-DB1\src\Form\AttachmentFormType.php:85 Part-DB1\src\Form\AttachmentFormType.php:82 @@ -5150,7 +5175,7 @@ Тип вложения - + Part-DB1\src\Form\AttachmentFormType.php:94 Part-DB1\src\Form\AttachmentFormType.php:91 @@ -5160,7 +5185,7 @@ Показать в таблице - + Part-DB1\src\Form\AttachmentFormType.php:105 Part-DB1\src\Form\AttachmentFormType.php:102 @@ -5170,7 +5195,7 @@ Личное вложение - + Part-DB1\src\Form\AttachmentFormType.php:119 Part-DB1\src\Form\AttachmentFormType.php:115 @@ -5180,7 +5205,7 @@ URL - + Part-DB1\src\Form\AttachmentFormType.php:133 Part-DB1\src\Form\AttachmentFormType.php:129 @@ -5190,7 +5215,7 @@ Скачать внешний файл - + Part-DB1\src\Form\AttachmentFormType.php:146 Part-DB1\src\Form\AttachmentFormType.php:142 @@ -5200,7 +5225,7 @@ Выгрузить файл - + Part-DB1\src\Form\LabelOptionsType.php:68 Part-DB1\src\Services\ElementTypeNameGenerator.php:86 @@ -5210,7 +5235,7 @@ Компонент - + Part-DB1\src\Form\LabelOptionsType.php:68 Part-DB1\src\Services\ElementTypeNameGenerator.php:87 @@ -5220,7 +5245,7 @@ Лот на компонент - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5229,7 +5254,7 @@ Ничто - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5238,7 +5263,7 @@ QR Код (рекомендуется) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5247,7 +5272,7 @@ Код 128 (рекомендуется) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5256,7 +5281,7 @@ Код 39 (рекомендуется) - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5265,7 +5290,7 @@ Код 93 - + Part-DB1\src\Form\LabelOptionsType.php:78 @@ -5274,7 +5299,7 @@ Матричный Штрихкод - + Part-DB1\src\Form\LabelOptionsType.php:122 @@ -5283,7 +5308,7 @@ HTML - + Part-DB1\src\Form\LabelOptionsType.php:122 @@ -5292,7 +5317,7 @@ Twig - + Part-DB1\src\Form\LabelOptionsType.php:126 @@ -5301,7 +5326,7 @@ Если здесь выбиран Twig, содержимое поля интерпретируется как шаблон Twig. Смотри <a href="https://twig.symfony.com/doc/3.x/templates.html">документация Twig</a> и <a href="https://github.com/Part-DB/Part-DB-symfony/wiki/Labels#twig-mode">Wiki</a> для пополнительной информации. - + Part-DB1\src\Form\LabelOptionsType.php:47 @@ -5310,7 +5335,7 @@ Размер этикетки - + Part-DB1\src\Form\LabelOptionsType.php:66 @@ -5319,7 +5344,7 @@ Тип элемента - + Part-DB1\src\Form\LabelOptionsType.php:75 @@ -5328,7 +5353,7 @@ Штрихкод - + Part-DB1\src\Form\LabelOptionsType.php:102 @@ -5337,7 +5362,7 @@ Содержание - + Part-DB1\src\Form\LabelOptionsType.php:111 @@ -5346,7 +5371,7 @@ Дополнительные стили (CSS) - + Part-DB1\src\Form\LabelOptionsType.php:120 @@ -5355,7 +5380,7 @@ Режим парсера - + Part-DB1\src\Form\LabelOptionsType.php:51 @@ -5364,7 +5389,7 @@ Ширина - + Part-DB1\src\Form\LabelOptionsType.php:60 @@ -5373,7 +5398,7 @@ Высота - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 @@ -5382,7 +5407,7 @@ Вы можете указать несколько ID (например 1,2,3) и/или диапазон (1-3) чтобы создать этикетки для нескольких элементов сразу. - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 @@ -5391,7 +5416,7 @@ ID элемента - + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 @@ -5400,7 +5425,7 @@ Обновить - + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 @@ -5409,7 +5434,7 @@ Ввод - + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 @@ -5418,7 +5443,7 @@ Отправить - + Part-DB1\src\Form\ParameterType.php:41 @@ -5427,7 +5452,7 @@ н.р. Коэффициент усиления - + Part-DB1\src\Form\ParameterType.php:50 @@ -5436,7 +5461,7 @@ н.р. h_{FE} - + Part-DB1\src\Form\ParameterType.php:60 @@ -5445,7 +5470,7 @@ н.р. Тестовые условия - + Part-DB1\src\Form\ParameterType.php:71 @@ -5454,7 +5479,7 @@ н.р. 350 - + Part-DB1\src\Form\ParameterType.php:82 @@ -5463,7 +5488,7 @@ н.р. 100 - + Part-DB1\src\Form\ParameterType.php:93 @@ -5472,7 +5497,7 @@ н.р. 200 - + Part-DB1\src\Form\ParameterType.php:103 @@ -5481,7 +5506,7 @@ н.р. V - + Part-DB1\src\Form\ParameterType.php:114 @@ -5490,7 +5515,7 @@ н.р. Технические спецификации - + Part-DB1\src\Form\Part\OrderdetailType.php:72 Part-DB1\src\Form\Part\OrderdetailType.php:75 @@ -5500,7 +5525,7 @@ Номер заказа - + Part-DB1\src\Form\Part\OrderdetailType.php:81 Part-DB1\src\Form\Part\OrderdetailType.php:84 @@ -5510,7 +5535,7 @@ Поставщик - + Part-DB1\src\Form\Part\OrderdetailType.php:87 Part-DB1\src\Form\Part\OrderdetailType.php:90 @@ -5520,7 +5545,7 @@ Ссылка на предложение - + Part-DB1\src\Form\Part\OrderdetailType.php:93 Part-DB1\src\Form\Part\OrderdetailType.php:96 @@ -5530,7 +5555,7 @@ Больше не доступно - + Part-DB1\src\Form\Part\OrderdetailType.php:75 Part-DB1\src\Form\Part\OrderdetailType.php:78 @@ -5540,7 +5565,7 @@ н.р. BC 547 - + Part-DB1\src\Form\Part\PartBaseType.php:101 Part-DB1\src\Form\Part\PartBaseType.php:99 @@ -5550,7 +5575,7 @@ Имя - + Part-DB1\src\Form\Part\PartBaseType.php:109 Part-DB1\src\Form\Part\PartBaseType.php:107 @@ -5560,7 +5585,7 @@ Описание - + Part-DB1\src\Form\Part\PartBaseType.php:120 Part-DB1\src\Form\Part\PartBaseType.php:118 @@ -5570,7 +5595,7 @@ Минимальный запас - + Part-DB1\src\Form\Part\PartBaseType.php:129 Part-DB1\src\Form\Part\PartBaseType.php:127 @@ -5580,7 +5605,7 @@ Категория - + Part-DB1\src\Form\Part\PartBaseType.php:135 Part-DB1\src\Form\Part\PartBaseType.php:133 @@ -5590,7 +5615,7 @@ Посадочное место - + Part-DB1\src\Form\Part\PartBaseType.php:142 Part-DB1\src\Form\Part\PartBaseType.php:140 @@ -5600,7 +5625,7 @@ Метки - + Part-DB1\src\Form\Part\PartBaseType.php:154 Part-DB1\src\Form\Part\PartBaseType.php:152 @@ -5610,7 +5635,7 @@ Производитель - + Part-DB1\src\Form\Part\PartBaseType.php:161 Part-DB1\src\Form\Part\PartBaseType.php:159 @@ -5620,7 +5645,7 @@ Ссылка на страницу продукта - + Part-DB1\src\Form\Part\PartBaseType.php:167 Part-DB1\src\Form\Part\PartBaseType.php:165 @@ -5630,7 +5655,7 @@ Код компонента производителя - + Part-DB1\src\Form\Part\PartBaseType.php:173 Part-DB1\src\Form\Part\PartBaseType.php:171 @@ -5640,7 +5665,7 @@ Статус производства - + Part-DB1\src\Form\Part\PartBaseType.php:181 Part-DB1\src\Form\Part\PartBaseType.php:179 @@ -5650,7 +5675,7 @@ Нужно рассмотреть - + Part-DB1\src\Form\Part\PartBaseType.php:189 Part-DB1\src\Form\Part\PartBaseType.php:187 @@ -5660,7 +5685,7 @@ Избранное - + Part-DB1\src\Form\Part\PartBaseType.php:197 Part-DB1\src\Form\Part\PartBaseType.php:195 @@ -5670,7 +5695,7 @@ Вес - + Part-DB1\src\Form\Part\PartBaseType.php:203 Part-DB1\src\Form\Part\PartBaseType.php:201 @@ -5680,7 +5705,7 @@ Единица измерения - + Part-DB1\src\Form\Part\PartBaseType.php:212 Part-DB1\src\Form\Part\PartBaseType.php:210 @@ -5690,7 +5715,7 @@ Комментарий - + Part-DB1\src\Form\Part\PartBaseType.php:250 Part-DB1\src\Form\Part\PartBaseType.php:246 @@ -5700,7 +5725,7 @@ Предварительный просмотр картинки - + Part-DB1\src\Form\Part\PartBaseType.php:295 Part-DB1\src\Form\Part\PartBaseType.php:276 @@ -5711,7 +5736,7 @@ Сохранить изменения - + Part-DB1\src\Form\Part\PartBaseType.php:296 Part-DB1\src\Form\Part\PartBaseType.php:277 @@ -5722,7 +5747,7 @@ Сбросить изменения - + Part-DB1\src\Form\Part\PartBaseType.php:105 Part-DB1\src\Form\Part\PartBaseType.php:103 @@ -5732,7 +5757,7 @@ н.р. BC547 - + Part-DB1\src\Form\Part\PartBaseType.php:115 Part-DB1\src\Form\Part\PartBaseType.php:113 @@ -5742,7 +5767,7 @@ н.р. NPN 45V, 0,1A, 0,5W - + Part-DB1\src\Form\Part\PartBaseType.php:123 Part-DB1\src\Form\Part\PartBaseType.php:121 @@ -5752,7 +5777,7 @@ н.р. 1 - + Part-DB1\src\Form\Part\PartLotType.php:69 Part-DB1\src\Form\Part\PartLotType.php:69 @@ -5762,7 +5787,7 @@ Описание - + Part-DB1\src\Form\Part\PartLotType.php:78 Part-DB1\src\Form\Part\PartLotType.php:78 @@ -5772,7 +5797,7 @@ Хранилище - + Part-DB1\src\Form\Part\PartLotType.php:89 Part-DB1\src\Form\Part\PartLotType.php:89 @@ -5782,7 +5807,7 @@ Количество - + Part-DB1\src\Form\Part\PartLotType.php:98 Part-DB1\src\Form\Part\PartLotType.php:97 @@ -5792,7 +5817,7 @@ Количество неизвестно - + Part-DB1\src\Form\Part\PartLotType.php:109 Part-DB1\src\Form\Part\PartLotType.php:108 @@ -5802,7 +5827,7 @@ Нужно пополнить - + Part-DB1\src\Form\Part\PartLotType.php:120 Part-DB1\src\Form\Part\PartLotType.php:119 @@ -5812,7 +5837,7 @@ Дата истечения - + Part-DB1\src\Form\Part\PartLotType.php:128 Part-DB1\src\Form\Part\PartLotType.php:125 @@ -5822,7 +5847,7 @@ Комментарий - + Part-DB1\src\Form\Permissions\PermissionsType.php:99 Part-DB1\src\Form\Permissions\PermissionsType.php:99 @@ -5832,7 +5857,7 @@ Разное - + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 Part-DB1\src\Form\TFAGoogleSettingsType.php:97 @@ -5842,7 +5867,7 @@ Включить приложение аутентификации - + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 Part-DB1\src\Form\TFAGoogleSettingsType.php:101 @@ -5852,7 +5877,7 @@ Выключить приложение аутентификации - + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 Part-DB1\src\Form\TFAGoogleSettingsType.php:74 @@ -5862,7 +5887,7 @@ Код подтверждения - + Part-DB1\src\Form\UserSettingsType.php:108 Part-DB1\src\Form\UserSettingsType.php:108 @@ -5873,7 +5898,7 @@ Временная зона - + Part-DB1\src\Form\UserSettingsType.php:133 Part-DB1\src\Form\UserSettingsType.php:132 @@ -5883,7 +5908,7 @@ Предпочитаемая валюта - + Part-DB1\src\Form\UserSettingsType.php:140 Part-DB1\src\Form\UserSettingsType.php:139 @@ -5894,7 +5919,7 @@ Применить изменения - + Part-DB1\src\Form\UserSettingsType.php:141 Part-DB1\src\Form\UserSettingsType.php:140 @@ -5905,7 +5930,7 @@ Отменить изменения - + Part-DB1\src\Form\UserSettingsType.php:104 Part-DB1\src\Form\UserSettingsType.php:104 @@ -5916,7 +5941,7 @@ Язык для всего сервера - + Part-DB1\src\Form\UserSettingsType.php:115 Part-DB1\src\Form\UserSettingsType.php:115 @@ -5927,7 +5952,7 @@ Временная зона для всего сервера - + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 Part-DB1\src\Services\ElementTypeNameGenerator.php:79 @@ -5937,7 +5962,7 @@ Вложение - + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 Part-DB1\src\Services\ElementTypeNameGenerator.php:81 @@ -5947,7 +5972,17 @@ Тип вложения - + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + + + project.label + Проект + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 Part-DB1\src\Services\ElementTypeNameGenerator.php:85 @@ -5957,7 +5992,7 @@ Единица измерения - + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 Part-DB1\src\Services\ElementTypeNameGenerator.php:90 @@ -5967,7 +6002,7 @@ Валюта - + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 Part-DB1\src\Services\ElementTypeNameGenerator.php:91 @@ -5977,7 +6012,7 @@ Детали заказа - + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 Part-DB1\src\Services\ElementTypeNameGenerator.php:92 @@ -5987,7 +6022,7 @@ Детали цены - + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 Part-DB1\src\Services\ElementTypeNameGenerator.php:94 @@ -5997,7 +6032,7 @@ Пользователь - + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 @@ -6006,7 +6041,7 @@ Параметр - + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 @@ -6015,7 +6050,7 @@ Профиль этикетки - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 @@ -6026,7 +6061,7 @@ Неизвестно - + Part-DB1\src\Services\MarkdownParser.php:73 Part-DB1\src\Services\MarkdownParser.php:73 @@ -6036,7 +6071,7 @@ Загрузка документа. Если это сообщение не пропадает, попробуйте перегрузить страницу. - + Part-DB1\src\Services\PasswordResetManager.php:98 Part-DB1\src\Services\PasswordResetManager.php:98 @@ -6046,7 +6081,7 @@ Сброс пароль для вашего Part-DB аккаунта - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 @@ -6055,7 +6090,7 @@ Инструменты - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 @@ -6066,7 +6101,7 @@ Редактировать - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 @@ -6077,7 +6112,7 @@ Показать - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 @@ -6087,7 +6122,7 @@ Система - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 @@ -6096,7 +6131,7 @@ Генератор этикеток - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 @@ -6105,7 +6140,7 @@ Сканер - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 @@ -6116,7 +6151,7 @@ Типы вложений - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 @@ -6127,7 +6162,18 @@ Категории - + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 + src\Services\ToolsTreeBuilder.php:66 + + + tree.tools.edit.projects + Проекты + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 @@ -6138,7 +6184,7 @@ Поставщики - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 @@ -6149,7 +6195,7 @@ Производители - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 @@ -6159,7 +6205,7 @@ Хранилища - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 @@ -6169,7 +6215,7 @@ Посадочные места - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 @@ -6179,7 +6225,7 @@ Валюты - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 @@ -6189,7 +6235,7 @@ Единица измерения - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 @@ -6198,7 +6244,7 @@ Профили этикетки - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 @@ -6208,7 +6254,7 @@ Компонент - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 @@ -6219,7 +6265,7 @@ Показать все компоненты - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 @@ -6229,7 +6275,7 @@ Вложения - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 @@ -6240,7 +6286,7 @@ Статистика - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 @@ -6250,7 +6296,7 @@ Пользователи - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 @@ -6260,7 +6306,7 @@ Группы - + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 @@ -6271,7 +6317,7 @@ Журнал событий - + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 @@ -6282,7 +6328,7 @@ Новый Элемент - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 obsolete @@ -6292,7 +6338,7 @@ Внешний файл - + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 obsolete @@ -6302,7 +6348,7 @@ Редактировать - + Part-DB1\templates\_navbar.html.twig:27 templates\base.html.twig:88 @@ -6313,7 +6359,7 @@ Сканировать штрихкод - + Part-DB1\src\Form\UserSettingsType.php:119 src\Form\UserSettingsType.php:49 @@ -6324,7 +6370,7 @@ Тема - + Part-DB1\src\Form\UserSettingsType.php:129 src\Form\UserSettingsType.php:50 @@ -6335,53 +6381,7 @@ Тема для всего сервера - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - M - М - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - k - к - - - - - - - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - m - м - - - - - Part-DB1\src\Form\Type\SIUnitType.php:141 - obsolete - - - µ - µ - - - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 new @@ -6392,7 +6392,7 @@ IP: - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 @@ -6406,7 +6406,7 @@ Отменить изменение - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 @@ -6420,7 +6420,7 @@ Элемент возвращен - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 new @@ -6431,7 +6431,7 @@ Старые запасы - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 new @@ -6442,7 +6442,7 @@ Старое имя - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 new @@ -6453,7 +6453,7 @@ Измененные поля - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 new @@ -6464,7 +6464,7 @@ Комментарий - + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 new @@ -6475,7 +6475,7 @@ Удаленный элемент: - + templates\base.html.twig:81 obsolete @@ -6486,7 +6486,7 @@ Поехали! - + templates\base.html.twig:109 obsolete @@ -6497,7 +6497,7 @@ Английский - + templates\base.html.twig:112 obsolete @@ -6508,7 +6508,7 @@ Немецкий - + obsolete obsolete @@ -6518,7 +6518,7 @@ Нужно сменить пароль! - + obsolete obsolete @@ -6528,7 +6528,7 @@ Тип вложения - + obsolete obsolete @@ -6538,7 +6538,7 @@ Связанный элемент - + obsolete obsolete @@ -6548,7 +6548,7 @@ Картинка? - + obsolete obsolete @@ -6558,7 +6558,7 @@ 3D модель ? - + obsolete obsolete @@ -6568,7 +6568,7 @@ Встроенный ресурс? - + obsolete obsolete @@ -6578,7 +6578,7 @@ н.р. полезно для переключения - + obsolete obsolete @@ -6588,7 +6588,7 @@ Сгенерировать новые резервные коды - + obsolete obsolete @@ -6598,7 +6598,7 @@ Элемент не может быть собственным родителем - + obsolete obsolete @@ -6608,17 +6608,7 @@ Родитель не может быть дочерним по отношению к себе - - - obsolete - obsolete - - - validator.isSelectable - Элемент должен быть выбираемым! - - - + obsolete obsolete @@ -6628,7 +6618,7 @@ Вы не можете увеличивать складские объемы в хранилище помеченном как "полное". (Новое макс. количество {{ old_amount }}) - + obsolete obsolete @@ -6638,7 +6628,7 @@ Вы не можете добавлять новые компоненты в хранилище которое отмечено как "полное". - + obsolete obsolete @@ -6648,7 +6638,7 @@ Вы не можете добавлять новые компоненты в хранилище которое помечено как "только существующие". - + obsolete obsolete @@ -6658,7 +6648,7 @@ Вы не можете добавлять новые компоненты в хранилище которое отмечено как "единственный компонент". - + obsolete obsolete @@ -6668,7 +6658,7 @@ Этот компонент сейчас и ближайшее время в процессе производства - + obsolete obsolete @@ -6678,7 +6668,7 @@ Этот компонент анонсирован но еще не доступен. - + obsolete obsolete @@ -6688,7 +6678,7 @@ Этот компонент больше не производится. - + obsolete obsolete @@ -6698,7 +6688,7 @@ Этог продукт больше не поддерживается и его производство скоро прекратится. - + obsolete obsolete @@ -6708,7 +6698,7 @@ Этот компонент еще выпускается но не рекомендуется для новых проектов. - + obsolete obsolete @@ -6718,7 +6708,7 @@ Статус изготовления компонента не известен. - + obsolete obsolete @@ -6728,7 +6718,7 @@ Успешно - + obsolete obsolete @@ -6738,7 +6728,7 @@ Ошибка - + obsolete obsolete @@ -6748,7 +6738,7 @@ Внимание - + obsolete obsolete @@ -6758,7 +6748,7 @@ Заметка - + obsolete obsolete @@ -6768,7 +6758,7 @@ Информация - + obsolete obsolete @@ -6778,7 +6768,7 @@ Вы не можете отозвать у себя разрешение на «изменение разрешения», чтобы предотвратить случайную блокировку аккаунта. - + obsolete obsolete @@ -6788,7 +6778,7 @@ Разрешенные расширения файла - + obsolete obsolete @@ -6798,7 +6788,7 @@ Вы можете задать тип вложения с помощью списка расширений или mimetypes используя запятую как разделитель. Чтобы указать что это изображение используйте image/*. - + obsolete obsolete @@ -6808,7 +6798,7 @@ н.р. .txt, application/pdf, image/* - + src\Form\PartType.php:63 obsolete @@ -6819,7 +6809,7 @@ н.р. BC547 - + obsolete obsolete @@ -6829,7 +6819,7 @@ Не выбираемое - + obsolete obsolete @@ -6840,7 +6830,7 @@ Удобно использовать если элемент предназначен только для группировки. - + obsolete obsolete @@ -6850,7 +6840,7 @@ Здесь вы можете использовать BBCode (н.р. [b]Жирный[/b]) - + obsolete obsolete @@ -6860,7 +6850,7 @@ Создать элемент - + obsolete obsolete @@ -6870,7 +6860,7 @@ Сохранить - + obsolete obsolete @@ -6880,7 +6870,7 @@ Выключить посад.места - + obsolete obsolete @@ -6890,7 +6880,7 @@ Если эта опция включена свойство "посадочное место" недоступно для компонентов этой категории. - + obsolete obsolete @@ -6900,7 +6890,7 @@ Выключить производителей - + obsolete obsolete @@ -6910,7 +6900,7 @@ Если эта опция включена свойство "производитель" недоступно для компонентов этой категории. - + obsolete obsolete @@ -6920,7 +6910,7 @@ Запретить автоматическое добавление ссылок на документацию - + obsolete obsolete @@ -6930,7 +6920,7 @@ Если эта опция включена ссылки на документацию автоматически не создаются для компонентов этой категории. - + obsolete obsolete @@ -6940,7 +6930,7 @@ Запретить свойства - + obsolete obsolete @@ -6950,7 +6940,7 @@ Если эта опция включена свойства компонентов этой категории недоступны. - + obsolete obsolete @@ -6960,7 +6950,7 @@ Примечание к имени - + obsolete obsolete @@ -6970,7 +6960,7 @@ н.р. 100nF - + obsolete obsolete @@ -6980,7 +6970,7 @@ Фильтр по имени - + obsolete obsolete @@ -6990,7 +6980,7 @@ Описание по умолчанию - + obsolete obsolete @@ -7000,7 +6990,7 @@ н.р. Конденсатор, 10mm x 10mm, SMD - + obsolete obsolete @@ -7010,7 +7000,7 @@ Комментарий по умолчанию - + obsolete obsolete @@ -7020,7 +7010,7 @@ Адрес - + obsolete obsolete @@ -7031,7 +7021,7 @@ Роднойгород - + obsolete obsolete @@ -7041,7 +7031,7 @@ Номер телефона - + obsolete obsolete @@ -7051,7 +7041,7 @@ +49 12345 6789 - + obsolete obsolete @@ -7061,7 +7051,7 @@ Номер факса - + obsolete obsolete @@ -7071,7 +7061,7 @@ Email - + obsolete obsolete @@ -7081,7 +7071,7 @@ напр. contact@foo.bar - + obsolete obsolete @@ -7091,7 +7081,7 @@ Веб сайт - + obsolete obsolete @@ -7101,7 +7091,7 @@ https://www.foo.bar - + obsolete obsolete @@ -7111,7 +7101,7 @@ Ссылка на продукт - + obsolete obsolete @@ -7121,7 +7111,7 @@ В этом поле задается ссылка на страницу компонента на сайте компании. %PARTNUMBER% будет заменено на номер заказа. - + obsolete obsolete @@ -7131,7 +7121,7 @@ https://foo.bar/product/%PARTNUMBER% - + obsolete obsolete @@ -7141,7 +7131,7 @@ Код ISO - + obsolete obsolete @@ -7151,7 +7141,7 @@ Курс обмена - + obsolete obsolete @@ -7161,7 +7151,7 @@ 3D модель - + obsolete obsolete @@ -7171,7 +7161,7 @@ Ввод - + obsolete obsolete @@ -7183,7 +7173,7 @@ Элемент 3 - + obsolete obsolete @@ -7193,7 +7183,7 @@ Создать - + obsolete obsolete @@ -7203,7 +7193,7 @@ Это целое число - + obsolete obsolete @@ -7213,7 +7203,7 @@ Если эта опция активирована, все значения данной единицы будут округлены до целых чисел. - + obsolete obsolete @@ -7223,7 +7213,7 @@ Использовать СИ - + obsolete obsolete @@ -7233,7 +7223,7 @@ Если эта опция активирована, значения выводятся в системе СИ (например, 1,2 кг вместо 1200 г) - + obsolete obsolete @@ -7243,7 +7233,7 @@ Символ единицы - + obsolete obsolete @@ -7253,7 +7243,7 @@ н.р. м - + obsolete obsolete @@ -7263,17 +7253,17 @@ Хранилище заполнено - + obsolete obsolete storelocation.edit.is_full.help - Если выбран этот параметр, невозможно добавить новые детали в это хранилище или увеличить количество существующих деталей. + Если выбран этот параметр, невозможно добавить новые детали в это хранилище или увеличить количество существующих компонентов. - + obsolete obsolete @@ -7283,17 +7273,17 @@ Только уже добавленные - + obsolete obsolete storelocation.limit_to_existing.help - Если эта опция активирована, невозможно добавить новые детали в это хранилище, но количество существующих деталей может быть увеличено. + Если эта опция активирована, невозможно добавить новые компоненты в это хранилище, но количество существующих компонентов может быть увеличено. - + obsolete obsolete @@ -7303,7 +7293,7 @@ Только один компонент - + obsolete obsolete @@ -7313,7 +7303,7 @@ Если эта опция активирована, в это хранилище может быть добавлен только единственный компонент (любым количеством). Полезно для небольших коробок с SMD или подающих устройств. - + obsolete obsolete @@ -7323,7 +7313,7 @@ Тип хранилища - + obsolete obsolete @@ -7333,7 +7323,7 @@ Здесь вы можете выбрать единицы измерения в которых обязан измеряться компонент предназначенный для данного хранилища. - + obsolete obsolete @@ -7343,7 +7333,7 @@ Валюта по умолчанию - + obsolete obsolete @@ -7353,7 +7343,7 @@ Цена доставки - + obsolete obsolete @@ -7363,7 +7353,7 @@ н.р. i.ivanov - + obsolete obsolete @@ -7373,7 +7363,7 @@ н.р. Евгений - + obsolete obsolete @@ -7383,7 +7373,7 @@ н.р. Иванов - + obsolete obsolete @@ -7393,7 +7383,7 @@ н.р. i.ivanov@ecorp.com - + obsolete obsolete @@ -7403,7 +7393,7 @@ н.р. Разработка - + obsolete obsolete @@ -7413,7 +7403,7 @@ Новый пароль - + obsolete obsolete @@ -7423,7 +7413,7 @@ Подтвердить новый пароль - + obsolete obsolete @@ -7433,7 +7423,7 @@ Пользователь должен сменить пароль - + obsolete obsolete @@ -7443,7 +7433,7 @@ Пользователь отключен (вход не возможен) - + obsolete obsolete @@ -7453,7 +7443,7 @@ Создать пользователя - + obsolete obsolete @@ -7463,7 +7453,7 @@ Сохранить - + obsolete obsolete @@ -7473,18 +7463,7 @@ Отменить изменения - - - templates\Parts\show_part_info.html.twig:161 - obsolete - obsolete - - - part.withdraw.caption: - Изъять компоненты: - - - + templates\Parts\show_part_info.html.twig:166 obsolete @@ -7495,7 +7474,7 @@ Изъять - + templates\Parts\show_part_info.html.twig:171 obsolete @@ -7506,7 +7485,7 @@ Комментарий/Цель - + templates\Parts\show_part_info.html.twig:189 obsolete @@ -7517,7 +7496,7 @@ Добавить компонент - + templates\Parts\show_part_info.html.twig:194 obsolete @@ -7528,7 +7507,7 @@ Добавить - + templates\Parts\show_part_info.html.twig:199 obsolete @@ -7539,7 +7518,7 @@ Комментарий/Цель - + templates\AdminPages\CompanyAdminBase.html.twig:15 obsolete @@ -7550,7 +7529,7 @@ Комментарий - + src\Form\PartType.php:83 obsolete @@ -7561,7 +7540,7 @@ Ссылка на производителя - + src\Form\PartType.php:66 obsolete @@ -7572,7 +7551,7 @@ н.р. NNPN 45V 0,1A 0,5W - + src\Form\PartType.php:69 obsolete @@ -7583,7 +7562,7 @@ н.р. 10 - + src\Form\PartType.php:72 obsolete @@ -7594,27 +7573,7 @@ н.р. 5 - - - obsolete - obsolete - - - homepage.basedOn - Создано на основе работ - - - - - obsolete - obsolete - - - homepage.others - и других - - - + obsolete obsolete @@ -7624,7 +7583,7 @@ Цена на - + obsolete obsolete @@ -7634,7 +7593,7 @@ Изъять компоненты - + obsolete obsolete @@ -7644,7 +7603,7 @@ _MENU_ - + obsolete obsolete @@ -7654,7 +7613,7 @@ Компоненты - + obsolete obsolete @@ -7664,7 +7623,7 @@ Структуры данных - + obsolete obsolete @@ -7674,7 +7633,7 @@ Система - + obsolete obsolete @@ -7684,7 +7643,7 @@ Общие - + obsolete obsolete @@ -7694,7 +7653,7 @@ Просмотр - + obsolete obsolete @@ -7704,7 +7663,7 @@ Редактировать - + obsolete obsolete @@ -7714,7 +7673,7 @@ Создать - + obsolete obsolete @@ -7724,7 +7683,7 @@ Изменить категорию - + obsolete obsolete @@ -7734,7 +7693,7 @@ Удалить - + obsolete obsolete @@ -7744,7 +7703,7 @@ Поиск - + obsolete obsolete @@ -7754,7 +7713,7 @@ Список всех компонентов - + obsolete obsolete @@ -7764,7 +7723,7 @@ Список всех компонентов без цен - + obsolete obsolete @@ -7774,7 +7733,7 @@ Список устаревших компонентов - + obsolete obsolete @@ -7784,7 +7743,7 @@ Показать компоненты с неизвестным количеством - + obsolete obsolete @@ -7794,7 +7753,7 @@ Изменить статус избранного - + obsolete obsolete @@ -7804,7 +7763,7 @@ Список избранных компонентов - + obsolete obsolete @@ -7814,7 +7773,7 @@ Показать последние отредактированные/измененные компоненты - + obsolete obsolete @@ -7824,7 +7783,7 @@ Показать последнего измененного пользователя - + obsolete obsolete @@ -7834,7 +7793,7 @@ Показать историю - + obsolete obsolete @@ -7844,7 +7803,7 @@ Имя - + obsolete obsolete @@ -7854,7 +7813,7 @@ Описание - + obsolete obsolete @@ -7864,7 +7823,7 @@ В наличии - + obsolete obsolete @@ -7874,7 +7833,7 @@ Минимально в наличии - + obsolete obsolete @@ -7884,7 +7843,7 @@ Комментарий - + obsolete obsolete @@ -7894,7 +7853,7 @@ Хранилище - + obsolete obsolete @@ -7904,7 +7863,7 @@ Производитель - + obsolete obsolete @@ -7914,7 +7873,7 @@ Данные заказа - + obsolete obsolete @@ -7924,7 +7883,7 @@ Цены - + obsolete obsolete @@ -7934,7 +7893,7 @@ Вложения файлов - + obsolete obsolete @@ -7944,7 +7903,7 @@ Заказы - + obsolete obsolete @@ -7954,7 +7913,7 @@ Хранилища - + obsolete obsolete @@ -7964,7 +7923,7 @@ Переместить - + obsolete obsolete @@ -7974,7 +7933,7 @@ Список компонентов - + obsolete obsolete @@ -7984,7 +7943,7 @@ Посадочные места - + obsolete obsolete @@ -7994,7 +7953,7 @@ Категории - + obsolete obsolete @@ -8004,7 +7963,7 @@ Поставщики - + obsolete obsolete @@ -8014,7 +7973,17 @@ Производители - + + + obsolete + obsolete + + + perm.projects + Проекты + + + obsolete obsolete @@ -8024,7 +7993,7 @@ Типы вложений - + obsolete obsolete @@ -8034,7 +8003,7 @@ Импорт - + obsolete obsolete @@ -8044,7 +8013,7 @@ Этикетки - + obsolete obsolete @@ -8054,7 +8023,7 @@ Калькулятор сопротивления - + obsolete obsolete @@ -8064,17 +8033,17 @@ Посадочные места - + obsolete obsolete perm.tools.ic_logos - логотипы ИС + Логотипы производителей - + obsolete obsolete @@ -8084,7 +8053,7 @@ Статистика - + obsolete obsolete @@ -8094,7 +8063,7 @@ Редактировать разрешения - + obsolete obsolete @@ -8104,7 +8073,7 @@ Редактировать имя пользователя - + obsolete obsolete @@ -8114,7 +8083,7 @@ Изменить группу - + obsolete obsolete @@ -8124,7 +8093,7 @@ Редактировать информацию - + obsolete obsolete @@ -8134,7 +8103,7 @@ Редактировать разрешения - + obsolete obsolete @@ -8144,7 +8113,7 @@ Установить пароль - + obsolete obsolete @@ -8154,7 +8123,7 @@ Изменить настройки пользователя - + obsolete obsolete @@ -8164,7 +8133,7 @@ Показать статус - + obsolete obsolete @@ -8174,7 +8143,7 @@ Обновить БД - + obsolete obsolete @@ -8184,7 +8153,7 @@ Прочитать настройки БД - + obsolete obsolete @@ -8194,7 +8163,7 @@ Записать настройки БД - + obsolete obsolete @@ -8204,7 +8173,7 @@ Прочитать конфигурацию - + obsolete obsolete @@ -8214,7 +8183,7 @@ Редактировать конфигурацию - + obsolete obsolete @@ -8224,7 +8193,7 @@ Инф. о сервере - + obsolete obsolete @@ -8234,7 +8203,7 @@ Использовать отладочные инструменты - + obsolete obsolete @@ -8244,7 +8213,7 @@ Показать логи - + obsolete obsolete @@ -8254,7 +8223,7 @@ Удалить логи - + obsolete obsolete @@ -8264,7 +8233,7 @@ Редактировать инф. - + obsolete obsolete @@ -8274,7 +8243,7 @@ Изменить имя пользователя - + obsolete obsolete @@ -8284,7 +8253,7 @@ Смотреть разрешения - + obsolete obsolete @@ -8294,7 +8263,7 @@ Показать собственные логи - + obsolete obsolete @@ -8304,7 +8273,7 @@ Создать этикетки - + obsolete obsolete @@ -8314,7 +8283,7 @@ Редактировать опции - + obsolete obsolete @@ -8324,7 +8293,7 @@ Удалить профили - + obsolete obsolete @@ -8334,7 +8303,7 @@ Редактировать профили - + obsolete obsolete @@ -8344,7 +8313,7 @@ Инструменты - + obsolete obsolete @@ -8354,7 +8323,7 @@ Группы - + obsolete obsolete @@ -8364,7 +8333,7 @@ Пользователи - + obsolete obsolete @@ -8374,7 +8343,7 @@ База данных - + obsolete obsolete @@ -8384,7 +8353,7 @@ Конфигурация - + obsolete obsolete @@ -8394,7 +8363,7 @@ Система - + obsolete obsolete @@ -8404,7 +8373,7 @@ Собственные - + obsolete obsolete @@ -8414,7 +8383,7 @@ Этикетки - + obsolete obsolete @@ -8424,7 +8393,7 @@ Категория - + obsolete obsolete @@ -8434,7 +8403,7 @@ Минимальное количество - + obsolete obsolete @@ -8444,7 +8413,7 @@ Посадочное место - + obsolete obsolete @@ -8454,7 +8423,7 @@ MPN - + obsolete obsolete @@ -8464,7 +8433,7 @@ Статус изготовления - + obsolete obsolete @@ -8474,7 +8443,7 @@ Метки - + obsolete obsolete @@ -8484,7 +8453,7 @@ Единица компонента - + obsolete obsolete @@ -8494,7 +8463,7 @@ Вес - + obsolete obsolete @@ -8504,7 +8473,7 @@ Склад - + obsolete obsolete @@ -8514,7 +8483,7 @@ Показать последнего измененного пользователя - + obsolete obsolete @@ -8524,7 +8493,7 @@ Валюты - + obsolete obsolete @@ -8534,7 +8503,7 @@ Единица измерения - + obsolete obsolete @@ -8544,7 +8513,7 @@ Старый пароль - + obsolete obsolete @@ -8554,7 +8523,7 @@ Сбросить пароль - + obsolete obsolete @@ -8564,7 +8533,7 @@ Ключ безопасности (U2F) - + obsolete obsolete @@ -8574,7 +8543,13 @@ Google - + + + tfa.provider.webauthn_two_factor_provider + Ключ безопасности + + + obsolete obsolete @@ -8584,17 +8559,17 @@ приложение Аутентификатор - + obsolete obsolete Login successful - Вход выполнен + Успешный вход - + obsolete obsolete @@ -8604,7 +8579,7 @@ Исключительная ситуация - + obsolete obsolete @@ -8614,7 +8589,7 @@ Пользователь вошел - + obsolete obsolete @@ -8624,7 +8599,7 @@ Пользователь вышел - + obsolete obsolete @@ -8634,7 +8609,7 @@ Неизвестный тип - + obsolete obsolete @@ -8644,7 +8619,7 @@ Элемент создан - + obsolete obsolete @@ -8654,7 +8629,7 @@ Элемент изменен - + obsolete obsolete @@ -8664,7 +8639,7 @@ Элемент удален - + obsolete obsolete @@ -8674,7 +8649,7 @@ Данные объекта обновлены - + obsolete @@ -8683,7 +8658,7 @@ Вернуть элемент - + obsolete @@ -8692,7 +8667,7 @@ Показать историю - + obsolete @@ -8701,7 +8676,7 @@ Показать последние действия - + obsolete @@ -8710,16 +8685,7 @@ Показать предыдущие версии элемента - - - obsolete - - - log.type. - Тип. - - - + obsolete @@ -8728,7 +8694,7 @@  Ключ безопасности успешно добавлен. - + obsolete @@ -8737,7 +8703,7 @@ Имя пользователя - + obsolete @@ -8746,7 +8712,7 @@ Прил. аутентификации запрещено - + obsolete @@ -8755,7 +8721,7 @@ Ключ безопасности удален - + obsolete @@ -8764,7 +8730,7 @@ Ключ безопасности добавлен - + obsolete @@ -8773,7 +8739,7 @@ Запасные ключи пересозданы - + obsolete @@ -8782,7 +8748,7 @@ Прил. Аутентификации разрешено - + obsolete @@ -8791,7 +8757,7 @@ Пароль изменен - + obsolete @@ -8800,7 +8766,7 @@ Доверенные устройства сброшены - + obsolete @@ -8809,7 +8775,7 @@ Элемент коллекции удален - + obsolete @@ -8818,7 +8784,7 @@ Пароль сброшен - + obsolete @@ -8827,7 +8793,7 @@ Администратор сбросил двухфакторную аутентификацию - + obsolete @@ -8836,7 +8802,7 @@ Попытка неавторизованного доступа - + obsolete @@ -8845,7 +8811,7 @@ Успешно - + obsolete @@ -8854,7 +8820,7 @@ 2D - + obsolete @@ -8863,7 +8829,7 @@ 1D - + obsolete @@ -8872,7 +8838,7 @@ Параметры - + obsolete @@ -8881,7 +8847,7 @@ Посмотреть личные вложения - + obsolete @@ -8890,7 +8856,7 @@ Сканер этикеток - + obsolete @@ -8899,7 +8865,7 @@ Читать профили - + obsolete @@ -8908,7 +8874,7 @@ Создать профили - + obsolete @@ -8917,281 +8883,3445 @@ Использовать режим Twig - + label_profile.showInDropdown Показать быстрый выбор - + group.edit.enforce_2fa Применить двухфакторную аутентификацию (2FA) - + group.edit.enforce_2fa.help Если эта опция разрешена, каждый член данной группы обязан сконфигурировать как минимум один вторичный способ аутентификации. Рекомендовано для административных групп с большими правами. - + selectpicker.empty Ничего не выбрано - + selectpicker.nothing_selected Ничего не выбрано - + entity.delete.must_not_contain_parts Элемент все еще содержит компоненты! Переместите все компоненты чтобы удалить этот элемент. - + entity.delete.must_not_contain_attachments Тип вложения по-прежнему содержит компоненты. Измените их тип, чтобы иметь возможность удалить этот тип вложения. - + entity.delete.must_not_contain_prices Валюта все еще содержит данные о ценах. Измените их валюту чтобы удалить этот элемент. - + entity.delete.must_not_contain_users Пользователи все еще используют на эту группу. Измените их группы чтобы удалить этот элемент. - + part.table.edit Изменить - + part.table.edit.title Изменить компонент - + part_list.action.action.title Выбрать действие - + part_list.action.action.group.favorite Избранный статус - + part_list.action.action.favorite Избранное - + part_list.action.action.unfavorite Удалить из избранного - + part_list.action.action.group.change_field Сменить поле - + part_list.action.action.change_category Сменить категорию - + part_list.action.action.change_footprint Сменить посадочное место - + part_list.action.action.change_manufacturer Сменить производителя - + part_list.action.action.change_unit Сменить ед.измерения компонента - + part_list.action.action.delete Удалить - + part_list.action.submit Отправить - + part_list.action.part_count %count% компонентов выбрано! - + company.edit.quick.website Открыть сайт - + company.edit.quick.email Отправить почту - + company.edit.quick.phone Позвонить по телефону - + company.edit.quick.fax Послать факс - + company.fax_number.placeholder н.р. +49 1234 567890 - + part.edit.save_and_clone Сохранить и клонировать - + validator.file_ext_not_allowed Такой тип файла не разрешен для данного вида вложений. - + tools.reel_calc.title Калькулятор катушек SMD - + tools.reel_calc.inner_dia Внутренний диаметр - + tools.reel_calc.outer_dia Наружный диаметр - + tools.reel_calc.tape_thick Толщина ленты - + tools.reel_calc.part_distance Расстояние между компонентами - + tools.reel_calc.update Обновить - + tools.reel_calc.parts_per_meter Компонентов на метр - + tools.reel_calc.result_length Длина ленты - + tools.reel_calc.result_amount Прибл. кол-во компонентов - + tools.reel_calc.outer_greater_inner_error Ошибка: Наружный диаметр должен быть больше внутреннего! - + tools.reel_calc.missing_values.error Пожалуйста заполните все значения! - + tools.reel_calc.load_preset Загрузить конфигурацию - + tools.reel_calc.explanation Этот калькулятор показывает приблизительный остаток SMD компонентов на катушке. - + perm.tools.reel_calculator Калькулятор катушки SMD - + tree.tools.tools.reel_calculator Калькулятор катушки SMD - + + + user.pw_change_needed.flash + Вам нужно сменить пароль! Пожалуйста, задайте новый. + + + + + tree.root_node.text + Корень + + + + + part_list.action.select_null + Пустой элемент + + + + + part_list.action.delete-title + Вы точно уверены, что хотите удалить эти компоненты? + + + + + part_list.action.delete-message + Эти компоненты и вся связанная с ними информация (прикрепленные документы, ценники и т.п.) будут удалены. Отменить действие будет невозможно! + + + + + part.table.actions.success + Успешно исполнено. + + + + + attachment.edit.delete.confirm + Вы точно хотите удалить это прикрепление? + + + + + filter.text_constraint.value.operator.EQ + Равно + + + + + filter.text_constraint.value.operator.NEQ + Не равно + + + + + filter.text_constraint.value.operator.STARTS + Начинается с + + + + + filter.text_constraint.value.operator.CONTAINS + Содержит + + + + + filter.text_constraint.value.operator.ENDS + Оканчивается на + + + + + filter.text_constraint.value.operator.LIKE + СООТВЕТСТВУЕТ шаблону + + + + + filter.text_constraint.value.operator.REGEX + Регулярное выражение + + + + + filter.number_constraint.value.operator.BETWEEN + Между + + + + + filter.number_constraint.AND + и + + + + + filter.entity_constraint.operator.EQ + Равно (исключая дочерние) + + + + + filter.entity_constraint.operator.NEQ + Не равно (исключая дочерние) + + + + + filter.entity_constraint.operator.INCLUDING_CHILDREN + Равно (включая дочерние) + + + + + filter.entity_constraint.operator.EXCLUDING_CHILDREN + Не равно (включая дочерние) + + + + + part.filter.dbId + ID базы данных + + + + + filter.tags_constraint.operator.ANY + Любая из меток + + + + + filter.tags_constraint.operator.ALL + Все отметки + + + + + filter.tags_constraint.operator.NONE + Ни одна из отметок + + + + + part.filter.lot_count + Количество лотов + + + + + part.filter.attachments_count + Количество вложений + + + + + part.filter.orderdetails_count + Количество подробностей о заказе. + + + + + part.filter.lotExpirationDate + Дата истечения лота + + + + + part.filter.lotNeedsRefill + Любой лот требующий пополнения + + + + + part.filter.lotUnknwonAmount + Любой лот с неизвестным количеством + + + + + part.filter.attachmentName + Имя вложения + + + + + filter.choice_constraint.operator.ANY + Любой из + + + + + filter.choice_constraint.operator.NONE + Ни один из + + + + + part.filter.amount_sum + Общее количество + + + + + filter.submit + Обновить + + + + + filter.discard + Сбросить изменения + + + + + filter.clear_filters + Очистить все фильтры + + + + + filter.title + Фильтр + + + + + filter.parameter_value_constraint.operator.= + Тип. Значение = + + + + + filter.parameter_value_constraint.operator.!= + Тип. Значение != + + + + + filter.parameter_value_constraint.operator.< + Тип. Значение < + + + + + filter.parameter_value_constraint.operator.> + Тип. Значение > + + + + + filter.parameter_value_constraint.operator.<= + Тип. Значение <= + + + + + filter.parameter_value_constraint.operator.>= + Тип. Значение >= + + + + + filter.parameter_value_constraint.operator.BETWEEN + Тип. Значение между + + + + + filter.parameter_value_constraint.operator.IN_RANGE + В диапазоне значений + + + + + filter.parameter_value_constraint.operator.NOT_IN_RANGE + Выходит за диапазон значений + + + + + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE + Больше диапазона значений + + + + + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE + Больше или равно диапазону значений + + + + + filter.parameter_value_constraint.operator.LESS_THAN_RANGE + Меньше диапазона значений + + + + + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE + Меньше или равно диапазону значений + + + + + filter.parameter_value_constraint.operator.RANGE_IN_RANGE + Диапазон полностью внутри диапазона значений + + + + + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE + Диапазон пересекает диапазон значений + + + + + filter.text_constraint.value + Значение не установлено + + + + + filter.number_constraint.value1 + Значение не установлено + + + + + filter.number_constraint.value2 + Максимальное значение + + + + + filter.datetime_constraint.value1 + Дата/время не установлены + + + + + filter.datetime_constraint.value2 + Максимальная дата/время + + + + + filter.constraint.add + Добавить ограничение + + + + + part.filter.parameters_count + Количество параметров + + + + + part.filter.lotDescription + Описание лота + + + + + parts_list.search.searching_for + Поиск деталей по ключевому слову <b>%keyword%</b> + + + + + parts_list.search_options.caption + Включенные параметры поиска + + + + + attachment.table.element_type + Тип связанного элемента + + + + + log.level.debug + Отладочный + + + + + log.level.info + Информационный + + + + + log.level.notice + Уведомления + + + + + log.level.warning + Предупреждения + + + + + log.level.error + Ошибки + + + + + log.level.critical + Критические + + + + + log.level.alert + Тревожный + + + + + log.level.emergency + Чрезвычайные + + + + + log.type.security + Безопасность + + + + + log.type.instock_changed + [СТАРОЕ] Наличие изменено + + + + + log.target_id + ID целевого элемента + + + + + entity.info.parts_count_recursive + Количество компонентов с этим элементом или его подэлементами. + + + + + tools.server_infos.title + Информация сервера + + + + + permission.preset.read_only + Только чтение + + + + + permission.preset.read_only.desc + Разрешить только операции чтения данных + + + + + permission.preset.all_inherit + Наследовать все + + + + + permission.preset.all_inherit.desc + Установить все разрешения в Наследовать + + + + + permission.preset.all_forbid + Запретить всё + + + + + permission.preset.all_forbid.desc + Установить все разрешения в Запретить + + + + + permission.preset.all_allow + Разрешить всё + + + + + permission.preset.all_allow.desc + Установить все разрешения в Разрешено + + + + + perm.server_infos + Информация сервера + + + + + permission.preset.editor + Редактор + + + + + permission.preset.editor.desc + Разрешить изменять компоненты и структуры данных + + + + + permission.preset.admin + Администратор + + + + + permission.preset.admin.desc + Разрешить административные действия + + + + + permission.preset.button + Применить шаблон + + + + + perm.attachments.show_private + Показать личные вложения + + + + + perm.attachments.list_attachments + Показать список всех вложений + + + + + user.edit.permission_success + Шаблон разрешений успешно применён. Проверьте что они удовлетворяют вашим требованиям. + + + + + perm.group.data + Дата + + + + + part_list.action.action.group.needs_review + Требуется Ревизия + + + + + part_list.action.action.set_needs_review + Установить статус "Требуется ревизия" + + + + + part_list.action.action.unset_needs_review + Убрать статус "Требуется ревизия" + + + + + part.edit.ipn + Внутренний номер компонента (IPN) + + + + + part.ipn.not_defined + Не задан + + + + + part.table.ipn + IPN + + + + + currency.edit.update_rate + Загрузить обменный курс + + + + + currency.edit.exchange_rate_update.unsupported_currency + Данная валюта не поддерживается поставщиком обменных курсов. Проверьте конфигурацию поставщика. + + + + + currency.edit.exchange_rate_update.generic_error + Не удалось запросить курс обмена. Пожалуйста, проверьте конфигурацию поставщика обменных курсов. + + + + + currency.edit.exchange_rate_updated.success + Обменный курс успешно загружен. + + + + + project.bom.quantity + BOM Кол-во + + + + + project.bom.mountnames + Имена сборок + + + + + project.bom.name + Имя + + + + + project.bom.comment + Заметки + + + + + project.bom.part + Компонент + + + + + project.bom.add_entry + Добавить запись + + + + + part_list.action.group.projects + Проекты + + + + + part_list.action.projects.add_to_project + Добавить компонент в проект + + + + + project.bom.delete.confirm + Вы уверены, что хотите удалить это запись BOM ? + + + + + project.add_parts_to_project + Добавить компонент в BOM проекта + + + + + part.info.add_part_to_project + Добавить этот компонент в проект + + + + + project_bom_entry.label + BOM запись + + + + + project.edit.status + Статус проекта + + + + + project.status.draft + Черновик + + + + + project.status.planning + Планирование + + + + + project.status.in_production + В производстве + + + + + project.status.finished + Завершен + + + + + project.status.archived + Архивный + + + + + part.new_build_part.error.build_part_already_exists + В проекте уже есть результирующий компонент! + + + + + project.edit.associated_build_part + Связанный результирующий компонент + + + + + project.edit.associated_build_part.add + Добавить результирующий компонент + + + + + project.edit.associated_build.hint + Этот компонент представляет собой произведенные экземпляры проекта, которые где-то хранятся. + + + + + part.info.projectBuildPart.hint + Этот компонент представляет собой построенные экземпляры следующего проекта и связан с ним. + + + + + part.is_build_part + Это результирующий компонент + + + + + project.info.title + Информация о проекте + + + + + project.info.bom_entries_count + Записи BOM + + + + + project.info.sub_projects_count + Подпроекты + + + + + project.info.bom_add_parts + Добавить записи BOM + + + + + project.info.info.label + Информация + + + + + project.info.sub_projects.label + Подпроекты + + + + + project.bom.price + Цена + + + + + part.info.withdraw_modal.title.withdraw + Убрать компонент из лота + + + + + part.info.withdraw_modal.title.add + Добавить компоненты в лот + + + + + part.info.withdraw_modal.title.move + Переместить компоненты в другой лот + + + + + part.info.withdraw_modal.amount + Количество + + + + + part.info.withdraw_modal.move_to + Переместить в + + + + + part.info.withdraw_modal.comment + Комментарий + + + + + part.info.withdraw_modal.comment.hint + Здесь вы можете ввести комментарий, описывающий, почему было выполнено это действие (например, зачем нужен этот компонент). Эта информация сохраняется в журнале. + + + + + modal.close + Закрыть + + + + + modal.submit + Отправить + + + + + part.withdraw.success + Компоненты успешно удалены/добавлены/перемещены. + + + + + perm.parts_stock + Компоненты в наличии + + + + + perm.parts_stock.withdraw + Снять компоненты со склада + + + + + perm.parts_stock.add + Добавить компоненты на склад + + + + + perm.parts_stock.move + Переместить компоненты между лотами + + + + + user.permissions_schema_updated + Схема разрешений вашей учетной записи пользователя обновлена ​​до последней версии. + + + + + log.type.part_stock_changed + Запас компонента изменен + + + + + log.part_stock_changed.withdraw + Запас убран + + + + + log.part_stock_changed.add + Запас добавлен + + + + + log.part_stock_changed.move + Запас перемещён + + + + + log.part_stock_changed.comment + Комментарий + + + + + log.part_stock_changed.change + Изменение + + + + + log.part_stock_changed.move_target + Перенесен в + + + + + tools.builtin_footprints_viewer.title + Галерея посадочных мест + + + + + tools.builtin_footprints_viewer.hint + Эта галерея показывает все доступные встроенные посадочные места. Если вы захотите использовать их во вложениях, наберите имя (или ключевое слово) в поле пути вложения и выберите изображение в появившемся выпадающем списке. + + + + + tools.ic_logos.title + Логотипы производителей + + + + + part_list.action.group.labels + Этикетки + + + + + part_list.action.projects.generate_label + Сформировать этикетки (для компонентов) + + + + + part_list.action.projects.generate_label_lot + Сформировать этикетки (для лотов) + + + + + part_list.action.generate_label.empty + Пустая этикетка + + + + + project.info.builds.label + Создать + + + + + project.builds.build_not_possible + Создание невозможно: Недостаточно компонентов. + + + + + project.builds.following_bom_entries_miss_instock + Запас следующих компонентов недостаточен даже для однократного производства проекта: + + + + + project.builds.stocked + запасено + + + + + project.builds.needed + нужно + + + + + project.builds.build_possible + Производство возможно + + + + + project.builds.number_of_builds_possible + У вас есть достаточно запасов для производства <b>%max_builds%</b> копий этого проекта. + + + + + project.builds.check_project_status + Статус производства текущего проекта <b>"%project_status%"</b>. Убедитесь что вы точно хотите произвести проект с таким статусом! + + + + + project.builds.following_bom_entries_miss_instock_n + У вас недостаточно запасов для производства %number_of_builds% копий данного проекта. Следующих компонентов нехватает: + + + + + project.build.flash.invalid_input + Проект не может быть произведен. Пожалуйста, проверьте свои записи! + + + + + project.build.required_qty + Требуемое количество + + + + + project.build.btn_build + Построить + + + + + project.build.help + Выберите, из каких запасов следует взять необходимые для производства компоненты (и в каком количестве). Установите флажок для каждой записи спецификации или используйте верхний флажок, чтобы установить все флажки одновременно. + + + + + project.build.buildsPartLot.new_lot + Создать новый лот + + + + + project.build.add_builds_to_builds_part + Добавить построенные экземпляры в производственную часть проекта. + + + + + project.build.builds_part_lot + Целевой лот + + + + + project.builds.number_of_builds + Произвести экземпляров + + + + + project.builds.no_stocked_builds + Количество сохраненных экземпляров сборки + + + + + user.change_avatar.label + Изменить аватар профиля + + + + + user_settings.change_avatar.label + Изменить аватар профиля + + + + + user_settings.remove_avatar.label + Удалить аватар профиля + + + + + part.edit.name.category_hint + Примечание из категории + + + + + category.edit.partname_regex.placeholder + e.g "/Конденсатор \d+ nF/i" + + + + + category.edit.partname_regex.help + PCRE-совместимое регулярное выражение которому должно соответствовать имя компонента. + + + + + entity.select.add_hint + Используй -> для создания вложенных структур, н.р. "Элемент 1->Элемент 1.1" + + + + + entity.select.group.new_not_added_to_DB + Новый (еще не добавленный в БД) + + + + + part.edit.save_and_new + Сохранить и открыть форму для нового компонента + + + + + homepage.first_steps.title + Первые шаги + + + + + homepage.first_steps.introduction + База данных на данный момент пуста. Возможно, вы захотите прочитать <a href="%url%">documentation</a> или начнете создавать следующие структуры данных: + + + + + homepage.first_steps.create_part + Или вы можете непосредственно <a href="%url%">создать новый компонент</a>. + + + + + homepage.first_steps.hide_hint + Это уведомление будет скрыто после создания первого компонента. + + + homepage.forum.text Все вопросы по Part-DB в ветке обсуждения на <a href="%href%" class="link-external" target="_blank">mikrocontroller.net</a> + + + log.element_edited.changed_fields.category + Категория + + + + + log.element_edited.changed_fields.footprint + Посадочное место + + + + + log.element_edited.changed_fields.manufacturer + Производитель + + + + + log.element_edited.changed_fields.value_typical + тип. значение + + + + + log.element_edited.changed_fields.pw_reset_expires + Сброс пароля + + + + + log.element_edited.changed_fields.comment + Заметки + + + + + log.element_edited.changed_fields.supplierpartnr + Код компонента у поставщика + + + + + log.element_edited.changed_fields.supplier_product_url + Ссылка на продукт + + + + + log.element_edited.changed_fields.price + Цена + + + + + log.element_edited.changed_fields.min_discount_quantity + Минимум для заказа со скидкой + + + + + log.element_edited.changed_fields.original_filename + Исходное имя файла + + + + + log.element_edited.changed_fields.path + Путь до файла + + + + + log.element_edited.changed_fields.description + Описание + + + + + log.element_edited.changed_fields.manufacturing_status + Статус производства + + + + + log.element_edited.changed_fields.options.barcode_type + Тип штрихкода + + + + + log.element_edited.changed_fields.status + Статус + + + + + log.element_edited.changed_fields.quantity + BOM Кол-во + + + + + log.element_edited.changed_fields.mountnames + Имена сборок + + + + + log.element_edited.changed_fields.name + Имя + + + + + log.element_edited.changed_fields.part + Компонент + + + + + log.element_edited.changed_fields.price_currency + Валюта цены + + + + + log.element_edited.changed_fields.partname_hint + Подсказка по названию + + + + + log.element_edited.changed_fields.partname_regex + Фильтр по имени + + + + + log.element_edited.changed_fields.disable_footprints + Отключить посадочные места + + + + + log.element_edited.changed_fields.disable_manufacturers + Отключить производителей + + + + + log.element_edited.changed_fields.disable_autodatasheets + Отключить автопривязку спецификации + + + + + log.element_edited.changed_fields.disable_properties + Отключить свойства + + + + + log.element_edited.changed_fields.default_description + Описание по умолчанию + + + + + log.element_edited.changed_fields.default_comment + Заметки по умолчанию + + + + + log.element_edited.changed_fields.filetype_filter + Разрешенные расширения файла + + + + + log.element_edited.changed_fields.not_selectable + Недоступно + + + + + log.element_edited.changed_fields.parent + Родительский элемент + + + + + log.element_edited.changed_fields.shipping_costs + Стоимость пересылки + + + + + log.element_edited.changed_fields.default_currency + Валюта по умолчанию + + + + + log.element_edited.changed_fields.address + Адрес + + + + + log.element_edited.changed_fields.phone_number + Номер телефона + + + + + log.element_edited.changed_fields.fax_number + Номер факса + + + + + log.element_edited.changed_fields.email_address + Email + + + + + log.element_edited.changed_fields.website + Web сайт + + + + + log.element_edited.changed_fields.auto_product_url + Ссылка на продукт + + + + + log.element_edited.changed_fields.is_full + Место хранения заполнено + + + + + log.element_edited.changed_fields.limit_to_existing_parts + Только существующие компоненты + + + + + log.element_edited.changed_fields.only_single_part + Только один компонент + + + + + log.element_edited.changed_fields.storage_type + Тип хранилища + + + + + log.element_edited.changed_fields.footprint_3d + 3D модель + + + + + log.element_edited.changed_fields.master_picture_attachment + Миниатюра + + + + + log.element_edited.changed_fields.exchange_rate + Курс обмена + + + + + log.element_edited.changed_fields.iso_code + Код ISO + + + + + log.element_edited.changed_fields.unit + Символ единицы + + + + + log.element_edited.changed_fields.is_integer + Целое число + + + + + log.element_edited.changed_fields.use_si_prefix + Использовать префиксы СИ + + + + + log.element_edited.changed_fields.options.width + Ширина + + + + + log.element_edited.changed_fields.options.height + Высота + + + + + log.element_edited.changed_fields.options.supported_element + Тип элемента + + + + + log.element_edited.changed_fields.options.additional_css + Дополнительные стили (CSS) + + + + + log.element_edited.changed_fields.options.lines + Содержимое + + + + + log.element_edited.changed_fields.permissions.data + Разрешения + + + + + log.element_edited.changed_fields.disabled + Отключено + + + + + log.element_edited.changed_fields.theme + Тема + + + + + log.element_edited.changed_fields.timezone + Временная зона + + + + + log.element_edited.changed_fields.language + Язык + + + + + log.element_edited.changed_fields.email + Email + + + + + log.element_edited.changed_fields.department + Отдел + + + + + log.element_edited.changed_fields.last_name + Фамилия + + + + + log.element_edited.changed_fields.first_name + Имя + + + + + log.element_edited.changed_fields.group + Группа + + + + + log.element_edited.changed_fields.currency + Выбранная валюта + + + + + log.element_edited.changed_fields.enforce2FA + Требовать 2FA + + + + + log.element_edited.changed_fields.symbol + Символ + + + + + log.element_edited.changed_fields.value_min + Мин. значение + + + + + log.element_edited.changed_fields.value_max + Макс. значение + + + + + log.element_edited.changed_fields.value_text + Текстовое значение + + + + + log.element_edited.changed_fields.show_in_table + Показать в таблице + + + + + log.element_edited.changed_fields.attachment_type + Тип вложения + + + + + log.element_edited.changed_fields.needs_review + Требует рассмотрения + + + + + log.element_edited.changed_fields.tags + Метки + + + + + log.element_edited.changed_fields.mass + Масса + + + + + log.element_edited.changed_fields.ipn + IPN + + + + + log.element_edited.changed_fields.favorite + Любимый + + + + + log.element_edited.changed_fields.minamount + Минимальный запас + + + + + log.element_edited.changed_fields.manufacturer_product_url + Ссылка на страницу продукта + + + + + log.element_edited.changed_fields.manufacturer_product_number + MPN + + + + + log.element_edited.changed_fields.partUnit + Единица измерения + + + + + log.element_edited.changed_fields.expiration_date + Дата истечения + + + + + log.element_edited.changed_fields.amount + Количество + + + + + log.element_edited.changed_fields.storage_location + Место хранения + + + + + attachment.max_file_size + Максимальный размер файла + + + + + user.saml_user + SSO / SAML пользователь + + + + + user.saml_user.pw_change_hint + Вы зашли используя систему единого входа (SSO). Поэтому вы не можете настроить здесь свой пароль и двухфакторную аутентификацию. Конфигурируйте их в настройках вашего SSO провайдера! + + + + + login.sso_saml_login + Система единого входа (SSO) + + + + + login.local_login_hint + Форму ниже можно использовать только для входа в систему под локальным пользователем. Если вы хотите вместо этого войти через систему единого входа, используйте кнопку выше. + + + + + part_list.action.action.export + Экспорт компонентов + + + + + part_list.action.export_json + Экспорт в виде JSON + + + + + part_list.action.export_csv + Экспорт в виде CSV + + + + + part_list.action.export_yaml + Экспорт в виде YAML + + + + + part_list.action.export_xml + Экспорт в виде XML + + + + + parts.import.title + Импорт компонентов + + + + + parts.import.errors.title + Импорт проблем + + + + + parts.import.flash.error + Во время экспорта возникли ошибки. Вероятно, это вызвано неверными данными. + + + + + parts.import.format.auto + Автоматически (в зависимости от расширения файла) + + + + + parts.import.flash.error.unknown_format + Формат не может быть определен автоматически. Пожалуйста, выберите правильный формат вручную! + + + + + parts.import.flash.error.invalid_file + Файл поврежден/неправильно отформатирован. Убедитесь, что вы выбрали правильный формат. + + + + + parts.import.part_category.label + Переопределение категории + + + + + parts.import.part_category.help + Если вы выберете здесь категорию, все импортированные компоненты будут отнесены к этой категории, независимо от того, что содержится в данных импорта. + + + + + import.create_unknown_datastructures + Создать неизвестные структуры данных + + + + + import.create_unknown_datastructures.help + Если выбран этот параметр, структуры данных (например, категории, посадочные места и т. д.), которые еще не существуют в базе данных, будут созданы автоматически. Если этот параметр не выбран, будут использоваться только те структуры данных, которые уже существуют в базе данных, а если подходящей структуры не существует, соответствующее поле компонента останется пустым. + + + + + import.path_delimiter + Разделитель пути + + + + + import.path_delimiter.help + Разделитель используется для разделения различных уровней структур данных таких как категории, посадочные места и т. д. + + + + + parts.import.help_documentation + Смотри <a href="%link%">documentation</a> для большей информации о формате файла. + + + + + parts.import.help + С помощью этого инструмента вы можете импортировать компоненты из существующих файлов. Компоненты сохраняются непосредственно в базу данных (без возможности предварительной их повторной проверки). Пожалуйста, проверьте файл импорта здесь перед загрузкой! + + + + + parts.import.flash.success + Компонент успешно импортирован! + + + + + parts.import.errors.imported_entities + Импортированные компоненты + + + + + perm.import + Импортировать данные + + + + + parts.import.part_needs_review.label + Пометить все импортированные компоненты как "Требуется проверки" + + + + + parts.import.part_needs_review.help + Если выбран этот параметр, все импортированные детали будут помечены как «Требуется проверка», независимо от того, что записано в данных импорта. + + + + + project.bom_import.flash.success + Успешно импортировано %count% записей BOM. + + + + + project.bom_import.type + Тип + + + + + project.bom_import.type.kicad_pcbnew + KiCAD Pcbnew BOM (CSV файл) + + + + + project.bom_import.clear_existing_bom + Удалить существующие записи BOM перед импортом. + + + + + project.bom_import.clear_existing_bom.help + Если выбран этот параметр, все записи спецификации, которые уже существуют в проекте, будут удалены и перезаписаны импортированными данными спецификации. + + + + + project.bom_import.flash.invalid_file + Не удалось импортировать файл. Убедитесь, что вы выбрали правильный тип файла. Сообщение об ошибке: %message% + + + + + project.bom_import.flash.invalid_entries + Ошибка проверки! Пожалуйста, убедитесь что данные корректны! + + + + + project.import_bom + Импортировать BOM для проекта + + + + + project.edit.bom.import_bom + Импортировать BOM + + + + + measurement_unit.new + Новая единица измерения + + + + + measurement_unit.edit + Изменить единицу измерения + + + + + user.aboutMe.label + Обо мне + + + + + storelocation.owner.label + Владелец + + + + + storelocation.part_owner_must_match.label + Владелец лота должен совпадать с владельцем места хранения. + + + + + part_lot.owner + Владелец + + + + + part_lot.owner.help + Только владелец может удалять или добавлять компоненты из этого лота. + + + + + log.element_edited.changed_fields.owner + Владелец + + + + + log.element_edited.changed_fields.instock_unknown + Количество неизвестно + + + + + log.element_edited.changed_fields.needs_refill + Необходимо пополнить + + + + + part.withdraw.access_denied + Вы не авторизованы для выполнения желаемого действия! Пожалуйста, проверьте свои полномочия и владельца инвентаря компонентов. + + + + + part.info.amount.less_than_desired + Меньше, чем хотелось бы + + + + + log.cli_user + Пользователь CLI + + + + + log.element_edited.changed_fields.part_owner_must_match + Владелец компонента должен совпадать с владельцем хранилища! + + + + + part.filter.lessThanDesired + Доступно меньше, чем хотелось бы (общее количество < мин. количества) + + + + + part.filter.lotOwner + Владелец инвентаря + + + + + user.show_email_on_profile.label + Показывать e-mail на странице публичного профиля + + + + + log.details.title + Подробности журнала + + + + + log.user_login.login_from_ip + Вход с IP адреса + + + + + log.user_login.ip_anonymize_hint + Если последние цифры IP-адреса отсутствуют, то активируется режим GDPR, в котором IP-адреса анонимизируются. + + + + + log.user_not_allowed.unauthorized_access_attempt_to + Попытка неавторизованного доступа к странице + + + + + log.user_not_allowed.hint + Запрос был заблокирован. Никаких дальнейших действий не требуется. + + + + + log.no_comment + Без комментариев + + + + + log.element_changed.field + Поле + + + + + log.element_changed.data_before + Данные до изменения + + + + + error_table.error + Во время запроса произошла ошибка. + + + + + part.table.invalid_regex + Неверное регулярное выражение (regex) + + + + + log.element_changed.data_after + Данные после изменения + + + + + log.element_changed.diff + Разница + + + + + log.undo.undo.short + Отмена + + + + + log.undo.revert.short + Вернуться к версии + + + + + log.view_version + Посмотреть версию + + + + + log.undo.undelete.short + Восстановить + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + Владелец + + + + + log.element_edited.changed_fields.parent_id + Родительский элемент + + + + + log.details.delete_entry + Удалить запись журнала + + + + + log.delete.message.title + Вы уверены, что хотите удалить эту запись из журнала ? + + + + + log.delete.message + Если это запись истории для элемента, то ее удаление приведет к потере данных истории! Это может привести к неожиданным результатам при использовании функции путешествия во времени. + + + + + log.collection_deleted.on_collection + в коллекции + + + + + log.element_edited.changed_fields.attachments + Вложения + + + + + tfa_u2f.add_key.registration_error + Произошла ошибка во время регистрации ключа безопасности. Попробуйте еще раз или используйте другой ключ! + + + + + log.target_type.none + Нет + + + + + ui.darkmode.light + Светлый + + + + + ui.darkmode.dark + Тёмный + + + + + ui.darkmode.auto + Авто (в зависимости от настроек системы) + + + + + label_generator.no_lines_given + Текстовое содержание не указано! Созданные ярлыки будут пустыми. + + + + + user.password_strength.very_weak + Очень слабый + + + + + user.password_strength.weak + Слабый + + + + + user.password_strength.medium + Средний + + + + + user.password_strength.strong + Сильный + + + + + user.password_strength.very_strong + Очень сильный + + + + + perm.users.impersonate + Выдать себя за другого пользователя + + + + + user.impersonated_by.label + Выдано пользователем + + + + + user.stop_impersonation + Прекратить «Выдавать себя за пользователя» + + + + + user.impersonate.btn + Выдавать себя за пользователя + + + + + user.impersonate.confirm.title + Вы уверены, что хотите выдать себя за этого пользователя? + + + + + user.impersonate.confirm.message + Это будет отражено в журнале. Должна быть веская причина для данного действия. + +Обратите внимание, что вы не можете выдавать себя за деактивированного пользователя. Если вы попробуете это сделать, вы получите сообщение «Доступ запрещен». + + + + + log.type.security.user_impersonated + Пользователь выдал себя за другое лицо + + + + + info_providers.providers_list.title + Источники информации + + + + + info_providers.providers_list.active + Активный + + + + + info_providers.providers_list.disabled + Отключенный + + + + + info_providers.capabilities.basic + Основные + + + + + info_providers.capabilities.footprint + Посадочное место + + + + + info_providers.capabilities.picture + Картинка + + + + + info_providers.capabilities.datasheet + Технические данные + + + + + info_providers.capabilities.price + Цены + + + + + part.info_provider_reference.badge + Источник информации, использованный для создания этого компонента. + + + + + part.info_provider_reference + Создано на основе источника информации + + + + + oauth_client.connect.btn + Подключить OAuth + + + + + info_providers.table.provider.label + Источник + + + + + info_providers.search.keyword + Ключевое слово + + + + + info_providers.search.submit + Найти + + + + + info_providers.search.providers.help + Выберите источники информации для поиска. + + + + + info_providers.search.providers + Источники + + + + + info_providers.search.info_providers_list + Посмотреть все доступные источники информации + + + + + info_providers.search.title + Создать компонент из источника информации + + + + + oauth_client.flash.connection_successful + Соединение с приложением OAuth успешно установлено! + + + + + perm.part.info_providers + Источники информации + + + + + perm.part.info_providers.create_parts + Создание компонентов + + + + + entity.edit.alternative_names.label + Альтернативные названия + + + + + entity.edit.alternative_names.help + Представленные здесь альтернативные имена используются для автоматического выбора этого элемента на основе данных, полученных из источников информации. + + + + + info_providers.form.help_prefix + Источник + + + + + update_manager.new_version_available.title + Доступна новая версия + + + + + update_manager.new_version_available.text + Доступна новая версия Part-DB. Здесь вы найдете больше информации + + + + + update_manager.new_version_available.only_administrators_can_see + Это сообщение могут видеть только администраторы. + + + + + perm.system.show_available_updates + Показать доступные обновления Part-DB + + + + + user.settings.api_tokens + Токены API + + + + + user.settings.api_tokens.description + Используя токен API, другие приложения могут получить доступ к Part-DB под вашими правами для выполнения различных действий с помощью REST API Part-DB. Если вы удалите токен API, приложение, использующее этот токен, больше не сможет получить доступ к Part-DB от его имени. + + + + + api_tokens.name + Имя + + + + + api_tokens.access_level + Уровень доступа + + + + + api_tokens.expiration_date + Дата истечения срока действия + + + + + api_tokens.added_date + Добавлен + + + + + api_tokens.last_time_used + Последний раз использован + + + + + datetime.never + Никогда + + + + + api_token.valid + Действительный + + + + + api_token.expired + Истекший + + + + + user.settings.show_api_documentation + Посмотреть документацию по API + + + + + api_token.create_new + Создать новый токен API + + + + + api_token.level.read_only + Только чтение + + + + + api_token.level.edit + Редактировать + + + + + api_token.level.admin + Админ + + + + + api_token.level.full + Полный + + + + + api_tokens.access_level.help + Вы можете ограничить доступ к токену API. Доступ всегда ограничен правами вашего пользователя. + + + + + api_tokens.expiration_date.help + После этой даты токен больше нельзя будет использовать. Если это поле оставить пустым, срок действия токена никогда не истечет. + + + + + api_tokens.your_token_is + Ваш токен API + + + + + api_tokens.please_save_it + Пожалуйста, сохраните эту информацию - вы больше не сможете её увидеть! + + + + + api_tokens.create_new.back_to_user_settings + Вернуться к настройкам пользователя + + + + + project.build.dont_check_quantity + Не проверять количество + + + + + project.build.dont_check_quantity.help + Если выбрана эта опция, выбранные количества будут удалены из запасов, без проверки фактически необходимого количества компонентов требуемых для сборки проекта. + + + + + part_list.action.invert_selection + Наоборот + + + + + perm.api + API + + + + + perm.api.access_api + Доступ через API + + + + + perm.api.manage_tokens + Управление токенами API + + + + + user.settings.api_tokens.delete.title + Вы уверены, что хотите удалить этот токен API? + + + + + user.settings.api_tokens.delete + Удалить + + + + + user.settings.api_tokens.delete.message + Приложение, использующее этот токен, больше не будет иметь доступа к Part-DB. Это не может быть отменено! + + + + + api_tokens.deleted + Токен API успешно удален! + + + + + user.settings.api_tokens.no_api_tokens_yet + Токены API еще не созданы. + + + + + api_token.ends_with + Заканчивается на + + + + + entity.select.creating_new_entities_not_allowed + Вы не имеете права создавать новые элементы этого типа! Пожалуйста, выберите один из существующих. + + + + + scan_dialog.mode + Тип штрих-кода + + + + + scan_dialog.mode.auto + Автоматическое распознавание + + + + + scan_dialog.mode.ipn + IPN штрих-код + + + + + scan_dialog.mode.internal + Штрих-код Part-DB + + + + + part_association.label + Связывание компонентов + + + + + part.edit.tab.associations + Связанные компоненты + + + + + part_association.edit.other_part + Связанный компонент + + + + + part_association.edit.type + Тип отношений + + + + + part_association.edit.comment + Примечания + + + + + part_association.edit.type.help + Здесь вы можете указать, как выбранный компонент связан с текущим компонентом. + + + + + part_association.table.from_this_part + Ссылки из этого компонента на другие + + + + + part_association.table.from + От + + + + + part_association.table.type + Отношение + + + + + part_association.table.to + К + + + + + part_association.type.compatible + Совместим с + + + + + part_association.table.to_this_part + Ссылки на этот компонент от других + + + + + part_association.type.other + Другое (своя цена) + + + + + part_association.type.supersedes + Заменены + + + + + part_association.edit.other_type + Самостоятельно выбранные отношения + + + + + part_association.edit.delete.confirm + Вы действительно хотите удалить это отношение? Это не может быть отменено. + + + + + part_lot.edit.advanced + Показать дополнительные параметры + + + + + part_lot.edit.vendor_barcode + Штрих-код поставщика + + + + + part_lot.edit.vendor_barcode.help + Если этот инвентарь уже имеет штрих-код (например, нанесенный поставщиком), вы можете ввести здесь содержимое штрих-кода, чтобы найти этот инвентарь путем сканирования штрих-кода. + + + + + scan_dialog.mode.vendor + Штрих-код поставщика (настраивается в перечне компонентов) + + + + + project.bom.instockAmount + Наличие на складе + + + + + collection_type.new_element.tooltip + Этот элемент был создан недавно и еще не сохранен в базе данных. + + + + + part.merge.title + Объединить компонент + + + + + part.merge.title.into + в + + + + + part.merge.confirm.title + Вы действительно хотите объединить <b>%other%</b> в <b>%target%</b>? + + + + + part.merge.confirm.message + <b>%other%</b> будет удален, а текущий компонент сохранён с отображаемыми данными. + + + + + part.info.merge_modal.title + Объединение компонентов + + + + + part.info.merge_modal.other_part + Другой компонент + + + + + part.info.merge_modal.other_into_this + Объединить другой компонент с этим (удалить другой компонент, оставить этот) + + + + + part.info.merge_modal.this_into_other + Объединить этот компонент с другим (удалить этот компонент, оставить другой) + + + + + part.info.merge_btn + Объединить компонент + + + + + part.update_part_from_info_provider.btn + Обновить компонент из источника информации + + + + + info_providers.update_part.title + Обновление компонента из источника информации + + + + + part.merge.flash.please_review + Данные еще не сохранены. Пожалуйста, просмотрите изменения и нажмите «Сохранить», чтобы сохранить данные. + + + + + user.edit.flash.permissions_fixed + Разрешения, необходимые для других разрешений, отсутствовали. Это исправлено. Пожалуйста, проверьте, соответствуют ли разрешения вашим ожиданиям. + + + + + permission.legend.dependency_note + Обратите внимание, что некоторые операции авторизации зависят друг от друга. Если вы получили предупреждение о том, что отсутствующие разрешения были исправлены и для разрешения снова установлено значение «разрешено», вам также необходимо установить для зависимой операции значение «запрещено». Зависимости обычно располагаются справа от операции. + + + + + log.part_stock_changed.timestamp + Время + + + + + part.info.withdraw_modal.timestamp + Время действия + + + + + part.info.withdraw_modal.timestamp.hint + Это поле позволяет указать действительную дату фактического выполнения складской операции, а не только дату ее регистрации. Это значение сохраняется в дополнительном поле записи журнала. + + + + + part.info.withdraw_modal.delete_lot_if_empty + Удалить этот инвентарь, когда он станет пустым + + + + + info_providers.search.error.client_exception + Произошла ошибка при общении с поставщиком информации. Просмотрите конфигурацию этого поставщика и, если возможно, обновите токены OAuth. + + + + + eda_info.reference_prefix.placeholder + напр. R + + + + + eda_info.reference_prefix + Ссылочный префикс + + + + + eda_info.kicad_section.title + Специальные настройки KiCad + + + + + eda_info.value + Стоимость + + + + + eda_info.value.placeholder + напр. 100n + + + + + eda_info.exclude_from_bom + Исключить компонент из спецификации + + + + + eda_info.exclude_from_board + Исключить деталь из печатной платы + + + + + eda_info.exclude_from_sim + Исключить компонент из моделирования + + + + + eda_info.kicad_symbol + Символ схемы KiCad + + + + + eda_info.kicad_symbol.placeholder + напр. Transistor_BJT:BC547 + + + + + eda_info.kicad_footprint + посадочное место KiCad + + + + + eda_info.kicad_footprint.placeholder + напр. Package_TO_SOT_THT:TO-92 + + + + + part.edit.tab.eda + Информация EDA + + + + + api.api_endpoints.title + Конечные точки API + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + корневой URL KiCad API + + + + + eda_info.visibility + Принудительная видимость + + + + + eda_info.visibility.help + По умолчанию видимость определяется автоматически в программном обеспечении EDA. Используя этот флажок, вы можете сделать компонент видимым или невидимым. + + + + + part.withdraw.zero_amount + Вы пытались удалить/добавить нулевое количество! Никаких действий предпринято не было. + + + + + login.flash.access_denied_please_login + В доступе отказано! Пожалуйста, войдите, чтобы продолжить. + + + + + attachment.upload_multiple_files + Загрузить файлы + + + + + entity.mass_creation_flash + Успешно создано %COUNT% элементов. + + + + + info_providers.search.number_of_results + %number% результатов + + + + + info_providers.search.no_results + У выбранных поставщиков результатов не найдено! Проверьте условия поиска или попробуйте выбрать дополнительных поставщиков. + + + + + tfa.check.code.confirmation + Созданный код + + + + + info_providers.search.show_existing_part + Показать существующую деталь + + + + + info_providers.search.edit_existing_part + Редактировать существующую деталь + + + + + info_providers.search.existing_part_found.short + Деталь уже существует + + + + + info_providers.search.existing_part_found + Эта деталь (или очень похожая) уже найдена в базе. Проверьте, та ли она, и хотите ли вы создать ее снова! + + + + + info_providers.search.update_existing_part + Обновить существующую деталь от поставщика информации + + + + + part.create_from_info_provider.no_category_yet + Категория не может быть автоматически определена поставщиком информации. Просмотрите данные и выберите категорию вручную. + + + + + part_lot.edit.user_barcode + Штрих-код пользователя + + + + + scan_dialog.mode.user + Определяемый пользователем штрих-код (настраивается для партии детали) + + + + + scan_dialog.mode.eigp + Штрих-код EIGP 114 (например, коды datamatrix в заказах digikey и mouser) + + + + + scan_dialog.info_mode + Информационный режим (декодирование штрих-кода и отображение его содержимого, но без перенаправления на деталь) + + + + + label_scanner.decoded_info.title + Расшифрованная информация + + + + + label_generator.edit_profiles + Редактировать профили + + + + + label_generator.profile_name_empty + Имя профиля не должно быть пустым! + + + + + label_generator.save_profile_name + Имя профиля + + + + + label_generator.save_profile + Сохранить как новый профиль + + + + + label_generator.profile_saved + Профиль сохранен! + + diff --git a/translations/messages.zh.xlf b/translations/messages.zh.xlf new file mode 100644 index 00000000..668c32f2 --- /dev/null +++ b/translations/messages.zh.xlf @@ -0,0 +1,12212 @@ + + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + templates\AdminPages\AttachmentTypeAdmin.html.twig:4 + + + attachment_type.caption + 附件类型 + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:12 + new + + + attachment_type.edit + 编辑文件类型 + + + + + Part-DB1\templates\AdminPages\AttachmentTypeAdmin.html.twig:16 + new + + + attachment_type.new + 新建类型 + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:22 + Part-DB1\templates\_sidebar.html.twig:7 + templates\AdminPages\CategoryAdmin.html.twig:4 + templates\base.html.twig:163 + templates\base.html.twig:170 + templates\base.html.twig:197 + templates\base.html.twig:225 + + + category.labelp + 类别 + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:19 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:11 + templates\AdminPages\CategoryAdmin.html.twig:8 + + + admin.options + 选项 + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:9 + Part-DB1\templates\AdminPages\CompanyAdminBase.html.twig:15 + templates\AdminPages\CategoryAdmin.html.twig:9 + + + admin.advanced + 高级 + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:13 + new + + + category.edit + 编辑类别 + + + + + Part-DB1\templates\AdminPages\CategoryAdmin.html.twig:17 + new + + + category.new + 新建类别 + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:4 + + + currency.caption + 货币 + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:12 + + + currency.iso_code.caption + 货币ISO代码 + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:15 + + + currency.symbol.caption + 货币符号 + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:29 + new + + + currency.edit + 编辑货币 + + + + + Part-DB1\templates\AdminPages\CurrencyAdmin.html.twig:33 + new + + + currency.new + 新建货币 + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:4 + templates\AdminPages\DeviceAdmin.html.twig:4 + + + project.caption + 项目 + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:8 + new + + + project.edit + 编辑项目 + + + + + Part-DB1\templates\AdminPages\DeviceAdmin.html.twig:12 + new + + + project.new + 新建项目 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:67 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:19 + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_sidebar.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:43 + Part-DB1\templates\_sidebar.html.twig:63 + templates\AdminPages\EntityAdminBase.html.twig:9 + templates\base.html.twig:80 + templates\base.html.twig:179 + templates\base.html.twig:206 + templates\base.html.twig:237 + + + search.placeholder + 搜索 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:23 + Part-DB1\templates\_sidebar.html.twig:3 + templates\AdminPages\EntityAdminBase.html.twig:13 + templates\base.html.twig:166 + templates\base.html.twig:193 + templates\base.html.twig:221 + + + expandAll + 全部展开 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:27 + Part-DB1\templates\_sidebar.html.twig:4 + templates\AdminPages\EntityAdminBase.html.twig:17 + templates\base.html.twig:167 + templates\base.html.twig:194 + templates\base.html.twig:222 + + + reduceAll + 全部收起 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:54 + Part-DB1\templates\Parts\info\_sidebar.html.twig:4 + + + part.info.timetravel_hint + 在 %timestamp% 之前,部件是这样显示的。 <i>请注意,这是试验性功能,信息可能不正确。</i> + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:60 + templates\AdminPages\EntityAdminBase.html.twig:42 + + + standard.label + 属性 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:61 + templates\AdminPages\EntityAdminBase.html.twig:43 + + + infos.label + 信息 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:63 + new + + + history.label + 历史 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:66 + templates\AdminPages\EntityAdminBase.html.twig:45 + + + export.label + 导出 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:68 + templates\AdminPages\EntityAdminBase.html.twig:47 + + + import_export.label + 导入/导出 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:69 + + + mass_creation.label + 大量创建 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:82 + templates\AdminPages\EntityAdminBase.html.twig:59 + + + admin.common + 基本 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:86 + + + admin.attachments + 附件 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:90 + + + admin.parameters + 参数 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:179 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:167 + templates\AdminPages\EntityAdminBase.html.twig:142 + + + export_all.label + 导出所有元素 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:185 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:173 + + + mass_creation.help + 每一行将被解析为新建元素的名称。可以通过缩进创建嵌套元素。 + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:45 + templates\AdminPages\EntityAdminBase.html.twig:35 + + + edit.caption + 编辑元素 "%name" + + + + + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + Part-DB1\templates\AdminPages\EntityAdminBase.html.twig:50 + templates\AdminPages\EntityAdminBase.html.twig:37 + + + new.caption + 新建元素 + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:9 + templates\base.html.twig:172 + templates\base.html.twig:199 + templates\base.html.twig:227 + + + footprint.labelp + 封装 + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:13 + new + + + footprint.edit + 编辑封装 + + + + + Part-DB1\templates\AdminPages\FootprintAdmin.html.twig:17 + new + + + footprint.new + 新建封装 + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:4 + + + group.edit.caption + + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:9 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:16 + + + user.edit.permissions + 权限 + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:24 + new + + + group.edit + 编辑组 + + + + + Part-DB1\templates\AdminPages\GroupAdmin.html.twig:28 + new + + + group.new + 新建组 + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:4 + + + label_profile.caption + 标签配置 + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:8 + + + label_profile.advanced + 高级 + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:9 + + + label_profile.comment + 注释 + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:55 + new + + + label_profile.edit + 编辑标签配置 + + + + + Part-DB1\templates\AdminPages\LabelProfileAdmin.html.twig:59 + new + + + label_profile.new + 新建标签配置 + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:4 + templates\AdminPages\ManufacturerAdmin.html.twig:4 + + + manufacturer.caption + 制造商 + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:8 + new + + + manufacturer.edit + 编辑制造商 + + + + + Part-DB1\templates\AdminPages\ManufacturerAdmin.html.twig:12 + new + + + manufacturer.new + 新建制造商 + + + + + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + Part-DB1\templates\AdminPages\MeasurementUnitAdmin.html.twig:4 + + + measurement_unit.caption + 计量单位 + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5 + Part-DB1\templates\_sidebar.html.twig:8 + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:4 + Part-DB1\templates\_sidebar.html.twig:8 + templates\base.html.twig:171 + templates\base.html.twig:198 + templates\base.html.twig:226 + + + storelocation.labelp + 储存位置 + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:32 + new + + + storelocation.edit + 编辑存储位置 + + + + + Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:36 + new + + + storelocation.new + 新建存储位置 + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:4 + templates\AdminPages\SupplierAdmin.html.twig:4 + + + supplier.caption + 供应商 + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:16 + new + + + supplier.edit + 编辑供应商 + + + + + Part-DB1\templates\AdminPages\SupplierAdmin.html.twig:20 + new + + + supplier.new + 新建供应商 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:8 + + + user.edit.caption + Users + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:14 + + + user.edit.configuration + 配置 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:15 + + + user.edit.password + 密码 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:45 + + + user.edit.tfa.caption + 2FA身份验证 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:47 + + + user.edit.tfa.google_active + 身份验证器应用处于活动状态 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:48 + Part-DB1\templates\Users\backup_codes.html.twig:15 + Part-DB1\templates\Users\_2fa_settings.html.twig:95 + + + tfa_backup.remaining_tokens + 剩余备份代码计数 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:49 + Part-DB1\templates\Users\backup_codes.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:96 + + + tfa_backup.generation_date + 备份代码的生成日期 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:53 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:60 + + + user.edit.tfa.disabled + 方法未启用 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:56 + + + user.edit.tfa.u2f_keys_count + 主动安全密钥 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_title + 确定继续? + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:72 + + + user.edit.tfa.disable_tfa_message + 这将禁用 <b>账号的所有活动2FA身份验证方法</b> 并删除 <b>备份代码</b>! +<br> +该账号将必须重新设置所有2FA身份验证方法并获取新的备份代码。 <br><br> +<b>应在完全确定 申请者身份 时才执行此操作,否则该账号可能会受到攻击。</b> + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + Part-DB1\templates\AdminPages\UserAdmin.html.twig:73 + + + user.edit.tfa.disable_tfa.btn + 禁用所有2FA身份验证方法 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:85 + new + + + user.edit + 编辑用户 + + + + + Part-DB1\templates\AdminPages\UserAdmin.html.twig:89 + new + + + user.new + 新建用户 + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\AdminPages\_attachments.html.twig:4 + Part-DB1\templates\Parts\edit\_attachments.html.twig:4 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:63 + + + attachment.delete + 删除 + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:41 + Part-DB1\templates\Parts\edit\_attachments.html.twig:38 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:35 + Part-DB1\src\DataTables\AttachmentDataTable.php:159 + Part-DB1\templates\Parts\edit\_attachments.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:159 + + + attachment.external + 外部 + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:49 + Part-DB1\templates\Parts\edit\_attachments.html.twig:47 + Part-DB1\templates\AdminPages\_attachments.html.twig:47 + Part-DB1\templates\Parts\edit\_attachments.html.twig:45 + + + attachment.preview.alt + 附件缩略图 + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:52 + Part-DB1\templates\Parts\edit\_attachments.html.twig:50 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + Part-DB1\templates\AdminPages\_attachments.html.twig:50 + Part-DB1\templates\Parts\edit\_attachments.html.twig:48 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:45 + + + attachment.view + 查看 + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:58 + Part-DB1\templates\Parts\edit\_attachments.html.twig:56 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:43 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + Part-DB1\templates\AdminPages\_attachments.html.twig:56 + Part-DB1\templates\Parts\edit\_attachments.html.twig:54 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:38 + Part-DB1\src\DataTables\AttachmentDataTable.php:166 + + + attachment.file_not_found + 文件未找到 + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:66 + Part-DB1\templates\Parts\edit\_attachments.html.twig:64 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:48 + Part-DB1\templates\Parts\edit\_attachments.html.twig:62 + + + attachment.secure + 私有附件 + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:79 + Part-DB1\templates\Parts\edit\_attachments.html.twig:77 + Part-DB1\templates\AdminPages\_attachments.html.twig:77 + Part-DB1\templates\Parts\edit\_attachments.html.twig:75 + + + attachment.create + 添加附件 + + + + + Part-DB1\templates\AdminPages\_attachments.html.twig:84 + Part-DB1\templates\Parts\edit\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + Part-DB1\templates\AdminPages\_attachments.html.twig:82 + Part-DB1\templates\Parts\edit\_attachments.html.twig:80 + Part-DB1\templates\Parts\edit\_lots.html.twig:33 + + + part_lot.edit.delete.confirm + 确认删除 该库存 ? 该操作不能被撤消 + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + Part-DB1\templates\AdminPages\_delete_form.html.twig:2 + templates\AdminPages\_delete_form.html.twig:2 + + + entity.delete.confirm_title + 确定删除 %name%? + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + Part-DB1\templates\AdminPages\_delete_form.html.twig:3 + templates\AdminPages\_delete_form.html.twig:3 + + + entity.delete.message + 该操作不能被撤销 +<br> +子元素将向上移动。 + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + Part-DB1\templates\AdminPages\_delete_form.html.twig:11 + templates\AdminPages\_delete_form.html.twig:9 + + + entity.delete + Delete element + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:45 + Part-DB1\src\Form\Part\PartBaseType.php:286 + Part-DB1\templates\AdminPages\_delete_form.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:43 + Part-DB1\src\Form\Part\PartBaseType.php:267 + new + + + edit.log_comment + 更改评论 + + + + + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + Part-DB1\templates\AdminPages\_delete_form.html.twig:24 + templates\AdminPages\_delete_form.html.twig:12 + + + entity.delete.recursive + 递归删除(所有子元素) + + + + + Part-DB1\templates\AdminPages\_duplicate.html.twig:3 + + + entity.duplicate + 重复元素 + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + Part-DB1\templates\AdminPages\_export_form.html.twig:4 + Part-DB1\src\Form\AdminPages\ImportType.php:76 + templates\AdminPages\_export_form.html.twig:4 + src\Form\ImportType.php:67 + + + export.format + 文件格式 + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + Part-DB1\templates\AdminPages\_export_form.html.twig:16 + templates\AdminPages\_export_form.html.twig:16 + + + export.level + 详细程度 + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + Part-DB1\templates\AdminPages\_export_form.html.twig:19 + templates\AdminPages\_export_form.html.twig:19 + + + export.level.simple + 简单 + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + Part-DB1\templates\AdminPages\_export_form.html.twig:20 + templates\AdminPages\_export_form.html.twig:20 + + + export.level.extended + 扩展 + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + Part-DB1\templates\AdminPages\_export_form.html.twig:21 + templates\AdminPages\_export_form.html.twig:21 + + + export.level.full + 完全 + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + Part-DB1\templates\AdminPages\_export_form.html.twig:31 + templates\AdminPages\_export_form.html.twig:31 + + + export.include_children + 在导出中包含子元素 + + + + + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + Part-DB1\templates\AdminPages\_export_form.html.twig:39 + templates\AdminPages\_export_form.html.twig:39 + + + export.btn + 导出 + + + + + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + Part-DB1\templates\AdminPages\_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:12 + Part-DB1\templates\Parts\info\show_part_info.html.twig:24 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:36 + templates\AdminPages\EntityAdminBase.html.twig:94 + templates\Parts\edit_part_info.html.twig:12 + templates\Parts\show_part_info.html.twig:11 + + + id.label + ID + + + + + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:76 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:77 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:77 + Part-DB1\templates\AdminPages\_info.html.twig:11 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:59 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:60 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:69 + Part-DB1\templates\Parts\info\_sidebar.html.twig:12 + Part-DB1\templates\Parts\lists\_info_card.html.twig:53 + templates\AdminPages\EntityAdminBase.html.twig:101 + templates\Parts\show_part_info.html.twig:248 + + + createdAt + 创建于 + + + + + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:73 + Part-DB1\templates\AdminPages\_info.html.twig:25 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:8 + Part-DB1\templates\Parts\lists\_info_card.html.twig:49 + templates\AdminPages\EntityAdminBase.html.twig:114 + templates\Parts\show_part_info.html.twig:263 + + + lastModified + 上次修改 + + + + + Part-DB1\templates\AdminPages\_info.html.twig:38 + Part-DB1\templates\AdminPages\_info.html.twig:38 + + + entity.info.parts_count + 具有该元素的部件数量 + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:6 + Part-DB1\templates\helper.twig:125 + Part-DB1\templates\Parts\edit\_specifications.html.twig:6 + + + specifications.property + 参数 + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:7 + Part-DB1\templates\Parts\edit\_specifications.html.twig:7 + + + specifications.symbol + 符号 + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:8 + Part-DB1\templates\Parts\edit\_specifications.html.twig:8 + + + specifications.value_min + 最小. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:9 + Part-DB1\templates\Parts\edit\_specifications.html.twig:9 + + + specifications.value_typ + 标称. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:10 + Part-DB1\templates\Parts\edit\_specifications.html.twig:10 + + + specifications.value_max + 最大. + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:11 + Part-DB1\templates\Parts\edit\_specifications.html.twig:11 + + + specifications.unit + 单位 + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:12 + Part-DB1\templates\Parts\edit\_specifications.html.twig:12 + + + specifications.text + 文本 + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:13 + Part-DB1\templates\Parts\edit\_specifications.html.twig:13 + + + specifications.group + Group + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:26 + Part-DB1\templates\Parts\edit\_specifications.html.twig:26 + + + specification.create + 新建参数 + + + + + Part-DB1\templates\AdminPages\_parameters.html.twig:31 + Part-DB1\templates\Parts\edit\_specifications.html.twig:31 + + + parameter.delete.confirm + 确实删除该参数? + + + + + Part-DB1\templates\attachment_list.html.twig:3 + Part-DB1\templates\attachment_list.html.twig:3 + + + attachment.list.title + 附件列表 + + + + + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + Part-DB1\templates\attachment_list.html.twig:10 + Part-DB1\templates\LogSystem\_log_table.html.twig:8 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:6 + + + part_list.loading.caption + 加载中... + + + + + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + Part-DB1\templates\attachment_list.html.twig:11 + Part-DB1\templates\LogSystem\_log_table.html.twig:9 + Part-DB1\templates\Parts\lists\_parts_list.html.twig:7 + + + part_list.loading.message + 这可能需要一些时间。如果此消息没有消失,请尝试重新加载页面。 + + + + + Part-DB1\templates\base.html.twig:68 + Part-DB1\templates\base.html.twig:68 + templates\base.html.twig:246 + + + vendor.base.javascript_hint + 需要激活 Javascript 才能使用所有功能 + + + + + Part-DB1\templates\base.html.twig:73 + Part-DB1\templates\base.html.twig:73 + + + sidebar.big.toggle + 显示/隐藏 侧边栏 + + + + + Part-DB1\templates\base.html.twig:95 + Part-DB1\templates\base.html.twig:95 + templates\base.html.twig:271 + + + loading.caption + 加载中: + + + + + Part-DB1\templates\base.html.twig:96 + Part-DB1\templates\base.html.twig:96 + templates\base.html.twig:272 + + + loading.message + 这可能需要一段时间。如果此消息长时间存在,请尝试重新加载页面。 + + + + + Part-DB1\templates\base.html.twig:101 + Part-DB1\templates\base.html.twig:101 + templates\base.html.twig:277 + + + loading.bar + 加载中... + + + + + Part-DB1\templates\base.html.twig:112 + Part-DB1\templates\base.html.twig:112 + templates\base.html.twig:288 + + + back_to_top + 返回页面顶部 + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:35 + Part-DB1\templates\Form\permissionLayout.html.twig:35 + + + permission.edit.permission + 权限 + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:36 + Part-DB1\templates\Form\permissionLayout.html.twig:36 + + + permission.edit.value + 配置 + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:53 + Part-DB1\templates\Form\permissionLayout.html.twig:53 + + + permission.legend.title + 状态说明 + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:57 + Part-DB1\templates\Form\permissionLayout.html.twig:57 + + + permission.legend.disallow + 禁止 + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:61 + Part-DB1\templates\Form\permissionLayout.html.twig:61 + + + permission.legend.allow + 允许 + + + + + Part-DB1\templates\Form\permissionLayout.html.twig:65 + Part-DB1\templates\Form\permissionLayout.html.twig:65 + + + permission.legend.inherit + 继承 + + + + + Part-DB1\templates\helper.twig:3 + Part-DB1\templates\helper.twig:3 + + + bool.true + TRUE + + + + + Part-DB1\templates\helper.twig:5 + Part-DB1\templates\helper.twig:5 + + + bool.false + FALSE + + + + + Part-DB1\templates\helper.twig:92 + Part-DB1\templates\helper.twig:87 + + + Yes + YES + + + + + Part-DB1\templates\helper.twig:94 + Part-DB1\templates\helper.twig:89 + + + No + NO + + + + + Part-DB1\templates\helper.twig:126 + + + specifications.value + + + + + + Part-DB1\templates\homepage.html.twig:7 + Part-DB1\templates\homepage.html.twig:7 + templates\homepage.html.twig:7 + + + version.caption + 版本 + + + + + Part-DB1\templates\homepage.html.twig:22 + Part-DB1\templates\homepage.html.twig:22 + templates\homepage.html.twig:19 + + + homepage.license + 许可信息 + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.caption + 项目页面 + + + + + Part-DB1\templates\homepage.html.twig:31 + Part-DB1\templates\homepage.html.twig:31 + templates\homepage.html.twig:28 + + + homepage.github.text + 源代码、下载、错误报告、待办事项列表等可以在 <a href="%href%" class="link-external" target="_blank">项目仓库</a> 上找到。 + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.caption + 帮助 + + + + + Part-DB1\templates\homepage.html.twig:32 + Part-DB1\templates\homepage.html.twig:32 + templates\homepage.html.twig:29 + + + homepage.help.text + 帮助和提示可以在 <a href="%href%" class="link-external" target="_blank">文档</a>中找到。 + + + + + Part-DB1\templates\homepage.html.twig:33 + Part-DB1\templates\homepage.html.twig:33 + templates\homepage.html.twig:30 + + + homepage.forum.caption + 论坛 + + + + + Part-DB1\templates\homepage.html.twig:45 + Part-DB1\templates\homepage.html.twig:45 + new + + + homepage.last_activity + 最近的事件 + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:3 + Part-DB1\templates\LabelSystem\dialog.html.twig:6 + + + label_generator.title + 标签生成器 + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:16 + + + label_generator.common + 基本 + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:20 + + + label_generator.advanced + 高级 + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:24 + + + label_generator.profiles + 标签配置 + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:58 + + + label_generator.selected_profile + 当前选择的配置 + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:62 + + + label_generator.edit_profile + 编辑配置 + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:75 + + + label_generator.load_profile + 载入配置 + + + + + Part-DB1\templates\LabelSystem\dialog.html.twig:102 + + + label_generator.download + 下载 + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:3 + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:5 + + + label_generator.label_btn + 生成标签 + + + + + Part-DB1\templates\LabelSystem\dropdown_macro.html.twig:20 + + + label_generator.label_empty + 新建空标签 + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:3 + + + label_scanner.title + 标签扫描器 + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.title + 未找到摄像头 + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:7 + + + label_scanner.no_cam_found.text + 您需要一个摄像头并授予权限。或在下面手动输入条形码。 + + + + + Part-DB1\templates\LabelSystem\Scanner\dialog.html.twig:27 + + + label_scanner.source_select + 选择源 + + + + + Part-DB1\templates\LogSystem\log_list.html.twig:3 + Part-DB1\templates\LogSystem\log_list.html.twig:3 + + + log.list.title + 系统日志 + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + Part-DB1\templates\LogSystem\_log_table.html.twig:1 + new + + + log.undo.confirm_title + 确定撤消更改/恢复到时间戳? + + + + + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + Part-DB1\templates\LogSystem\_log_table.html.twig:2 + new + + + log.undo.confirm_message + 确定撤消给定的更改/将元素重置到给定的时间戳? + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.email_sent_by + 这封电子邮件是自动发送的,由 + + + + + Part-DB1\templates\mail\base.html.twig:24 + Part-DB1\templates\mail\base.html.twig:24 + + + mail.footer.dont_reply + 不要回复此电子邮件。 + + + + + Part-DB1\templates\mail\pw_reset.html.twig:6 + Part-DB1\templates\mail\pw_reset.html.twig:6 + + + email.hi %name% + 你好 %name% + + + + + Part-DB1\templates\mail\pw_reset.html.twig:7 + Part-DB1\templates\mail\pw_reset.html.twig:7 + + + email.pw_reset.message + 有人请求重置您的密码。 如果此请求不是您提出的,请忽略此邮件。 + + + + + Part-DB1\templates\mail\pw_reset.html.twig:9 + Part-DB1\templates\mail\pw_reset.html.twig:9 + + + email.pw_reset.button + 单击此处重置密码 + + + + + Part-DB1\templates\mail\pw_reset.html.twig:11 + Part-DB1\templates\mail\pw_reset.html.twig:11 + + + email.pw_reset.fallback + 如果这对您不起作用,请转到 <a href="%url%">%url%</a> 并输入以下信息 + + + + + Part-DB1\templates\mail\pw_reset.html.twig:16 + Part-DB1\templates\mail\pw_reset.html.twig:16 + + + email.pw_reset.username + 用户名 + + + + + Part-DB1\templates\mail\pw_reset.html.twig:19 + Part-DB1\templates\mail\pw_reset.html.twig:19 + + + email.pw_reset.token + Token + + + + + Part-DB1\templates\mail\pw_reset.html.twig:24 + Part-DB1\templates\mail\pw_reset.html.twig:24 + + + email.pw_reset.valid_unit %date% + 重置令牌将在 <i>%date%</i> 之前有效。 + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:18 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:78 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:58 + + + orderdetail.delete + Delete + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:39 + + + pricedetails.edit.min_qty + 最低折扣数量 + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:40 + + + pricedetails.edit.price + 价格 + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:41 + + + pricedetails.edit.price_qty + 数量 + + + + + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + Part-DB1\templates\Parts\edit\edit_form_styles.html.twig:54 + + + pricedetail.create + 添加价格 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:4 + templates\Parts\edit_part_info.html.twig:4 + + + part.edit.title + 编辑部件 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:9 + templates\Parts\edit_part_info.html.twig:9 + + + part.edit.card_title + 编辑部件 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:22 + + + part.edit.tab.common + 基础 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:28 + + + part.edit.tab.manufacturer + 制造商 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:34 + + + part.edit.tab.advanced + 高级 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40 + + + part.edit.tab.part_lots + 库存 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:46 + + + part.edit.tab.attachments + 附件 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:52 + + + part.edit.tab.orderdetails + 采购信息 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.specifications + 参数 + + + + + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:64 + Part-DB1\templates\Parts\edit\edit_part_info.html.twig:58 + + + part.edit.tab.comment + 注释 + + + + + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + Part-DB1\templates\Parts\edit\new_part.html.twig:8 + templates\Parts\new_part.html.twig:8 + + + part.new.card_title + 创建新部件 + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + Part-DB1\templates\Parts\edit\_lots.html.twig:5 + + + part_lot.delete + 删除 + + + + + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + Part-DB1\templates\Parts\edit\_lots.html.twig:28 + + + part_lot.create + 增加库存 + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:13 + + + orderdetail.create + 添加经销商 + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:18 + + + pricedetails.edit.delete.confirm + 确实删除此价格? 该操作不能被撤消 + + + + + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:62 + Part-DB1\templates\Parts\edit\_orderdetails.html.twig:61 + + + orderdetails.edit.delete.confirm + 确实要删除此经销商信息? 该操作不能被撤消 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + Part-DB1\templates\Parts\info\show_part_info.html.twig:4 + Part-DB1\templates\Parts\info\show_part_info.html.twig:19 + templates\Parts\show_part_info.html.twig:4 + templates\Parts\show_part_info.html.twig:9 + + + part.info.title + 部件的详细信息 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + Part-DB1\templates\Parts\info\show_part_info.html.twig:47 + + + part.part_lots.label + 库存 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:56 + Part-DB1\templates\Parts\lists\_info_card.html.twig:43 + Part-DB1\templates\_navbar_search.html.twig:31 + Part-DB1\templates\_navbar_search.html.twig:26 + templates\base.html.twig:62 + templates\Parts\show_part_info.html.twig:74 + src\Form\PartType.php:86 + + + comment.label + 注释 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + + + part.info.specifications + 参数 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:74 + Part-DB1\templates\Parts\info\show_part_info.html.twig:64 + templates\Parts\show_part_info.html.twig:82 + + + attachment.labelp + 附件 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:83 + Part-DB1\templates\Parts\info\show_part_info.html.twig:71 + templates\Parts\show_part_info.html.twig:88 + + + vendor.partinfo.shopping_infos + 采购信息 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:91 + Part-DB1\templates\Parts\info\show_part_info.html.twig:78 + templates\Parts\show_part_info.html.twig:94 + + + vendor.partinfo.history + 历史 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:97 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + Part-DB1\templates\Parts\info\show_part_info.html.twig:84 + Part-DB1\templates\_sidebar.html.twig:54 + Part-DB1\templates\_sidebar.html.twig:13 + templates\base.html.twig:176 + templates\base.html.twig:203 + templates\base.html.twig:217 + templates\base.html.twig:231 + templates\Parts\show_part_info.html.twig:100 + + + tools.label + 工具 + + + + + Part-DB1\templates\Parts\info\show_part_info.html.twig:103 + Part-DB1\templates\Parts\info\show_part_info.html.twig:90 + + + extended_info.label + 扩展信息 + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:7 + + + attachment.name + 名称 + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:8 + + + attachment.attachment_type + 附件类型 + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:9 + + + attachment.file_name + 文件名 + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:10 + + + attachment.file_size + 文件大小 + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:54 + + + attachment.preview + 预览图片 + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:67 + Part-DB1\templates\Parts\info\_attachments_info.html.twig:50 + + + attachment.download + 下载 + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:11 + new + + + user.creating_user + 创建此部件的用户 + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:13 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:28 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:50 + + + Unknown + 未知 + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:15 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:30 + new + + + accessDenied + 拒绝访问 + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:26 + new + + + user.last_editing_user + 最后编辑此部件的用户 + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:41 + + + part.isFavorite + Favorite + + + + + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + Part-DB1\templates\Parts\info\_extended_infos.html.twig:46 + + + part.minOrderAmount + 最低订购量 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:46 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + Part-DB1\templates\Parts\info\_main_infos.html.twig:8 + Part-DB1\templates\_navbar_search.html.twig:41 + Part-DB1\src\Services\ElementTypeNameGenerator.php:84 + templates\base.html.twig:70 + templates\Parts\show_part_info.html.twig:24 + src\Form\PartType.php:80 + + + manufacturer.label + 制造商 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:24 + Part-DB1\templates\_navbar_search.html.twig:11 + templates\base.html.twig:54 + src\Form\PartType.php:62 + + + name.label + 名称 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + Part-DB1\templates\Parts\info\_main_infos.html.twig:27 + new + + + part.back_to_info + 返回当前版本 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:19 + Part-DB1\templates\Parts\info\_main_infos.html.twig:32 + Part-DB1\templates\_navbar_search.html.twig:18 + templates\base.html.twig:58 + templates\Parts\show_part_info.html.twig:31 + src\Form\PartType.php:65 + + + description.label + 描述 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:15 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + Part-DB1\templates\Parts\info\_main_infos.html.twig:34 + Part-DB1\templates\_navbar_search.html.twig:14 + Part-DB1\src\Services\ElementTypeNameGenerator.php:80 + templates\base.html.twig:56 + templates\Parts\show_part_info.html.twig:32 + src\Form\PartType.php:74 + + + category.label + 类别 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + Part-DB1\templates\Parts\info\_main_infos.html.twig:39 + templates\Parts\show_part_info.html.twig:42 + src\Form\PartType.php:69 + + + instock.label + 在库 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + Part-DB1\templates\Parts\info\_main_infos.html.twig:41 + templates\Parts\show_part_info.html.twig:44 + src\Form\PartType.php:72 + + + mininstock.label + 最低库存 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:52 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + Part-DB1\templates\Parts\info\_main_infos.html.twig:45 + Part-DB1\templates\_navbar_search.html.twig:47 + Part-DB1\src\Services\ElementTypeNameGenerator.php:83 + templates\base.html.twig:73 + templates\Parts\show_part_info.html.twig:47 + + + footprint.label + 封装 + + + + + Part-DB1\templates\Parts\info\_main_infos.html.twig:56 + Part-DB1\templates\Parts\info\_main_infos.html.twig:59 + Part-DB1\templates\Parts\info\_main_infos.html.twig:57 + Part-DB1\templates\Parts\info\_main_infos.html.twig:60 + templates\Parts\show_part_info.html.twig:51 + + + part.avg_price.label + 平均价格 + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + Part-DB1\templates\Parts\info\_order_infos.html.twig:5 + + + part.supplier.name + 名称 + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + Part-DB1\templates\Parts\info\_order_infos.html.twig:6 + + + part.supplier.partnr + 合作伙伴. + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + Part-DB1\templates\Parts\info\_order_infos.html.twig:28 + + + part.order.minamount + 最低数量 + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + Part-DB1\templates\Parts\info\_order_infos.html.twig:29 + + + part.order.price + 价格 + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + Part-DB1\templates\Parts\info\_order_infos.html.twig:31 + + + part.order.single_price + 单价 + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + Part-DB1\templates\Parts\info\_order_infos.html.twig:71 + + + edit.caption_short + 编辑 + + + + + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + Part-DB1\templates\Parts\info\_order_infos.html.twig:72 + + + delete.caption + 删除 + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + Part-DB1\templates\Parts\info\_part_lots.html.twig:6 + + + part_lots.description + 描述 + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + Part-DB1\templates\Parts\info\_part_lots.html.twig:7 + + + part_lots.storage_location + 存储位置 + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:9 + Part-DB1\templates\Parts\info\_part_lots.html.twig:8 + + + part_lots.amount + 数量 + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:24 + Part-DB1\templates\Parts\info\_part_lots.html.twig:22 + + + part_lots.location_unknown + 存储位置未知 + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:31 + Part-DB1\templates\Parts\info\_part_lots.html.twig:29 + + + part_lots.instock_unknown + 数量未知 + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:40 + Part-DB1\templates\Parts\info\_part_lots.html.twig:38 + + + part_lots.expiration_date + 到期时间 + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:48 + Part-DB1\templates\Parts\info\_part_lots.html.twig:46 + + + part_lots.is_expired + 已到期 + + + + + Part-DB1\templates\Parts\info\_part_lots.html.twig:55 + Part-DB1\templates\Parts\info\_part_lots.html.twig:53 + + + part_lots.need_refill + 需要补充 + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:15 + Part-DB1\templates\Parts\info\_picture.html.twig:15 + + + part.info.prev_picture + 上一张图片 + + + + + Part-DB1\templates\Parts\info\_picture.html.twig:19 + Part-DB1\templates\Parts\info\_picture.html.twig:19 + + + part.info.next_picture + 下一张图片 + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + Part-DB1\templates\Parts\info\_sidebar.html.twig:21 + + + part.mass.tooltip + 重量 + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + Part-DB1\templates\Parts\info\_sidebar.html.twig:30 + + + part.needs_review.badge + 需要审查 + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + Part-DB1\templates\Parts\info\_sidebar.html.twig:39 + + + part.favorite.badge + 收藏 + + + + + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + Part-DB1\templates\Parts\info\_sidebar.html.twig:47 + + + part.obsolete.badge + 不再可用 + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:10 + + + parameters.extracted_from_description + 从描述中自动提取 + + + + + Part-DB1\templates\Parts\info\_specifications.html.twig:15 + + + parameters.auto_extracted_from_comment + 从注释中自动提取 + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:6 + Part-DB1\templates\Parts\info\_tools.html.twig:4 + templates\Parts\show_part_info.html.twig:125 + + + part.edit.btn + 编辑部件 + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:16 + Part-DB1\templates\Parts\info\_tools.html.twig:14 + templates\Parts\show_part_info.html.twig:135 + + + part.clone.btn + 克隆部件 + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:24 + Part-DB1\templates\Parts\lists\_action_bar.html.twig:4 + templates\Parts\show_part_info.html.twig:143 + + + part.create.btn + 新建部件 + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:31 + Part-DB1\templates\Parts\info\_tools.html.twig:29 + + + part.delete.confirm_title + 确定删除该部件? + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:32 + Part-DB1\templates\Parts\info\_tools.html.twig:30 + + + part.delete.message + 此部件与它的任何相关信息(如附件、价格信息等)将被删除。 该操作不能被撤消 + + + + + Part-DB1\templates\Parts\info\_tools.html.twig:39 + Part-DB1\templates\Parts\info\_tools.html.twig:37 + + + part.delete + 删除部件 + + + + + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + Part-DB1\templates\Parts\lists\all_list.html.twig:4 + + + parts_list.all.title + 所有部件 + + + + + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + Part-DB1\templates\Parts\lists\category_list.html.twig:4 + + + parts_list.category.title + 部件(根据类别) + + + + + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + Part-DB1\templates\Parts\lists\footprint_list.html.twig:4 + + + parts_list.footprint.title + 部件(根据封装) + + + + + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + Part-DB1\templates\Parts\lists\manufacturer_list.html.twig:4 + + + parts_list.manufacturer.title + 部件(根据制造商) + + + + + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + Part-DB1\templates\Parts\lists\search_list.html.twig:4 + + + parts_list.search.title + 搜索部件 + + + + + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + Part-DB1\templates\Parts\lists\store_location_list.html.twig:4 + + + parts_list.storelocation.title + 部件(根据存储位置) + + + + + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + Part-DB1\templates\Parts\lists\supplier_list.html.twig:4 + + + parts_list.supplier.title + 部件(根据供应商) + + + + + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + Part-DB1\templates\Parts\lists\tags_list.html.twig:4 + + + parts_list.tags.title + 部件(根据标签) + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:22 + Part-DB1\templates\Parts\lists\_info_card.html.twig:17 + + + entity.info.common.tab + 基本 + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:26 + Part-DB1\templates\Parts\lists\_info_card.html.twig:20 + + + entity.info.statistics.tab + 统计数据 + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:31 + + + entity.info.attachments.tab + 附件 + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:37 + + + entity.info.parameters.tab + 参数 + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:54 + Part-DB1\templates\Parts\lists\_info_card.html.twig:30 + + + entity.info.name + 名称 + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:58 + Part-DB1\templates\Parts\lists\_info_card.html.twig:96 + Part-DB1\templates\Parts\lists\_info_card.html.twig:34 + Part-DB1\templates\Parts\lists\_info_card.html.twig:67 + + + entity.info.parent + 父元素 + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:70 + Part-DB1\templates\Parts\lists\_info_card.html.twig:46 + + + entity.edit.btn + 编辑 + + + + + Part-DB1\templates\Parts\lists\_info_card.html.twig:92 + Part-DB1\templates\Parts\lists\_info_card.html.twig:63 + + + entity.info.children_count + 子元素计数 + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + Part-DB1\templates\security\2fa_base_form.html.twig:3 + Part-DB1\templates\security\2fa_base_form.html.twig:5 + + + tfa.check.title + 需要2FA身份验证 + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:39 + Part-DB1\templates\security\2fa_base_form.html.twig:39 + + + tfa.code.trusted_pc + 这是受信任的计算机(如果启用此功能,则不会在此计算机上执行进一步的2FA查询) + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + Part-DB1\templates\security\2fa_base_form.html.twig:52 + Part-DB1\templates\security\login.html.twig:58 + + + login.btn + 登录 + + + + + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:42 + Part-DB1\templates\security\2fa_base_form.html.twig:53 + Part-DB1\templates\security\U2F\u2f_login.html.twig:13 + Part-DB1\templates\_navbar.html.twig:40 + + + user.logout + 注销 + + + + + Part-DB1\templates\security\2fa_form.html.twig:6 + Part-DB1\templates\security\2fa_form.html.twig:6 + + + tfa.check.code.label + 身份验证器应用代码 + + + + + Part-DB1\templates\security\2fa_form.html.twig:10 + Part-DB1\templates\security\2fa_form.html.twig:10 + + + tfa.check.code.help + 输入身份验证器应用中的6位代码,如果身份验证器不可用,请输入备用代码之一。 + + + + + Part-DB1\templates\security\login.html.twig:3 + Part-DB1\templates\security\login.html.twig:3 + templates\security\login.html.twig:3 + + + login.title + 登录 + + + + + Part-DB1\templates\security\login.html.twig:7 + Part-DB1\templates\security\login.html.twig:7 + templates\security\login.html.twig:7 + + + login.card_title + 登录 + + + + + Part-DB1\templates\security\login.html.twig:31 + Part-DB1\templates\security\login.html.twig:31 + templates\security\login.html.twig:31 + + + login.username.label + 用户名 + + + + + Part-DB1\templates\security\login.html.twig:34 + Part-DB1\templates\security\login.html.twig:34 + templates\security\login.html.twig:34 + + + login.username.placeholder + 用户名 + + + + + Part-DB1\templates\security\login.html.twig:38 + Part-DB1\templates\security\login.html.twig:38 + templates\security\login.html.twig:38 + + + login.password.label + 密码 + + + + + Part-DB1\templates\security\login.html.twig:40 + Part-DB1\templates\security\login.html.twig:40 + templates\security\login.html.twig:40 + + + login.password.placeholder + 密码 + + + + + Part-DB1\templates\security\login.html.twig:50 + Part-DB1\templates\security\login.html.twig:50 + templates\security\login.html.twig:50 + + + login.rememberme + 记住我(不应在公共计算机上使用) + + + + + Part-DB1\templates\security\login.html.twig:64 + Part-DB1\templates\security\login.html.twig:64 + + + pw_reset.password_forget + 忘记用户名/密码? + + + + + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + Part-DB1\templates\security\pw_reset_new_pw.html.twig:5 + + + pw_reset.new_pw.header.title + 设置新密码 + + + + + Part-DB1\templates\security\pw_reset_request.html.twig:5 + Part-DB1\templates\security\pw_reset_request.html.twig:5 + + + pw_reset.request.header.title + 要求新的密码 + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + Part-DB1\templates\security\U2F\u2f_login.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:10 + + + tfa_u2f.http_warning + 您正在使用不安全的 HTTP 方法访问此页面,因此 U2F 很可能无法工作(错误请求错误消息)。 如果您想使用安全密钥,请要求管理员设置安全 HTTPS 方法。 + + + + + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + Part-DB1\templates\security\U2F\u2f_login.html.twig:10 + Part-DB1\templates\security\U2F\u2f_register.html.twig:22 + + + r_u2f_two_factor.pressbutton + 请插入您的安全密钥并按下其按钮 + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + Part-DB1\templates\security\U2F\u2f_register.html.twig:3 + + + tfa_u2f.add_key.title + 添加安全密钥 + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + Part-DB1\templates\security\U2F\u2f_register.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:111 + + + tfa_u2f.explanation + 借助 U2F/FIDO 兼容的安全密钥(例如 YubiKey 或 NitroKey),可以实现用户友好且安全的2FA身份验证。 可以在此处注册安全密钥,如果需要两步验证,只需通过 USB 插入密钥或通过 NFC 在设备上输入密钥即可。 + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + Part-DB1\templates\security\U2F\u2f_register.html.twig:7 + + + tfa_u2f.add_key.backup_hint + 为了确保即使密钥丢失也能访问,建议注册第二个密钥作为备份并将其存放在安全的地方! + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + Part-DB1\templates\security\U2F\u2f_register.html.twig:16 + + + r_u2f_two_factor.name + 显示的密钥名称(例如备份) + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + Part-DB1\templates\security\U2F\u2f_register.html.twig:19 + + + tfa_u2f.add_key.add_button + 添加安全密钥 + + + + + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + Part-DB1\templates\security\U2F\u2f_register.html.twig:27 + + + tfa_u2f.add_key.back_to_settings + 返回设置 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + Part-DB1\templates\Statistics\statistics.html.twig:5 + Part-DB1\templates\Statistics\statistics.html.twig:8 + new + + + statistics.title + 统计数据 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:14 + Part-DB1\templates\Statistics\statistics.html.twig:14 + new + + + statistics.parts + 部件 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:19 + Part-DB1\templates\Statistics\statistics.html.twig:19 + new + + + statistics.data_structures + 数据结构 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:24 + Part-DB1\templates\Statistics\statistics.html.twig:24 + new + + + statistics.attachments + 附件 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + Part-DB1\templates\Statistics\statistics.html.twig:34 + Part-DB1\templates\Statistics\statistics.html.twig:59 + Part-DB1\templates\Statistics\statistics.html.twig:104 + new + + + statistics.property + 特性 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + Part-DB1\templates\Statistics\statistics.html.twig:35 + Part-DB1\templates\Statistics\statistics.html.twig:60 + Part-DB1\templates\Statistics\statistics.html.twig:105 + new + + + statistics.value + + + + + + Part-DB1\templates\Statistics\statistics.html.twig:40 + Part-DB1\templates\Statistics\statistics.html.twig:40 + new + + + statistics.distinct_parts_count + 部件总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:44 + Part-DB1\templates\Statistics\statistics.html.twig:44 + new + + + statistics.parts_instock_sum + 库存总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:48 + Part-DB1\templates\Statistics\statistics.html.twig:48 + new + + + statistics.parts_with_price + 部件总数(拥有价格) + + + + + Part-DB1\templates\Statistics\statistics.html.twig:65 + Part-DB1\templates\Statistics\statistics.html.twig:65 + new + + + statistics.categories_count + 类别总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:69 + Part-DB1\templates\Statistics\statistics.html.twig:69 + new + + + statistics.footprints_count + 封装总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:73 + Part-DB1\templates\Statistics\statistics.html.twig:73 + new + + + statistics.manufacturers_count + 制造商总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:77 + Part-DB1\templates\Statistics\statistics.html.twig:77 + new + + + statistics.storelocations_count + 存储位置总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:81 + Part-DB1\templates\Statistics\statistics.html.twig:81 + new + + + statistics.suppliers_count + 供应商总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:85 + Part-DB1\templates\Statistics\statistics.html.twig:85 + new + + + statistics.currencies_count + 货币种类总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:89 + Part-DB1\templates\Statistics\statistics.html.twig:89 + new + + + statistics.measurement_units_count + 度量单位总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:93 + Part-DB1\templates\Statistics\statistics.html.twig:93 + new + + + statistics.devices_count + 项目总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:110 + Part-DB1\templates\Statistics\statistics.html.twig:110 + new + + + statistics.attachment_types_count + 附件类型总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:114 + Part-DB1\templates\Statistics\statistics.html.twig:114 + new + + + statistics.all_attachments_count + 附件总数 + + + + + Part-DB1\templates\Statistics\statistics.html.twig:118 + Part-DB1\templates\Statistics\statistics.html.twig:118 + new + + + statistics.user_uploaded_attachments_count + 附件总数(用户上传) + + + + + Part-DB1\templates\Statistics\statistics.html.twig:122 + Part-DB1\templates\Statistics\statistics.html.twig:122 + new + + + statistics.private_attachments_count + 附件数量(私有) + + + + + Part-DB1\templates\Statistics\statistics.html.twig:126 + Part-DB1\templates\Statistics\statistics.html.twig:126 + new + + + statistics.external_attachments_count + 附件总数(外部) + + + + + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + Part-DB1\templates\Users\backup_codes.html.twig:3 + Part-DB1\templates\Users\backup_codes.html.twig:9 + + + tfa_backup.codes.title + 备份代码 + + + + + Part-DB1\templates\Users\backup_codes.html.twig:12 + Part-DB1\templates\Users\backup_codes.html.twig:12 + + + tfa_backup.codes.explanation + 打印这些代码并将其保存在安全的地方! + + + + + Part-DB1\templates\Users\backup_codes.html.twig:13 + Part-DB1\templates\Users\backup_codes.html.twig:13 + + + tfa_backup.codes.help + 如果您无法再使用身份验证器应用程序访问您的设备(智能手机丢失、数据丢失等),您可以使用这些代码之一来访问您的帐户,并可能设置一个新的身份验证器应用程序。 每个代码只能使用一次,建议删除已使用的代码。 有权访问这些代码的任何人都可能访问您的帐户,因此请将它们保存在安全的地方。 + + + + + Part-DB1\templates\Users\backup_codes.html.twig:16 + Part-DB1\templates\Users\backup_codes.html.twig:16 + + + tfa_backup.username + 用户名 + + + + + Part-DB1\templates\Users\backup_codes.html.twig:29 + Part-DB1\templates\Users\backup_codes.html.twig:29 + + + tfa_backup.codes.page_generated_on + 页面生成于 %date% + + + + + Part-DB1\templates\Users\backup_codes.html.twig:32 + Part-DB1\templates\Users\backup_codes.html.twig:32 + + + tfa_backup.codes.print + 打印 + + + + + Part-DB1\templates\Users\backup_codes.html.twig:35 + Part-DB1\templates\Users\backup_codes.html.twig:35 + + + tfa_backup.codes.copy_clipboard + 复制到剪贴板 + + + + + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:40 + Part-DB1\templates\Users\user_info.html.twig:3 + Part-DB1\templates\Users\user_info.html.twig:6 + Part-DB1\templates\_navbar.html.twig:38 + templates\base.html.twig:99 + templates\Users\user_info.html.twig:3 + templates\Users\user_info.html.twig:6 + + + user.info.label + 用户信息 + + + + + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + Part-DB1\templates\Users\user_info.html.twig:18 + Part-DB1\src\Form\UserSettingsType.php:77 + templates\Users\user_info.html.twig:18 + src\Form\UserSettingsType.php:32 + + + user.firstName.label + 名称 + + + + + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + Part-DB1\templates\Users\user_info.html.twig:24 + Part-DB1\src\Form\UserSettingsType.php:82 + templates\Users\user_info.html.twig:24 + src\Form\UserSettingsType.php:35 + + + user.lastName.label + 姓氏 + + + + + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + Part-DB1\templates\Users\user_info.html.twig:30 + Part-DB1\src\Form\UserSettingsType.php:92 + templates\Users\user_info.html.twig:30 + src\Form\UserSettingsType.php:41 + + + user.email.label + 邮件 + + + + + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + Part-DB1\templates\Users\user_info.html.twig:37 + Part-DB1\src\Form\UserSettingsType.php:87 + templates\Users\user_info.html.twig:37 + src\Form\UserSettingsType.php:38 + + + user.department.label + 部门 + + + + + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + Part-DB1\templates\Users\user_info.html.twig:47 + Part-DB1\src\Form\UserSettingsType.php:73 + templates\Users\user_info.html.twig:47 + src\Form\UserSettingsType.php:30 + + + user.username.label + 用户名 + + + + + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + Part-DB1\templates\Users\user_info.html.twig:53 + Part-DB1\src\Services\ElementTypeNameGenerator.php:93 + templates\Users\user_info.html.twig:53 + + + group.label + 组: + + + + + Part-DB1\templates\Users\user_info.html.twig:67 + Part-DB1\templates\Users\user_info.html.twig:67 + + + user.permissions + 权限 + + + + + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:39 + Part-DB1\templates\Users\user_settings.html.twig:3 + Part-DB1\templates\Users\user_settings.html.twig:6 + Part-DB1\templates\_navbar.html.twig:37 + templates\base.html.twig:98 + templates\Users\user_settings.html.twig:3 + templates\Users\user_settings.html.twig:6 + + + user.settings.label + 用户设置 + + + + + Part-DB1\templates\Users\user_settings.html.twig:18 + Part-DB1\templates\Users\user_settings.html.twig:18 + templates\Users\user_settings.html.twig:14 + + + user_settings.data.label + 个人资料 + + + + + Part-DB1\templates\Users\user_settings.html.twig:22 + Part-DB1\templates\Users\user_settings.html.twig:22 + templates\Users\user_settings.html.twig:18 + + + user_settings.configuration.label + 配置 + + + + + Part-DB1\templates\Users\user_settings.html.twig:55 + Part-DB1\templates\Users\user_settings.html.twig:55 + templates\Users\user_settings.html.twig:48 + + + user.settings.change_pw + 修改密码 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + Part-DB1\templates\Users\_2fa_settings.html.twig:6 + + + user.settings.2fa_settings + 2FA身份验证 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + Part-DB1\templates\Users\_2fa_settings.html.twig:13 + + + tfa.settings.google.tab + 验证器应用 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + Part-DB1\templates\Users\_2fa_settings.html.twig:17 + + + tfa.settings.bakup.tab + 备份代码 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + Part-DB1\templates\Users\_2fa_settings.html.twig:21 + + + tfa.settings.u2f.tab + 安全密钥 (U2F) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + Part-DB1\templates\Users\_2fa_settings.html.twig:25 + + + tfa.settings.trustedDevices.tab + 可信设备 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_title + 确定禁用身份验证器应用? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + Part-DB1\templates\Users\_2fa_settings.html.twig:33 + + + tfa_google.disable.confirm_message + 如果禁用验证器应用,所有备份代码将被删除。<br> +请注意,如果没有2FA身份验证,您的帐户就被攻击的风险会提供。 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + Part-DB1\templates\Users\_2fa_settings.html.twig:39 + + + tfa_google.disabled_message + 身份验证器应用已停用! + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + Part-DB1\templates\Users\_2fa_settings.html.twig:48 + + + tfa_google.step.download + 下载验证器应用(例如<a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> oder <a class="link-external" target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp">FreeOTP Authenticator</a>) + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + Part-DB1\templates\Users\_2fa_settings.html.twig:49 + + + tfa_google.step.scan + 使用应用程序扫描二维码或手动输入数据 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + Part-DB1\templates\Users\_2fa_settings.html.twig:50 + + + tfa_google.step.input_code + 在下方输入生成的代码并确认 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + Part-DB1\templates\Users\_2fa_settings.html.twig:51 + + + tfa_google.step.download_backup + 打印备份代码并妥善保存 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + Part-DB1\templates\Users\_2fa_settings.html.twig:58 + + + tfa_google.manual_setup + 手动设置 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + Part-DB1\templates\Users\_2fa_settings.html.twig:62 + + + tfa_google.manual_setup.type + 类型 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + Part-DB1\templates\Users\_2fa_settings.html.twig:63 + + + tfa_google.manual_setup.username + 用户名 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + Part-DB1\templates\Users\_2fa_settings.html.twig:64 + + + tfa_google.manual_setup.secret + 保密 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + Part-DB1\templates\Users\_2fa_settings.html.twig:65 + + + tfa_google.manual_setup.digit_count + 位数 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + Part-DB1\templates\Users\_2fa_settings.html.twig:74 + + + tfa_google.enabled_message + 身份验证器应用已启用 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + Part-DB1\templates\Users\_2fa_settings.html.twig:83 + + + tfa_backup.disabled + 备份代码已禁用。 设置验证器应用以启用备份代码。 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + Part-DB1\templates\Users\_2fa_settings.html.twig:84 + Part-DB1\templates\Users\_2fa_settings.html.twig:92 + + + tfa_backup.explanation + 即使身份验证器应用设备丢失,也可以使用备份代码来访问帐户。 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_title + 确认重置备份代码? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + Part-DB1\templates\Users\_2fa_settings.html.twig:88 + + + tfa_backup.reset_codes.confirm_message + 这将删除所有旧备份代码并生成新备份代码。操作不能被撤消。 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + Part-DB1\templates\Users\_2fa_settings.html.twig:91 + + + tfa_backup.enabled + 备份代码已启用 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + Part-DB1\templates\Users\_2fa_settings.html.twig:99 + + + tfa_backup.show_codes + 显示备份代码 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + Part-DB1\templates\Users\_2fa_settings.html.twig:114 + + + tfa_u2f.table_caption + 已注册的安全密钥 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + Part-DB1\templates\Users\_2fa_settings.html.twig:115 + + + tfa_u2f.delete_u2f.confirm_title + 确认删除安全密钥? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + Part-DB1\templates\Users\_2fa_settings.html.twig:116 + + + tfa_u2f.delete_u2f.confirm_message + 如果删除此密钥,将无法再使用此密钥登录。如果没有可用的安全密钥,2FA身份验证将被禁用。 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + Part-DB1\templates\Users\_2fa_settings.html.twig:123 + + + tfa_u2f.keys.name + 密钥名称 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + Part-DB1\templates\Users\_2fa_settings.html.twig:124 + + + tfa_u2f.keys.added_date + 注册日期 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + Part-DB1\templates\Users\_2fa_settings.html.twig:134 + + + tfa_u2f.key_delete + 删除密钥 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + Part-DB1\templates\Users\_2fa_settings.html.twig:141 + + + tfa_u2f.no_keys_registered + 尚未注册密钥。 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + Part-DB1\templates\Users\_2fa_settings.html.twig:144 + + + tfa_u2f.add_new_key + 注册新的安全密钥 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + Part-DB1\templates\Users\_2fa_settings.html.twig:148 + + + tfa_trustedDevices.explanation + 在检查第二因素时,可以将当前计算机标记为可信,因此不再需要对此计算机进行2FA检查。 +如果您执行此操作不正确或者计算机不再受信任,您可以在此处重置 <i>所有 </i>计算机的状态。 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + Part-DB1\templates\Users\_2fa_settings.html.twig:149 + + + tfa_trustedDevices.invalidate.confirm_title + 确认删除所有受信任的计算机? + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + Part-DB1\templates\Users\_2fa_settings.html.twig:150 + + + tfa_trustedDevices.invalidate.confirm_message + 必须在所有计算机上再次执行2FA身份验证。确保有可用的身份验证器应用设备。 + + + + + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + Part-DB1\templates\Users\_2fa_settings.html.twig:154 + + + tfa_trustedDevices.invalidate.btn + 重置受信任设备 + + + + + Part-DB1\templates\_navbar.html.twig:4 + Part-DB1\templates\_navbar.html.twig:4 + templates\base.html.twig:29 + + + sidebar.toggle + 切换侧边栏 + + + + + Part-DB1\templates\_navbar.html.twig:22 + + + navbar.scanner.link + 扫描器 + + + + + Part-DB1\templates\_navbar.html.twig:38 + Part-DB1\templates\_navbar.html.twig:36 + templates\base.html.twig:97 + + + user.loggedin.label + 登录: + + + + + Part-DB1\templates\_navbar.html.twig:44 + Part-DB1\templates\_navbar.html.twig:42 + templates\base.html.twig:103 + + + user.login + 登录 + + + + + Part-DB1\templates\_navbar.html.twig:50 + Part-DB1\templates\_navbar.html.twig:48 + + + ui.toggle_darkmode + 暗色模式 + + + + + Part-DB1\templates\_navbar.html.twig:54 + Part-DB1\src\Form\UserSettingsType.php:97 + Part-DB1\templates\_navbar.html.twig:52 + Part-DB1\src\Form\UserSettingsType.php:97 + templates\base.html.twig:106 + src\Form\UserSettingsType.php:44 + + + user.language_select + 切换语言 + + + + + Part-DB1\templates\_navbar_search.html.twig:4 + Part-DB1\templates\_navbar_search.html.twig:4 + templates\base.html.twig:49 + + + search.options.label + 搜索选项 + + + + + Part-DB1\templates\_navbar_search.html.twig:23 + + + tags.label + 标签 + + + + + Part-DB1\templates\_navbar_search.html.twig:27 + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + Part-DB1\src\Services\ElementTypeNameGenerator.php:88 + templates\base.html.twig:60 + templates\Parts\show_part_info.html.twig:36 + src\Form\PartType.php:77 + + + storelocation.label + 存储位置 + + + + + Part-DB1\templates\_navbar_search.html.twig:36 + Part-DB1\templates\_navbar_search.html.twig:31 + templates\base.html.twig:65 + + + ordernumber.label.short + 供应商合作伙伴 + + + + + Part-DB1\templates\_navbar_search.html.twig:40 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + Part-DB1\templates\_navbar_search.html.twig:35 + Part-DB1\src\Services\ElementTypeNameGenerator.php:89 + templates\base.html.twig:67 + + + supplier.label + 供应商 + + + + + Part-DB1\templates\_navbar_search.html.twig:57 + Part-DB1\templates\_navbar_search.html.twig:52 + templates\base.html.twig:75 + + + search.deactivateBarcode + 停用条码 + + + + + Part-DB1\templates\_navbar_search.html.twig:61 + Part-DB1\templates\_navbar_search.html.twig:56 + templates\base.html.twig:77 + + + search.regexmatching + 正则匹配 + + + + + Part-DB1\templates\_navbar_search.html.twig:68 + Part-DB1\templates\_navbar_search.html.twig:62 + + + search.submit + GO! + + + + + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + Part-DB1\templates\_sidebar.html.twig:37 + Part-DB1\templates\_sidebar.html.twig:12 + templates\base.html.twig:175 + templates\base.html.twig:189 + templates\base.html.twig:202 + templates\base.html.twig:230 + + + project.labelp + 项目 + + + + + Part-DB1\templates\_sidebar.html.twig:2 + Part-DB1\templates\_sidebar.html.twig:2 + templates\base.html.twig:165 + templates\base.html.twig:192 + templates\base.html.twig:220 + + + actions + 操作 + + + + + Part-DB1\templates\_sidebar.html.twig:6 + Part-DB1\templates\_sidebar.html.twig:6 + templates\base.html.twig:169 + templates\base.html.twig:196 + templates\base.html.twig:224 + + + datasource + 数据源 + + + + + Part-DB1\templates\_sidebar.html.twig:10 + Part-DB1\templates\_sidebar.html.twig:10 + templates\base.html.twig:173 + templates\base.html.twig:200 + templates\base.html.twig:228 + + + manufacturer.labelp + 制造商 + + + + + Part-DB1\templates\_sidebar.html.twig:11 + Part-DB1\templates\_sidebar.html.twig:11 + templates\base.html.twig:174 + templates\base.html.twig:201 + templates\base.html.twig:229 + + + supplier.labelp + 供应商 + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:213 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:293 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:293 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:181 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:243 + Part-DB1\src\Controller\PartController.php:173 + Part-DB1\src\Controller\PartController.php:268 + + + attachment.download_failed + 外部附件下载失败。 + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:222 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:190 + + + entity.edit_flash + 更改保存成功。 + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:231 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:196 + + + entity.edit_flash.invalid + 无法保存更改。请检查输入 + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:302 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:252 + + + entity.created_flash + 元素已创建。 + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:308 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:258 + + + entity.created_flash.invalid + 无法创建元素。请检查输入 + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:399 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:352 + src\Controller\BaseAdminController.php:154 + + + attachment_type.deleted + 元素已删除。 + + + + + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:401 + Part-DB1\src\Controller\UserController.php:109 + Part-DB1\src\Controller\UserSettingsController.php:159 + Part-DB1\src\Controller\UserSettingsController.php:193 + Part-DB1\src\Controller\AdminPages\BaseAdminController.php:354 + Part-DB1\src\Controller\UserController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:150 + Part-DB1\src\Controller\UserSettingsController.php:182 + + + csfr_invalid + CSRF Token 无效。如果此消息仍然存在,请重新加载此页面或联系管理员。 + + + + + Part-DB1\src\Controller\LabelController.php:125 + + + label_generator.no_entities_found + 未找到匹配的实体。 + + + + + Part-DB1\src\Controller\LogController.php:149 + Part-DB1\src\Controller\LogController.php:154 + new + + + log.undo.target_not_found + 在数据库中找不到目标元素。 + + + + + Part-DB1\src\Controller\LogController.php:156 + Part-DB1\src\Controller\LogController.php:160 + new + + + log.undo.revert_success + 已成功恢复时间戳。 + + + + + Part-DB1\src\Controller\LogController.php:176 + Part-DB1\src\Controller\LogController.php:180 + new + + + log.undo.element_undelete_success + 已成功取消删除元素。 + + + + + Part-DB1\src\Controller\LogController.php:178 + Part-DB1\src\Controller\LogController.php:182 + new + + + log.undo.element_element_already_undeleted + 元素已取消删除。 + + + + + Part-DB1\src\Controller\LogController.php:185 + Part-DB1\src\Controller\LogController.php:189 + new + + + log.undo.element_delete_success + 元素删除成功。 + + + + + Part-DB1\src\Controller\LogController.php:187 + Part-DB1\src\Controller\LogController.php:191 + new + + + log.undo.element.element_already_delted + 元素已被删除。 + + + + + Part-DB1\src\Controller\LogController.php:194 + Part-DB1\src\Controller\LogController.php:198 + new + + + log.undo.element_change_undone + 更改撤消成功。 + + + + + Part-DB1\src\Controller\LogController.php:196 + Part-DB1\src\Controller\LogController.php:200 + new + + + log.undo.do_undelete_before + 必须先取消删除该元素,才能撤消此更改。 + + + + + Part-DB1\src\Controller\LogController.php:199 + Part-DB1\src\Controller\LogController.php:203 + new + + + log.undo.log_type_invalid + 此日志条目无法撤消。 + + + + + Part-DB1\src\Controller\PartController.php:182 + Part-DB1\src\Controller\PartController.php:182 + src\Controller\PartController.php:80 + + + part.edited_flash + 已保存更改。 + + + + + Part-DB1\src\Controller\PartController.php:186 + Part-DB1\src\Controller\PartController.php:186 + + + part.edited_flash.invalid + 保存时出错。请检查输入 + + + + + Part-DB1\src\Controller\PartController.php:216 + Part-DB1\src\Controller\PartController.php:219 + + + part.deleted + 部件删除成功。 + + + + + Part-DB1\src\Controller\PartController.php:302 + Part-DB1\src\Controller\PartController.php:277 + Part-DB1\src\Controller\PartController.php:317 + src\Controller\PartController.php:113 + src\Controller\PartController.php:142 + + + part.created_flash + 部件已创建。 + + + + + Part-DB1\src\Controller\PartController.php:308 + Part-DB1\src\Controller\PartController.php:283 + + + part.created_flash.invalid + 创建过程中出错。请检查输入 + + + + + Part-DB1\src\Controller\ScanController.php:68 + Part-DB1\src\Controller\ScanController.php:90 + + + scan.qr_not_found + 未找到匹配条形码的元素。 + + + + + Part-DB1\src\Controller\ScanController.php:71 + + + scan.format_unknown + 格式未知。 + + + + + Part-DB1\src\Controller\ScanController.php:86 + + + scan.qr_success + 找到元素。 + + + + + Part-DB1\src\Controller\SecurityController.php:114 + Part-DB1\src\Controller\SecurityController.php:109 + + + pw_reset.user_or_email + 用户名或邮件 + + + + + Part-DB1\src\Controller\SecurityController.php:131 + Part-DB1\src\Controller\SecurityController.php:126 + + + pw_reset.request.success + 重置请求成功。请检查邮箱 + + + + + Part-DB1\src\Controller\SecurityController.php:162 + Part-DB1\src\Controller\SecurityController.php:160 + + + pw_reset.username + 用户名 + + + + + Part-DB1\src\Controller\SecurityController.php:165 + Part-DB1\src\Controller\SecurityController.php:163 + + + pw_reset.token + Token + + + + + Part-DB1\src\Controller\SecurityController.php:194 + Part-DB1\src\Controller\SecurityController.php:192 + + + pw_reset.new_pw.error + 用户名或Token无效。请检查输入 + + + + + Part-DB1\src\Controller\SecurityController.php:196 + Part-DB1\src\Controller\SecurityController.php:194 + + + pw_reset.new_pw.success + 密码重置成功。现在可以使用新密码登录。 + + + + + Part-DB1\src\Controller\UserController.php:107 + Part-DB1\src\Controller\UserController.php:99 + + + user.edit.reset_success + 成功禁用所有2FA身份验证。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:101 + Part-DB1\src\Controller\UserSettingsController.php:92 + + + tfa_backup.no_codes_enabled + 未启用备份代码。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:138 + Part-DB1\src\Controller\UserSettingsController.php:132 + + + tfa_u2f.u2f_delete.not_existing + 不存在此ID的安全密钥。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:145 + Part-DB1\src\Controller\UserSettingsController.php:139 + + + tfa_u2f.u2f_delete.access_denied + 无法删除其他用户的安全密钥。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:153 + Part-DB1\src\Controller\UserSettingsController.php:147 + + + tfa.u2f.u2f_delete.success + 成功删除安全密钥。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:188 + Part-DB1\src\Controller\UserSettingsController.php:180 + + + tfa_trustedDevice.invalidate.success + 成功重置受信任设备。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:235 + Part-DB1\src\Controller\UserSettingsController.php:226 + src\Controller\UserController.php:98 + + + user.settings.saved_flash + 设置已保存。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:297 + Part-DB1\src\Controller\UserSettingsController.php:288 + src\Controller\UserController.php:130 + + + user.settings.pw_changed_flash + 密码已更改。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:317 + Part-DB1\src\Controller\UserSettingsController.php:306 + + + user.settings.2fa.google.activated + 成功激活身份验证器应用。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:328 + Part-DB1\src\Controller\UserSettingsController.php:315 + + + user.settings.2fa.google.disabled + 成功停用身份验证器应用。 + + + + + Part-DB1\src\Controller\UserSettingsController.php:346 + Part-DB1\src\Controller\UserSettingsController.php:332 + + + user.settings.2fa.backup_codes.regenerated + 成功生成新备份代码。 + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + Part-DB1\src\DataTables\AttachmentDataTable.php:148 + + + attachment.table.filename + 文件名 + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + Part-DB1\src\DataTables\AttachmentDataTable.php:153 + + + attachment.table.filesize + 文件大小 + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:245 + Part-DB1\src\DataTables\PartsDataTable.php:252 + Part-DB1\src\DataTables\AttachmentDataTable.php:183 + Part-DB1\src\DataTables\AttachmentDataTable.php:191 + Part-DB1\src\DataTables\AttachmentDataTable.php:200 + Part-DB1\src\DataTables\AttachmentDataTable.php:209 + Part-DB1\src\DataTables\PartsDataTable.php:193 + Part-DB1\src\DataTables\PartsDataTable.php:200 + + + true + TRUE + + + + + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:246 + Part-DB1\src\DataTables\PartsDataTable.php:253 + Part-DB1\src\Form\Type\SIUnitType.php:139 + Part-DB1\src\DataTables\AttachmentDataTable.php:184 + Part-DB1\src\DataTables\AttachmentDataTable.php:192 + Part-DB1\src\DataTables\AttachmentDataTable.php:201 + Part-DB1\src\DataTables\AttachmentDataTable.php:210 + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:201 + Part-DB1\src\Form\Type\SIUnitType.php:139 + + + false + FALSE + + + + + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:128 + Part-DB1\src\DataTables\Column\LogEntryTargetColumn.php:119 + + + log.target_deleted + 已删除 + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:57 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:60 + new + + + log.undo.undelete + 撤销删除 + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:63 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:66 + new + + + log.undo.undo + 撤消更改 + + + + + Part-DB1\src\DataTables\Column\RevertLogColumn.php:83 + Part-DB1\src\DataTables\Column\RevertLogColumn.php:86 + new + + + log.undo.revert + 恢复到时间戳 + + + + + Part-DB1\src\DataTables\LogDataTable.php:173 + Part-DB1\src\DataTables\LogDataTable.php:161 + + + log.id + ID + + + + + Part-DB1\src\DataTables\LogDataTable.php:178 + Part-DB1\src\DataTables\LogDataTable.php:166 + + + log.timestamp + 时间戳 + + + + + Part-DB1\src\DataTables\LogDataTable.php:183 + Part-DB1\src\DataTables\LogDataTable.php:171 + + + log.type + 事件 + + + + + Part-DB1\src\DataTables\LogDataTable.php:191 + Part-DB1\src\DataTables\LogDataTable.php:179 + + + log.level + 等级 + + + + + Part-DB1\src\DataTables\LogDataTable.php:200 + Part-DB1\src\DataTables\LogDataTable.php:188 + + + log.user + 用户 + + + + + Part-DB1\src\DataTables\LogDataTable.php:213 + Part-DB1\src\DataTables\LogDataTable.php:201 + + + log.target_type + 目标类型 + + + + + Part-DB1\src\DataTables\LogDataTable.php:226 + Part-DB1\src\DataTables\LogDataTable.php:214 + + + log.target + 目标 + + + + + Part-DB1\src\DataTables\LogDataTable.php:231 + Part-DB1\src\DataTables\LogDataTable.php:218 + new + + + log.extra + 其他 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:168 + Part-DB1\src\DataTables\PartsDataTable.php:116 + + + part.table.name + 名称 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:178 + Part-DB1\src\DataTables\PartsDataTable.php:126 + + + part.table.id + Id + + + + + Part-DB1\src\DataTables\PartsDataTable.php:182 + Part-DB1\src\DataTables\PartsDataTable.php:130 + + + part.table.description + 描述 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:185 + Part-DB1\src\DataTables\PartsDataTable.php:133 + + + part.table.category + 类别 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:190 + Part-DB1\src\DataTables\PartsDataTable.php:138 + + + part.table.footprint + 封装 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:194 + Part-DB1\src\DataTables\PartsDataTable.php:142 + + + part.table.manufacturer + 制造商 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:197 + Part-DB1\src\DataTables\PartsDataTable.php:145 + + + part.table.storeLocations + 储存地点 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:216 + Part-DB1\src\DataTables\PartsDataTable.php:164 + + + part.table.amount + 数量 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:224 + Part-DB1\src\DataTables\PartsDataTable.php:172 + + + part.table.minamount + 最小数量 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:232 + Part-DB1\src\DataTables\PartsDataTable.php:180 + + + part.table.partUnit + 计量单位 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:236 + Part-DB1\src\DataTables\PartsDataTable.php:184 + + + part.table.addedDate + 创建时间 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:240 + Part-DB1\src\DataTables\PartsDataTable.php:188 + + + part.table.lastModified + 修改时间 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:244 + Part-DB1\src\DataTables\PartsDataTable.php:192 + + + part.table.needsReview + 需要审查 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:251 + Part-DB1\src\DataTables\PartsDataTable.php:199 + + + part.table.favorite + 收藏 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:258 + Part-DB1\src\DataTables\PartsDataTable.php:206 + + + part.table.manufacturingStatus + 状态 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:260 + Part-DB1\src\DataTables\PartsDataTable.php:262 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:208 + Part-DB1\src\DataTables\PartsDataTable.php:210 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.unknown + 未知 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:263 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:211 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.announced + 公布 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:264 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.active + 活动 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:265 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:213 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.nrfnd + 不推荐用于新设计 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:266 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:214 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.eol + 即将停产 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:267 + Part-DB1\src\Form\Part\PartBaseType.php:90 + Part-DB1\src\DataTables\PartsDataTable.php:215 + Part-DB1\src\Form\Part\PartBaseType.php:88 + + + m_status.discontinued + 停产 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:271 + Part-DB1\src\DataTables\PartsDataTable.php:219 + + + part.table.mpn + MPN + + + + + Part-DB1\src\DataTables\PartsDataTable.php:275 + Part-DB1\src\DataTables\PartsDataTable.php:223 + + + part.table.mass + 重量 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:279 + Part-DB1\src\DataTables\PartsDataTable.php:227 + + + part.table.tags + 标签 + + + + + Part-DB1\src\DataTables\PartsDataTable.php:283 + Part-DB1\src\DataTables\PartsDataTable.php:231 + + + part.table.attachments + 附件 + + + + + Part-DB1\src\EventSubscriber\UserSystem\LoginSuccessSubscriber.php:82 + Part-DB1\src\EventSubscriber\LoginSuccessListener.php:82 + + + flash.login_successful + 登陆成功 + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + JSON + JSON + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + XML + XML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + CSV + CSV + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:77 + Part-DB1\src\Form\AdminPages\ImportType.php:77 + src\Form\ImportType.php:68 + + + YAML + YAML + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:124 + Part-DB1\src\Form\AdminPages\ImportType.php:124 + + + import.abort_on_validation.help + 遇到无效数据时停止导入 + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:86 + Part-DB1\src\Form\AdminPages\ImportType.php:86 + src\Form\ImportType.php:70 + + + import.csv_separator + CSV分隔符 + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:93 + Part-DB1\src\Form\AdminPages\ImportType.php:93 + src\Form\ImportType.php:72 + + + parent.label + 父元素 + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:101 + Part-DB1\src\Form\AdminPages\ImportType.php:101 + src\Form\ImportType.php:75 + + + import.file + 文件 + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:111 + Part-DB1\src\Form\AdminPages\ImportType.php:111 + src\Form\ImportType.php:78 + + + import.preserve_children + 导入时保留子元素 + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:120 + Part-DB1\src\Form\AdminPages\ImportType.php:120 + src\Form\ImportType.php:80 + + + import.abort_on_validation + 遇到无效数据时中止 + + + + + Part-DB1\src\Form\AdminPages\ImportType.php:132 + Part-DB1\src\Form\AdminPages\ImportType.php:132 + src\Form\ImportType.php:85 + + + import.btn + 导入 + + + + + Part-DB1\src\Form\AttachmentFormType.php:113 + Part-DB1\src\Form\AttachmentFormType.php:109 + + + attachment.edit.secure_file.help + 私有附件只能通过授权的用户访问。私有附件不会生成缩略图,文件访问性能会降低。 + + + + + Part-DB1\src\Form\AttachmentFormType.php:127 + Part-DB1\src\Form\AttachmentFormType.php:123 + + + attachment.edit.url.help + 可以在此处指定外部文件的URL,或输入用于搜索内置资源的关键字 + + + + + Part-DB1\src\Form\AttachmentFormType.php:82 + Part-DB1\src\Form\AttachmentFormType.php:79 + + + attachment.edit.name + 名称 + + + + + Part-DB1\src\Form\AttachmentFormType.php:85 + Part-DB1\src\Form\AttachmentFormType.php:82 + + + attachment.edit.attachment_type + 附件类型 + + + + + Part-DB1\src\Form\AttachmentFormType.php:94 + Part-DB1\src\Form\AttachmentFormType.php:91 + + + attachment.edit.show_in_table + 显示在表中 + + + + + Part-DB1\src\Form\AttachmentFormType.php:105 + Part-DB1\src\Form\AttachmentFormType.php:102 + + + attachment.edit.secure_file + 私有附件 + + + + + Part-DB1\src\Form\AttachmentFormType.php:119 + Part-DB1\src\Form\AttachmentFormType.php:115 + + + attachment.edit.url + URL + + + + + Part-DB1\src\Form\AttachmentFormType.php:133 + Part-DB1\src\Form\AttachmentFormType.php:129 + + + attachment.edit.download_url + 下载外部文件 + + + + + Part-DB1\src\Form\AttachmentFormType.php:146 + Part-DB1\src\Form\AttachmentFormType.php:142 + + + attachment.edit.file + 上传文件 + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:86 + + + part.label + 部件 + + + + + Part-DB1\src\Form\LabelOptionsType.php:68 + Part-DB1\src\Services\ElementTypeNameGenerator.php:87 + + + part_lot.label + 部件批次 + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.none + + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.qr + 二维码(推荐) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code128 + Code 128(推荐) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code39 + Code 39(推荐) + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.code93 + Code 93 + + + + + Part-DB1\src\Form\LabelOptionsType.php:78 + + + label_options.barcode_type.datamatrix + 数据矩阵 + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label_options.lines_mode.html + 占位符 + + + + + Part-DB1\src\Form\LabelOptionsType.php:122 + + + label.options.lines_mode.twig + Twig + + + + + Part-DB1\src\Form\LabelOptionsType.php:126 + + + label_options.lines_mode.help + 如果您在此处选择Twig,则内容字段将被解释为Twig模板。前往 <a href="https://twig.symfony.com/doc/3.x/templates.html">Twig文档</a> and <a href="https://docs.part-db.de/usage/labels.html#twig-mode">Wiki</a> 了解更多信息。 + + + + + Part-DB1\src\Form\LabelOptionsType.php:47 + + + label_options.page_size.label + Label size + + + + + Part-DB1\src\Form\LabelOptionsType.php:66 + + + label_options.supported_elements.label + 目标类型 + + + + + Part-DB1\src\Form\LabelOptionsType.php:75 + + + label_options.barcode_type.label + 条码 + + + + + Part-DB1\src\Form\LabelOptionsType.php:102 + + + label_profile.lines.label + 内容 + + + + + Part-DB1\src\Form\LabelOptionsType.php:111 + + + label_options.additional_css.label + 附加样式(CSS) + + + + + Part-DB1\src\Form\LabelOptionsType.php:120 + + + label_options.lines_mode.label + 解析器模式 + + + + + Part-DB1\src\Form\LabelOptionsType.php:51 + + + label_options.width.placeholder + 宽度 + + + + + Part-DB1\src\Form\LabelOptionsType.php:60 + + + label_options.height.placeholder + 高度 + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:49 + + + label_generator.target_id.range_hint + 可以在此处指定多个ID(1、2、3 或 1-3) ,为多个元素生成标签。 + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:46 + + + label_generator.target_id.label + 目标 ID + + + + + Part-DB1\src\Form\LabelSystem\LabelDialogType.php:59 + + + label_generator.update + Update + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:36 + + + scan_dialog.input + 输入 + + + + + Part-DB1\src\Form\LabelSystem\ScanDialogType.php:44 + + + scan_dialog.submit + Submit + + + + + Part-DB1\src\Form\ParameterType.php:41 + + + parameters.name.placeholder + + + + + + Part-DB1\src\Form\ParameterType.php:50 + + + parameters.symbol.placeholder + + + + + + Part-DB1\src\Form\ParameterType.php:60 + + + parameters.text.placeholder + + + + + + Part-DB1\src\Form\ParameterType.php:71 + + + parameters.max.placeholder + + + + + + Part-DB1\src\Form\ParameterType.php:82 + + + parameters.min.placeholder + + + + + + Part-DB1\src\Form\ParameterType.php:93 + + + parameters.typical.placeholder + + + + + + Part-DB1\src\Form\ParameterType.php:103 + + + parameters.unit.placeholder + + + + + + Part-DB1\src\Form\ParameterType.php:114 + + + parameter.group.placeholder + + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:72 + Part-DB1\src\Form\Part\OrderdetailType.php:75 + + + orderdetails.edit.supplierpartnr + 供应商部件号 + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:81 + Part-DB1\src\Form\Part\OrderdetailType.php:84 + + + orderdetails.edit.supplier + 供应商 + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:87 + Part-DB1\src\Form\Part\OrderdetailType.php:90 + + + orderdetails.edit.url + 供应商链接 + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:93 + Part-DB1\src\Form\Part\OrderdetailType.php:96 + + + orderdetails.edit.obsolete + 不再可用 + + + + + Part-DB1\src\Form\Part\OrderdetailType.php:75 + Part-DB1\src\Form\Part\OrderdetailType.php:78 + + + orderdetails.edit.supplierpartnr.placeholder + + + + + + Part-DB1\src\Form\Part\PartBaseType.php:101 + Part-DB1\src\Form\Part\PartBaseType.php:99 + + + part.edit.name + 名称 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:109 + Part-DB1\src\Form\Part\PartBaseType.php:107 + + + part.edit.description + 描述 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:120 + Part-DB1\src\Form\Part\PartBaseType.php:118 + + + part.edit.mininstock + 最小库存 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:129 + Part-DB1\src\Form\Part\PartBaseType.php:127 + + + part.edit.category + 类别 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:135 + Part-DB1\src\Form\Part\PartBaseType.php:133 + + + part.edit.footprint + 封装 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:142 + Part-DB1\src\Form\Part\PartBaseType.php:140 + + + part.edit.tags + 标签 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:154 + Part-DB1\src\Form\Part\PartBaseType.php:152 + + + part.edit.manufacturer.label + 制造商 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:161 + Part-DB1\src\Form\Part\PartBaseType.php:159 + + + part.edit.manufacturer_url.label + 制造商链接 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:167 + Part-DB1\src\Form\Part\PartBaseType.php:165 + + + part.edit.mpn + 制造商部件号 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:173 + Part-DB1\src\Form\Part\PartBaseType.php:171 + + + part.edit.manufacturing_status + 生产状态 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:181 + Part-DB1\src\Form\Part\PartBaseType.php:179 + + + part.edit.needs_review + 需要审查 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:189 + Part-DB1\src\Form\Part\PartBaseType.php:187 + + + part.edit.is_favorite + 收藏 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:197 + Part-DB1\src\Form\Part\PartBaseType.php:195 + + + part.edit.mass + 重量 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:203 + Part-DB1\src\Form\Part\PartBaseType.php:201 + + + part.edit.partUnit + 计量单位 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:212 + Part-DB1\src\Form\Part\PartBaseType.php:210 + + + part.edit.comment + 注释 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:250 + Part-DB1\src\Form\Part\PartBaseType.php:246 + + + part.edit.master_attachment + 预览图像 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:295 + Part-DB1\src\Form\Part\PartBaseType.php:276 + src\Form\PartType.php:91 + + + part.edit.save + 保存更改 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:296 + Part-DB1\src\Form\Part\PartBaseType.php:277 + src\Form\PartType.php:92 + + + part.edit.reset + 重置更改 + + + + + Part-DB1\src\Form\Part\PartBaseType.php:105 + Part-DB1\src\Form\Part\PartBaseType.php:103 + + + part.edit.name.placeholder + + + + + + Part-DB1\src\Form\Part\PartBaseType.php:115 + Part-DB1\src\Form\Part\PartBaseType.php:113 + + + part.edit.description.placeholder + + + + + + Part-DB1\src\Form\Part\PartBaseType.php:123 + Part-DB1\src\Form\Part\PartBaseType.php:121 + + + part.editmininstock.placeholder + + + + + + Part-DB1\src\Form\Part\PartLotType.php:69 + Part-DB1\src\Form\Part\PartLotType.php:69 + + + part_lot.edit.description + 描述 + + + + + Part-DB1\src\Form\Part\PartLotType.php:78 + Part-DB1\src\Form\Part\PartLotType.php:78 + + + part_lot.edit.location + 存储位置 + + + + + Part-DB1\src\Form\Part\PartLotType.php:89 + Part-DB1\src\Form\Part\PartLotType.php:89 + + + part_lot.edit.amount + 数量 + + + + + Part-DB1\src\Form\Part\PartLotType.php:98 + Part-DB1\src\Form\Part\PartLotType.php:97 + + + part_lot.edit.instock_unknown + 数量未知 + + + + + Part-DB1\src\Form\Part\PartLotType.php:109 + Part-DB1\src\Form\Part\PartLotType.php:108 + + + part_lot.edit.needs_refill + 需要补充 + + + + + Part-DB1\src\Form\Part\PartLotType.php:120 + Part-DB1\src\Form\Part\PartLotType.php:119 + + + part_lot.edit.expiration_date + 有效期 + + + + + Part-DB1\src\Form\Part\PartLotType.php:128 + Part-DB1\src\Form\Part\PartLotType.php:125 + + + part_lot.edit.comment + 注释 + + + + + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + Part-DB1\src\Form\Permissions\PermissionsType.php:99 + + + perm.group.other + 杂项 + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + Part-DB1\src\Form\TFAGoogleSettingsType.php:97 + + + tfa_google.enable + 启用验证器应用 + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + Part-DB1\src\Form\TFAGoogleSettingsType.php:101 + + + tfa_google.disable + 停用验证器应用 + + + + + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + Part-DB1\src\Form\TFAGoogleSettingsType.php:74 + + + google_confirmation + 验证码 + + + + + Part-DB1\src\Form\UserSettingsType.php:108 + Part-DB1\src\Form\UserSettingsType.php:108 + src\Form\UserSettingsType.php:46 + + + user.timezone.label + 时区 + + + + + Part-DB1\src\Form\UserSettingsType.php:133 + Part-DB1\src\Form\UserSettingsType.php:132 + + + user.currency.label + 首选货币 + + + + + Part-DB1\src\Form\UserSettingsType.php:140 + Part-DB1\src\Form\UserSettingsType.php:139 + src\Form\UserSettingsType.php:53 + + + save + 应用更改 + + + + + Part-DB1\src\Form\UserSettingsType.php:141 + Part-DB1\src\Form\UserSettingsType.php:140 + src\Form\UserSettingsType.php:54 + + + reset + 放弃更改 + + + + + Part-DB1\src\Form\UserSettingsType.php:104 + Part-DB1\src\Form\UserSettingsType.php:104 + src\Form\UserSettingsType.php:45 + + + user_settings.language.placeholder + 默认语言 + + + + + Part-DB1\src\Form\UserSettingsType.php:115 + Part-DB1\src\Form\UserSettingsType.php:115 + src\Form\UserSettingsType.php:48 + + + user_settings.timezone.placeholder + 默认时区 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + Part-DB1\src\Services\ElementTypeNameGenerator.php:79 + + + attachment.label + 附件 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + Part-DB1\src\Services\ElementTypeNameGenerator.php:81 + + + attachment_type.label + 附件类型 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + Part-DB1\src\Services\ElementTypeNameGenerator.php:82 + + + project.label + 项目 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + Part-DB1\src\Services\ElementTypeNameGenerator.php:85 + + + measurement_unit.label + 计量单位 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + Part-DB1\src\Services\ElementTypeNameGenerator.php:90 + + + currency.label + 货币 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + Part-DB1\src\Services\ElementTypeNameGenerator.php:91 + + + orderdetail.label + 订单详情 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + Part-DB1\src\Services\ElementTypeNameGenerator.php:92 + + + pricedetail.label + 价格详情 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + Part-DB1\src\Services\ElementTypeNameGenerator.php:94 + + + user.label + 用户 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:95 + + + parameter.label + 参数 + + + + + Part-DB1\src\Services\ElementTypeNameGenerator.php:96 + + + label_profile.label + 标签配置 + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:176 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:161 + new + + + log.element_deleted.old_name.unknown + 未知 + + + + + Part-DB1\src\Services\MarkdownParser.php:73 + Part-DB1\src\Services\MarkdownParser.php:73 + + + markdown.loading + 正在加载 Markdown。如果此消息没有消失,请尝试重新加载页面。 + + + + + Part-DB1\src\Services\PasswordResetManager.php:98 + Part-DB1\src\Services\PasswordResetManager.php:98 + + + pw_reset.email.subject + 重置密码 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + + + tree.tools.tools + 工具 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:107 + src\Services\ToolsTreeBuilder.php:74 + + + tree.tools.edit + 编辑 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:110 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:108 + src\Services\ToolsTreeBuilder.php:81 + + + tree.tools.show + 统计 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:111 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:109 + + + tree.tools.system + 系统 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:123 + + + tree.tools.tools.label_dialog + 标签生成器 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:130 + + + tree.tools.tools.label_scanner + 扫描器 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:149 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:126 + src\Services\ToolsTreeBuilder.php:62 + + + tree.tools.edit.attachment_types + 附件类型 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:155 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:132 + src\Services\ToolsTreeBuilder.php:64 + + + tree.tools.edit.categories + 类别 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:161 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:138 + src\Services\ToolsTreeBuilder.php:66 + + + tree.tools.edit.projects + 项目 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:167 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:144 + src\Services\ToolsTreeBuilder.php:68 + + + tree.tools.edit.suppliers + 供应商 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:173 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:150 + src\Services\ToolsTreeBuilder.php:70 + + + tree.tools.edit.manufacturer + 制造商 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:179 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:156 + + + tree.tools.edit.storelocation + 储存位置 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:185 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:162 + + + tree.tools.edit.footprint + 封装 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:191 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:168 + + + tree.tools.edit.currency + 货币 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:174 + + + tree.tools.edit.measurement_unit + 计量单位 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.edit.label_profile + 标签配置 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:209 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:180 + + + tree.tools.edit.part + 新建部件 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:226 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:197 + src\Services\ToolsTreeBuilder.php:77 + + + tree.tools.show.all_parts + 所有部件 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:232 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203 + + + tree.tools.show.all_attachments + 所有附件 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:239 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:210 + new + + + tree.tools.show.statistics + 统计数据 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:258 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:229 + + + tree.tools.system.users + 用户 + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:264 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:235 + + + tree.tools.system.groups + + + + + + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:271 + Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:242 + new + + + tree.tools.system.event_log + 操作日志 + + + + + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + Part-DB1\src\Services\Trees\TreeViewGenerator.php:95 + src\Services\TreeBuilder.php:124 + + + entity.tree.new + 新建 + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:34 + obsolete + + + attachment.external_file + 外部文件 + + + + + Part-DB1\templates\Parts\info\_attachments_info.html.twig:62 + obsolete + + + attachment.edit + 编辑 + + + + + Part-DB1\templates\_navbar.html.twig:27 + templates\base.html.twig:88 + obsolete + + + barcode.scan + 扫描条码 + + + + + Part-DB1\src\Form\UserSettingsType.php:119 + src\Form\UserSettingsType.php:49 + obsolete + + + user.theme.label + 主题 + + + + + Part-DB1\src\Form\UserSettingsType.php:129 + src\Form\UserSettingsType.php:50 + obsolete + + + user_settings.theme.placeholder + 默认主题 + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:100 + new + obsolete + + + log.user_login.ip + IP + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:128 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:150 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:169 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:207 + new + obsolete + + + log.undo_mode.undo + 更改已撤消 + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:130 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:152 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:171 + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:209 + new + obsolete + + + log.undo_mode.revert + 元素已恢复 + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:139 + new + obsolete + + + log.element_created.original_instock + 旧库存 + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:160 + new + obsolete + + + log.element_deleted.old_name + 旧名称 + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:184 + new + obsolete + + + log.element_edited.changed_fields + 变更字段 + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:198 + new + obsolete + + + log.instock_changed.comment + 批注 + + + + + Part-DB1\src\Services\LogSystem\LogEntryExtraFormatter.php:214 + new + obsolete + + + log.collection_deleted.deleted + 已删除元素: + + + + + templates\base.html.twig:81 + obsolete + obsolete + + + go.exclamation + GO! + + + + + templates\base.html.twig:109 + obsolete + obsolete + + + language.english + 英语 + + + + + templates\base.html.twig:112 + obsolete + obsolete + + + language.german + 德语 + + + + + obsolete + obsolete + + + flash.password_change_needed + 需要修改密码。 + + + + + obsolete + obsolete + + + attachment.table.type + 附件类型 + + + + + obsolete + obsolete + + + attachment.table.element + 关联元素 + + + + + obsolete + obsolete + + + attachment.edit.isPicture + 图片? + + + + + obsolete + obsolete + + + attachment.edit.is3DModel + 3D模型? + + + + + obsolete + obsolete + + + attachment.edit.isBuiltin + 内置? + + + + + obsolete + obsolete + + + category.edit.default_comment.placeholder + + + + + + obsolete + obsolete + + + tfa_backup.regenerate_codes + 生成新备份代码 + + + + + obsolete + obsolete + + + validator.noneofitschild.self + 子元素不能是它的父元素。 + + + + + obsolete + obsolete + + + validator.noneofitschild.children + 父元素不能是它的子元素。 + + + + + obsolete + obsolete + + + validator.part_lot.location_full.no_increasment + 存储位置已标记为已满,无法增加库存量。(新的库存上限 {{ old_amount }}) + + + + + obsolete + obsolete + + + validator.part_lot.location_full + 存储位置已标记为已满,无法添加新部件。 + + + + + obsolete + obsolete + + + validator.part_lot.only_existing + 存储位置被标记为 "仅现有",无法添加新部件。 + + + + + obsolete + obsolete + + + validator.part_lot.single_part + 存储位置被标记为 "单部件", 无法添加新部件 + + + + + obsolete + obsolete + + + m_status.active.help + 该部件在目前和预期未来都在生产中 + + + + + obsolete + obsolete + + + m_status.announced.help + 该部件已公布,但尚未发布。 + + + + + obsolete + obsolete + + + m_status.discontinued.help + 该部件已停产。 + + + + + obsolete + obsolete + + + m_status.eol.help + 该部件即将停产。 + + + + + obsolete + obsolete + + + m_status.nrfnd.help + 不建议用于新设计。 + + + + + obsolete + obsolete + + + m_status.unknown.help + 生产状态未知。 + + + + + obsolete + obsolete + + + flash.success + 成功 + + + + + obsolete + obsolete + + + flash.error + 错误 + + + + + obsolete + obsolete + + + flash.warning + 警告 + + + + + obsolete + obsolete + + + flash.notice + 注意 + + + + + obsolete + obsolete + + + flash.info + 信息 + + + + + obsolete + obsolete + + + validator.noLockout + 不能撤回自己的 "变更权限" 的权限, 以防止锁定自己。 + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter + 允许的文件扩展名 + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.help + 您可以指定以逗号分隔的文件扩展名或MIME类型,上传文件的类型在分配给此附件类型时必须在其中。要允许所有支持的图像文件,使用 image/*。 + + + + + obsolete + obsolete + + + attachment_type.edit.filetype_filter.placeholder + + + + + + src\Form\PartType.php:63 + obsolete + obsolete + + + part.name.placeholder + + + + + + obsolete + obsolete + + + entity.edit.not_selectable + 不可选择 + + + + + obsolete + obsolete + + + entity.edit.not_selectable.help + 禁止将此元素分配给部件。适用于将元素仅用于分组。 + + + + + obsolete + obsolete + + + bbcode.hint + 可以在此处使用BBCode + + + + + obsolete + obsolete + + + entity.create + 创建元素 + + + + + obsolete + obsolete + + + entity.edit.save + 保存 + + + + + obsolete + obsolete + + + category.edit.disable_footprints + 禁用封装 + + + + + obsolete + obsolete + + + category.edit.disable_footprints.help + 禁用该类别中部件的封装属性。 + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers + 禁用制造商 + + + + + obsolete + obsolete + + + category.edit.disable_manufacturers.help + 禁用该类别中部件的制造商属性。 + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets + 禁用自动数据文档链接 + + + + + obsolete + obsolete + + + category.edit.disable_autodatasheets.help + 禁用该类别中部件的 DataSheet 自动链接。 + + + + + obsolete + obsolete + + + category.edit.disable_properties + 禁用属性 + + + + + obsolete + obsolete + + + category.edit.disable_properties.help + 禁用该类别中部件的属性。 + + + + + obsolete + obsolete + + + category.edit.partname_hint + 部件名称提示 + + + + + obsolete + obsolete + + + category.edit.partname_hint.placeholder + + + + + + obsolete + obsolete + + + category.edit.partname_regex + 名称过滤器 + + + + + obsolete + obsolete + + + category.edit.default_description + 默认描述 + + + + + obsolete + obsolete + + + category.edit.default_description.placeholder + + + + + + obsolete + obsolete + + + category.edit.default_comment + 默认注释 + + + + + obsolete + obsolete + + + company.edit.address + 地址 + + + + + obsolete + obsolete + + + company.edit.address.placeholder + + + + + + obsolete + obsolete + + + company.edit.phone_number + 电话 + + + + + obsolete + obsolete + + + company.edit.phone_number.placeholder + + + + + + obsolete + obsolete + + + company.edit.fax_number + 传真 + + + + + obsolete + obsolete + + + company.edit.email + 邮件 + + + + + obsolete + obsolete + + + company.edit.email.placeholder + + + + + + obsolete + obsolete + + + company.edit.website + 网站 + + + + + obsolete + obsolete + + + company.edit.website.placeholder + + + + + + obsolete + obsolete + + + company.edit.auto_product_url + 产品链接 + + + + + obsolete + obsolete + + + company.edit.auto_product_url.help + 指向制造商网站的部件链接。 + + + + + obsolete + obsolete + + + company.edit.auto_product_url.placeholder + + + + + + obsolete + obsolete + + + currency.edit.iso_code + ISO代码 + + + + + obsolete + obsolete + + + currency.edit.exchange_rate + 汇率 + + + + + obsolete + obsolete + + + footprint.edit.3d_model + 3D模型 + + + + + obsolete + obsolete + + + mass_creation.lines + 输入 + + + + + obsolete + obsolete + + + mass_creation.lines.placeholder + Element 1 + Element 1.1 + Element 1.1.1 + Element 1.2 +Element 2 +Element 3 + + + + + obsolete + obsolete + + + entity.mass_creation.btn + 创建 + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer + 整数 + + + + + obsolete + obsolete + + + measurement_unit.edit.is_integer.help + 输入的值将四舍五入为整数 + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix + 使用SI前缀 + + + + + obsolete + obsolete + + + measurement_unit.edit.use_si_prefix.help + 输出带有 SI 前缀的值(例如 1,2kg 而不是 1200g) + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol + 单位符号 + + + + + obsolete + obsolete + + + measurement_unit.edit.unit_symbol.placeholder + + + + + + obsolete + obsolete + + + storelocation.edit.is_full.label + 存储位置已满 + + + + + obsolete + obsolete + + + storelocation.edit.is_full.help + 禁止添加新部件,禁止增加已有部件的数量。 + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.label + 仅限当前部件 + + + + + obsolete + obsolete + + + storelocation.limit_to_existing.help + 禁止添加新部件。 + + + + + obsolete + obsolete + + + storelocation.only_single_part.label + 仅单个部分 + + + + + obsolete + obsolete + + + storelocation.only_single_part.help + 只允许存在一个部件 + + + + + obsolete + obsolete + + + storelocation.storage_type.label + 存储类型 + + + + + obsolete + obsolete + + + storelocation.storage_type.help + 选择计量单位,部件必须满足才能分配到该储存位置 + + + + + obsolete + obsolete + + + supplier.edit.default_currency + 默认货币 + + + + + obsolete + obsolete + + + supplier.shipping_costs.label + 运输费 + + + + + obsolete + obsolete + + + user.username.placeholder + + + + + + obsolete + obsolete + + + user.firstName.placeholder + + + + + + obsolete + obsolete + + + user.lastName.placeholder + + + + + + obsolete + obsolete + + + user.email.placeholder + + + + + + obsolete + obsolete + + + user.department.placeholder + + + + + + obsolete + obsolete + + + user.settings.pw_new.label + 新密码 + + + + + obsolete + obsolete + + + user.settings.pw_confirm.label + 确认新密码 + + + + + obsolete + obsolete + + + user.edit.needs_pw_change + 用户需要更改密码 + + + + + obsolete + obsolete + + + user.edit.user_disabled + 用户已禁用(无法登录) + + + + + obsolete + obsolete + + + user.create + 创建用户 + + + + + obsolete + obsolete + + + user.edit.save + 保存 + + + + + obsolete + obsolete + + + entity.edit.reset + 放弃更改 + + + + + templates\Parts\show_part_info.html.twig:166 + obsolete + obsolete + + + part.withdraw.btn + 提取 + + + + + templates\Parts\show_part_info.html.twig:171 + obsolete + obsolete + + + part.withdraw.comment: + 注解/目的 + + + + + templates\Parts\show_part_info.html.twig:189 + obsolete + obsolete + + + part.add.caption + 添加部件 + + + + + templates\Parts\show_part_info.html.twig:194 + obsolete + obsolete + + + part.add.btn + 增加 + + + + + templates\Parts\show_part_info.html.twig:199 + obsolete + obsolete + + + part.add.comment + 注解/目的 + + + + + templates\AdminPages\CompanyAdminBase.html.twig:15 + obsolete + obsolete + + + admin.comment + 注释 + + + + + src\Form\PartType.php:83 + obsolete + obsolete + + + manufacturer_url.label + 制造商链接 + + + + + src\Form\PartType.php:66 + obsolete + obsolete + + + part.description.placeholder + + + + + + src\Form\PartType.php:69 + obsolete + obsolete + + + part.instock.placeholder + + + + + + src\Form\PartType.php:72 + obsolete + obsolete + + + part.mininstock.placeholder + + + + + + obsolete + obsolete + + + part.order.price_per + 每件价格 + + + + + obsolete + obsolete + + + part.withdraw.caption + 取出零件 + + + + + obsolete + obsolete + + + datatable.datatable.lengthMenu + _MENU_ + + + + + obsolete + obsolete + + + perm.group.parts + 部件 + + + + + obsolete + obsolete + + + perm.group.structures + 数据结构 + + + + + obsolete + obsolete + + + perm.group.system + 系统 + + + + + obsolete + obsolete + + + perm.parts + 部件 + + + + + obsolete + obsolete + + + perm.read + 查看 + + + + + obsolete + obsolete + + + perm.edit + 编辑 + + + + + obsolete + obsolete + + + perm.create + 创建 + + + + + obsolete + obsolete + + + perm.part.move + 更改类别 + + + + + obsolete + obsolete + + + perm.delete + 删除 + + + + + obsolete + obsolete + + + perm.part.search + 搜索 + + + + + obsolete + obsolete + + + perm.part.all_parts + 列出所有部件 + + + + + obsolete + obsolete + + + perm.part.no_price_parts + 列出没有价格信息的部件 + + + + + obsolete + obsolete + + + perm.part.obsolete_parts + 列出过时的部件 + + + + + obsolete + obsolete + + + perm.part.unknown_instock_parts + 显示库存未知的部件 + + + + + obsolete + obsolete + + + perm.part.change_favorite + 更改收藏状态 + + + + + obsolete + obsolete + + + perm.part.show_favorite + 列出收藏的部件 + + + + + obsolete + obsolete + + + perm.part.show_last_edit_parts + 显示最后编辑/添加的部件 + + + + + obsolete + obsolete + + + perm.part.show_users + 显示最后修改的用户 + + + + + obsolete + obsolete + + + perm.part.show_history + 显示历史记录 + + + + + obsolete + obsolete + + + perm.part.name + 名称 + + + + + obsolete + obsolete + + + perm.part.description + 描述 + + + + + obsolete + obsolete + + + perm.part.instock + 在库 + + + + + obsolete + obsolete + + + perm.part.mininstock + 最小库存 + + + + + obsolete + obsolete + + + perm.part.comment + 注释 + + + + + obsolete + obsolete + + + perm.part.storelocation + 存储位置 + + + + + obsolete + obsolete + + + perm.part.manufacturer + 制造商 + + + + + obsolete + obsolete + + + perm.part.orderdetails + 订单信息 + + + + + obsolete + obsolete + + + perm.part.prices + 价格 + + + + + obsolete + obsolete + + + perm.part.attachments + 附件 + + + + + obsolete + obsolete + + + perm.part.order + 订单 + + + + + obsolete + obsolete + + + perm.storelocations + 储存位置 + + + + + obsolete + obsolete + + + perm.move + 移动 + + + + + obsolete + obsolete + + + perm.list_parts + 列出部件 + + + + + obsolete + obsolete + + + perm.part.footprints + 封装 + + + + + obsolete + obsolete + + + perm.part.categories + 类别 + + + + + obsolete + obsolete + + + perm.part.supplier + 供应商 + + + + + obsolete + obsolete + + + perm.part.manufacturers + 制造商 + + + + + obsolete + obsolete + + + perm.projects + 项目 + + + + + obsolete + obsolete + + + perm.part.attachment_types + 附件类型 + + + + + obsolete + obsolete + + + perm.tools.import + 导入 + + + + + obsolete + obsolete + + + perm.tools.labels + 标签 + + + + + obsolete + obsolete + + + perm.tools.calculator + 电阻计算器 + + + + + obsolete + obsolete + + + perm.tools.footprints + 封装 + + + + + obsolete + obsolete + + + perm.tools.ic_logos + 芯片图标 + + + + + obsolete + obsolete + + + perm.tools.statistics + 统计数据 + + + + + obsolete + obsolete + + + perm.edit_permissions + 编辑权限 + + + + + obsolete + obsolete + + + perm.users.edit_user_name + 编辑用户名 + + + + + obsolete + obsolete + + + perm.users.edit_change_group + 更改组 + + + + + obsolete + obsolete + + + perm.users.edit_infos + 编辑信息 + + + + + obsolete + obsolete + + + perm.users.edit_permissions + 编辑权限 + + + + + obsolete + obsolete + + + perm.users.set_password + 设置密码 + + + + + obsolete + obsolete + + + perm.users.change_user_settings + 更改用户设置 + + + + + obsolete + obsolete + + + perm.database.see_status + 显示状态 + + + + + obsolete + obsolete + + + perm.database.update_db + 更新数据库 + + + + + obsolete + obsolete + + + perm.database.read_db_settings + 读取数据库设置 + + + + + obsolete + obsolete + + + perm.database.write_db_settings + 写入数据库设置 + + + + + obsolete + obsolete + + + perm.config.read_config + 读取配置 + + + + + obsolete + obsolete + + + perm.config.edit_config + 编辑配置 + + + + + obsolete + obsolete + + + perm.config.server_info + 服务器信息 + + + + + obsolete + obsolete + + + perm.config.use_debug + 使用调试工具 + + + + + obsolete + obsolete + + + perm.show_logs + 显示日志 + + + + + obsolete + obsolete + + + perm.delete_logs + 删除日志 + + + + + obsolete + obsolete + + + perm.self.edit_infos + 编辑信息 + + + + + obsolete + obsolete + + + perm.self.edit_username + 编辑用户名 + + + + + obsolete + obsolete + + + perm.self.show_permissions + 查看权限 + + + + + obsolete + obsolete + + + perm.self.show_logs + 显示自己的日志条目 + + + + + obsolete + obsolete + + + perm.self.create_labels + 创建标签 + + + + + obsolete + obsolete + + + perm.self.edit_options + 编辑选项 + + + + + obsolete + obsolete + + + perm.self.delete_profiles + 删除配置 + + + + + obsolete + obsolete + + + perm.self.edit_profiles + 编辑配置 + + + + + obsolete + obsolete + + + perm.part.tools + 工具 + + + + + obsolete + obsolete + + + perm.groups + + + + + + obsolete + obsolete + + + perm.users + 用户 + + + + + obsolete + obsolete + + + perm.database + 数据库 + + + + + obsolete + obsolete + + + perm.config + 配置 + + + + + obsolete + obsolete + + + perm.system + 系统 + + + + + obsolete + obsolete + + + perm.self + 自己 + + + + + obsolete + obsolete + + + perm.labels + 标签 + + + + + obsolete + obsolete + + + perm.part.category + 类别 + + + + + obsolete + obsolete + + + perm.part.minamount + 最低数量 + + + + + obsolete + obsolete + + + perm.part.footprint + 封装 + + + + + obsolete + obsolete + + + perm.part.mpn + MPN + + + + + obsolete + obsolete + + + perm.part.status + 生产状态 + + + + + obsolete + obsolete + + + perm.part.tags + 标签 + + + + + obsolete + obsolete + + + perm.part.unit + 部件单位 + + + + + obsolete + obsolete + + + perm.part.mass + 重量 + + + + + obsolete + obsolete + + + perm.part.lots + 部件批次 + + + + + obsolete + obsolete + + + perm.show_users + 显示最后修改的用户 + + + + + obsolete + obsolete + + + perm.currencies + 货币 + + + + + obsolete + obsolete + + + perm.measurement_units + 计量单位 + + + + + obsolete + obsolete + + + user.settings.pw_old.label + 旧密码 + + + + + obsolete + obsolete + + + pw_reset.submit + 重设密码 + + + + + obsolete + obsolete + + + u2f_two_factor + 安全密钥 (U2F) + + + + + obsolete + obsolete + + + google + Google + + + + + tfa.provider.webauthn_two_factor_provider + 安全密钥 + + + + + obsolete + obsolete + + + tfa.provider.google + 验证器应用 + + + + + obsolete + obsolete + + + Login successful + 登陆成功 + + + + + obsolete + obsolete + + + log.type.exception + 未处理的异常(已过时) + + + + + obsolete + obsolete + + + log.type.user_login + 用户登录 + + + + + obsolete + obsolete + + + log.type.user_logout + 用户注销 + + + + + obsolete + obsolete + + + log.type.unknown + 未知 + + + + + obsolete + obsolete + + + log.type.element_created + 元素已创建 + + + + + obsolete + obsolete + + + log.type.element_edited + 元素已编辑 + + + + + obsolete + obsolete + + + log.type.element_deleted + 元素已删除 + + + + + obsolete + obsolete + + + log.type.database_updated + 数据库已更新 + + + + + obsolete + + + perm.revert_elements + 恢复元素 + + + + + obsolete + + + perm.show_history + 显示历史记录 + + + + + obsolete + + + perm.tools.lastActivity + 显示最近活动 + + + + + obsolete + + + perm.tools.timeTravel + 显示旧元素版本(时间旅行) + + + + + obsolete + + + tfa_u2f.key_added_successful + 安全密钥添加成功。 + + + + + obsolete + + + Username + 用户名 + + + + + obsolete + + + log.type.security.google_disabled + 身份验证器应用已禁用 + + + + + obsolete + + + log.type.security.u2f_removed + 已移除安全密钥 + + + + + obsolete + + + log.type.security.u2f_added + 已添加安全密钥 + + + + + obsolete + + + log.type.security.backup_keys_reset + 重新生成备份密钥 + + + + + obsolete + + + log.type.security.google_enabled + 身份验证器应用已启用 + + + + + obsolete + + + log.type.security.password_changed + 密码已更改 + + + + + obsolete + + + log.type.security.trusted_device_reset + 已重置可信设备 + + + + + obsolete + + + log.type.collection_element_deleted + 集合元素已删除 + + + + + obsolete + + + log.type.security.password_reset + 重设密码 + + + + + obsolete + + + log.type.security.2fa_admin_reset + 管理员进行了2FA重置 + + + + + obsolete + + + log.type.user_not_allowed + 未经授权的访问尝试 + + + + + obsolete + + + log.database_updated.success + 成功 + + + + + obsolete + + + label_options.barcode_type.2D + 2D + + + + + obsolete + + + label_options.barcode_type.1D + 1D + + + + + obsolete + + + perm.part.parameters + 参数 + + + + + obsolete + + + perm.attachment_show_private + 查看私有附件 + + + + + obsolete + + + perm.tools.label_scanner + 标签扫描仪 + + + + + obsolete + + + perm.self.read_profiles + 读取配置 + + + + + obsolete + + + perm.self.create_profiles + 创建配置 + + + + + obsolete + + + perm.labels.use_twig + 使用TWIG模式 + + + + + label_profile.showInDropdown + 在快速选择中显示 + + + + + group.edit.enforce_2fa + 实施2FA身份验证 + + + + + group.edit.enforce_2fa.help + 该组的每个直接成员都必须配置至少一个的第二因素进行身份验证。 + + + + + selectpicker.empty + 未选择任何内容 + + + + + selectpicker.nothing_selected + 未选择任何内容 + + + + + entity.delete.must_not_contain_parts + "%PATH%" 仍然包含部件。所有移除所有关联才能删除 + + + + + entity.delete.must_not_contain_attachments + 附件类型仍在被使用。必须移除所有关联才能删除 + + + + + entity.delete.must_not_contain_prices + 货币仍在被使用。必须移除所有关联才能删除 + + + + + entity.delete.must_not_contain_users + 组仍在被使用。必须移除所有关联才能删除 + + + + + part.table.edit + 编辑 + + + + + part.table.edit.title + 编辑部件 + + + + + part_list.action.action.title + 选择操作 + + + + + part_list.action.action.group.favorite + 收藏状态 + + + + + part_list.action.action.favorite + 收藏 + + + + + part_list.action.action.unfavorite + 取消收藏 + + + + + part_list.action.action.group.change_field + 更改字段 + + + + + part_list.action.action.change_category + 更改类别 + + + + + part_list.action.action.change_footprint + 更改封装 + + + + + part_list.action.action.change_manufacturer + 更改制造商 + + + + + part_list.action.action.change_unit + 更改部件单位 + + + + + part_list.action.action.delete + 删除 + + + + + part_list.action.submit + 提交 + + + + + part_list.action.part_count + 选定 %count% 个部件 + + + + + company.edit.quick.website + 打开网站 + + + + + company.edit.quick.email + 发送邮件 + + + + + company.edit.quick.phone + 拨打电话 + + + + + company.edit.quick.fax + 发送传真 + + + + + company.fax_number.placeholder + + + + + + part.edit.save_and_clone + 保存并克隆 + + + + + validator.file_ext_not_allowed + 该文件扩展名不属于此附件类型。 + + + + + tools.reel_calc.title + 卷盘计算器 + + + + + tools.reel_calc.inner_dia + 内径 + + + + + tools.reel_calc.outer_dia + 外径 + + + + + tools.reel_calc.tape_thick + 编带厚度 + + + + + tools.reel_calc.part_distance + 元件距离 + + + + + tools.reel_calc.update + 更新 + + + + + tools.reel_calc.parts_per_meter + 每米元件数 + + + + + tools.reel_calc.result_length + 编带长度 + + + + + tools.reel_calc.result_amount + 大概的元件数量 + + + + + tools.reel_calc.outer_greater_inner_error + 错误:外径必须大于内径。 + + + + + tools.reel_calc.missing_values.error + 请填写所有值。 + + + + + tools.reel_calc.load_preset + 加载预设 + + + + + tools.reel_calc.explanation + 该计算器可估算SMD卷轴上剩余的零件数量。测量卷轴上注明的尺寸(或使用一些预设),然后单击 "更新" 获得结果。 + + + + + perm.tools.reel_calculator + SMD卷盘计算器 + + + + + tree.tools.tools.reel_calculator + SMD卷盘计算器 + + + + + user.pw_change_needed.flash + 密码需要更改。请设置新密码 + + + + + tree.root_node.text + 根节点 + + + + + part_list.action.select_null + 空元素 + + + + + part_list.action.delete-title + 确认删除这些部件? + + + + + part_list.action.delete-message + 这些部件及任何属性信息将被删除。操作不能被撤消 + + + + + part.table.actions.success + 操作成功完成。 + + + + + attachment.edit.delete.confirm + 确实删除此附件? + + + + + filter.text_constraint.value.operator.EQ + 等于 + + + + + filter.text_constraint.value.operator.NEQ + 不等于 + + + + + filter.text_constraint.value.operator.STARTS + 以开头 + + + + + filter.text_constraint.value.operator.CONTAINS + 包含 + + + + + filter.text_constraint.value.operator.ENDS + 以结尾 + + + + + filter.text_constraint.value.operator.LIKE + 相似匹配 + + + + + filter.text_constraint.value.operator.REGEX + 正则表达式 + + + + + filter.number_constraint.value.operator.BETWEEN + 之间 + + + + + filter.number_constraint.AND + AND + + + + + filter.entity_constraint.operator.EQ + 等于(不包括子项) + + + + + filter.entity_constraint.operator.NEQ + 不等于(不包括子项) + + + + + filter.entity_constraint.operator.INCLUDING_CHILDREN + 等于(含子项) + + + + + filter.entity_constraint.operator.EXCLUDING_CHILDREN + 不等于(含子项) + + + + + part.filter.dbId + 数据库ID + + + + + filter.tags_constraint.operator.ANY + 任何标签 + + + + + filter.tags_constraint.operator.ALL + 所有标签 + + + + + filter.tags_constraint.operator.NONE + 无标签 + + + + + part.filter.lot_count + 批次数量 + + + + + part.filter.attachments_count + 附件数量 + + + + + part.filter.orderdetails_count + 订单明细数量 + + + + + part.filter.lotExpirationDate + 批次的有效期 + + + + + part.filter.lotNeedsRefill + 任何需要补充的批次 + + + + + part.filter.lotUnknwonAmount + 未知金额 + + + + + part.filter.attachmentName + 附件名称 + + + + + filter.choice_constraint.operator.ANY + 任意 + + + + + filter.choice_constraint.operator.NONE + + + + + + part.filter.amount_sum + 总数量 + + + + + filter.submit + 更新 + + + + + filter.discard + 放弃更改 + + + + + filter.clear_filters + 清除所有过滤器 + + + + + filter.title + 过滤器 + + + + + filter.parameter_value_constraint.operator.= + 标称值 等于 + + + + + filter.parameter_value_constraint.operator.!= + 标称值 不等于 + + + + + filter.parameter_value_constraint.operator.< + 标称值 小于 + + + + + filter.parameter_value_constraint.operator.> + 标称值 大于 + + + + + filter.parameter_value_constraint.operator.<= + 标称值 小于等于 + + + + + filter.parameter_value_constraint.operator.>= + 标称值 大于等于 + + + + + filter.parameter_value_constraint.operator.BETWEEN + 标称值 之间 + + + + + filter.parameter_value_constraint.operator.IN_RANGE + 范围 内 + + + + + filter.parameter_value_constraint.operator.NOT_IN_RANGE + 范围 外 + + + + + filter.parameter_value_constraint.operator.GREATER_THAN_RANGE + 范围 大于 + + + + + filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE + 范围 大于等于 + + + + + filter.parameter_value_constraint.operator.LESS_THAN_RANGE + 范围 小于 + + + + + filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE + 范围 小于等于 + + + + + filter.parameter_value_constraint.operator.RANGE_IN_RANGE + 范围 包含 + + + + + filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE + 范围 相交 + + + + + filter.text_constraint.value + 无值 + + + + + filter.number_constraint.value1 + 无值 + + + + + filter.number_constraint.value2 + 最大值 + + + + + filter.datetime_constraint.value1 + 未设置日期时间 + + + + + filter.datetime_constraint.value2 + 最大日期时间 + + + + + filter.constraint.add + 添加约束 + + + + + part.filter.parameters_count + 参数的数量 + + + + + part.filter.lotDescription + 批次的说明 + + + + + parts_list.search.searching_for + 搜索部件关键字 <b>%keyword%</b> + + + + + parts_list.search_options.caption + 启用搜索选项 + + + + + attachment.table.element_type + 关联元素类型 + + + + + log.level.debug + 调试 + + + + + log.level.info + 信息 + + + + + log.level.notice + 注意 + + + + + log.level.warning + 警告 + + + + + log.level.error + 错误 + + + + + log.level.critical + 关键 + + + + + log.level.alert + 警报 + + + + + log.level.emergency + 紧急 + + + + + log.type.security + 安全事件 + + + + + log.type.instock_changed + [旧版] 库存已更改 + + + + + log.target_id + 目标元素ID + + + + + entity.info.parts_count_recursive + 具有该元素或其子元素的部件数 + + + + + tools.server_infos.title + 服务器信息 + + + + + permission.preset.read_only + 只读 + + + + + permission.preset.read_only.desc + 只允许进行读取操作 + + + + + permission.preset.all_inherit + 全部继承 + + + + + permission.preset.all_inherit.desc + 将所有权限设置为继承 + + + + + permission.preset.all_forbid + 全部禁止 + + + + + permission.preset.all_forbid.desc + 将所有权限设置为禁止 + + + + + permission.preset.all_allow + 全部允许 + + + + + permission.preset.all_allow.desc + 将所有权限设置为允许 + + + + + perm.server_infos + 服务器信息 + + + + + permission.preset.editor + 编辑器 + + + + + permission.preset.editor.desc + 允许更改部件和数据结构 + + + + + permission.preset.admin + 管理员 + + + + + permission.preset.admin.desc + 允许执行管理操作 + + + + + permission.preset.button + 应用预设 + + + + + perm.attachments.show_private + 显示私有附件 + + + + + perm.attachments.list_attachments + 所有附件列表 + + + + + user.edit.permission_success + 权限预设应用成功。检查新的权限是否满足 + + + + + perm.group.data + 数据 + + + + + part_list.action.action.group.needs_review + 要求审查 + + + + + part_list.action.action.set_needs_review + 设置要求审核状态 + + + + + part_list.action.action.unset_needs_review + 设置要求审核状态 + + + + + part.edit.ipn + 内部零件号 (IPN) + + + + + part.ipn.not_defined + 没有定义 + + + + + part.table.ipn + IPN + + + + + currency.edit.update_rate + 更新汇率 + + + + + currency.edit.exchange_rate_update.unsupported_currency + 该货币不受汇率提供商支持。检查汇率提供商配置 + + + + + currency.edit.exchange_rate_update.generic_error + 无法更新汇率。检查汇率提供商配置 + + + + + currency.edit.exchange_rate_updated.success + 成功更新汇率。 + + + + + project.bom.quantity + 数量 + + + + + project.bom.mountnames + 装配名称 + + + + + project.bom.name + 名称 + + + + + project.bom.comment + 注释 + + + + + project.bom.part + 部件 + + + + + project.bom.add_entry + 添加条目 + + + + + part_list.action.group.projects + 项目 + + + + + part_list.action.projects.add_to_project + 将部件添加到项目中 + + + + + project.bom.delete.confirm + 确实删除此BOM条目? + + + + + project.add_parts_to_project + 将部件添加到项目中 + + + + + part.info.add_part_to_project + 将部件添加到项目中 + + + + + project_bom_entry.label + BOM条目 + + + + + project.edit.status + 项目状态 + + + + + project.status.draft + 草稿 + + + + + project.status.planning + 策划 + + + + + project.status.in_production + 生产中 + + + + + project.status.finished + 已完成 + + + + + project.status.archived + 已存档 + + + + + part.new_build_part.error.build_part_already_exists + 已映射到部件 + + + + + project.edit.associated_build_part + 映射到部件 + + + + + project.edit.associated_build_part.add + 创建生产成果部件 + + + + + project.edit.associated_build.hint + 该部件映射到该项目的生产成果,使项目成果可纳入储存管理中。 + + + + + part.info.projectBuildPart.hint + 该部件为生产成果,映射到项目 + + + + + part.is_build_part + 是项目生产的部件 + + + + + project.info.title + 项目信息 + + + + + project.info.bom_entries_count + BOM条目 + + + + + project.info.sub_projects_count + 子项目 + + + + + project.info.bom_add_parts + 添加BOM条目 + + + + + project.info.info.label + 信息 + + + + + project.info.sub_projects.label + 子项目 + + + + + project.bom.price + 价格 + + + + + part.info.withdraw_modal.title.withdraw + 从批次中取出部件 + + + + + part.info.withdraw_modal.title.add + 添加部件到批次中 + + + + + part.info.withdraw_modal.title.move + 移动部件到另一个批次 + + + + + part.info.withdraw_modal.amount + 数量 + + + + + part.info.withdraw_modal.move_to + 移动到 + + + + + part.info.withdraw_modal.comment + 批注/目的 + + + + + part.info.withdraw_modal.comment.hint + 描述您执行此操作的原因。此信息将保存在日志中。 + + + + + modal.close + 关闭 + + + + + modal.submit + 提交 + + + + + part.withdraw.success + 已成功添加/移动/撤回部件。 + + + + + perm.parts_stock + 部件库存 + + + + + perm.parts_stock.withdraw + 从库存中提取部件 + + + + + perm.parts_stock.add + 添加部件到库存 + + + + + perm.parts_stock.move + 在批次之间移动部件 + + + + + user.permissions_schema_updated + 该用户权限架构已升级到最新版本。 + + + + + log.type.part_stock_changed + 部件库存已更改 + + + + + log.part_stock_changed.withdraw + 部件库存提取已更改 + + + + + log.part_stock_changed.add + 库存已添加 + + + + + log.part_stock_changed.move + 库存已移动 + + + + + log.part_stock_changed.comment + 批注/目的 + + + + + log.part_stock_changed.change + 更改 + + + + + log.part_stock_changed.move_target + 移动目标 + + + + + tools.builtin_footprints_viewer.title + 内置封装图像库 + + + + + tools.builtin_footprints_viewer.hint + 所有可用的内置封装图像。在附件的路径字段中输入名称或关键字,然后从下拉列表中选择图像。 + + + + + tools.ic_logos.title + 芯片图标 + + + + + part_list.action.group.labels + 标签 + + + + + part_list.action.projects.generate_label + 生成标签(针对部件) + + + + + part_list.action.projects.generate_label_lot + 生成标签(针对部件批次) + + + + + part_list.action.generate_label.empty + 空标签 + + + + + project.info.builds.label + 生产 + + + + + project.builds.build_not_possible + 无法生产。部件没有库存 + + + + + project.builds.following_bom_entries_miss_instock + 以下部件没有足够的库存,构建至少一次该项目: + + + + + project.builds.stocked + 在库 + + + + + project.builds.needed + 所需 + + + + + project.builds.build_possible + 可生产 + + + + + project.builds.number_of_builds_possible + 与足够的库存生产 <b>%max_builds%</b> 个该项目 + + + + + project.builds.check_project_status + 当前项目状态为 <b>"%project_status%"</b>。检查是否以该状态生产项目 + + + + + project.builds.following_bom_entries_miss_instock_n + 没有足够的部件生产该项目 %number_of_builds% 次。 以下部件需要补充: + + + + + project.build.flash.invalid_input + 无法生产项目。 检查输入 + + + + + project.build.required_qty + 所需数量 + + + + + project.build.btn_build + 生产 + + + + + project.build.help + 选择应该从哪个部件批次中,获取用于生产该项目的库存(以及数量)。完成提取部件后,请选中每个BOM条目的复选框或使用顶部全选。 + + + + + project.build.buildsPartLot.new_lot + 创建新批次 + + + + + project.build.add_builds_to_builds_part + Add builds to project builds part + + + + + project.build.builds_part_lot + 目标批次 + + + + + project.builds.number_of_builds + 生产数量 + + + + + project.builds.no_stocked_builds + 库存生产数量 + + + + + user.change_avatar.label + 更改配置图片 + + + + + user_settings.change_avatar.label + 更改配置图片 + + + + + user_settings.remove_avatar.label + 移除配置图片 + + + + + part.edit.name.category_hint + 来自类别的提示 + + + + + category.edit.partname_regex.placeholder + + + + + + category.edit.partname_regex.help + 与PCRE兼容的正则表达式,部分名称必须匹配。 + + + + + entity.select.add_hint + 使用 -> 创建嵌套结构,例如 "Node 1->Node 1.1" + + + + + entity.select.group.new_not_added_to_DB + 新建(尚未添加到数据库) + + + + + part.edit.save_and_new + 保存并创建新的空部件 + + + + + homepage.first_steps.title + 第一步 + + + + + homepage.first_steps.introduction + 数据库仍是空的。浏览 <a href="%url%">文档</a> 或创建以下数据结构: + + + + + homepage.first_steps.create_part + 或可以直接 <a href="%url%">创建新部件</a>. + + + + + homepage.first_steps.hide_hint + 一旦创建了第一个部件,此框就会隐藏。 + + + + + homepage.forum.text + 有关 Part-DB 的问题请访问 <a href="%href%" class="link-external" target="_blank">讨论</a> + + + + + log.element_edited.changed_fields.category + 类别 + + + + + log.element_edited.changed_fields.footprint + 封装 + + + + + log.element_edited.changed_fields.manufacturer + 制造商 + + + + + log.element_edited.changed_fields.value_typical + 标称值 + + + + + log.element_edited.changed_fields.pw_reset_expires + 重置密码 + + + + + log.element_edited.changed_fields.comment + 注释 + + + + + log.element_edited.changed_fields.supplierpartnr + 供应商部件号 + + + + + log.element_edited.changed_fields.supplier_product_url + 供应商链接 + + + + + log.element_edited.changed_fields.price + 价格 + + + + + log.element_edited.changed_fields.min_discount_quantity + 最低折扣数量 + + + + + log.element_edited.changed_fields.original_filename + 原始文件名 + + + + + log.element_edited.changed_fields.path + 文件路径 + + + + + log.element_edited.changed_fields.description + 描述 + + + + + log.element_edited.changed_fields.manufacturing_status + 生产状态 + + + + + log.element_edited.changed_fields.options.barcode_type + 条码类型 + + + + + log.element_edited.changed_fields.status + 状态 + + + + + log.element_edited.changed_fields.quantity + 数量 + + + + + log.element_edited.changed_fields.mountnames + 装配名称 + + + + + log.element_edited.changed_fields.name + 名称 + + + + + log.element_edited.changed_fields.part + 部件 + + + + + log.element_edited.changed_fields.price_currency + 价格币种 + + + + + log.element_edited.changed_fields.partname_hint + 部件名称提示 + + + + + log.element_edited.changed_fields.partname_regex + 名称过滤器 + + + + + log.element_edited.changed_fields.disable_footprints + 禁用封装 + + + + + log.element_edited.changed_fields.disable_manufacturers + 禁用制造商 + + + + + log.element_edited.changed_fields.disable_autodatasheets + 禁用自动 DataSheet 链接 + + + + + log.element_edited.changed_fields.disable_properties + 禁用属性 + + + + + log.element_edited.changed_fields.default_description + 默认描述 + + + + + log.element_edited.changed_fields.default_comment + 默认注释 + + + + + log.element_edited.changed_fields.filetype_filter + 允许的文件扩展名 + + + + + log.element_edited.changed_fields.not_selectable + 未选择 + + + + + log.element_edited.changed_fields.parent + 父元素 + + + + + log.element_edited.changed_fields.shipping_costs + 运输费 + + + + + log.element_edited.changed_fields.default_currency + 默认货币 + + + + + log.element_edited.changed_fields.address + 地址 + + + + + log.element_edited.changed_fields.phone_number + 电话号码 + + + + + log.element_edited.changed_fields.fax_number + 传真号码 + + + + + log.element_edited.changed_fields.email_address + 邮件 + + + + + log.element_edited.changed_fields.website + 网站 + + + + + log.element_edited.changed_fields.auto_product_url + 产品链接 + + + + + log.element_edited.changed_fields.is_full + 储存位置已满 + + + + + log.element_edited.changed_fields.limit_to_existing_parts + 仅限当前部件 + + + + + log.element_edited.changed_fields.only_single_part + 仅单部件 + + + + + log.element_edited.changed_fields.storage_type + 储存类型 + + + + + log.element_edited.changed_fields.footprint_3d + 3D模型 + + + + + log.element_edited.changed_fields.master_picture_attachment + 预览图像 + + + + + log.element_edited.changed_fields.exchange_rate + 汇率 + + + + + log.element_edited.changed_fields.iso_code + 汇率 + + + + + log.element_edited.changed_fields.unit + 单位符号 + + + + + log.element_edited.changed_fields.is_integer + 整数 + + + + + log.element_edited.changed_fields.use_si_prefix + 使用 SI 前缀 + + + + + log.element_edited.changed_fields.options.width + + + + + + log.element_edited.changed_fields.options.height + + + + + + log.element_edited.changed_fields.options.supported_element + 目标类型 + + + + + log.element_edited.changed_fields.options.additional_css + 附加样式(CSS) + + + + + log.element_edited.changed_fields.options.lines + 内容 + + + + + log.element_edited.changed_fields.permissions.data + 权限 + + + + + log.element_edited.changed_fields.disabled + 禁用 + + + + + log.element_edited.changed_fields.theme + 主题 + + + + + log.element_edited.changed_fields.timezone + 时区 + + + + + log.element_edited.changed_fields.language + 语言 + + + + + log.element_edited.changed_fields.email + 邮件 + + + + + log.element_edited.changed_fields.department + 部门 + + + + + log.element_edited.changed_fields.last_name + + + + + + log.element_edited.changed_fields.first_name + + + + + + log.element_edited.changed_fields.group + + + + + + log.element_edited.changed_fields.currency + 首选货币 + + + + + log.element_edited.changed_fields.enforce2FA + 执行 2FA + + + + + log.element_edited.changed_fields.symbol + 符号 + + + + + log.element_edited.changed_fields.value_min + 最小值 + + + + + log.element_edited.changed_fields.value_max + 最大值 + + + + + log.element_edited.changed_fields.value_text + 文本值 + + + + + log.element_edited.changed_fields.show_in_table + 显示在表中 + + + + + log.element_edited.changed_fields.attachment_type + 显示在表中 + + + + + log.element_edited.changed_fields.needs_review + 需要审查 + + + + + log.element_edited.changed_fields.tags + 标签 + + + + + log.element_edited.changed_fields.mass + 重量 + + + + + log.element_edited.changed_fields.ipn + IPN + + + + + log.element_edited.changed_fields.favorite + 收藏 + + + + + log.element_edited.changed_fields.minamount + 最小库存 + + + + + log.element_edited.changed_fields.manufacturer_product_url + 产品链接 + + + + + log.element_edited.changed_fields.manufacturer_product_number + MPN + + + + + log.element_edited.changed_fields.partUnit + 计量单位 + + + + + log.element_edited.changed_fields.expiration_date + 有效期 + + + + + log.element_edited.changed_fields.amount + 数量 + + + + + log.element_edited.changed_fields.storage_location + 存储位置 + + + + + attachment.max_file_size + 最大文件大小 + + + + + user.saml_user + SSO/SAML 用户 + + + + + user.saml_user.pw_change_hint + 该用户使用单点登录 (SSO),无法在此处更改密码和2FA设置,请在SSO提供商上配置。 + + + + + login.sso_saml_login + 单点登录 (SSO) + + + + + login.local_login_hint + 下面的表单仅适用于本地用户登录。如果要通过单点登录进行登录请使用上面的按钮。 + + + + + part_list.action.action.export + 导出部件 + + + + + part_list.action.export_json + 导出 JSON + + + + + part_list.action.export_csv + 导出 CSV + + + + + part_list.action.export_yaml + 导出 YAML + + + + + part_list.action.export_xml + 导出 XML + + + + + parts.import.title + 导入部件 + + + + + parts.import.errors.title + 导入违规 + + + + + parts.import.flash.error + 导入出错。可能存在无效数据 + + + + + parts.import.format.auto + 自动(基于文件扩展名) + + + + + parts.import.flash.error.unknown_format + 无法判断给定文件的格式。 + + + + + parts.import.flash.error.invalid_file + 文件无效。请检查格式选择 + + + + + parts.import.part_category.label + 类别覆盖 + + + + + parts.import.part_category.help + 强制导入所有的部件到此类别,忽略文件中设置的类别。 + + + + + import.create_unknown_datastructures + 创建未知的数据结构 + + + + + import.create_unknown_datastructures.help + 自动创建数据库中尚不存在的数据结构。如果不自动创建,不会导入未匹配的数据 + + + + + import.path_delimiter + 路径分隔符 + + + + + import.path_delimiter.help + 分隔符用于标记数据结构的路径。 + + + + + parts.import.help_documentation + 有关文件格式的更多信息请查看 <a href="%link%">文档</a> + + + + + parts.import.help + 使用此工具从文件导入部件。操作将直接写入数据库,请确保文件内容正确。 + + + + + parts.import.flash.success + 部件导入成功! + + + + + parts.import.errors.imported_entities + 已导入部件 + + + + + perm.import + 导入数据 + + + + + parts.import.part_needs_review.label + 强制 "需要审核" + + + + + parts.import.part_needs_review.help + 将所有部件配置为 "需要审查" (忽略导入文件内的部件属性) + + + + + project.bom_import.flash.success + 已成功导入 %count% 个BOM条目。 + + + + + project.bom_import.type + Type + + + + + project.bom_import.type.kicad_pcbnew + KiCAD PCB编辑器 BOM(.CSV) + + + + + project.bom_import.clear_existing_bom + 导入前删除现有BOM条目 + + + + + project.bom_import.clear_existing_bom.help + 删除项目中所有旧的BOM条目,再从文件导入新的 + + + + + project.bom_import.flash.invalid_file + 无法导入文件,请检查文件类型。错误信息:%message% + + + + + project.bom_import.flash.invalid_entries + 验证错误。请检查数据 + + + + + project.import_bom + 为项目导入BOM + + + + + project.edit.bom.import_bom + 导入BOM + + + + + measurement_unit.new + 新建度量单位 + + + + + measurement_unit.edit + 编辑度量单位 + + + + + user.aboutMe.label + 关于我 + + + + + storelocation.owner.label + 所有者 + + + + + storelocation.part_owner_must_match.label + 部件批次所有者 必须与 存储位置所有者 匹配 + + + + + part_lot.owner + 所有者 + + + + + part_lot.owner.help + 只有所有者可以提取或添加该批次的库存。 + + + + + log.element_edited.changed_fields.owner + 所有者 + + + + + log.element_edited.changed_fields.instock_unknown + 数量不明 + + + + + log.element_edited.changed_fields.needs_refill + 需要补充 + + + + + part.withdraw.access_denied + 拒绝操作。请检查权限 + + + + + part.info.amount.less_than_desired + 低于预期 + + + + + log.cli_user + 命令行用户 + + + + + log.element_edited.changed_fields.part_owner_must_match + 部件所有者 必须与 存储位置所有者 匹配 + + + + + part.filter.lessThanDesired + 库存低于预期 + + + + + part.filter.lotOwner + 批次所有者 + + + + + user.show_email_on_profile.label + 在公共个人资料页面上显示电子邮件 + + + + + log.details.title + 日志详情 + + + + + log.user_login.login_from_ip + 从IP地址登录 + + + + + log.user_login.ip_anonymize_hint + 如果IP地址的最后一位丢失,则启用 GDPR 模式,其中IP地址将被匿名化。 + + + + + log.user_not_allowed.unauthorized_access_attempt_to + 访问尝试未授权的页面 + + + + + log.user_not_allowed.hint + 请求被拒绝。 + + + + + log.no_comment + 无注释 + + + + + log.element_changed.field + 字段 + + + + + log.element_changed.data_before + 变更前数据 + + + + + error_table.error + 请求期间发生了错误。 + + + + + part.table.invalid_regex + 无效的正则表达式(regex) + + + + + log.element_changed.data_after + 变更后数据 + + + + + log.element_changed.diff + 差异 + + + + + log.undo.undo.short + 撤销 + + + + + log.undo.revert.short + 恢复到此时间戳 + + + + + log.view_version + 查看版本 + + + + + log.undo.undelete.short + 取消删除 + + + + + log.element_edited.changed_fields.id + ID + + + + + log.element_edited.changed_fields.id_owner + 所有者 + + + + + log.element_edited.changed_fields.parent_id + 父元素 + + + + + log.details.delete_entry + 删除日志条目 + + + + + log.delete.message.title + 确实删除该日志条目? + + + + + log.delete.message + 如果这是元素历史记录条目,则会破坏元素历史记录!使用时间旅行功能时,这可能会导致意外结果。 + + + + + log.collection_deleted.on_collection + 收藏中 + + + + + log.element_edited.changed_fields.attachments + 附件 + + + + + tfa_u2f.add_key.registration_error + 注册安全密钥期间发生错误。再试一次或使用另一个安全密钥 + + + + + log.target_type.none + + + + + + ui.darkmode.light + 亮色 + + + + + ui.darkmode.dark + 暗色 + + + + + ui.darkmode.auto + 自动(跟随系统设置) + + + + + label_generator.no_lines_given + 没有文字内容。标签将保持为空 + + + + + user.password_strength.very_weak + 非常弱 + + + + + user.password_strength.weak + + + + + + user.password_strength.medium + + + + + + user.password_strength.strong + + + + + + user.password_strength.very_strong + 非常强 + + + + + perm.users.impersonate + 模仿其他用户 + + + + + user.impersonated_by.label + 模仿用户 + + + + + user.stop_impersonation + 停止模仿 + + + + + user.impersonate.btn + 模仿 + + + + + user.impersonate.confirm.title + 确认模仿该用户? + + + + + user.impersonate.confirm.message + 他将被登录。确保在理由充分的情况执行该操作。 + +请注意不能模仿禁用的用户。如果尝试会收到 "拒绝访问" 消息。 + + + + + log.type.security.user_impersonated + 用户被模仿 + + + + + info_providers.providers_list.title + 信息提供者 + + + + + info_providers.providers_list.active + 活动 + + + + + info_providers.providers_list.disabled + 禁用 + + + + + info_providers.capabilities.basic + 基本 + + + + + info_providers.capabilities.footprint + 封装 + + + + + info_providers.capabilities.picture + 图片 + + + + + info_providers.capabilities.datasheet + 数据文档 + + + + + info_providers.capabilities.price + 价格 + + + + + part.info_provider_reference.badge + 用于创建此部件的信息提供者。 + + + + + part.info_provider_reference + 由信息提供者创建 + + + + + oauth_client.connect.btn + 连接 OAuth + + + + + info_providers.table.provider.label + 提供者 + + + + + info_providers.search.keyword + 关键词 + + + + + info_providers.search.submit + 搜索 + + + + + info_providers.search.providers.help + 选择被搜索的提供商。 + + + + + info_providers.search.providers + 提供者 + + + + + info_providers.search.info_providers_list + 显示所有可用的信息提供者 + + + + + info_providers.search.title + 从信息提供者创建部件 + + + + + oauth_client.flash.connection_successful + 已成功连接到 OAuth 应用程序 + + + + + perm.part.info_providers + 信息提供者 + + + + + perm.part.info_providers.create_parts + 从信息提供者创建部件 + + + + + entity.edit.alternative_names.label + 替代名称 + + + + + entity.edit.alternative_names.help + 此处给出的替代名称用于根据信息提供者的结果查找该元素。 + + + + + info_providers.form.help_prefix + 提供者 + + + + + update_manager.new_version_available.title + 新版本可用 + + + + + update_manager.new_version_available.text + Part-DB 新版本,已推出 + + + + + update_manager.new_version_available.only_administrators_can_see + 只有管理员才能看到此消息。 + + + + + perm.system.show_available_updates + 显示可用的 Part-DB 更新 + + + + + user.settings.api_tokens + API 令牌 + + + + + user.settings.api_tokens.description + 使用 API 令牌,第三方软件可以以特定用户的权限访问 Part-DB,使用 REST API 执行各种操作。如果删除 API 令牌,则使用该令牌的软件将无法访问 Part-DB。 + + + + + api_tokens.name + 名称 + + + + + api_tokens.access_level + 访问权限 + + + + + api_tokens.expiration_date + 有效期 + + + + + api_tokens.added_date + 添加于 + + + + + api_tokens.last_time_used + 上次使用时间 + + + + + datetime.never + 未使用过 + + + + + api_token.valid + 有效 + + + + + api_token.expired + 已到期 + + + + + user.settings.show_api_documentation + 显示API文档 + + + + + api_token.create_new + 创建新的 API 令牌 + + + + + api_token.level.read_only + 只读 + + + + + api_token.level.edit + 编辑 + + + + + api_token.level.admin + 管理员 + + + + + api_token.level.full + 所有权限 + + + + + api_tokens.access_level.help + API令牌访问始终受用户权限限制。 + + + + + api_tokens.expiration_date.help + 在此日期之后,令牌将不再可用。如果需要令牌永不过期,请保留为空。 + + + + + api_tokens.your_token_is + API令牌: + + + + + api_tokens.please_save_it + 请保存。以后将无法查看到完整的API令牌 + + + + + api_tokens.create_new.back_to_user_settings + 返回用户设置 + + + + + project.build.dont_check_quantity + 不检查数量 + + + + + project.build.dont_check_quantity.help + 无论生产该项目实际需要更多或更少的部件,都将按给定的数量进行提取。 + + + + + part_list.action.invert_selection + 反转选择 + + + + + perm.api + API + + + + + perm.api.access_api + 访问 API + + + + + perm.api.manage_tokens + 管理 API 令牌 + + + + + user.settings.api_tokens.delete.title + 确实删除此API令牌? + + + + + user.settings.api_tokens.delete + 删除 + + + + + user.settings.api_tokens.delete.message + 使用此 API 令牌的第三方软件将无法再访问 Part-DB。该操作无法撤消 + + + + + api_tokens.deleted + API令牌删除成功 + + + + + user.settings.api_tokens.no_api_tokens_yet + 无任何 API 令牌。 + + + + + api_token.ends_with + 结尾为 + + + + + entity.select.creating_new_entities_not_allowed + 不能创建这种类型的新实体。请选择一个现有的 + + + + + scan_dialog.mode + 条码类型 + + + + + scan_dialog.mode.auto + 自动检测 + + + + + scan_dialog.mode.ipn + IPN条码 + + + + + scan_dialog.mode.internal + Part-DB条码 + + + + + part_association.label + 部件关联 + + + + + part.edit.tab.associations + 相关部件 + + + + + part_association.edit.other_part + 相关的部件 + + + + + part_association.edit.type + 类型 + + + + + part_association.edit.comment + 注释 + + + + + part_association.edit.type.help + 选择所选部件与该部件的关联方式。 + + + + + part_association.table.from_this_part + 从该部件关联到其他部件 + + + + + part_association.table.from + 来自 + + + + + part_association.table.type + 关系 + + + + + part_association.table.to + + + + + + part_association.type.compatible + 兼容 + + + + + part_association.table.to_this_part + 其他与该部分的关联 + + + + + part_association.type.other + 其他(自定义值) + + + + + part_association.type.supersedes + 代替 + + + + + part_association.edit.other_type + 自定义类型 + + + + + part_association.edit.delete.confirm + 确实删除该关联? 操作不能被撤消 + + + + + part_lot.edit.advanced + 展开高级选项 + + + + + part_lot.edit.vendor_barcode + 供应商条形码 + + + + + part_lot.edit.vendor_barcode.help + 如果该批次已有条形码,可以在此处输入其内容,以便轻松扫描。 + + + + + scan_dialog.mode.vendor + 供应商条形码(配置于部件批次) + + + + + project.bom.instockAmount + 库存数量 + + + + + collection_type.new_element.tooltip + 该元素是新创建的,尚未写入到数据库中。 + + + + + part.merge.title + 合并部件 + + + + + part.merge.title.into + + + + + + part.merge.confirm.title + 确实要合并 <b>%other%</b> 到 <b>%target%</b> ? + + + + + part.merge.confirm.message + 将删除 <b>%other%</b>。部件将以显示的信息保存。 + + + + + part.info.merge_modal.title + 合并部件 + + + + + part.info.merge_modal.other_part + 其他部件 + + + + + part.info.merge_modal.other_into_this + 将其他部件合并到此部件(删除其他,保留自己) + + + + + part.info.merge_modal.this_into_other + 将此部件合并到其他部件(删除自己,保留其他) + + + + + part.info.merge_btn + 合并部件 + + + + + part.update_part_from_info_provider.btn + 从信息提供者更新部件 + + + + + info_providers.update_part.title + 从信息提供者更新现有部件 + + + + + part.merge.flash.please_review + 数据尚未保存。查看更改并单击 保存 以保留新数据 + + + + + user.edit.flash.permissions_fixed + 权限缺失,已更正。请检查权限是否正确 + + + + + permission.legend.dependency_note + 请注意,有些权限操作是相互依赖的。如果遇到权限缺失并已更正的警告,并且某个权限被设置为允许,则必须同时将依赖操作也设置为禁止。依赖通常位于操作的右侧。 + + + + + log.part_stock_changed.timestamp + 时间戳 + + + + + part.info.withdraw_modal.timestamp + 操作时间戳 + + + + + part.info.withdraw_modal.timestamp.hint + 该字段允许指定真实日期,即实际执行库存操作的日期,而不仅仅是记录日期。该值将保存在日志条目的额外字段中。 + + + + + part.info.withdraw_modal.delete_lot_if_empty + 如果该批次是空的,请删除 + + + + + info_providers.search.error.client_exception + 与信息提供者通信时发生错误。检查此提供程序的配置并刷新 OAuth 令牌(如果可能)。 + + + + + eda_info.reference_prefix.placeholder + e + + + + + eda_info.reference_prefix + 参考前缀 + + + + + eda_info.kicad_section.title + KiCad 设置 + + + + + eda_info.value + + + + + + eda_info.value.placeholder + + + + + + eda_info.exclude_from_bom + 从BOM中排除 + + + + + eda_info.exclude_from_board + 从PCB中排除 + + + + + eda_info.exclude_from_sim + 从仿真中排除 + + + + + eda_info.kicad_symbol + KiCad 符号名 + + + + + eda_info.kicad_symbol.placeholder + + + + + + eda_info.kicad_footprint + KiCad 封装名 + + + + + eda_info.kicad_footprint.placeholder + + + + + + part.edit.tab.eda + EDA信息 + + + + + api.api_endpoints.title + API端点 + + + + + api.api_endpoints.partdb + Part-DB API + + + + + api.api_endpoints.kicad_root_url + KiCad API 根链接 + + + + + eda_info.visibility + 保持可见 + + + + + eda_info.visibility.help + 强制零件可见。默认情况下,EDA软件将自动确定可见性。 + + + + + part.withdraw.zero_amount + 尝试提取/添加的数量为零。未执行任何操作 + + + + + login.flash.access_denied_please_login + 拒绝访问。请登录 + + + + + attachment.upload_multiple_files + 上传文件 + + + + + entity.mass_creation_flash + 成功创建 %COUNT% 个元素。 + + + + diff --git a/translations/security.cs.xlf b/translations/security.cs.xlf new file mode 100644 index 00000000..b4a58697 --- /dev/null +++ b/translations/security.cs.xlf @@ -0,0 +1,17 @@ + + + + + + user.login_error.user_disabled + Váš účet je deaktivován! Pokud si myslíte, že je to špatně, kontaktujte správce. + + + + + saml.error.cannot_login_local_user_per_saml + Přes SSO se nelze přihlásit jako místní uživatel! Místo toho použijte heslo místního uživatele. + + + + diff --git a/translations/security.da.xlf b/translations/security.da.xlf new file mode 100644 index 00000000..0b1423fe --- /dev/null +++ b/translations/security.da.xlf @@ -0,0 +1,17 @@ + + + + + + user.login_error.user_disabled + Din konto er deaktiveret! Kontakt en administrator, hvis du mener, at dette er en fejl. + + + + + saml.error.cannot_login_local_user_per_saml + Du kan ikke logge ind som lokal bruger via SSO! Brug dit lokale password i stedet. + + + + diff --git a/translations/security.de.xlf b/translations/security.de.xlf index c646538e..a0498be8 100644 --- a/translations/security.de.xlf +++ b/translations/security.de.xlf @@ -1,11 +1,23 @@ - + user.login_error.user_disabled Ihr Account ist deaktiviert! Kontaktiere einen Administrator, wenn Sie denken, dass dies ein Fehler ist. + + + saml.error.cannot_login_local_user_per_saml + Sie können sich per SSO nicht als lokaler Nutzer einloggen! Nutzen Sie stattdessen ihr lokales Passwort. + + + + + saml.error.cannot_login_saml_user_locally + Sie können sich nicht mittels lokaler Authentifizierung als SAML Benutzer einloggen! Benutzen Sie stattdessen den SSO login. + + diff --git a/translations/security.en.xlf b/translations/security.en.xlf index e0bb95f4..0b0b4569 100644 --- a/translations/security.en.xlf +++ b/translations/security.en.xlf @@ -1,11 +1,23 @@ - + user.login_error.user_disabled Your account is disabled! Contact an administrator if you think this is wrong. + + + saml.error.cannot_login_local_user_per_saml + You cannot login as local user via SSO! Use your local user password instead. + + + + + saml.error.cannot_login_saml_user_locally + You cannot use local authentication to login as SAML user! Use SSO login instead. + + diff --git a/translations/security.es.xlf b/translations/security.es.xlf new file mode 100644 index 00000000..e9baee3d --- /dev/null +++ b/translations/security.es.xlf @@ -0,0 +1,23 @@ + + + + + + user.login_error.user_disabled + Su cuenta ha sido deshabilitada. Contacte con el administrador si cree que podría ser un error. + + + + + saml.error.cannot_login_local_user_per_saml + No puede identificarse como usuario local vía SSO. Utilice su contraseña local en su lugar. + + + + + saml.error.cannot_login_saml_user_locally + No puede identificarse como usuario SAML utilizando su usuario local. Utilice su identificación SSO en su lugar. + + + + diff --git a/translations/security.fr.xlf b/translations/security.fr.xlf index 1be6efa1..337509be 100644 --- a/translations/security.fr.xlf +++ b/translations/security.fr.xlf @@ -1,11 +1,23 @@ - + user.login_error.user_disabled Votre compte est désactivé ! Contactez un administrateur si vous pensez que c'est une erreur. + + + saml.error.cannot_login_local_user_per_saml + Impossible de se connecter via le SSO (Single Sign On) ! + + + + + saml.error.cannot_login_saml_user_locally + Vous ne pouvez pas utiliser l'authentification par mot de passe ! Veuillez utiliser le SSO! + + diff --git a/translations/security.hr.xlf b/translations/security.hr.xlf new file mode 100644 index 00000000..46664730 --- /dev/null +++ b/translations/security.hr.xlf @@ -0,0 +1,17 @@ + + + + + + user.login_error.user_disabled + Vaš račun je onemogućen. Molimo kontaktirajte administratora ako mislite da je ovo pogrešno. + + + + + saml.error.cannot_login_local_user_per_saml + Ne možete se prijaviti kao lokalni koristnik preko SSO! Molimo koristite vašu lokalnu zaporku. + + + + diff --git a/translations/security.it.xlf b/translations/security.it.xlf new file mode 100644 index 00000000..9200458c --- /dev/null +++ b/translations/security.it.xlf @@ -0,0 +1,23 @@ + + + + + + user.login_error.user_disabled + Il tuo account è disabilitato! Contatta un amministratore se ritieni che non sia corretto. + + + + + saml.error.cannot_login_local_user_per_saml + Non è possibile accedere come utente locale tramite SSO! Usare invece la password locale. + + + + + saml.error.cannot_login_saml_user_locally + Non si può usare l'autenticazione locale per effettuare l'accesso come utente SAML! Usare l'accesso SSO. + + + + diff --git a/translations/security.nl.xlf b/translations/security.nl.xlf new file mode 100644 index 00000000..7ba9fcc1 --- /dev/null +++ b/translations/security.nl.xlf @@ -0,0 +1,23 @@ + + + + + + user.login_error.user_disabled + Uw account is gedeactiveerd! Neem contact op met een beheerder indien dit incorrect is. + + + + + saml.error.cannot_login_local_user_per_saml + U kunt niet inloggen als lokale gebruiker met SSO! Gebruik uw lokale wachtwoord. + + + + + saml.error.cannot_login_saml_user_locally + U kunt geen lokale authenticatie gebruiken om in te loggen als SAML-gebruiker! Gebruik in plaats daarvan SSO-aanmelding. + + + + diff --git a/translations/security.pl.xlf b/translations/security.pl.xlf new file mode 100644 index 00000000..232bea19 --- /dev/null +++ b/translations/security.pl.xlf @@ -0,0 +1,17 @@ + + + + + + user.login_error.user_disabled + Twoje konto jest wyłączone! Skontaktuj się z administratorem, jeśli uważasz, że jest to niewłaściwe. + + + + + saml.error.cannot_login_local_user_per_saml + Nie możesz zalogować się jako użytkownik lokalny poprzez SSO! Zamiast tego użyj hasła użytkownika lokalnego. + + + + diff --git a/translations/security.ru.xlf b/translations/security.ru.xlf index ef7687b0..ebd29669 100644 --- a/translations/security.ru.xlf +++ b/translations/security.ru.xlf @@ -1,11 +1,23 @@ - + user.login_error.user_disabled Ваша учетная запись отключена! Свяжитесь с администратором, если вы считаете, что это неправильно. + + + saml.error.cannot_login_local_user_per_saml + Вы не можете войти в систему как локальный пользователь через SSO! Используйте локального пользователя и его пароль. + + + + + saml.error.cannot_login_saml_user_locally + Вы не можете использовать локальную аутентификацию для входа в качестве пользователя SAML! Вместо этого используйте вход SSO. + + diff --git a/translations/security.vi.xlf b/translations/security.vi.xlf new file mode 100644 index 00000000..de4647bf --- /dev/null +++ b/translations/security.vi.xlf @@ -0,0 +1,17 @@ + + + + + + user.login_error.user_disabled + Tài khoản của bạn đã bị vô hiệu hóa! Hãy liên hệ với quản trị viên nếu bạn cho rằng điều này sai. + + + + + saml.error.cannot_login_local_user_per_saml + Bạn không thể đăng nhập qua SSO! Thay vào đó hãy sử dụng mật khẩu người dùng cục bộ của bạn. + + + + diff --git a/translations/security.zh.xlf b/translations/security.zh.xlf new file mode 100644 index 00000000..58fbb26f --- /dev/null +++ b/translations/security.zh.xlf @@ -0,0 +1,23 @@ + + + + + + user.login_error.user_disabled + 账户已被禁用。请联系管理员 + + + + + saml.error.cannot_login_local_user_per_saml + 无法通过 SSO 以本地用户身份登录。请使用本地用户密码 + + + + + saml.error.cannot_login_saml_user_locally + 无法使用本地身份验证器以SAML用户身份登录!请改用SSO登录 + + + + diff --git a/translations/validators.cs.xlf b/translations/validators.cs.xlf new file mode 100644 index 00000000..4a6886fd --- /dev/null +++ b/translations/validators.cs.xlf @@ -0,0 +1,351 @@ + + + + + + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\LabelSystem\LabelProfile.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + part.master_attachment.must_be_picture + Příloha náhledu musí být platný obrázek! + + + + + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + src\Entity\AttachmentType.php:0 + src\Entity\Category.php:0 + src\Entity\Company.php:0 + src\Entity\Device.php:0 + src\Entity\Footprint.php:0 + src\Entity\Group.php:0 + src\Entity\Manufacturer.php:0 + src\Entity\PartsContainingDBElement.php:0 + src\Entity\Storelocation.php:0 + src\Entity\StructuralDBElement.php:0 + src\Entity\Supplier.php:0 + + + structural.entity.unique_name + Prvek s tímto názvem již na této úrovni existuje! + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_typical + Hodnota musí být menší nebo rovna typické hodnotě ({{ compared_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_max + Hodnota musí být menší než maximální hodnota ({{ compared_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.max_greater_typical + Hodnota musí být větší nebo rovna typické hodnotě ({{ compared_value }}). + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + validator.user.username_already_used + Uživatel s tímto jménem již existuje + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + user.invalid_username + Uživatelské jméno musí obsahovat pouze písmena, číslice, podtržítka, tečky, plusy nebo mínusy! + + + + + obsolete + + + validator.noneofitschild.self + Prvek nemůže být svým vlastním rodičem! + + + + + obsolete + + + validator.noneofitschild.children + Podřízený prvek nemůže být nadřazeným prvkem! + + + + + validator.select_valid_category + Vyberte prosím platnou kategorii! + + + + + validator.part_lot.only_existing + Do tohoto umístění nelze přidávat nové díly, protože je označeno jako "Pouze existující". + + + + + validator.part_lot.location_full.no_increase + Místo je obsazeno. Množství nelze navýšit (nová hodnota musí být menší než {{ old_amount }}). + + + + + validator.part_lot.location_full + Místo je obsazeno. Nelze do něj přidávat nové díly. + + + + + validator.part_lot.single_part + Toto umístění může obsahovat pouze jeden díl, takže do něj nelze přídávat další! + + + + + validator.attachment.must_not_be_null + Musíte vybrat typ přílohy! + + + + + validator.orderdetail.supplier_must_not_be_null + Musíte si vybrat dodavatele! + + + + + validator.measurement_unit.use_si_prefix_needs_unit + Chcete-li povolit předpony SI, musíte nastavit symbol jednotky! + + + + + part.ipn.must_be_unique + Interní číslo dílu musí být jedinečné. {{ value }} se již používá! + + + + + validator.project.bom_entry.name_or_part_needed + Musíte vybrat díl pro položku BOM dílu nebo nastavit název pro položku BOM bez dílu. + + + + + project.bom_entry.name_already_in_bom + Již existuje položka BOM s tímto názvem! + + + + + project.bom_entry.part_already_in_bom + Tento díl již existuje v tomto BOM! + + + + + project.bom_entry.mountnames_quantity_mismatch + Počet názvů sestav musí odpovídat počtu komponent v BOM! + + + + + project.bom_entry.can_not_add_own_builds_part + Seznam BOM projektu nelze přidat do BOM. + + + + + project.bom_has_to_include_all_subelement_parts + BOM projektu musí obsahovat všechny výrobní díly dílčích projektů. Díl %part_name% projektu %project_name% chybí! + + + + + project.bom_entry.price_not_allowed_on_parts + U položek komponent BOM nelze nastavit cenu. Zadejte cenu samotného dílu. + + + + + validator.project_build.lot_bigger_than_needed + Zvolili jste větší množství pro vychystávání, než je nutné. Odstraňte přebytečné množství + + + + + validator.project_build.lot_smaller_than_needed + Zvolili jste menší množství k odebrání, než je potřeba pro sestavení! Přidejte další množství. + + + + + part.name.must_match_category_regex + Název komponenty neodpovídá regulárnímu výrazu zadanému pro kategorii: %regex% + + + + + validator.attachment.name_not_blank + Vyberte hodnotu nebo nahrajte soubor, aby se jeho název automaticky použil jako název této přílohy. + + + + + validator.part_lot.owner_must_match_storage_location_owner + Vlastník inventáře této komponenty a vybrané umístění se musí shodovat (%owner_name%)! + + + + + validator.part_lot.owner_must_not_be_anonymous + Vlastníkem nemůže být anonymní uživatel! + + + + + validator.part_association.must_set_an_value_if_type_is_other + Pokud nastavíte typ na "jiný", musíte pro něj nastavit popisnou hodnotu! + + + + + validator.part_association.part_cannot_be_associated_with_itself + Díl nemůže být spojen sám se sebou! + + + + + validator.part_association.already_exists + Asociace s tímto dílem již existuje! + + + + + validator.part_lot.vendor_barcode_must_be_unique + Tato hodnota čárového kódu dodavatele již byla použita v jiném inventáře. Čárový kód musí být jedinečný! + + + + + validator.year_2038_bug_on_32bit + Kvůli technickým omezením není možné na 32bitových systémech vybrat datumpo 19.1.2038! + + + + diff --git a/translations/validators.da.xlf b/translations/validators.da.xlf new file mode 100644 index 00000000..21149f0e --- /dev/null +++ b/translations/validators.da.xlf @@ -0,0 +1,345 @@ + + + + + + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\LabelSystem\LabelProfile.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + part.master_attachment.must_be_picture + Forhåndsvisnings-bilaget skal være et rigtigt billede! + + + + + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + src\Entity\AttachmentType.php:0 + src\Entity\Category.php:0 + src\Entity\Company.php:0 + src\Entity\Device.php:0 + src\Entity\Footprint.php:0 + src\Entity\Group.php:0 + src\Entity\Manufacturer.php:0 + src\Entity\PartsContainingDBElement.php:0 + src\Entity\Storelocation.php:0 + src\Entity\StructuralDBElement.php:0 + src\Entity\Supplier.php:0 + + + structural.entity.unique_name + Der eksisterer allerede et element med dette navn på dette niveau! + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_typical + Værdi skal være mindre end eller lig med den typiske værdi ({{ compared_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_max + Værdi skal være mindre end maksumumværdien ({{ compared_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.max_greater_typical + Værdi skal være større eller lig med den typiske værdi ({{ compared_value }}). + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + validator.user.username_already_used + Der eksisterer allerede en bruger med dette navn + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + user.invalid_username + Brugernavn skal må kun indeholde bogstager, tal, understregningstegn, punktummer, plusser og minusser! + + + + + obsolete + + + validator.noneofitschild.self + Et element kan ikke være dets eget overordnede element! + + + + + obsolete + + + validator.noneofitschild.children + Et underelement kan ikke være dets overordnede element (Dette ville resultere i løkker)! + + + + + validator.select_valid_category + Vælg venligst en gyldig kategori! + + + + + validator.part_lot.only_existing + Lagerlokationen er markeret som "Kun eksisterende dele", så derfor kan nye dele ikke tilføjes. + + + + + validator.part_lot.location_full.no_increase + Lokationen er fuld. Antal kan ikke forøges (Ny værdi skal være mindre end {{ old_amount }}). + + + + + validator.part_lot.location_full + Lokation er fuld. Kan ikke tilføje nye dele til denne. + + + + + validator.part_lot.single_part + Lagerlokationen er markeret som "Kun én komponent", så der kan ikke tilføjes yderligere. + + + + + validator.attachment.must_not_be_null + Du skal vælge en bilagstype! + + + + + validator.orderdetail.supplier_must_not_be_null + Du skal vælge en leverandør! + + + + + validator.measurement_unit.use_si_prefix_needs_unit + For at kunne aktivere SI-prefixes, så skal du have indtastet et enhedsymbol! + + + + + part.ipn.must_be_unique + Det interne partnummer skal være unikt. {{ value }} værdien er allerede i brug! + + + + + validator.project.bom_entry.name_or_part_needed + Du skal vælge en komponent eller angive et navn til en ikke-komponent styklistepost! + + + + + project.bom_entry.name_already_in_bom + Der findes allerede en BOM linie med dette navn! + + + + + project.bom_entry.part_already_in_bom + Delen eksisterer allerede i denne BOM! + + + + + project.bom_entry.mountnames_quantity_mismatch + Antallet af bestykningsnavne skal svare til BOM antallet af komponenter! + + + + + project.bom_entry.can_not_add_own_builds_part + En projekt BOM-liste kan ikke tilføjet til en BOM. + + + + + project.bom_has_to_include_all_subelement_parts + Projekt BOM skal indeholde alle underprojekters styklister. Komponent %part_name% fra projekt %project_name% mangler! + + + + + project.bom_entry.price_not_allowed_on_parts + Du kan ikke sætte pris for komponent-BOM indtastninger. Indtast prisen på selve komponenten selv. + + + + + validator.project_build.lot_bigger_than_needed + Du har valgt en større mængde til plukning end nødvendigt. Fjern det overskydende antal + + + + + validator.project_build.lot_smaller_than_needed + Du har valgt et for lille antal til plukning end der er nødvendigt for dette byg. Tilføj yderligere mængde. + + + + + part.name.must_match_category_regex + Komponentnavnet matcher ikke med det regulære udtryk (regular expression) som der er specificeret for kategorien: %regex% + + + + + validator.attachment.name_not_blank + Vælg en værdi, eller upload en fil for automatisk at bruge dens filnavn som navn for denne vedhæftede fil. + + + + + validator.part_lot.owner_must_match_storage_location_owner + Ejeren af ​​denne komponentbeholdning og den valgte lagerplacering skal matche (%owner_name%)! + + + + + validator.part_lot.owner_must_not_be_anonymous + Ejeren kan ikke være den anonyme bruger! + + + + + validator.part_association.must_set_an_value_if_type_is_other + Hvis linktypen er sat til "Andet", skal du angive en beskrivende værdi! + + + + + validator.part_association.part_cannot_be_associated_with_itself + En komponent kan ikke knyttes til sig selv! + + + + + validator.part_association.already_exists + Et link til denne komponent findes allerede! + + + + + validator.part_lot.vendor_barcode_must_be_unique + Denne leverandørstregkodeværdi er allerede brugt til en anden beholdning. Stregkoden skal være unik! + + + + diff --git a/translations/validators.de.xlf b/translations/validators.de.xlf index abdd0886..dc3f94df 100644 --- a/translations/validators.de.xlf +++ b/translations/validators.de.xlf @@ -1,7 +1,7 @@ - + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 Part-DB1\src\Entity\Attachments\AttachmentType.php:0 @@ -42,7 +42,7 @@ Der Vorschauanhang muss ein gültiges Bild sein! - + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 Part-DB1\src\Entity\Base\AbstractCompany.php:0 @@ -87,7 +87,7 @@ Es kann auf jeder Ebene nur ein Objekt mit dem gleichem Namen geben! - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -107,7 +107,7 @@ Wert muss kleiner oder gleich als der typische Wert sein ({{ compared_value }}). - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -127,7 +127,7 @@ Wert muss kleiner als der Maximalwert sein ({{ compared_value }}). - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -147,7 +147,7 @@ Wert muss größer oder gleich dem typischen Wert sein ({{ compared_value }}). - + Part-DB1\src\Entity\UserSystem\User.php:0 Part-DB1\src\Entity\UserSystem\User.php:0 @@ -157,147 +157,207 @@ Es existiert bereits ein Benutzer mit diesem Namen. - + Part-DB1\src\Entity\UserSystem\User.php:0 Part-DB1\src\Entity\UserSystem\User.php:0 user.invalid_username - Der Benutzername darf nur Buchstaben, Zahlen, Unterstriche, Punkte, Plus- oder Minuszeichen enthalten. + Der Benutzername darf nur Buchstaben, Zahlen, Unterstriche, Punkte, Plus- oder Minuszeichen enthalten und darf nicht mit einem @ beginnen. - + obsolete validator.noneofitschild.self - Ein Element kann nicht sein eigenenes übergeordnetes Element sein. + Ein Element kann nicht sein eigenenes übergeordnetes Element sein! - + obsolete - + validator.noneofitschild.children Ein Kindelement kann nicht das übergeordnete Element sein! - + validator.select_valid_category Bitte wählen Sie eine gültige Kategorie. - + validator.part_lot.only_existing Der Lagerort wurde als "nur bestehende Teile" markiert, daher können keine neuen Teile hinzugefügt werden. - + validator.part_lot.location_full.no_increase Lagerort ist voll. Bestand kann nicht erhöht werden (neuer Wert muss kleiner sein als {{old_amount}}). - - + + validator.part_lot.location_full Der Lagerort ist voll, daher können keine neue Teile hinzugefügt werden. - - + + validator.part_lot.single_part Der Lagerort wurde als "Nur ein Bauteil" markiert, daher kann kein neues Bauteil hinzugefügt werden. - + validator.attachment.must_not_be_null Sie müssen ein Dateitypen auswählen! - + validator.orderdetail.supplier_must_not_be_null Sie müssen einen Lieferanten auswählen! - + validator.measurement_unit.use_si_prefix_needs_unit Um SI-Prefixe zu aktivieren, müssen Sie einen Einheitensymbol setzen! - + part.ipn.must_be_unique Die Internal Part Number (IPN) muss einzigartig sein. Der Wert {{value}} wird bereits benutzt! - + validator.project.bom_entry.name_or_part_needed Sie müssen ein Bauteil auswählen, oder einen Namen für ein nicht-Bauteil BOM-Eintrag setzen! - + project.bom_entry.name_already_in_bom Es gibt bereits einen BOM Eintrag mit diesem Namen! - + project.bom_entry.part_already_in_bom Dieses Bauteil existiert bereits in der BOM! - + project.bom_entry.mountnames_quantity_mismatch Die Anzahl der Bestückungsnamen muss mit der Menge der zu bestückenden Bauteile übereinstimmen! - + project.bom_entry.can_not_add_own_builds_part Die BOM eines Projektes kann nicht das eigene Produktionsbauteil enthalten! - + project.bom_has_to_include_all_subelement_parts Die Projekt-BOM muss alle Produktionsbauteile der Unterprojekte enthalten. Bauteil %part_name% des Projektes %project_name% fehlt! - + project.bom_entry.price_not_allowed_on_parts Sie können keinen Preis für Bauteil-BOM-Einträge definieren. Definieren Sie die Preise stattdessen auf dem Bauteil. - + validator.project_build.lot_bigger_than_needed Sie haben mehr zur Entnahme ausgewählt als notwendig. Entfernen Sie die überflüssige Anzahl. - + validator.project_build.lot_smaller_than_needed Sie haben weniger zur Entnahme ausgewählt, als zum Bau notwendig ist! Fügen Sie mehr hinzu. - + part.name.must_match_category_regex Der Bauteilename entspricht nicht dem regulären Ausdruck, der von der Kategorie vorgegeben wurde: %regex% + + + validator.attachment.name_not_blank + Wählen Sie einen Wert, oder laden Sie eine Datei hoch, um dessen Dateiname automatisch als Namen für diesen Anhang zu nutzen. + + + + + validator.part_lot.owner_must_match_storage_location_owner + Der Besitzer dieses Bauteilebestandes und des gewählten Lagerortes müssen übereinstimmen (%owner_name%)! + + + + + validator.part_lot.owner_must_not_be_anonymous + Der Eigentümer darf nicht der anonymous Benutzer sein! + + + + + validator.part_association.must_set_an_value_if_type_is_other + Wenn die Art der Verknüpfung auf "Andere" gesetzt wurde, müssen Sie einen beschreibenden Wert setzen! + + + + + validator.part_association.part_cannot_be_associated_with_itself + Ein Bauteil kann nicht mit sich selbst verknüpft werden! + + + + + validator.part_association.already_exists + Eine Verknüpfung mit diesem Bauteil existiert bereits! + + + + + validator.part_lot.vendor_barcode_must_be_unique + Dieser Lieferanten Barcode Wert wird bereits bei einem anderen Bestand verwendet. Der Barcode muss eindeutig sein! + + + + + validator.year_2038_bug_on_32bit + Aufgrund technischer Beschränkungen ist es nicht möglich, ein Datum nach dem 19.01.2038 auf 32-Bit Systemen auszuwählen! + + + + + validator.invalid_range + Der gegebene Bereich ist nicht gültig! + + + + + validator.google_code.wrong_code + Ungültiger Code. Überprüfen Sie, dass die Authenticator App korrekt eingerichtet ist und dass der Server und das Gerät beide die korrekte Uhrzeit eingestellt haben. + + diff --git a/translations/validators.en.xlf b/translations/validators.en.xlf index e639fa67..95a633e2 100644 --- a/translations/validators.en.xlf +++ b/translations/validators.en.xlf @@ -1,7 +1,7 @@ - + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 Part-DB1\src\Entity\Attachments\AttachmentType.php:0 @@ -42,7 +42,7 @@ The preview attachment must be a valid picture! - + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 Part-DB1\src\Entity\Base\AbstractCompany.php:0 @@ -87,7 +87,7 @@ An element with this name already exists on this level! - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -107,7 +107,7 @@ Value must be lesser or equal the the typical value ({{ compared_value }}). - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -127,7 +127,7 @@ Value must be lesser than the maximum value ({{ compared_value }}). - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -147,7 +147,7 @@ Value must be greater or equal than the typical value ({{ compared_value }}). - + Part-DB1\src\Entity\UserSystem\User.php:0 Part-DB1\src\Entity\UserSystem\User.php:0 @@ -157,147 +157,207 @@ A user with this name is already exisiting - + Part-DB1\src\Entity\UserSystem\User.php:0 Part-DB1\src\Entity\UserSystem\User.php:0 user.invalid_username - The username must contain only letters, numbers, underscores, dots, pluses or minuses. + The username must contain only letters, numbers, underscores, dots, pluses or minuses and must not begin with an @! - + obsolete validator.noneofitschild.self - An element can not be its own parent. + An element can not be its own parent! - + obsolete validator.noneofitschild.children - You can not assign children element as parent (This would cause loops). + You can not assign children element as parent (This would cause loops)! - + validator.select_valid_category Please select a valid category! - + validator.part_lot.only_existing Can not add new parts to this location as it is marked as "Only Existing" - + validator.part_lot.location_full.no_increase Location is full. Amount can not be increased (new value must be smaller than {{ old_amount }}). - + validator.part_lot.location_full Location is full. Can not add new parts to it. - + validator.part_lot.single_part This location can only contain a single part and it is already full! - + validator.attachment.must_not_be_null You must select an attachment type! - + validator.orderdetail.supplier_must_not_be_null You must select an supplier! - + validator.measurement_unit.use_si_prefix_needs_unit To enable SI prefixes, you have to set a unit symbol! - + part.ipn.must_be_unique The internal part number must be unique. {{ value }} is already in use! - + validator.project.bom_entry.name_or_part_needed You have to choose a part for a part BOM entry or set a name for a non-part BOM entry. - + project.bom_entry.name_already_in_bom There is already an BOM entry with this name! - + project.bom_entry.part_already_in_bom This part already exists in the BOM! - + project.bom_entry.mountnames_quantity_mismatch The number of mountnames has to match the BOMs quantity! - + project.bom_entry.can_not_add_own_builds_part You can not add a project's own builds part to the BOM. - + project.bom_has_to_include_all_subelement_parts The project BOM has to include all subprojects builds parts. Part %part_name% of project %project_name% missing! - + project.bom_entry.price_not_allowed_on_parts Prices are not allowed on BOM entries associated with a part. Define the price on the part instead. - + validator.project_build.lot_bigger_than_needed You have selected more quantity to withdraw than needed! Remove unnecessary quantity. - + validator.project_build.lot_smaller_than_needed You have selected less quantity to withdraw than needed for the build! Add additional quantity. - + part.name.must_match_category_regex The part name does not match the regular expression stated by the category: %regex% + + + validator.attachment.name_not_blank + Set a value here, or upload a file to automatically use its filename as name for the attachment. + + + + + validator.part_lot.owner_must_match_storage_location_owner + The owner of this lot must match the owner of the selected storage location (%owner_name%)! + + + + + validator.part_lot.owner_must_not_be_anonymous + A lot owner must not be the anonymous user! + + + + + validator.part_association.must_set_an_value_if_type_is_other + If you set the type to "other", then you have to set a descriptive value for it! + + + + + validator.part_association.part_cannot_be_associated_with_itself + A part can not be associated with itself! + + + + + validator.part_association.already_exists + The association with this part already exists! + + + + + validator.part_lot.vendor_barcode_must_be_unique + This vendor barcode value was already used in another lot. The barcode must be unique! + + + + + validator.year_2038_bug_on_32bit + Due to technical limitations, it is not possible to select dates after the 2038-01-19 on 32-bit systems! + + + + + validator.invalid_range + The given range is not valid! + + + + + validator.google_code.wrong_code + Invalid code. Check that your authenticator app is set up correctly and that both the server and authentication device has the time set correctly. + + diff --git a/translations/validators.hr.xlf b/translations/validators.hr.xlf new file mode 100644 index 00000000..29e32a16 --- /dev/null +++ b/translations/validators.hr.xlf @@ -0,0 +1,363 @@ + + + + + + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\LabelSystem\LabelProfile.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + part.master_attachment.must_be_picture + Pretpregledna fotografija mora biti validna fotografija! + + + + + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + src\Entity\AttachmentType.php:0 + src\Entity\Category.php:0 + src\Entity\Company.php:0 + src\Entity\Device.php:0 + src\Entity\Footprint.php:0 + src\Entity\Group.php:0 + src\Entity\Manufacturer.php:0 + src\Entity\PartsContainingDBElement.php:0 + src\Entity\Storelocation.php:0 + src\Entity\StructuralDBElement.php:0 + src\Entity\Supplier.php:0 + + + structural.entity.unique_name + Element s ovim nazivom već postoji! + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_typical + Vrijednost mora biti manja ili jednaka od tipične vrijednosti ({{ compared_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_max + Vrijednost mora biti manja od maksimalne vrijednosti ({{ compared_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.max_greater_typical + Vrijednost mora biti veća ili jednaka od tipične vrijednosti ({{ compared_value }}). + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + validator.user.username_already_used + Korisnik s ovim imenom već postoji. + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + user.invalid_username + Korisničko ime mora sadržavati samo slova, brojeve, donje crte, točke, pluseve ili minuse! + + + + + obsolete + + + validator.noneofitschild.self + Element ne može biti vlastiti roditelj (element iznad)! + + + + + obsolete + + + validator.noneofitschild.children + Ne možete postaviti podelemente kao parent elemente (Ovo bi uzrokovalo loopove)! + + + + + validator.select_valid_category + Molimo odaberite validnu kategoriju! + + + + + validator.part_lot.only_existing + Ova skladišna lokacija je označena kao "samo postoji", pa joj ne možete dodati novi dio. + + + + + validator.part_lot.location_full.no_increase + Ova skladišna lokacija je označena punom, pa ne možete povećati dostupnu količinu. (Nova maksimalna količina {{ old_amount }}) + + + + + validator.part_lot.location_full + Ova skladišna lokacija je označena punom, pa joj ne možete dodati novi dio + + + + + validator.part_lot.single_part + Ova skladišna lokacija je označena kao "jedan dio", pa joj ne možete dodati novi dio. + + + + + validator.attachment.must_not_be_null + Morate odabrati tip privitka! + + + + + validator.orderdetail.supplier_must_not_be_null + Morate odabrati dobavljača! + + + + + validator.measurement_unit.use_si_prefix_needs_unit + Kako biste omogućili SI prefikse, morate staviti simbol jedinice! + + + + + part.ipn.must_be_unique + Internal part number (IPN) mora biti jedinstven. {{ value }} je već u uporabi! + + + + + validator.project.bom_entry.name_or_part_needed + Morate odabrati dio za unos u BOM ili postaviti naziv za unos koji nije dio. + + + + + project.bom_entry.name_already_in_bom + Već postoji unos u BOM-u sa ovim imenom! + + + + + project.bom_entry.part_already_in_bom + Ovaj dio već postoji u BOM-u! + + + + + project.bom_entry.mountnames_quantity_mismatch + Broj montažnih imena mora biti jednak količini u BOM-u! + + + + + project.bom_entry.can_not_add_own_builds_part + Ne možete dodati vlastiti dio projekta u BOM. + + + + + project.bom_has_to_include_all_subelement_parts + Projektni BOM mora sadržavati sve podprojektove sastavne dijelove. Dio %part_name% projetka %project_name% nedostaje! + + + + + project.bom_entry.price_not_allowed_on_parts + Cijene nisu dozvoljene na sastavnicama u BOM-u asocirane s dijelom. Definirajte cijenu na dijelu. + + + + + validator.project_build.lot_bigger_than_needed + Odabrali ste veću količinu za povlačenje nego što je potrebno! Uklonite nepotrebnu količinu. + + + + + validator.project_build.lot_smaller_than_needed + Odabrali ste manju količinu za povlačenje nego što je potrebno za izgradnju! Dodajte dodatnu količinu. + + + + + part.name.must_match_category_regex + Naziv dijela ne odgovara regularnom izrazu navedenom u kategoriji: %regex% + + + + + validator.attachment.name_not_blank + Postavite vrijednost ovdje, ili prenesite datoteku da automatski koristi njezino ime kao ime privitka. + + + + + validator.part_lot.owner_must_match_storage_location_owner + Vlasnik ove serije dijelova se mora podudarati s vlasnikom odabrane skladišne lokacije (%owner_name%)! + + + + + validator.part_lot.owner_must_not_be_anonymous + Vlasnik serije dijelova ne može biti anonimni korisnik! + + + + + validator.part_association.must_set_an_value_if_type_is_other + Ako postavite tip na "ostalo", onda morate postaviti objašnjivu vrijednost! + + + + + validator.part_association.part_cannot_be_associated_with_itself + Dio se ne može povezati sam sa sobom! + + + + + validator.part_association.already_exists + Asocijacija na ovaj dio već postoji! + + + + + validator.part_lot.vendor_barcode_must_be_unique + Ova vrijednost barkoda dobavljača već je korištena u drugoj seriji dijelova. Barkod mora biti jedinstven! + + + + + validator.year_2038_bug_on_32bit + Zbog tehničkih ograničenja, nije moguće odabrati datume nakon 2038-01-19 na 32-bitnim sustavima! + + + + + validator.invalid_range + Navedeni raspon nije valjan! + + + + + validator.google_code.wrong_code + Neispravan kod. Provjerite je li vaša aplikacija za autentifikaciju ispravno postavljena i jesu li poslužitelj i uređaj za autentifikaciju ispravno postavili vrijeme. + + + + diff --git a/translations/validators.it.xlf b/translations/validators.it.xlf new file mode 100644 index 00000000..7043f4f3 --- /dev/null +++ b/translations/validators.it.xlf @@ -0,0 +1,363 @@ + + + + + + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\LabelSystem\LabelProfile.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + part.master_attachment.must_be_picture + L'anteprima di un allegato deve essere un'immagine valida! + + + + + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + src\Entity\AttachmentType.php:0 + src\Entity\Category.php:0 + src\Entity\Company.php:0 + src\Entity\Device.php:0 + src\Entity\Footprint.php:0 + src\Entity\Group.php:0 + src\Entity\Manufacturer.php:0 + src\Entity\PartsContainingDBElement.php:0 + src\Entity\Storelocation.php:0 + src\Entity\StructuralDBElement.php:0 + src\Entity\Supplier.php:0 + + + structural.entity.unique_name + Un elemento con questo nome esiste già a questo livello! + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_typical + Il valore deve essere inferiore o uguale al valore tipico ({{ compared_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_max + Il valore deve essere inferiore al valore massimo ({{ compared_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.max_greater_typical + Il valore deve essere maggiore o uguale al valore tipico ({{ compared_value }}). + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + validator.user.username_already_used + Esiste già un utente con questo nome + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + user.invalid_username + Il nome utente deve contenere solo lettere, numeri, trattini bassi, punti, più o meno! + + + + + obsolete + + + validator.noneofitschild.self + Un elemento non può essere il proprio elemento padre! + + + + + obsolete + + + validator.noneofitschild.children + Un elemento figlio non può essere anche elemento padre! + + + + + validator.select_valid_category + Selezionare una categoria valida. + + + + + validator.part_lot.only_existing + Questa ubicazione è stata contrassegnata come "solo parti esistenti", quindi non è possibile aggiungere nuove parti. + + + + + validator.part_lot.location_full.no_increase + Questa ubicazione è piena. La quantità non può essere superiore a {{old_amount}}. + + + + + validator.part_lot.location_full + Questa ubicazione è piena, non è possibile aggiungere nuovi componenti. + + + + + validator.part_lot.single_part + L'ubicazione è stata contrassegnata come "singolo componente", quindi non vi si possono aggiungere componenti. + + + + + validator.attachment.must_not_be_null + Bisogna selezionare un tipo di file! + + + + + validator.orderdetail.supplier_must_not_be_null + Bisogna selezionare un fornitore! + + + + + validator.measurement_unit.use_si_prefix_needs_unit + Per attivare i prefissi SI, è necessario impostare un simbolo di unità! + + + + + part.ipn.must_be_unique + Il codice interno (IPN) deve essere univoco. Il valore {{value}} è già in uso! + + + + + validator.project.bom_entry.name_or_part_needed + È necessario selezionare un componente o assegnare un nome ad una voce BOM che non indica un componente! + + + + + project.bom_entry.name_already_in_bom + Esiste già una voce BOM con questo nome! + + + + + project.bom_entry.part_already_in_bom + Questo componente esiste già nella BOM! + + + + + project.bom_entry.mountnames_quantity_mismatch + La quantità dei nomi delle parti deve coincidere con la quantità prevista in BOM! + + + + + project.bom_entry.can_not_add_own_builds_part + Non è possibile aggiungere un componente di produzione interno del progetto alla lista dei materiali (BOM). + + + + + project.bom_has_to_include_all_subelement_parts + Il progetto BOM (lista dei materiali) deve contenere tutti i componenti di produzione dei sottoprogetti. Manca il componente %part_name% del progetto %project_name%! + + + + + project.bom_entry.price_not_allowed_on_parts + Non è possibile definire un prezzo per le voci BOM (lista dei materiali). Definisci invece i prezzi nella scheda del componente. + + + + + validator.project_build.lot_bigger_than_needed + E' stato selezionato più del necessario per il prelievo. Rimuovere la quantità superflua. + + + + + validator.project_build.lot_smaller_than_needed + E' stato selezionato meno del necessario per la costruzione! Aggiungere la quantità necessaria. + + + + + part.name.must_match_category_regex + Il nome del componente non corrisponde all'espressione regolare specificata dalla categoria: %regex% + + + + + validator.attachment.name_not_blank + Seleziona un valore, o carica un file per usare automaticamente il suo nome di file come nome per quell'allegato. + + + + + validator.part_lot.owner_must_match_storage_location_owner + Il proprietario di questo stock di componenti e quello dell'ubicazione scelta devono corrispondere (%owner_name%)! + + + + + validator.part_lot.owner_must_not_be_anonymous + Il proprietario non può essere un utente anonimo! + + + + + validator.part_association.must_set_an_value_if_type_is_other + Se si imposta il tipo su "altro", è necessario definirne un valore descrittivo. + + + + + validator.part_association.part_cannot_be_associated_with_itself + Non è possibile associare un componente a se stesso. + + + + + validator.part_association.already_exists + L'associazione con questo componente esiste già. + + + + + validator.part_lot.vendor_barcode_must_be_unique + Il valore del codice a barre di questo fornitore è già stato utilizzato in un altro lotto. Il codice a barre deve essere unico. + + + + + validator.year_2038_bug_on_32bit + A causa di limitazioni tecniche, non è possibile selezionare date successive al 19-01-2038 su sistemi a 32 bit! + + + + + validator.invalid_range + L'intervallo indicato non è valido! + + + + + validator.google_code.wrong_code + Codice non valido. Controlla che la tua app di autenticazione sia impostata correttamente e che sia il server che il dispositivo di autenticazione abbiano l'ora impostata correttamente. + + + + diff --git a/translations/validators.pl.xlf b/translations/validators.pl.xlf new file mode 100644 index 00000000..6c997798 --- /dev/null +++ b/translations/validators.pl.xlf @@ -0,0 +1,363 @@ + + + + + + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\LabelSystem\LabelProfile.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + part.master_attachment.must_be_picture + Załącznik podglądowy musi zawierać prawidłowe zdjęcie! + + + + + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + src\Entity\AttachmentType.php:0 + src\Entity\Category.php:0 + src\Entity\Company.php:0 + src\Entity\Device.php:0 + src\Entity\Footprint.php:0 + src\Entity\Group.php:0 + src\Entity\Manufacturer.php:0 + src\Entity\PartsContainingDBElement.php:0 + src\Entity\Storelocation.php:0 + src\Entity\StructuralDBElement.php:0 + src\Entity\Supplier.php:0 + + + structural.entity.unique_name + Element o tej nazwie już istnieje na tym poziomie! + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_typical + Wartość musi być mniejsza lub równa wartości nominalnej ({{compare_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_max + Wartość musi być mniejsza niż wartość maksymalna ({{ compare_value }}). + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.max_greater_typical + Wartość musi być większa lub równa wartości nominalnej ({{ compare_value }}). + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + validator.user.username_already_used + Użytkownik o tej nazwie już istnieje + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + user.invalid_username + Nazwa użytkownika może zawierać wyłącznie litery, cyfry, podkreślenia, kropki, plusy i minusy! + + + + + obsolete + + + validator.noneofitschild.self + Element nie może być swoim własnym elementem nadrzędnym! + + + + + obsolete + + + validator.noneofitschild.children + Nie możesz przypisać elementu podrzędnego jako elementu nadrzędnego (spowodowałoby to pętle)! + + + + + validator.select_valid_category + Proszę wybrać prawidłową kategorię! + + + + + validator.part_lot.only_existing + Nie można dodać nowych części do tej lokalizacji, ponieważ jest ona oznaczona jako „Tylko istniejące” + + + + + validator.part_lot.location_full.no_increase + Lokalizacja jest pełna. Ilości nie można zwiększyć (nowa wartość musi być mniejsza niż {{ old_amount }}). + + + + + validator.part_lot.location_full + Lokalizacja jest pełna. Nie można do niego dodawać nowych części. + + + + + validator.part_lot.single_part + Miejsce przechowywania zostało oznaczone jako „Tylko jeden komponent”, więc nie można dodać nowego komponentu. + + + + + validator.attachment.must_not_be_null + Musisz wybrać typ załącznika! + + + + + validator.orderdetail.supplier_must_not_be_null + Musisz wybrać dostawcę! + + + + + validator.measurement_unit.use_si_prefix_needs_unit + Aby włączyć przedrostki SI, musisz ustawić symbol jednostki! + + + + + part.ipn.must_be_unique + Wewnętrzny numer części musi być unikalny. {{value }} jest już w użyciu! + + + + + validator.project.bom_entry.name_or_part_needed + Należy wybrać część dla wpisu BOM części lub ustawić nazwę dla wpisu BOM niebędącego częścią. + + + + + project.bom_entry.name_already_in_bom + Istnieje już pozycja BOM o tej nazwie! + + + + + project.bom_entry.part_already_in_bom + Ta część już istnieje w BOM-ie! + + + + + project.bom_entry.mountnames_quantity_mismatch + Ta część już istnieje w BOM-ie! Liczba nazw montowań musi odpowiadać liczbie BOM-ów! + + + + + project.bom_entry.can_not_add_own_builds_part + Do zestawienia komponentów nie można dodać własnej części konstrukcyjnej projektu. + + + + + project.bom_has_to_include_all_subelement_parts + BOM projektu musi zawierać wszystkie komponenty produkcyjne podprojektów. Brakuje komponentu %part_name% projektu %project_name%! + + + + + project.bom_entry.price_not_allowed_on_parts + Ceny nie są dozwolone we wpisach BOM powiązanych z częścią. Zamiast tego zdefiniuj cenę części. + + + + + validator.project_build.lot_bigger_than_needed + Wybrałeś większą ilość, niż jest to konieczne! Usuń niepotrzebną ilość. + + + + + validator.project_build.lot_smaller_than_needed + Wybrałeś mniejszą ilość do pobrania, niż jest to potrzebne do kompilacji! Dodaj dodatkową ilość. + + + + + part.name.must_match_category_regex + Nazwa części nie pasuje do wyrażenia regularnego określonego w kategorii: %regex% + + + + + validator.attachment.name_not_blank + Ustaw tutaj wartość lub prześlij plik, aby automatycznie użyć jego nazwy jako nazwy załącznika. + + + + + validator.part_lot.owner_must_match_storage_location_owner + Właściciel tego zestawu komponentów i wybrana lokalizacja przechowywania muszą być zgodne (%owner_name%)! + + + + + validator.part_lot.owner_must_not_be_anonymous + Właściciel nie może być anonimowym użytkownikiem! + + + + + validator.part_association.must_set_an_value_if_type_is_other + Jeśli ustawisz typ na „inny”, musisz ustawić dla niego wartość opisową! + + + + + validator.part_association.part_cannot_be_associated_with_itself + Część nie może być powiązana sama ze sobą! + + + + + validator.part_association.already_exists + Powiązanie z tą częścią już istnieje! + + + + + validator.part_lot.vendor_barcode_must_be_unique + Ta wartość kodu kreskowego dostawcy jest już używana w innym magazynie. Kod kreskowy musi być unikalny! + + + + + validator.year_2038_bug_on_32bit + Ze względu na ograniczenia techniczne nie jest możliwe wybranie daty po 19 stycznia 2038 w systemach 32-bitowych! + + + + + validator.invalid_range + Podany zakres jest nieprawidłowy! + + + + + validator.google_code.wrong_code + Nieprawidłowy kod. Sprawdź, czy aplikacja uwierzytelniająca jest poprawnie skonfigurowana i czy zarówno serwer, jak i urządzenie uwierzytelniające mają poprawnie ustawiony czas. + + + + diff --git a/translations/validators.ru.xlf b/translations/validators.ru.xlf index ce055f1a..0f97c478 100644 --- a/translations/validators.ru.xlf +++ b/translations/validators.ru.xlf @@ -1,7 +1,7 @@ - + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 Part-DB1\src\Entity\Attachments\AttachmentType.php:0 @@ -42,7 +42,7 @@ Предварительный просмотр возможен только для картинок! - + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 Part-DB1\src\Entity\Base\AbstractCompany.php:0 @@ -87,7 +87,7 @@ Элемент с таким именем уже существует на данном уровне! - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -107,7 +107,7 @@ Значение должно быть меньше или равно типичного значения ({{ compared_value }}). - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -127,7 +127,7 @@ Значение должно быть меньше максимального значения ({{ compared_value }}). - + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 @@ -147,7 +147,7 @@ Значение должно быть больше или равно типичного значения ({{ compared_value }}). - + Part-DB1\src\Entity\UserSystem\User.php:0 Part-DB1\src\Entity\UserSystem\User.php:0 @@ -157,7 +157,7 @@ Пользователь с таким именем уже существует - + Part-DB1\src\Entity\UserSystem\User.php:0 Part-DB1\src\Entity\UserSystem\User.php:0 @@ -167,7 +167,7 @@ Имя пользователя должно содержать только буквы, цифры, знак подчеркивания, знаки препинания, плюс и минус. - + obsolete @@ -176,7 +176,7 @@ Элемент не может быть собственным родителем - + obsolete @@ -185,23 +185,179 @@ Родитель не может быть дочерним по отношению к себе - + + + validator.select_valid_category + Пожалуйста, выберите действительную категорию! + + + validator.part_lot.only_existing Вы не можете добавлять новые компоненты в хранилище которое помечено как "только существующие". - + + + validator.part_lot.location_full.no_increase + Место хранения заполнено. Запас не может быть увеличен (новое значение должно быть меньше {{old_amount}}). + + + validator.part_lot.location_full Вы не можете добавлять новые компоненты в хранилище которое отмечено как "полное". - + validator.part_lot.single_part Вы не можете добавлять новые компоненты в хранилище которое отмечено как "единственный компонент". + + + validator.attachment.must_not_be_null + Вы должны выбрать тип файла! + + + + + validator.orderdetail.supplier_must_not_be_null + Вы должны выбрать поставщика! + + + + + validator.measurement_unit.use_si_prefix_needs_unit + Чтобы включить префиксы СИ, необходимо установить символ единицы! + + + + + part.ipn.must_be_unique + Внутренний номер детали (IPN) должен быть уникальным. Значение {{value}} уже используется! + + + + + validator.project.bom_entry.name_or_part_needed + Вам необходимо выбрать компонент или задать имя для BOM, не относящейся к компоненту! + + + + + project.bom_entry.name_already_in_bom + Запись BOM с таким именем уже существует! + + + + + project.bom_entry.part_already_in_bom + Этот компонент уже существует в BOM! + + + + + project.bom_entry.mountnames_quantity_mismatch + Количество наименований сборок должно соответствовать количеству собираемых компонентов! + + + + + project.bom_entry.can_not_add_own_builds_part + BOM проекта не может содержать собственную производственную составляющую! + + + + + project.bom_has_to_include_all_subelement_parts + BOM проекта должна содержать все производственные компоненты подпроектов. Компонент %part_name% проекта %project_name% отсутствует! + + + + + project.bom_entry.price_not_allowed_on_parts + Невозможно определить цену для BOM записей компонента. Вместо этого определите цену на сам компонент. + + + + + validator.project_build.lot_bigger_than_needed + Вы выбрали для удаления больше, чем необходимо. Уберите лишнее количество. + + + + + validator.project_build.lot_smaller_than_needed + Они выбрали меньшее количество, чем необходимо для сборки! Добавьте больше. + + + + + part.name.must_match_category_regex + Имя компонента не соответствует регулярному выражению, указанному в категории: %regex% + + + + + validator.attachment.name_not_blank + Выберите значение или загрузите файл, чтобы автоматически использовать его имя в качестве имени для этого вложения. + + + + + validator.part_lot.owner_must_match_storage_location_owner + Владелец этого инвентаря и выбранное место хранения должны совпадать (%owner_name%)! + + + + + validator.part_lot.owner_must_not_be_anonymous + Владелец не может быть анонимным пользователем! + + + + + validator.part_association.must_set_an_value_if_type_is_other + Если тип ссылки установлен на «Другое», необходимо установить описательное значение! + + + + + validator.part_association.part_cannot_be_associated_with_itself + Компонент не может быть связан сам с собой! + + + + + validator.part_association.already_exists + Ссылка на этот компонент уже существует! + + + + + validator.part_lot.vendor_barcode_must_be_unique + Штрих-код этого поставщика уже используется в другом инвентаре. Штрих-код должен быть уникальным! + + + + + validator.year_2038_bug_on_32bit + Из-за технических ограничений невозможно выбрать дату позднее чем 19 января 2038 года на 32-битных системах! + + + + + validator.invalid_range + Указанный диапазон недействителен! + + + + + validator.google_code.wrong_code + Неверный код. Проверьте, что приложение аутентификации настроено правильно и что на сервере и устройстве аутентификации установлено правильное время. + + diff --git a/translations/validators.zh.xlf b/translations/validators.zh.xlf new file mode 100644 index 00000000..08c9f014 --- /dev/null +++ b/translations/validators.zh.xlf @@ -0,0 +1,351 @@ + + + + + + Part-DB1\src\Entity\Attachments\AttachmentContainingDBElement.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\LabelSystem\LabelProfile.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Part.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + part.master_attachment.must_be_picture + 预览附件必须是有效的图片 + + + + + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + Part-DB1\src\Entity\Attachments\AttachmentType.php:0 + Part-DB1\src\Entity\Base\AbstractCompany.php:0 + Part-DB1\src\Entity\Base\AbstractPartsContainingDBElement.php:0 + Part-DB1\src\Entity\Base\AbstractStructuralDBElement.php:0 + Part-DB1\src\Entity\Devices\Device.php:0 + Part-DB1\src\Entity\Parts\Category.php:0 + Part-DB1\src\Entity\Parts\Footprint.php:0 + Part-DB1\src\Entity\Parts\Manufacturer.php:0 + Part-DB1\src\Entity\Parts\MeasurementUnit.php:0 + Part-DB1\src\Entity\Parts\Storelocation.php:0 + Part-DB1\src\Entity\Parts\Supplier.php:0 + Part-DB1\src\Entity\PriceInformations\Currency.php:0 + Part-DB1\src\Entity\UserSystem\Group.php:0 + src\Entity\AttachmentType.php:0 + src\Entity\Category.php:0 + src\Entity\Company.php:0 + src\Entity\Device.php:0 + src\Entity\Footprint.php:0 + src\Entity\Group.php:0 + src\Entity\Manufacturer.php:0 + src\Entity\PartsContainingDBElement.php:0 + src\Entity\Storelocation.php:0 + src\Entity\StructuralDBElement.php:0 + src\Entity\Supplier.php:0 + + + structural.entity.unique_name + 相同层下已存在同名元素 + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_typical + 值必须小于或等于标称值 ({{compare_value}})。 + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.min_lesser_max + 值必须小于最大值 ({{compare_value}})。 + + + + + Part-DB1\src\Entity\Parameters\AbstractParameter.php:0 + Part-DB1\src\Entity\Parameters\AttachmentTypeParameter.php:0 + Part-DB1\src\Entity\Parameters\CategoryParameter.php:0 + Part-DB1\src\Entity\Parameters\CurrencyParameter.php:0 + Part-DB1\src\Entity\Parameters\DeviceParameter.php:0 + Part-DB1\src\Entity\Parameters\FootprintParameter.php:0 + Part-DB1\src\Entity\Parameters\GroupParameter.php:0 + Part-DB1\src\Entity\Parameters\ManufacturerParameter.php:0 + Part-DB1\src\Entity\Parameters\MeasurementUnitParameter.php:0 + Part-DB1\src\Entity\Parameters\PartParameter.php:0 + Part-DB1\src\Entity\Parameters\StorelocationParameter.php:0 + Part-DB1\src\Entity\Parameters\SupplierParameter.php:0 + + + parameters.validator.max_greater_typical + 值必须大于或等于标称值 ({{compare_value}})。 + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + validator.user.username_already_used + 已存在同名用户 + + + + + Part-DB1\src\Entity\UserSystem\User.php:0 + Part-DB1\src\Entity\UserSystem\User.php:0 + + + user.invalid_username + 用户名只能包含字母、数字、下划线、点、加号或减号。 + + + + + obsolete + + + validator.noneofitschild.self + 一个元素不能是它自己的父元素。 + + + + + obsolete + + + validator.noneofitschild.children + 不能将子元素指定为父元素(会导致循环)。 + + + + + validator.select_valid_category + 请选择一个有效的类别。 + + + + + validator.part_lot.only_existing + 无法将新部件添加到此位置,因为它被标记为 "仅现有" + + + + + validator.part_lot.location_full.no_increase + 位置已满。数量无法增加(增加值必须小于 {{ old_amount }})。 + + + + + validator.part_lot.location_full + 位置已满。无法添加新部件。 + + + + + validator.part_lot.single_part + 该位置只能储存一个部件。 + + + + + validator.attachment.must_not_be_null + 必须选择附件类型。 + + + + + validator.orderdetail.supplier_must_not_be_null + 必须选择供应商。 + + + + + validator.measurement_unit.use_si_prefix_needs_unit + 要启用 SI 前缀,必须设置单位符号。 + + + + + part.ipn.must_be_unique + 内部部件号是唯一的。{{ value }} 已被使用! + + + + + validator.project.bom_entry.name_or_part_needed + 您必须为 BOM 条目选择部件,或为非部件 BOM 条目设置名称。 + + + + + project.bom_entry.name_already_in_bom + 已存在具有该名称的 BOM 条目。 + + + + + project.bom_entry.part_already_in_bom + 该部件已存在于 BOM 中。 + + + + + project.bom_entry.mountnames_quantity_mismatch + 挂载名称的数量必须与 BOM 数量匹配。 + + + + + project.bom_entry.can_not_add_own_builds_part + 您无法将项目自己的生产映射部件添加到 BOM 中。 + + + + + project.bom_has_to_include_all_subelement_parts + 项目 BOM 必须包括所有子项目生产的部件。项目 %project_name% 的 %part_name% 部件丢失。 + + + + + project.bom_entry.price_not_allowed_on_parts + 与部件关联的 BOM 条目上不允许有价格。请在部件上定义价格。 + + + + + validator.project_build.lot_bigger_than_needed + 选择的提取数量超出所需数量。 + + + + + validator.project_build.lot_smaller_than_needed + 选择的提取数量少于所需数量。 + + + + + part.name.must_match_category_regex + 部件名称与类别指定的正则表达式不匹配:%regex% + + + + + validator.attachment.name_not_blank + 手动设置值,或上传文件使用其文件名作为附件的名称。 + + + + + validator.part_lot.owner_must_match_storage_location_owner + 该 批次的所有者 必须与 所选存储位置的所有者(%owner_name%) 匹配! + + + + + validator.part_lot.owner_must_not_be_anonymous + 批次所有者不能是匿名用户。 + + + + + validator.part_association.must_set_an_value_if_type_is_other + 如果将类型设置为 "other" 则必须为其设置一个描述性值。 + + + + + validator.part_association.part_cannot_be_associated_with_itself + 部件不能与自己关联。 + + + + + validator.part_association.already_exists + 与此部件的关联已存在。 + + + + + validator.part_lot.vendor_barcode_must_be_unique + 该供应商条码已在另一批次中使用。条形码必须是唯一的 + + + + + validator.year_2038_bug_on_32bit + 由于技术限制,在32位系统中无法选择2038年1月19日之后的日期! + + + + diff --git a/webpack.config.js b/webpack.config.js index 19596143..43e04997 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -24,7 +24,7 @@ var Encore = require('@symfony/webpack-encore'); const zlib = require('zlib'); const CompressionPlugin = require("compression-webpack-plugin"); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -const CKEditorWebpackPlugin = require( '@ckeditor/ckeditor5-dev-webpack-plugin' ); +const { CKEditorTranslationsPlugin } = require( '@ckeditor/ckeditor5-dev-translations' ); const { styles } = require( '@ckeditor/ckeditor5-dev-utils' ); // Manually configure the runtime environment if not already configured yet by the "encore" command. @@ -36,17 +36,16 @@ if (!Encore.isRuntimeEnvironmentConfigured()) { Encore // directory where compiled assets will be stored .setOutputPath('public/build/') - // public path used by the web server to access the output path - .setPublicPath('/build') - // only needed for CDN's or subdirectory deploy + // Do not use a / prefix, here as that would break asset loading when serving Part-DB under a prefix! + .setPublicPath('build') + // only needed for CDN's or subdirectory deploy (this should not be needeed, as we use auto public path) //.setManifestKeyPrefix('build/') - /** - * If you are putting Part-DB into a sub directory you have to uncomment these lines and - * replace "part-db/" with your path to Part-DB - */ - //.setPublicPath('/part-db/build') - //.setManifestKeyPrefix('build/') + //Use build/ as public path inisde the manifest.json (instead of "auto") + //Without this all webpack managed stuff which is loaded via the assets() twig function will not work + .configureManifestPlugin(options => { + options.publicPath = 'build/'; + }) /* * ENTRY CONFIG @@ -60,17 +59,21 @@ Encore .addEntry('app', './assets/js/app.js') .addEntry('webauthn_tfa', './assets/js/webauthn_tfa.js') + //Configure to just output the zxing wasm file, without parsing it + .addRule({ + test: /zxing_reader\.wasm$/, + type: "asset/resource" + }) - - - // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js) - .enableStimulusBridge('./assets/controllers.json') //.addEntry('page1', './assets/js/page1.js') //.addEntry('page2', './assets/js/page2.js') // When enabled, Webpack "splits" your files into smaller pieces for greater optimization. .splitEntryChunks() + // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js) + .enableStimulusBridge('./assets/controllers.json') + // will require an extra script tag for runtime.js // but, you probably want this, unless you're building a single-page app .enableSingleRuntimeChunk() @@ -117,7 +120,7 @@ Encore // uncomment if you're having problems with a jQuery plugin .autoProvidejQuery() - .addPlugin( new CKEditorWebpackPlugin( { + .addPlugin( new CKEditorTranslationsPlugin( { // See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html language: 'en', addMainLanguageTranslationsToAllAssets: true, @@ -191,3 +194,11 @@ if (Encore.isDev()) { module.exports = Encore.getWebpackConfig(); + +//Enable webassembly support +module.exports.experiments = module.exports.experiments || {}; +module.exports.experiments.asyncWebAssembly = true; + +//Enable webpack auto public path +//We do it here to supress a warning caused by webpack Encore +module.exports.output.publicPath = 'auto'; diff --git a/yarn.lock b/yarn.lock index 78fb609e..1d2aa6a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,972 +2,985 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@algolia/autocomplete-core@1.19.1": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.19.1.tgz#70b6bea6f6549be6f9f25a48f4e7001eb3dd67fa" + integrity sha512-MeZZN1NSPgfp2zhiaCyAW02jOWMftCJ06qoeEVEQ8v+kMlXL15SUYBpQwj7Gd+nV46KHqDrW+g5EGqhCsX6zWg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" + "@algolia/autocomplete-plugin-algolia-insights" "1.19.1" + "@algolia/autocomplete-shared" "1.19.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@algolia/autocomplete-js@1.19.1", "@algolia/autocomplete-js@^1.17.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-js/-/autocomplete-js-1.19.1.tgz#14bb7f0cd94f87eeb2e01cbb65eb3d7efb572a23" + integrity sha512-4uVK+2GQ3JbnaOtX2eD6mYew62jHY5hJ/0ZPn/0awaVnvlqpvXo4iRo4RNbrZ/7GyMNNlsBeE1p4PZJ+hDB5UQ== dependencies: - "@babel/highlight" "^7.18.6" + "@algolia/autocomplete-core" "1.19.1" + "@algolia/autocomplete-preset-algolia" "1.19.1" + "@algolia/autocomplete-shared" "1.19.1" + htm "^3.1.1" + preact "^10.13.2" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" - integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== +"@algolia/autocomplete-plugin-algolia-insights@1.19.1": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.1.tgz#2089b8aa239180e2b71db3764edee272ac7c855e" + integrity sha512-TPVerIGKbfOwd69ZNrriI6voKRwwj6Vmy4tD12/3RCh8zdvDOdRvc7LxeKPtgwJzavZkfH7nK9pb+4E+PCdgdg== + dependencies: + "@algolia/autocomplete-shared" "1.19.1" + +"@algolia/autocomplete-plugin-recent-searches@^1.17.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-plugin-recent-searches/-/autocomplete-plugin-recent-searches-1.19.1.tgz#83beacfd16c618f0c3a55c313994dd0c0c4b22aa" + integrity sha512-U1jxgqqwQb5xBxu4nQ9esLi9r4Z18mw2M4xbl/U6ztE/lNSzEqh4IM9q43chKXXwZfKuBL5k6h/0F4PX0aykcA== + dependencies: + "@algolia/autocomplete-core" "1.19.1" + "@algolia/autocomplete-js" "1.19.1" + "@algolia/autocomplete-preset-algolia" "1.19.1" + "@algolia/autocomplete-shared" "1.19.1" + +"@algolia/autocomplete-preset-algolia@1.19.1": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.19.1.tgz#800162ad98dc57e8d26242b748702f79ea24279e" + integrity sha512-8VTPeE7UwyfvNF44IEkaNOnRoofv0Ejtsqg3tu9T8gj+pbVMj8NBErlbYD88es893EseqJj7oWAhqcq1bgnxcQ== + dependencies: + "@algolia/autocomplete-shared" "1.19.1" + +"@algolia/autocomplete-shared@1.19.1": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.1.tgz#ce15e1fb682a2ea0ddc8e19814d4c5eaf1457355" + integrity sha512-/GcOv70emuVdSHBAERW7/r4czMzJROkgLOl45+ugyxK79RzSkXV2esY638qSo6mzC7EVwglSJ8BQ0kAVKVxMjA== + +"@algolia/autocomplete-theme-classic@^1.17.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-theme-classic/-/autocomplete-theme-classic-1.19.1.tgz#900bb530d3844eeea1238acf6056a13eb5cf953a" + integrity sha512-cNYN9Zosz+4hS+N8bE3yaKu4N0JD0CiNoNEqtZbemI+2xDSDo2MjRVVwWRz7gBn7wyimfp90apWgRJ3mnuGvDw== + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== + dependencies: + "@babel/helper-validator-identifier" "^7.27.1" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.27.2.tgz#4183f9e642fd84e74e3eea7ffa93a412e3b102c9" + integrity sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ== "@babel/core@^7.19.6": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.27.1.tgz#89de51e86bd12246003e3524704c49541b16c3e6" + integrity sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ== dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" - convert-source-map "^1.7.0" + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.27.1" + "@babel/helper-compilation-targets" "^7.27.1" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helpers" "^7.27.1" + "@babel/parser" "^7.27.1" + "@babel/template" "^7.27.1" + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" + json5 "^2.2.3" + semver "^6.3.1" -"@babel/generator@^7.20.7": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== +"@babel/generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.1.tgz#862d4fad858f7208edd487c28b58144036b76230" + integrity sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w== dependencies: - "@babel/types" "^7.20.7" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" + "@babel/parser" "^7.27.1" + "@babel/types" "^7.27.1" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== +"@babel/helper-annotate-as-pure@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz#4345d81a9a46a6486e24d069469f13e60445c05d" + integrity sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.27.1" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== +"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" + integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" - -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" + "@babel/compat-data" "^7.27.2" + "@babel/helper-validator-option" "^7.27.1" + browserslist "^4.24.0" lru-cache "^5.1.1" - semver "^6.3.0" + semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" - integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== +"@babel/helper-create-class-features-plugin@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz#5bee4262a6ea5ddc852d0806199eb17ca3de9281" + integrity sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.27.1" + "@babel/helper-member-expression-to-functions" "^7.27.1" + "@babel/helper-optimise-call-expression" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + "@babel/traverse" "^7.27.1" + semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" - integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz#05b0882d97ba1d4d03519e4bce615d70afa18c53" + integrity sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.2.1" + "@babel/helper-annotate-as-pure" "^7.27.1" + regexpu-core "^6.2.0" + semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" - integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== +"@babel/helper-define-polyfill-provider@^0.6.3", "@babel/helper-define-polyfill-provider@^0.6.4": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz#15e8746368bfa671785f5926ff74b3064c291fab" + integrity sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw== dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" - semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-member-expression-to-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz#ea1211276be93e798ce19037da6f06fbb994fa44" + integrity sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA== + dependencies: + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" -"@babel/helper-explode-assignable-expression@^7.18.6": +"@babel/helper-module-imports@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== + dependencies: + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/helper-module-transforms@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz#e1663b8b71d2de948da5c4fb2a20ca4f3ec27a6f" + integrity sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g== + dependencies: + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@babel/traverse" "^7.27.1" + +"@babel/helper-optimise-call-expression@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" + integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== + dependencies: + "@babel/types" "^7.27.1" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== + +"@babel/helper-remap-async-to-generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz#4601d5c7ce2eb2aea58328d43725523fcd362ce6" + integrity sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.27.1" + "@babel/helper-wrap-function" "^7.27.1" + "@babel/traverse" "^7.27.1" + +"@babel/helper-replace-supers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" + integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.27.1" + "@babel/helper-optimise-call-expression" "^7.27.1" + "@babel/traverse" "^7.27.1" + +"@babel/helper-skip-transparent-expression-wrappers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" + integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== + dependencies: + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + +"@babel/helper-validator-option@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== + +"@babel/helper-wrap-function@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz#b88285009c31427af318d4fe37651cd62a142409" + integrity sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ== + dependencies: + "@babel/template" "^7.27.1" + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/helpers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.1.tgz#ffc27013038607cdba3288e692c3611c06a18aa4" + integrity sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ== + dependencies: + "@babel/template" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/parser@^7.18.9", "@babel/parser@^7.27.1", "@babel/parser@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.2.tgz#577518bedb17a2ce4212afd052e01f7df0941127" + integrity sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw== + dependencies: + "@babel/types" "^7.27.1" + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz#61dd8a8e61f7eb568268d1b5f129da3eee364bf9" + integrity sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.27.1" + +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz#43f70a6d7efd52370eefbdf55ae03d91b293856d" + integrity sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz#beb623bd573b8b6f3047bd04c32506adc3e58a72" + integrity sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz#e134a5479eb2ba9c02714e8c1ebf1ec9076124fd" + integrity sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + "@babel/plugin-transform-optional-chaining" "^7.27.1" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz#bb1c25af34d75115ce229a1de7fa44bf8f955670" + integrity sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.27.1" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + +"@babel/plugin-syntax-import-assertions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" + integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-syntax-import-attributes@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-member-expression-to-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" - integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== - dependencies: - "@babel/types" "^7.20.7" - -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" - -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" - integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== - dependencies: - "@babel/types" "^7.20.0" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helper-wrap-function@^7.18.9": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" - integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== - dependencies: - "@babel/helper-function-name" "^7.19.0" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - -"@babel/helpers@^7.20.7": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" - integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.13" - "@babel/types" "^7.20.7" - -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.18.9", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" - integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" - integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.7" - -"@babel/plugin-proposal-async-generator-functions@^7.20.1": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-class-static-block@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz#92592e9029b13b15be0f7ce6a7aedc2879ca45a7" - integrity sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" - integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" - integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" - integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - -"@babel/plugin-proposal-optional-catch-binding@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" - integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" - integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz#309c7668f2263f1c711aa399b5a9a6291eef6135" - integrity sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" - integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== +"@babel/plugin-transform-arrow-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz#6e2061067ba3ab0266d834a9f94811196f2aba9a" + integrity sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== +"@babel/plugin-transform-async-generator-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.27.1.tgz#ca433df983d68e1375398e7ca71bf2a4f6fd89d7" + integrity sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA== dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-remap-async-to-generator" "^7.27.1" + "@babel/traverse" "^7.27.1" -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== +"@babel/plugin-transform-async-to-generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz#9a93893b9379b39466c74474f55af03de78c66e7" + integrity sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-remap-async-to-generator" "^7.27.1" -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== +"@babel/plugin-transform-block-scoped-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz#558a9d6e24cf72802dd3b62a4b51e0d62c0f57f9" + integrity sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== +"@babel/plugin-transform-block-scoping@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.1.tgz#bc0dbe8ac6de5602981ba58ef68c6df8ef9bfbb3" + integrity sha512-QEcFlMl9nGTgh1rn2nIeU5bkfb9BAjaQcWbiP4LvKxUot52ABcTkpcyJ7f2Q2U2RuQ84BNLgts3jRme2dTx6Fw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" - integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== +"@babel/plugin-transform-class-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925" + integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-create-class-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== +"@babel/plugin-transform-class-static-block@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz#7e920d5625b25bbccd3061aefbcc05805ed56ce4" + integrity sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-create-class-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== +"@babel/plugin-transform-classes@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz#03bb04bea2c7b2f711f0db7304a8da46a85cced4" + integrity sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== - dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - -"@babel/plugin-transform-block-scoped-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-block-scoping@^7.20.2": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz#3e1b2aa9cbbe1eb8d644c823141a9c5c2a22392d" - integrity sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-classes@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" - integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.27.1" + "@babel/helper-compilation-targets" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" + "@babel/traverse" "^7.27.1" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== +"@babel/plugin-transform-computed-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz#81662e78bf5e734a97982c2b7f0a793288ef3caa" + integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/template" "^7.20.7" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/template" "^7.27.1" -"@babel/plugin-transform-destructuring@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== +"@babel/plugin-transform-destructuring@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.1.tgz#d5916ef7089cb254df0418ae524533c1b72ba656" + integrity sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== +"@babel/plugin-transform-dotall-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz#aa6821de864c528b1fecf286f0a174e38e826f4d" + integrity sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-duplicate-keys@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== +"@babel/plugin-transform-duplicate-keys@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz#f1fbf628ece18e12e7b32b175940e68358f546d1" + integrity sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-exponentiation-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz#5043854ca620a94149372e69030ff8cb6a9eb0ec" + integrity sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-for-of@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== +"@babel/plugin-transform-dynamic-import@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz#4c78f35552ac0e06aa1f6e3c573d67695e8af5a4" + integrity sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== +"@babel/plugin-transform-exponentiation-operator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz#fc497b12d8277e559747f5a3ed868dd8064f83e1" + integrity sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== +"@babel/plugin-transform-export-namespace-from@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz#71ca69d3471edd6daa711cf4dfc3400415df9c23" + integrity sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-member-expression-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== +"@babel/plugin-transform-for-of@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz#bc24f7080e9ff721b63a70ac7b2564ca15b6c40a" + integrity sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" -"@babel/plugin-transform-modules-amd@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" - integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== +"@babel/plugin-transform-function-name@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz#4d0bf307720e4dce6d7c30fcb1fd6ca77bdeb3a7" + integrity sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-compilation-targets" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.27.1" -"@babel/plugin-transform-modules-commonjs@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" - integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== +"@babel/plugin-transform-json-strings@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz#a2e0ce6ef256376bd527f290da023983527a4f4c" + integrity sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-modules-systemjs@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" - integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== +"@babel/plugin-transform-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz#baaefa4d10a1d4206f9dcdda50d7d5827bb70b24" + integrity sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA== dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-modules-umd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== +"@babel/plugin-transform-logical-assignment-operators@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz#890cb20e0270e0e5bebe3f025b434841c32d5baa" + integrity sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== +"@babel/plugin-transform-member-expression-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz#37b88ba594d852418e99536f5612f795f23aeaf9" + integrity sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-new-target@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== +"@babel/plugin-transform-modules-amd@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz#a4145f9d87c2291fe2d05f994b65dba4e3e7196f" + integrity sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-object-super@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== +"@babel/plugin-transform-modules-commonjs@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" + integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" - integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== +"@babel/plugin-transform-modules-systemjs@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz#00e05b61863070d0f3292a00126c16c0e024c4ed" + integrity sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@babel/traverse" "^7.27.1" -"@babel/plugin-transform-property-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== +"@babel/plugin-transform-modules-umd@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz#63f2cf4f6dc15debc12f694e44714863d34cd334" + integrity sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-regenerator@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== +"@babel/plugin-transform-named-capturing-groups-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz#f32b8f7818d8fc0cc46ee20a8ef75f071af976e1" + integrity sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - regenerator-transform "^0.15.1" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-reserved-words@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== +"@babel/plugin-transform-new-target@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz#259c43939728cad1706ac17351b7e6a7bea1abeb" + integrity sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-shorthand-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== +"@babel/plugin-transform-nullish-coalescing-operator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz#4f9d3153bf6782d73dd42785a9d22d03197bc91d" + integrity sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-spread@^7.19.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== +"@babel/plugin-transform-numeric-separator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz#614e0b15cc800e5997dadd9bd6ea524ed6c819c6" + integrity sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-sticky-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== +"@babel/plugin-transform-object-rest-spread@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.2.tgz#67f9ab822347aa2bcee91e8996763da79bdea973" + integrity sha512-AIUHD7xJ1mCrj3uPozvtngY3s0xpv7Nu7DoUSnzNY6Xam1Cy4rUznR//pvMHOhQ4AvbCexhbqXCtpxGHOGOO6g== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/plugin-transform-destructuring" "^7.27.1" + "@babel/plugin-transform-parameters" "^7.27.1" -"@babel/plugin-transform-template-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== +"@babel/plugin-transform-object-super@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz#1c932cd27bf3874c43a5cac4f43ebf970c9871b5" + integrity sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" -"@babel/plugin-transform-typeof-symbol@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== +"@babel/plugin-transform-optional-catch-binding@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz#84c7341ebde35ccd36b137e9e45866825072a30c" + integrity sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== +"@babel/plugin-transform-optional-chaining@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz#874ce3c4f06b7780592e946026eb76a32830454f" + integrity sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" -"@babel/plugin-transform-unicode-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== +"@babel/plugin-transform-parameters@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz#80334b54b9b1ac5244155a0c8304a187a618d5a7" + integrity sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-private-methods@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz#fdacbab1c5ed81ec70dfdbb8b213d65da148b6af" + integrity sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-private-property-in-object@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz#4dbbef283b5b2f01a21e81e299f76e35f900fb11" + integrity sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-property-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz#07eafd618800591e88073a0af1b940d9a42c6424" + integrity sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-regenerator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.1.tgz#0a471df9213416e44cd66bf67176b66f65768401" + integrity sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-regexp-modifiers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz#df9ba5577c974e3f1449888b70b76169998a6d09" + integrity sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-reserved-words@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz#40fba4878ccbd1c56605a4479a3a891ac0274bb4" + integrity sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-shorthand-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz#532abdacdec87bfee1e0ef8e2fcdee543fe32b90" + integrity sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-spread@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz#1a264d5fc12750918f50e3fe3e24e437178abb08" + integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + +"@babel/plugin-transform-sticky-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz#18984935d9d2296843a491d78a014939f7dcd280" + integrity sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-template-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz#1a0eb35d8bb3e6efc06c9fd40eb0bcef548328b8" + integrity sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-typeof-symbol@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz#70e966bb492e03509cf37eafa6dcc3051f844369" + integrity sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-unicode-escapes@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz#3e3143f8438aef842de28816ece58780190cf806" + integrity sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-unicode-property-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz#bdfe2d3170c78c5691a3c3be934c8c0087525956" + integrity sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-unicode-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz#25948f5c395db15f609028e370667ed8bae9af97" + integrity sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-unicode-sets-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz#6ab706d10f801b5c72da8bb2548561fa04193cd1" + integrity sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/preset-env@^7.19.4": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" - integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.27.2.tgz#106e6bfad92b591b1f6f76fd4cf13b7725a7bf9a" + integrity sha512-Ma4zSuYSlGNRlCLO+EAzLnCmJK2vdstgv+n7aUP+/IKZrOfWHOJVdSJtuub8RzHTj3ahD37k5OKJWvzf16TQyQ== dependencies: - "@babel/compat-data" "^7.20.1" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.20.1" - "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.18.6" - "@babel/plugin-proposal-dynamic-import" "^7.18.6" - "@babel/plugin-proposal-export-namespace-from" "^7.18.9" - "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" - "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.20.2" - "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-private-methods" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object" "^7.18.6" - "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.20.0" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.18.6" - "@babel/plugin-transform-async-to-generator" "^7.18.6" - "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.20.2" - "@babel/plugin-transform-classes" "^7.20.2" - "@babel/plugin-transform-computed-properties" "^7.18.9" - "@babel/plugin-transform-destructuring" "^7.20.2" - "@babel/plugin-transform-dotall-regex" "^7.18.6" - "@babel/plugin-transform-duplicate-keys" "^7.18.9" - "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.18.8" - "@babel/plugin-transform-function-name" "^7.18.9" - "@babel/plugin-transform-literals" "^7.18.9" - "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.19.6" - "@babel/plugin-transform-modules-commonjs" "^7.19.6" - "@babel/plugin-transform-modules-systemjs" "^7.19.6" - "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" - "@babel/plugin-transform-new-target" "^7.18.6" - "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.20.1" - "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.18.6" - "@babel/plugin-transform-reserved-words" "^7.18.6" - "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.19.0" - "@babel/plugin-transform-sticky-regex" "^7.18.6" - "@babel/plugin-transform-template-literals" "^7.18.9" - "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.10" - "@babel/plugin-transform-unicode-regex" "^7.18.6" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.20.2" - babel-plugin-polyfill-corejs2 "^0.3.3" - babel-plugin-polyfill-corejs3 "^0.6.0" - babel-plugin-polyfill-regenerator "^0.4.1" - core-js-compat "^3.25.1" - semver "^6.3.0" + "@babel/compat-data" "^7.27.2" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-validator-option" "^7.27.1" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.27.1" + "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.27.1" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.27.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.27.1" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.27.1" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-import-assertions" "^7.27.1" + "@babel/plugin-syntax-import-attributes" "^7.27.1" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.27.1" + "@babel/plugin-transform-async-generator-functions" "^7.27.1" + "@babel/plugin-transform-async-to-generator" "^7.27.1" + "@babel/plugin-transform-block-scoped-functions" "^7.27.1" + "@babel/plugin-transform-block-scoping" "^7.27.1" + "@babel/plugin-transform-class-properties" "^7.27.1" + "@babel/plugin-transform-class-static-block" "^7.27.1" + "@babel/plugin-transform-classes" "^7.27.1" + "@babel/plugin-transform-computed-properties" "^7.27.1" + "@babel/plugin-transform-destructuring" "^7.27.1" + "@babel/plugin-transform-dotall-regex" "^7.27.1" + "@babel/plugin-transform-duplicate-keys" "^7.27.1" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.27.1" + "@babel/plugin-transform-dynamic-import" "^7.27.1" + "@babel/plugin-transform-exponentiation-operator" "^7.27.1" + "@babel/plugin-transform-export-namespace-from" "^7.27.1" + "@babel/plugin-transform-for-of" "^7.27.1" + "@babel/plugin-transform-function-name" "^7.27.1" + "@babel/plugin-transform-json-strings" "^7.27.1" + "@babel/plugin-transform-literals" "^7.27.1" + "@babel/plugin-transform-logical-assignment-operators" "^7.27.1" + "@babel/plugin-transform-member-expression-literals" "^7.27.1" + "@babel/plugin-transform-modules-amd" "^7.27.1" + "@babel/plugin-transform-modules-commonjs" "^7.27.1" + "@babel/plugin-transform-modules-systemjs" "^7.27.1" + "@babel/plugin-transform-modules-umd" "^7.27.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.27.1" + "@babel/plugin-transform-new-target" "^7.27.1" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.27.1" + "@babel/plugin-transform-numeric-separator" "^7.27.1" + "@babel/plugin-transform-object-rest-spread" "^7.27.2" + "@babel/plugin-transform-object-super" "^7.27.1" + "@babel/plugin-transform-optional-catch-binding" "^7.27.1" + "@babel/plugin-transform-optional-chaining" "^7.27.1" + "@babel/plugin-transform-parameters" "^7.27.1" + "@babel/plugin-transform-private-methods" "^7.27.1" + "@babel/plugin-transform-private-property-in-object" "^7.27.1" + "@babel/plugin-transform-property-literals" "^7.27.1" + "@babel/plugin-transform-regenerator" "^7.27.1" + "@babel/plugin-transform-regexp-modifiers" "^7.27.1" + "@babel/plugin-transform-reserved-words" "^7.27.1" + "@babel/plugin-transform-shorthand-properties" "^7.27.1" + "@babel/plugin-transform-spread" "^7.27.1" + "@babel/plugin-transform-sticky-regex" "^7.27.1" + "@babel/plugin-transform-template-literals" "^7.27.1" + "@babel/plugin-transform-typeof-symbol" "^7.27.1" + "@babel/plugin-transform-unicode-escapes" "^7.27.1" + "@babel/plugin-transform-unicode-property-regex" "^7.27.1" + "@babel/plugin-transform-unicode-regex" "^7.27.1" + "@babel/plugin-transform-unicode-sets-regex" "^7.27.1" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.11.0" + babel-plugin-polyfill-regenerator "^0.6.1" + core-js-compat "^3.40.0" + semver "^6.3.1" -"@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/regjsgen@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" - integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== - -"@babel/runtime@^7.8.4": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" - integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== +"@babel/template@^7.27.1": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== dependencies: - regenerator-runtime "^0.13.11" + "@babel/code-frame" "^7.27.1" + "@babel/parser" "^7.27.2" + "@babel/types" "^7.27.1" -"@babel/template@^7.18.10", "@babel/template@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== +"@babel/traverse@^7.18.9", "@babel/traverse@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.1.tgz#4db772902b133bbddd1c4f7a7ee47761c1b9f291" + integrity sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/traverse@^7.18.9", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" - debug "^4.1.0" + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.27.1" + "@babel/parser" "^7.27.1" + "@babel/template" "^7.27.1" + "@babel/types" "^7.27.1" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.4.4": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== +"@babel/types@^7.27.1", "@babel/types@^7.4.4": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560" + integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" -"@ckeditor/ckeditor5-alignment@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-alignment/-/ckeditor5-alignment-36.0.1.tgz#4d55ae3c20b2986dad42a8e823f325d9673ea34e" - integrity sha512-9JfoV6hlJap3Ympgf3nlNQwj+yJMc0GtGoY3LUC6rg+snPJGjDhjJNBqM5rhr/+HWKOCMrx7OaczS3yJArXW5g== +"@ckeditor/ckeditor5-adapter-ckfinder@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-adapter-ckfinder/-/ckeditor5-adapter-ckfinder-44.3.0.tgz#16132180c770f8f6728ded722b4a2db7488261d6" + integrity sha512-+LYQi4DJmK87Mx3FU7q8Ei3Jv/BrXScR7P1rJVw9bYSySLISw1lPPGEuzNaDm+aI/IMwxAw6Laf7kp+bBfZg1A== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-upload" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-autoformat@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-autoformat/-/ckeditor5-autoformat-36.0.1.tgz#5add5fc01de366bc024164a34bab8ddf2cc5b8bc" - integrity sha512-kve+Ergl40L6DzbtvyTMhbwfMZgpl0SJgsAzukQ72OyvJykQywworJ3zt/PgLCJKD+EKN80X9nR8kfBzXRG/vw== +"@ckeditor/ckeditor5-alignment@44.3.0", "@ckeditor/ckeditor5-alignment@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-alignment/-/ckeditor5-alignment-44.3.0.tgz#43342043d1a208ec254d038a8dc475e919f24bc8" + integrity sha512-PJLPQPJTaEs/TxmXovb5gZWHFk04VdyFxwpy+LFiVKTewV4T5Mz2jinXwL6+DYmlHh0oDu66O2joSdYeL6F9xw== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-basic-styles@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-basic-styles/-/ckeditor5-basic-styles-36.0.1.tgz#847f722be4153b11237df159f9f5c4fc66e39b37" - integrity sha512-5qbgzsgmJP7lecf78sy6QpqbsF2BLs7WxziMrJUXQytgq4S7o+Q/uqGF/itpkQMBBnLulRFJ8/x055iOBugreQ== +"@ckeditor/ckeditor5-autoformat@44.3.0", "@ckeditor/ckeditor5-autoformat@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-autoformat/-/ckeditor5-autoformat-44.3.0.tgz#e249086f0b4046467d7cb339a12934bc480d03f3" + integrity sha512-cKRN73eWaci0px4RL/pu+uFx+/joJyicXjDogqRCqsrHtFnNziAoDxg1aU9F7eReMp+RJc5dqn9R94fZqxdaKQ== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-heading" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-block-quote@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-block-quote/-/ckeditor5-block-quote-36.0.1.tgz#d5e7549cd46b5e54fd4bf7dac4747212c23c6811" - integrity sha512-kLvkHGiu5lAnPiUnRUc0M0Nlls179PmcOvX+YIIp62YBYTxG/R6oCC5clnnhodNiDQBEgwbxqoqTKNSkOX0VWw== +"@ckeditor/ckeditor5-autosave@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-autosave/-/ckeditor5-autosave-44.3.0.tgz#593eb9b7d78c626c455b21b021dc0193fb98f0c9" + integrity sha512-vZS8lMUNwtpUplx1WtiywEmgqy2rXSTrwbCqkENZZ6vIOpYZg9sW0XxWAdCMslBiNZRhAF2et3jT+Es01/aEyg== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-clipboard@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-clipboard/-/ckeditor5-clipboard-36.0.1.tgz#96e150756c5b8e2979bdbf9a23987ac7cccb3829" - integrity sha512-CFh5oO29dUXwsUf/BPM8g5l8MV3hiG27/h6fpC1qRUTAVXDAj43j+cfYYs8MwNi3hv0c/1KyT1bLySqQ/LZctw== +"@ckeditor/ckeditor5-basic-styles@44.3.0", "@ckeditor/ckeditor5-basic-styles@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-basic-styles/-/ckeditor5-basic-styles-44.3.0.tgz#6704af3086540f9e3969d0397280dd69963b9e69" + integrity sha512-QxwJqzIMYD8Nq7I8utEm2N1wFYve3jR9W1RoxrP005V7F9hXubcG4VFkk1QspVjiPBBtWjYJh7Iq6LjeMnDXgg== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-engine" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" - "@ckeditor/ckeditor5-widget" "^36.0.1" - lodash-es "^4.17.11" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-code-block@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-code-block/-/ckeditor5-code-block-36.0.1.tgz#519dd8081d427bd5b913e2486f2aa4095f6e3b23" - integrity sha512-jCWIM7WWg2ohC43/3cOKAqBTe2AVBxE3mfpLTqZ7W1TC7RZgRbzsrxzy5ZnjN3Ze9N6e7b+n1cUEMFn+ilhgbw== +"@ckeditor/ckeditor5-block-quote@44.3.0", "@ckeditor/ckeditor5-block-quote@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-block-quote/-/ckeditor5-block-quote-44.3.0.tgz#84fd41de294d5cf9e97aa578187fd23e0e448733" + integrity sha512-J+t36widj2/f1DCZqZjCssOu4hdKCpX/mDWOAJwp4BNIei0NATmtHoblH4Lb98P0mF5UeneoLqR4XZlwMYD7tw== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-enter" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-core@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-core/-/ckeditor5-core-36.0.1.tgz#637c7b340b42166bd52f6e2c2891eba46d057de2" - integrity sha512-50O+DYXtaf4dMOn+3mGUvfYiE2qsCgVn114WQj7Tj3pZl5w+pAIv/2P+9cBB2Kb7QxgPZqZ7bvsi9wHqIJ6KCw== +"@ckeditor/ckeditor5-bookmark@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-bookmark/-/ckeditor5-bookmark-44.3.0.tgz#11d72f2fece8a7586742879685d0ce1573c986c5" + integrity sha512-e3p6hUYC4LavTSVTDaz9VfpMaXpi35HNXqqCpIGJGtMKq7mYPaGf1ZZqIPEWuZz3UH/h/E445Mrm4KOgRo9hfg== dependencies: - "@ckeditor/ckeditor5-engine" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-dev-translations@^33.0.0": - version "33.0.0" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-dev-translations/-/ckeditor5-dev-translations-33.0.0.tgz#534de19747503155563dc418be56a836494eb817" - integrity sha512-X124CV09kf3y3rSLiWELU3BlM7jlu8ip/mveWqPW6WWRbYMK8nBx4b4OYvl3Khe1bBgOP98xzgNqdpmP+OuXeQ== +"@ckeditor/ckeditor5-ckbox@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-ckbox/-/ckeditor5-ckbox-44.3.0.tgz#0eaf4edfaf16c7f98606048d2d4b64fad9e54b59" + integrity sha512-R0cHl7aafBU1dvfpFu/dMq+7SMTKa7dAi1wQyu5RqmdN0glCAGXoyffe5h2gujcHGlUqzjGE9oX3207rrPi12A== + dependencies: + "@ckeditor/ckeditor5-cloud-services" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-image" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-upload" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + blurhash "2.0.5" + ckeditor5 "44.3.0" + lodash-es "4.17.21" + +"@ckeditor/ckeditor5-ckfinder@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-ckfinder/-/ckeditor5-ckfinder-44.3.0.tgz#a4a90b960c9c0dc6845c5e6e806c78164dec5420" + integrity sha512-h/kQy9nUG2VmxAL28b56xZnVQDrr/N12cu2QE+ODfHIiumMNPPTx2Q+g7s8/eVhKAmNQuZJPXygokjXrirC8TA== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-image" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + +"@ckeditor/ckeditor5-clipboard@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-clipboard/-/ckeditor5-clipboard-44.3.0.tgz#715c8db0788d1233d0fb1859fbe21e78e5c67bb7" + integrity sha512-oCQfsHX0Z7RySzszvkpnsFi/p3pqEjpQs4FVpPXXWasK+3F00kWReYNNQFe+0z0eIw7alo/ZzqYTQG/FQHaQUw== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + lodash-es "4.17.21" + +"@ckeditor/ckeditor5-cloud-services@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-cloud-services/-/ckeditor5-cloud-services-44.3.0.tgz#8153944f92169aef85fad1e124c0e4d301dd96bf" + integrity sha512-c/jLU3lhoqPmwfwEXcU9fGw0ab6dQUcUjQUjamBH1x92p6NnEJTtXJD97trwgKJryZWZ6bk7vJG5nOC4i0XDZA== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + +"@ckeditor/ckeditor5-code-block@44.3.0", "@ckeditor/ckeditor5-code-block@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-code-block/-/ckeditor5-code-block-44.3.0.tgz#5c3ce5011bfadff576521d9eb16c8dae876082fc" + integrity sha512-LNFRr7OIdvyZTfkmyNW/m48EXTsYdrQyDS/9hp4fpHrv9cC3rHhnP4/rS3vkmPh9FOv5I2JvXS36nn75BjWRRQ== + dependencies: + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-enter" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + +"@ckeditor/ckeditor5-core@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-core/-/ckeditor5-core-44.3.0.tgz#26d3f61d405a2bc0f925485410c8e311fe2b9f5c" + integrity sha512-QYyk00JI2wgE2Lr8/A3roRsms3rSOfwMAF88I6Ff2Qf+c8hT6O11gSX18jdd/bGLhfZ0c4IjWc8v67ohgUdMFg== + dependencies: + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-watchdog" "44.3.0" + lodash-es "4.17.21" + +"@ckeditor/ckeditor5-dev-translations@^43.0.1": + version "43.0.1" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-dev-translations/-/ckeditor5-dev-translations-43.0.1.tgz#fdd9e8ae31ec806f2ba1755048aecf5e7fef3aad" + integrity sha512-V2exqknJanhLvL0Y3Pxmuab5cbzrM5TzaFGigDJvMdADYvdPggEocqyNSnB0YiS+vka6lWG+iGtyu0b8JYyRBQ== dependencies: "@babel/parser" "^7.18.9" "@babel/traverse" "^7.18.9" @@ -976,360 +989,707 @@ rimraf "^3.0.2" webpack-sources "^2.0.1" -"@ckeditor/ckeditor5-dev-utils@^31.1.13": - version "31.1.13" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-dev-utils/-/ckeditor5-dev-utils-31.1.13.tgz#649ee881b5f04c5a4961f99bb74e505113fcb3c6" - integrity sha512-U/C7ZLY7yeSMWDQNSDG6UyHxoUjOoXpFafpXnF3VrmkBEJ/PD67wUI645AXgRKE5W8jkCyDfueiI0Wq7JHjD9A== +"@ckeditor/ckeditor5-dev-utils@^43.0.1": + version "43.0.1" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-dev-utils/-/ckeditor5-dev-utils-43.0.1.tgz#878d1c47d81572f8965a31b0b7d0f9349a86241b" + integrity sha512-GyobheNObVkcqLTU8KVCoPOQWDR56qSWAv+SbGc45+itPh7/VFfdA5/Q7R5oQQk8nXWocL50qLG6EcFpMjMngA== dependencies: - "@babel/parser" "^7.18.9" - "@babel/traverse" "^7.18.9" - "@ckeditor/ckeditor5-dev-webpack-plugin" "^31.1.13" + "@ckeditor/ckeditor5-dev-translations" "^43.0.1" chalk "^3.0.0" cli-cursor "^3.1.0" cli-spinners "^2.6.1" - cssnano "^5.0.0" + css-loader "^5.2.7" + cssnano "^6.0.3" del "^5.0.0" - escodegen "^1.9.0" - fs-extra "^8.1.0" + esbuild-loader "~3.0.1" + fs-extra "^11.2.0" is-interactive "^1.0.0" javascript-stringify "^1.6.0" - pofile "^1.0.9" + mini-css-extract-plugin "^2.4.2" + mocha "^7.1.2" postcss "^8.4.12" postcss-import "^14.1.0" postcss-loader "^4.3.0" postcss-mixins "^9.0.2" - postcss-nesting "^10.1.4" + postcss-nesting "^13.0.0" raw-loader "^4.0.1" shelljs "^0.8.1" style-loader "^2.0.0" terser-webpack-plugin "^4.2.3" through2 "^3.0.1" - ts-loader "^9.3.0" -"@ckeditor/ckeditor5-dev-utils@^33.0.0": - version "33.0.0" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-dev-utils/-/ckeditor5-dev-utils-33.0.0.tgz#8cc87243cc50d82089d53112dcd1740683c6510a" - integrity sha512-oQwAgRMA9y3fROs3wkgKxNcAs3qphATPqIwHZBbHwSF4+lNWtyJGoMpujcK1J8WwUNoSqFQDCHKKQEABq9I0tg== +"@ckeditor/ckeditor5-easy-image@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-easy-image/-/ckeditor5-easy-image-44.3.0.tgz#6e7e16b99074cb12f13fb400db99a651f31dc697" + integrity sha512-s1Qpf45/31J4rW4RC0ArKLb5QqD0VKTn9LCB83qhyCLfGluk18MOZ2Fc7gfYoxZClzhCwdMQE6mhWOC9bQUmPw== dependencies: - "@ckeditor/ckeditor5-dev-translations" "^33.0.0" - chalk "^3.0.0" - cli-cursor "^3.1.0" - cli-spinners "^2.6.1" - cssnano "^5.0.0" - del "^5.0.0" - fs-extra "^8.1.0" - is-interactive "^1.0.0" - javascript-stringify "^1.6.0" - postcss "^8.4.12" - postcss-import "^14.1.0" - postcss-loader "^4.3.0" - postcss-mixins "^9.0.2" - postcss-nesting "^10.1.4" - raw-loader "^4.0.1" - shelljs "^0.8.1" - style-loader "^2.0.0" - terser-webpack-plugin "^4.2.3" - through2 "^3.0.1" - ts-loader "^9.3.0" + "@ckeditor/ckeditor5-cloud-services" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-upload" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-dev-webpack-plugin@^31.1.13": - version "31.1.13" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-dev-webpack-plugin/-/ckeditor5-dev-webpack-plugin-31.1.13.tgz#64dd4866121ae7b50107adeb71dc3b6286d71c53" - integrity sha512-XcMjwGO8hb5OQRWzYnX4M0aOuqLqd5Bo5t7chwwXWztvNjlj4pjzb9aRdrnN5DiRLdw26t0EPo2vnx2REe9gZw== +"@ckeditor/ckeditor5-editor-balloon@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-editor-balloon/-/ckeditor5-editor-balloon-44.3.0.tgz#174c60df943c15867ef6df091d5a4e2a111ce5cc" + integrity sha512-Lw6GIIUW37Pi/ErFqx+ozsqRmbBbvhrJhDJ1zIhILEYcwzADHGP32O2pKGLHDf5Z4QTfwunAyG73jvww/ks3kg== dependencies: - "@ckeditor/ckeditor5-dev-utils" "^31.1.13" - chalk "^4.0.0" - rimraf "^3.0.2" - webpack-sources "^2.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-editor-classic@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-editor-classic/-/ckeditor5-editor-classic-36.0.1.tgz#ae7b5659b9312a2487ef2e92363df8ec73215361" - integrity sha512-/euO79HzgIxILgxbqpGaMxO3f2BSGazljAGTc+aGJQ4bs9YnkxpeCc2wxYXuUzMNev2vVqhaPTvRJzg2Bb2r9w== +"@ckeditor/ckeditor5-editor-classic@44.3.0", "@ckeditor/ckeditor5-editor-classic@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-editor-classic/-/ckeditor5-editor-classic-44.3.0.tgz#af7d64b1febcc4c3339bc9774ab36fd098730f52" + integrity sha512-Vi1tNQdKUCw6X66X2RPhtJPjXJHw/UqHw1Y0J4Vpmi5rf5UHerEj5Sjn+KrjUwDNbmeHtVO/egibLhwRMjKn8A== dependencies: - ckeditor5 "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-engine@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-engine/-/ckeditor5-engine-36.0.1.tgz#44092d4d42d7974a8bcad0652ece6255a2256a69" - integrity sha512-Ze2omTguUggwiL9vvLvPk+QpjEBbIOPS1Hi/ROYlsW8QSdnPbSTv+6rCIYThqJMFeonxLZWV5XwbcqpgSAs0MA== +"@ckeditor/ckeditor5-editor-decoupled@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-editor-decoupled/-/ckeditor5-editor-decoupled-44.3.0.tgz#aa8dc8f5dee4284fb3daab0b98495e0cc3690c11" + integrity sha512-sG4KO0pPhfwQpiegtncDyL9G7nlCsjcWqDieZvRfwy2Uig7EaChny3BoIQhacjO4xKV8ZhXw+dTBJgZRtfePdQ== dependencies: - "@ckeditor/ckeditor5-utils" "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-enter@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-enter/-/ckeditor5-enter-36.0.1.tgz#00a473406e2d310c6d7b76c32483b58233e24f15" - integrity sha512-FHZ43bltDGFyihfBOTwBOgsS2mMU2ATR9xxFEKlDP+8+P1bq4e0YkC3t4PuwtHIZMvNHiFxqvmFtA2eznGS7sQ== +"@ckeditor/ckeditor5-editor-inline@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-editor-inline/-/ckeditor5-editor-inline-44.3.0.tgz#e5aab976d12f36a88f3b5966286036c6ae49fc05" + integrity sha512-asa9uWhdTGA6BDda7leJEj57UT3lkDmFAHPFL7fWsvQpL2mszbdf7P6L/rOedGcj2/7a9F8CZfb5aVmENrM3Zw== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-engine" "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-essentials@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-essentials/-/ckeditor5-essentials-36.0.1.tgz#4b5e6eca13591301b8ac5653766dbb964d729755" - integrity sha512-f6yEeSA75/hAqK+hZ09LnUj+2PSXFSTHFBzC32WKr+e2qlTx3/mArvwnnQcTj7lo7SuUmctpg7X5DD3/Rcg+HA== +"@ckeditor/ckeditor5-editor-multi-root@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-editor-multi-root/-/ckeditor5-editor-multi-root-44.3.0.tgz#df6c9a64433c437a3a85b399aada0c0609e7d2aa" + integrity sha512-2k13l0m9+UGMDOzz2uGkj706iMFsayvn+wp6D4niGUOoDGtKOZSX6gNUwq+WcHJ2+54fsXAAJ2xVZB9juXSuhg== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-find-and-replace@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-find-and-replace/-/ckeditor5-find-and-replace-36.0.1.tgz#baa2d3e769b4991d2af5ef72c01a12f12746a898" - integrity sha512-UX8sAEpuUy017EMDrQqrKy+T6UkmXOezi1SNdtG/AdxUhvtD4yDjwAaFLnbMTMyDUFor21Tao3Lvs6aj5n4r4w== +"@ckeditor/ckeditor5-emoji@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-emoji/-/ckeditor5-emoji-44.3.0.tgz#16796b71758280f11b20dad119c851d53252b736" + integrity sha512-NevoSyV8f9F44oTYFJ8DVaOGmqS1XzCisg/lSW5iwfu2R0+6WXCfU6zlGwp9wVedolznUu5OiYpHK0Emz3dmBw== dependencies: - "@ckeditor/ckeditor5-ui" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" - ckeditor5 "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-mention" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + fuzzysort "3.1.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-font@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-font/-/ckeditor5-font-36.0.1.tgz#9498b403188580d469a274fd0904fb8f71796ced" - integrity sha512-mn6nAG9AQ7bC7ywsEf5+pE09yhPSGupttfZ94bzjqTtQAAEahkiD/ympzfypWyJWCkTUlqa0/ZmF22nqcNlTEQ== +"@ckeditor/ckeditor5-engine@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-engine/-/ckeditor5-engine-44.3.0.tgz#6cc5930787ab34c1d8d24d41b5cb76f22d2db1f3" + integrity sha512-76nq2oHwQocaGQfRKlaDaYYFSps3yVNfbC4204i4/S0moKtB9fSy9IBKwYm5D9iB+0qjqCYGnsn17wWNuKjCYQ== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-utils" "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-heading@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-heading/-/ckeditor5-heading-36.0.1.tgz#2fc3b81e8f33789f3a5ccb640ab9cca2345bdf83" - integrity sha512-elVL6y8kFFB4wCjmVTYsbx5vFoNCsPn9fAJ4/T2tElb9wszs4BsbMjScUNwVomOxuSPy3x6Qt4HG1NFVoh1FKg== +"@ckeditor/ckeditor5-enter@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-enter/-/ckeditor5-enter-44.3.0.tgz#ae0586b376902f54af8354eb2086f341e43f6d49" + integrity sha512-M5pv6XC5SIqwa9ZiQQsmmvCAot/uiBN/8ax3KvLz8S78eXDHyHKE6mZMWUd0MJCfZ/0b+CnrFVY/kvZkS+h/7g== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" -"@ckeditor/ckeditor5-highlight@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-highlight/-/ckeditor5-highlight-36.0.1.tgz#3e79374c95d57dc9c14d64f310654766f1ac4e71" - integrity sha512-ETMR6RHd7W0sBm9AECx0OXR16XPBehUJd11yEiiubt9+sjV5GuHKYoFAyU3Vv8fsAoWAEnKNroRxm7Xvga8jZw== +"@ckeditor/ckeditor5-essentials@44.3.0", "@ckeditor/ckeditor5-essentials@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-essentials/-/ckeditor5-essentials-44.3.0.tgz#05aea4555d304513a6e0ab4d12dc552e8d5d4770" + integrity sha512-DkE6u0pD3gcFLkyZRNA4IZVvjLQUpgoEeTCaB/QaCQRljSSj9300AtkIFpCdRiWo9RAGw7rs9XmVRoCsTPY4tQ== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-enter" "44.3.0" + "@ckeditor/ckeditor5-select-all" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-undo" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-horizontal-line@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-horizontal-line/-/ckeditor5-horizontal-line-36.0.1.tgz#eac037c2575c6a6d7c9a47c863dce7b537acf55f" - integrity sha512-ht3mRhImB4BNVYzr73k8syOiXLM3UNBBvaPGmbFylXiAj9+Hbh4EFR0i8e1v5fFCtGBMilBRZeFN1QlMYXHtvg== +"@ckeditor/ckeditor5-find-and-replace@44.3.0", "@ckeditor/ckeditor5-find-and-replace@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-find-and-replace/-/ckeditor5-find-and-replace-44.3.0.tgz#388dd70567054d436a1b5c6c50e852427abaee03" + integrity sha512-d4fLo5IWyxPmfuoIqoaWaekoodgtFurKALc2ZyNvto7H9m+ul+uWwhF4A9buovbETv0N0mertDruGENygiuOCg== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-html-embed@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-html-embed/-/ckeditor5-html-embed-36.0.1.tgz#277329cf80d58499ba219073d57364c5bce0c41f" - integrity sha512-bK7i3ccypZc+oQPzcop4DdQWv3pR+S1NJiD8Qqq/LHJyp6CI48PNNa1ZeWn8rQTbpkD51a7FUunXkmBtlbfF8g== +"@ckeditor/ckeditor5-font@44.3.0", "@ckeditor/ckeditor5-font@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-font/-/ckeditor5-font-44.3.0.tgz#d1f9a389bee4c936d0578fa8b2380b702ca5d460" + integrity sha512-F1cdHNbtXkM/1nvqbCO4XeSCHahNiMnwkQqCm36njxuBRXbAei/qTwxqrwPAukE6Yb2q9boX1prUbvEBbPAqhw== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-html-support@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-html-support/-/ckeditor5-html-support-36.0.1.tgz#196b2238a3e169435e3fefe2f1f996f3b8f0aa42" - integrity sha512-ofOZb+RKKNXikbigyArewijN5lnmCFDUp5k9GTUWpjDCNwEgx+sdBU6qqMKqciJGxY4/gM4qLJ4VQ56dv7r0Wg== +"@ckeditor/ckeditor5-heading@44.3.0", "@ckeditor/ckeditor5-heading@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-heading/-/ckeditor5-heading-44.3.0.tgz#f54da5512f21689ced8fb5e15dd53961a28629a1" + integrity sha512-7uCSHN2UMTzn/nZpoACOiUyx2dX4ZlC8bfLbpdgMhDPM9vEaa0Tr/lgSFD5C1ugLFFSEJL3pAPGWStvZ4wD6YA== dependencies: - ckeditor5 "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-paragraph" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-image@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-image/-/ckeditor5-image-36.0.1.tgz#82cfb3b3f30d2c9692daec0ca2552159bf287167" - integrity sha512-HElSvvBXL4kmwtwrAw28oIuJN1gXMr0kOYnR+sLkJGlfeymOQ4RKEqW3vOPMmN6s2RMJVoNkpytPbhzalFn3Sg== +"@ckeditor/ckeditor5-highlight@44.3.0", "@ckeditor/ckeditor5-highlight@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-highlight/-/ckeditor5-highlight-44.3.0.tgz#980b777a13e5cd9620cddfbc1888733307653f06" + integrity sha512-BwdMrcAbS0J/3q3dmtY0F6sVARVR9oSrWj4/850EfXp0hw9gkpQyEjrds17sIrIchCG+/8/0LM4Z9sADYzmo9w== dependencies: - "@ckeditor/ckeditor5-ui" "^36.0.1" - ckeditor5 "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-indent@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-indent/-/ckeditor5-indent-36.0.1.tgz#82da3df0018f9f8e5645914309a6d39dc46b503d" - integrity sha512-3E+3UCP9Krr41yP8g5a/QmJEJCChOFWdrLShi0YYODkJlhI/UUJFWVDTx54x1GnnDhXt8HhpOVGHCo14+KuRfA== +"@ckeditor/ckeditor5-horizontal-line@44.3.0", "@ckeditor/ckeditor5-horizontal-line@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-horizontal-line/-/ckeditor5-horizontal-line-44.3.0.tgz#f7df090ed4468e544ed87da4a7de6f0ffef278e3" + integrity sha512-k8YuWptng4IKUbXVE+ZjVyNm8JpGU50aFWckYunimlpajNOKC52L8pneIEqIHr/BjA+2P/vtAIf8HDZs+AoNjw== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-link@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-link/-/ckeditor5-link-36.0.1.tgz#72855ca3a0f6c016b053692ca25bcdb4dfe36ce4" - integrity sha512-u0E22wCzgAWMlg5BemYAUHws6zausSek+tmI6VmTRibogsUsfso3JnWI0RXNiEd4i2w1wRspvb9S/D8iy7n4fA== +"@ckeditor/ckeditor5-html-embed@44.3.0", "@ckeditor/ckeditor5-html-embed@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-html-embed/-/ckeditor5-html-embed-44.3.0.tgz#039f583c769e33fb347679f6b077bd08a6c5c060" + integrity sha512-rJV3ikojykVPw9CrtzInh8DsgGuk/2UOi8KUr3b6DEtuYOqJkG5cfpqRy5QVoSRbtpLhVOWNpEKhYGgiv+HUgw== dependencies: - "@ckeditor/ckeditor5-ui" "^36.0.1" - ckeditor5 "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-list@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-list/-/ckeditor5-list-36.0.1.tgz#a1b86a32b81a8761e76933910a4d334a5bba121c" - integrity sha512-v4h5iXCqbOpjaiQRZNp1SExU7BzKfcDU9VUGZpUzFLC0S1JKVO1DAJKMxhqgUCvgGrTOrYHZoGaqNWQZXXwZQg== +"@ckeditor/ckeditor5-html-support@44.3.0", "@ckeditor/ckeditor5-html-support@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-html-support/-/ckeditor5-html-support-44.3.0.tgz#26fd4cf8dfc3562cda321e01448ad55f8de78162" + integrity sha512-KFBHMzBNU9/YBj3Eil03qYBLNk7wAuholdZ3YijohgCt2YRw1cRu9krjGaOyhF9E5MEu0cqTJvTjPGBhGE8mVQ== dependencies: - "@ckeditor/ckeditor5-ui" "^36.0.1" - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-enter" "44.3.0" + "@ckeditor/ckeditor5-heading" "44.3.0" + "@ckeditor/ckeditor5-image" "44.3.0" + "@ckeditor/ckeditor5-list" "44.3.0" + "@ckeditor/ckeditor5-table" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-markdown-gfm@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-markdown-gfm/-/ckeditor5-markdown-gfm-36.0.1.tgz#4cd59abae7d23267524efb5de9f75742328a8a6b" - integrity sha512-rc2DjcaAyNCo8GgV/3U0yrN78KBz9MazKT1t/8VSD5h/9XpkSYnpRKwginui5alUQ7wwzfo9IOsgezGzpRFUbg== +"@ckeditor/ckeditor5-image@44.3.0", "@ckeditor/ckeditor5-image@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-image/-/ckeditor5-image-44.3.0.tgz#62b23b93a5715feea42b6d2752caa121d1fa20e5" + integrity sha512-3mRgGSOXQ4e/TJZcB7N8SPSnN+pdUzA72kwiN6FrjjdV0CNqehNoSUCFrwra4ctHaFjLBBqFVV+hEvwFlgmlxA== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-undo" "44.3.0" + "@ckeditor/ckeditor5-upload" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" + +"@ckeditor/ckeditor5-indent@44.3.0", "@ckeditor/ckeditor5-indent@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-indent/-/ckeditor5-indent-44.3.0.tgz#f1dc84e7375494b9397eb32222eb5eefda292111" + integrity sha512-Gju3Lt2/OQB2mzhk/TTtonHTEcmFua4Ag4keB75EbPxthpLZZnMQm3Gl7MvrecjUv4+3nRlEBv/Bon00OIXcqA== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-heading" "44.3.0" + "@ckeditor/ckeditor5-list" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + +"@ckeditor/ckeditor5-language@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-language/-/ckeditor5-language-44.3.0.tgz#fd33839e582109ec6b51e308fb004355b296e4b4" + integrity sha512-lnhnnsSwMnnyFpUktrvAxuj6tlwn2zjSm7+ZDxMiQpsZuRsmABnSapeKqjlfs8B1rQwrcZIhp1FsHVvnaVOzug== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + +"@ckeditor/ckeditor5-link@44.3.0", "@ckeditor/ckeditor5-link@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-link/-/ckeditor5-link-44.3.0.tgz#f652d0d16592d7b64536c0f7adde923e548682dd" + integrity sha512-OmOWje1i7tuEBNHoIi/n6M69xE1K97P2xmbSJ9HB5nFFvelfCCMnuLa+7QmT1+/Sxg0VYM175dtTclinEeWJhQ== + dependencies: + "@ckeditor/ckeditor5-bookmark" "44.3.0" + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-image" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" + +"@ckeditor/ckeditor5-list@44.3.0", "@ckeditor/ckeditor5-list@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-list/-/ckeditor5-list-44.3.0.tgz#6e96caaa22e21bcdf25019330ea0551e7ce6a37f" + integrity sha512-66f3n8ASdhA7wxPoGt/sI+Eg/IhDFutSkCEcqL2tsJFVRB0MuhhQIklxdGRVfPrjbpMSUPSRJPMddAFhRCzfUQ== + dependencies: + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-enter" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + +"@ckeditor/ckeditor5-markdown-gfm@44.3.0", "@ckeditor/ckeditor5-markdown-gfm@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-markdown-gfm/-/ckeditor5-markdown-gfm-44.3.0.tgz#3c7fd198eacc2f1be7bea80fb6a77064133b9539" + integrity sha512-q+vblaqjjwQQkcrFfsOCD7ejWl1ltqV982jfFK9LTx7DiJIuR2UwByNrRC00Xeol2rDeC8lYoIH77AZHavFIbw== + dependencies: + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@types/marked" "4.3.2" + "@types/turndown" "5.0.5" + ckeditor5 "44.3.0" marked "4.0.12" - turndown "^6.0.0" - turndown-plugin-gfm "^1.0.2" + turndown "7.2.0" + turndown-plugin-gfm "1.0.2" -"@ckeditor/ckeditor5-media-embed@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-media-embed/-/ckeditor5-media-embed-36.0.1.tgz#4d90ac7e331969dd39ad3bbb4c7f64bada54fb58" - integrity sha512-TBl9bh+wBndkHfjjULqTYdYjbwtpO0ug4QTE5PQuAXrfvNeTAGvpYRqnJtLlASzqvUcgc3uBCXI9sO90ZUnFWw== +"@ckeditor/ckeditor5-media-embed@44.3.0", "@ckeditor/ckeditor5-media-embed@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-media-embed/-/ckeditor5-media-embed-44.3.0.tgz#9f7c7738f343cab72ffafe51fb4bfc343c26776d" + integrity sha512-wlAITZKeNEZfB164hwODXfhg63pYaWynCuIvVCtaeVUmoBqnSkK8gxuZ2ehxUdi4SPlVJwt84afDsD4RU8UYDQ== dependencies: - "@ckeditor/ckeditor5-ui" "^36.0.1" - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-undo" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-paragraph@^36.0.0", "@ckeditor/ckeditor5-paragraph@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-paragraph/-/ckeditor5-paragraph-36.0.1.tgz#c3a75e7e4df69f565651d0deb16cbdf785b17cff" - integrity sha512-NF1PH9WVfY81vkohj6GV1W/u1ogbZnfy60wrDy8Qp3iaFK1LJBsKNz3q1tf0WkBghaX/MUjR9E06l/OrTtaTZw== +"@ckeditor/ckeditor5-mention@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-mention/-/ckeditor5-mention-44.3.0.tgz#d4608aa8485dc1f7cba830a10bb9db6ce7e07edc" + integrity sha512-pmf7prEvIJgP4rbyeTtY6Ynl2i5jo52G+DlFwJbWUzZDyOZIR41mh4hBimK50QHR0szaXMBGbM+alFu924vZCA== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-ui" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-paste-from-office@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-paste-from-office/-/ckeditor5-paste-from-office-36.0.1.tgz#ece30fb7f49a81865fd24d394c87076d53882711" - integrity sha512-JG3Ex/0mQ94E6coU16/CpnjikadCLebRLKXKYQyK/Wag7MbkKh8jgzpnZTC4Br4HvpQORfx7eC475BWF927FeQ== +"@ckeditor/ckeditor5-minimap@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-minimap/-/ckeditor5-minimap-44.3.0.tgz#f40f9214da1c39555a8ecaa207d230533f8e8c8f" + integrity sha512-iRO1t1lyeyiFZg4il5JGRVwyoqRYqsRcj9uSh90xNJJAxH5yiPlWAGIvCMzWDwlINFJe/hfaNd0RDzlbNHytgA== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-remove-format@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-remove-format/-/ckeditor5-remove-format-36.0.1.tgz#ab7d577b5f06074cda2e11c6b5cfd3c4f13e9fcb" - integrity sha512-bOE4s5e+DiTujGm7L2AQLOtv4/hV5dQkv3czrC3OZ6wtXxAnSYHb3wWPUeIrtJTOisri4Pf9oL6If7uFKWkZNg== +"@ckeditor/ckeditor5-page-break@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-page-break/-/ckeditor5-page-break-44.3.0.tgz#b38534a39373cdb311f5fa2eb1659f1b0db7bb31" + integrity sha512-PzvOsEDNBPWCtN9S6nhJPWYwF/5BoE0klXkNzTgoA28EwsoSA4evU8gztcoxxGkVOskdI2OzZfz1QUaPFczLDQ== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-select-all@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-select-all/-/ckeditor5-select-all-36.0.1.tgz#ffc105545e7d04f30a38fc5230c2a17861fe9470" - integrity sha512-8rPxcenTAoqXy1gleZvfsc7VD0IltPFq8R+blD4JL+sSMMDUtKhPuok4TA9h6Yh0nP6LfRK2fBUKIWH3xRPABg== +"@ckeditor/ckeditor5-paragraph@44.3.0", "@ckeditor/ckeditor5-paragraph@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-paragraph/-/ckeditor5-paragraph-44.3.0.tgz#22a7f9fae16410f4aba950def218e4b88d67bc07" + integrity sha512-kxZBn/xmz+woXB0fIKWA/ZrFZXqyubX5BhcOYpM2j3CtjsJUJsL6ekwBq/y9HNSkkKfNKHaTzK7IDiPBKblcrw== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-ui" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" -"@ckeditor/ckeditor5-source-editing@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-source-editing/-/ckeditor5-source-editing-36.0.1.tgz#3fc48bf48b6b5dfac9ccfa21fa37c0caeadf79b8" - integrity sha512-8Hxbmt60G530zDhpVHDeY+USdRKrdGn0spx1YREVbWAhQ1kMV6FUWDGtDJx9Rbj8BRe19AtlT40ctSbKS/XVzQ== +"@ckeditor/ckeditor5-paste-from-office@44.3.0", "@ckeditor/ckeditor5-paste-from-office@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-paste-from-office/-/ckeditor5-paste-from-office-44.3.0.tgz#90d92b6497af54f1d8eda8830688da1ec136da25" + integrity sha512-IHl3YVE6ONGXTxHf+uHvvBMFR90nxTz7nB9eA8DzuWskF+fOo+wtVdfZaafLUs0kJJXBTRebsv2Sa5onLLL6tA== dependencies: - "@ckeditor/ckeditor5-theme-lark" "^36.0.1" - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-special-characters@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-special-characters/-/ckeditor5-special-characters-36.0.1.tgz#ad8d29caa1f4d6454670d0dd7a581a22a598a245" - integrity sha512-vYloQvPPU1aGJAkjDWTZvU9BbK7CLsyVg8FV+NSSbdPKAY6K4IPS52jvbCDZBHQqzmeFVEy5U8iMhbZxYdkI6g== +"@ckeditor/ckeditor5-remove-format@44.3.0", "@ckeditor/ckeditor5-remove-format@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-remove-format/-/ckeditor5-remove-format-44.3.0.tgz#7ae535bae6626d754dcc8241d1cc02eb2a41c908" + integrity sha512-ZlbJNaX+IGui9C3NDC/W/4M308RzD3+jnk5iDGXENjBstNB99AnwpfDXel+Tc0t5rTd7D1fSr9eaxScnPLdgwA== dependencies: - ckeditor5 "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-table@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-table/-/ckeditor5-table-36.0.1.tgz#8891e3b3701c1da1a9097b11b878adfd27bef926" - integrity sha512-ishbq3Rp8n7aypswmbqAVWT5W0iYZfAfm6wS+dpSoFqzAPX6ko8+bnoTkq+Wbs1S4+JDaua7QOJw9A1L1cv17A== +"@ckeditor/ckeditor5-restricted-editing@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-restricted-editing/-/ckeditor5-restricted-editing-44.3.0.tgz#646286bc21ef73192cf0d40648159fd370b5aa0b" + integrity sha512-0V/can4jw70DMBPwex1MRiUHRsvFdbjZp/RaHagazen6gOZZOAo3R7vJjp+qBQrHmXP6T6aTGn89Y9NxYI9OIw== dependencies: - ckeditor5 "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-theme-lark@^36.0.0", "@ckeditor/ckeditor5-theme-lark@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-theme-lark/-/ckeditor5-theme-lark-36.0.1.tgz#404c022946a8b172117eb6fbed18817bfe0a66e0" - integrity sha512-R7VnSjET52mxHEAt6+GxSHmBKddsRjkVHDxbd13GTkDl01SKjbYfVF+Ek6iVuf6eVyNu6eedRJugShZSsHhXiA== +"@ckeditor/ckeditor5-select-all@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-select-all/-/ckeditor5-select-all-44.3.0.tgz#badcb7c674d24f23468e9818db96e3be38052999" + integrity sha512-GDNl6Iex1cyHAQgjR6cqIklH8QPO1x1Z87gwi93lX3mgm47DW6Q12jvwfRc36+d1zGf5W7+eH0xaP7jQPWYUPg== dependencies: - "@ckeditor/ckeditor5-ui" "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" -"@ckeditor/ckeditor5-typing@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-typing/-/ckeditor5-typing-36.0.1.tgz#4a38533848986bcb33905ae22068dd575ce1867f" - integrity sha512-NwXQxNxf/LLANiiteEVaLr1ZzvJd7y6+O5a6x0Tv9Uuheu80aw2Axm3icneODt05G/XT+iJmCHzUYdEiCsHUaw== +"@ckeditor/ckeditor5-show-blocks@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-show-blocks/-/ckeditor5-show-blocks-44.3.0.tgz#4fc3402a23ec0914056dab964207c03e69a96219" + integrity sha512-4Qp992YWOby7+MiyX0PwD8bg/+PgsRFNXd9DLyhCvmmiFmkwUzbYmA8t3lp+V3uefg5SrcylzWn4QHlUNVg1iw== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-engine" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-ui@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-ui/-/ckeditor5-ui-36.0.1.tgz#66f660638f73b3e76787eb025dcf23a12cff75f7" - integrity sha512-wPqAdXUZnEAD3XPvS5vEGU2kxzTQah3wne3B1esdlbJ7dSLZDdx8r1dyKyp3/WCpCT8G+PqGvSUrlnqcy7WI5A== +"@ckeditor/ckeditor5-source-editing@44.3.0", "@ckeditor/ckeditor5-source-editing@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-source-editing/-/ckeditor5-source-editing-44.3.0.tgz#4dda04c06dd45c7ad62f7cfc1c7f5a384f679db0" + integrity sha512-OCv2oO6ok0UrZJHOoJRnywFpv3+UntOs7TuvEw35zDYJCgcmICfzqqTZ++ZcZaq1oOERbwk2L4E4uy2H8KXWQA== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-theme-lark" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-undo@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-undo/-/ckeditor5-undo-36.0.1.tgz#b392c926c34cd5162d08a55e8faebdd4d29c80a0" - integrity sha512-TBm6TAnur59f5hqlgUZ89NV3cPQw0xqqpSuVMFrvFruPmPiIay2E34Mapb5KRXr83AEPhNN8GAE2YM7HmMqKrQ== +"@ckeditor/ckeditor5-special-characters@44.3.0", "@ckeditor/ckeditor5-special-characters@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-special-characters/-/ckeditor5-special-characters-44.3.0.tgz#26cb3608cdd91826021e1eb3bdf4c1b2ed464e8f" + integrity sha512-K3cOw/aMUmkXofUVFpUXTHLNYlwvcT3KUyfvTO3oT8ae3ecQlG4btTjgnzgVD2zIXFSEpguSBYywVUhMAZWNMA== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-engine" "^36.0.1" - "@ckeditor/ckeditor5-ui" "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" -"@ckeditor/ckeditor5-upload@^36.0.0", "@ckeditor/ckeditor5-upload@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-upload/-/ckeditor5-upload-36.0.1.tgz#4d9d2e59f8d0a2d7eb409e0c9843a2144c82b1f9" - integrity sha512-932vnvO++SzfZ6EpNkOj6PPKN6vVgn5aW3gu0c1D51cF9KkCFiaMhBDsOzDRVvq/bbnDqUQ9v3j1cNo2UlC8oA== +"@ckeditor/ckeditor5-style@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-style/-/ckeditor5-style-44.3.0.tgz#e1b364d085ba00147430004f2030c45d01aed2a4" + integrity sha512-IUd5yRea8uh/VyAJ6p1NdXL9wWywQYfIOubzNgiP1b58q0psfWYxwsREekE6Epd+sMHC1H4wc+dZsIxfi+stKw== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-ui" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-html-support" "44.3.0" + "@ckeditor/ckeditor5-list" "44.3.0" + "@ckeditor/ckeditor5-table" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-utils@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-utils/-/ckeditor5-utils-36.0.1.tgz#d7a9943704fa4a492f79f84eda953116ac152b4b" - integrity sha512-TW/YRb0OQe88IyqRoq2xS02iXOG82NQybzMuFDnGyCaFV0hA/9ysOyWwFpxDLcPDhDgVJZIxuwvFaek9x+uCMA== +"@ckeditor/ckeditor5-table@44.3.0", "@ckeditor/ckeditor5-table@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-table/-/ckeditor5-table-44.3.0.tgz#6eeb68309146d3c75a6ceb6fba70e7ec7855d2c2" + integrity sha512-kFPvQWe/7Iq9tPzq788HLax7Ss8SJaS5+bkhbUmH+k4RM4NABl1ABRa+EzyMIrdqz9KLYr0B57jUoCU8Y2HLsg== dependencies: - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-watchdog@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-watchdog/-/ckeditor5-watchdog-36.0.1.tgz#fa4c91f4781d8c55c9e3e1604e65644eb0720945" - integrity sha512-Zev2EB8dHDA3/K01+McnfHVvxKuoocLCfDF3DNMNRVxQ5kdQGbPgY0JCvTKrLgiieWpoelQveG2xQnIEaQphZQ== +"@ckeditor/ckeditor5-theme-lark@44.3.0", "@ckeditor/ckeditor5-theme-lark@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-theme-lark/-/ckeditor5-theme-lark-44.3.0.tgz#cd41689a693d5363c4e7bd3fb2951bae14be1c03" + integrity sha512-Druy7S1e3bjwMVLkld7ZPGIU6tdI9E6bn/AS+OrQF3mDck14mFP+K4qnMbYNCeYngMqv8Se0eSHtAmQhU7It0A== dependencies: - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-ui" "44.3.0" -"@ckeditor/ckeditor5-widget@^36.0.1": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-widget/-/ckeditor5-widget-36.0.1.tgz#af4c87ce2d5a714ea0f4c1a8a41b631afbd91de0" - integrity sha512-on92cCRYLWTfwrpnFi5z4FY7NDhY+X+p6yuYStnB9d9jM3FJi3/7y2q9ojr8fSMot4pQsXCiFXzaF6yez+77wA== +"@ckeditor/ckeditor5-typing@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-typing/-/ckeditor5-typing-44.3.0.tgz#fe6f98bfd4b6dd025af0f88cbc7797ab4194f3cd" + integrity sha512-ojAuhhd/PfO5Oko94ObgEmuvQzqApARb2H0R5YEySSRj9oek4Nm1NGMhHMvIC0sMP2hSVxhfSbHcHY3vDjGwiA== dependencies: - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-engine" "^36.0.1" - "@ckeditor/ckeditor5-enter" "^36.0.1" - "@ckeditor/ckeditor5-typing" "^36.0.1" - "@ckeditor/ckeditor5-ui" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + lodash-es "4.17.21" -"@ckeditor/ckeditor5-word-count@^36.0.0": - version "36.0.1" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-word-count/-/ckeditor5-word-count-36.0.1.tgz#cf1c28ebe0ee73f266769107a09afd69ce5218c3" - integrity sha512-gU5faioZwAySQEbzYgvIV02GwFscuYBxsfQ6GPMrD3/ZMObQYog1gabRbUk+5gNULep5vXsMoyJ2hd8dWiJyOQ== +"@ckeditor/ckeditor5-ui@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-ui/-/ckeditor5-ui-44.3.0.tgz#b58109978d17ba641e5048127ec7d7e6dde2b728" + integrity sha512-Sotme1g/xwhmDYes+bXEU/9hbGXcaKC952Yck6UiYgZyhei28lErlNwtSOAKfpQ2gHbPjWRt7rigSH22EN+iqw== dependencies: - ckeditor5 "^36.0.1" - lodash-es "^4.17.15" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-editor-multi-root" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@types/color-convert" "2.0.4" + color-convert "2.0.1" + color-parse "1.4.2" + lodash-es "4.17.21" + vanilla-colorful "0.7.2" -"@csstools/selector-specificity@^2.0.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz#c9c61d9fe5ca5ac664e1153bb0aa0eba1c6d6308" - integrity sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw== +"@ckeditor/ckeditor5-undo@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-undo/-/ckeditor5-undo-44.3.0.tgz#ce63abe6ad56f2f1d2ba241d2591d15a1508a6de" + integrity sha512-cogFPl7QoDrmoUPKTZI0/LiUYoFboqm0lqBK168nh5VRgy53RuGgbXJ4+hCOHNuzdUnVUlWvl/2u0vR4/R07+A== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" -"@discoveryjs/json-ext@^0.5.0": +"@ckeditor/ckeditor5-upload@44.3.0", "@ckeditor/ckeditor5-upload@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-upload/-/ckeditor5-upload-44.3.0.tgz#c4ecf65ce3e4526b3c5482d7c6c53875fbaa157a" + integrity sha512-oJgsw374nHeVKx1qvnlQzEyDhfFvC/su84f00nOdDGWfsHabU3hE0hZE+yoBtreRViORwwe6Ftg5qbhl5OVyuA== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + +"@ckeditor/ckeditor5-utils@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-utils/-/ckeditor5-utils-44.3.0.tgz#43ace318eb1b55e65c91353bb3968a31dcc8fe2e" + integrity sha512-Z/ttfLhu9QnuMc9BBdQfH9Do6JD01ijUkUmUB41BPByYfOjZbGYGfZmhywH9OjXmHnSDQSUqwRnxqqM3nceR1g== + dependencies: + "@ckeditor/ckeditor5-ui" "44.3.0" + "@types/lodash-es" "4.17.12" + lodash-es "4.17.21" + +"@ckeditor/ckeditor5-watchdog@44.3.0", "@ckeditor/ckeditor5-watchdog@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-watchdog/-/ckeditor5-watchdog-44.3.0.tgz#afc48bd217d5af74e0b369573f6ddb4e3052b7aa" + integrity sha512-7Y4FOWB021CLEUDsIf61tZzd9ZrZ8JkD8lQeRZOmv+bBLTxgjyz2/7MCjIDR7NN0xdASXTFwRS6XO32HbdrN0Q== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-editor-multi-root" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + lodash-es "4.17.21" + +"@ckeditor/ckeditor5-widget@44.3.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-widget/-/ckeditor5-widget-44.3.0.tgz#6119814e85b5044c548a6d25de64cb2938d664d3" + integrity sha512-nFEKBE33RnZ2bOeD9L9jfyKYaFw/xKY543ZNQWxWHcmI70AX3U2KJ8wUnuMjMknHafyMuH6bSlWjKtn5vz3w+g== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-enter" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + lodash-es "4.17.21" + +"@ckeditor/ckeditor5-word-count@44.3.0", "@ckeditor/ckeditor5-word-count@^44.0.0": + version "44.3.0" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-word-count/-/ckeditor5-word-count-44.3.0.tgz#4ddacfc04920b5798c635ebfefd26dc7369d011a" + integrity sha512-9iNiF1wq1f9npT/KhBAgDfM6kFbJ0GyUdlXt3V6zyLY2K3SILQ6q5B23jhtABZny8zxA+f528ClxDSTFh9bXDA== + dependencies: + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + ckeditor5 "44.3.0" + lodash-es "4.17.21" + +"@csstools/selector-resolve-nested@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz#704a9b637975680e025e069a4c58b3beb3e2752a" + integrity sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ== + +"@csstools/selector-specificity@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz#037817b574262134cabd68fc4ec1a454f168407b" + integrity sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw== + +"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@foliojs-fork/fontkit@^1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz#8124649168eb5273f580f66697a139fb5041296b" - integrity sha512-U589voc2/ROnvx1CyH9aNzOQWJp127JGU1QAylXGQ7LoEAF6hMmahZLQ4eqAcgHUw+uyW4PjtCItq9qudPkK3A== +"@esbuild/android-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" + integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== + +"@esbuild/android-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" + integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== + +"@esbuild/android-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" + integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== + +"@esbuild/darwin-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" + integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + +"@esbuild/darwin-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" + integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== + +"@esbuild/freebsd-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" + integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== + +"@esbuild/freebsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" + integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== + +"@esbuild/linux-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" + integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== + +"@esbuild/linux-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" + integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== + +"@esbuild/linux-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" + integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== + +"@esbuild/linux-loong64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" + integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== + +"@esbuild/linux-mips64el@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" + integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== + +"@esbuild/linux-ppc64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" + integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== + +"@esbuild/linux-riscv64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" + integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== + +"@esbuild/linux-s390x@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" + integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== + +"@esbuild/linux-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== + +"@esbuild/netbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" + integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== + +"@esbuild/openbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" + integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== + +"@esbuild/sunos-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" + integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== + +"@esbuild/win32-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" + integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== + +"@esbuild/win32-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" + integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== + +"@esbuild/win32-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" + integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== + +"@foliojs-fork/fontkit@^1.9.2": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@foliojs-fork/fontkit/-/fontkit-1.9.2.tgz#94241c195bc6204157bc84c33f34bdc967eca9c3" + integrity sha512-IfB5EiIb+GZk+77TRB86AHroVaqfq8JRFlUbz0WEwsInyCG0epX2tCPOy+UfaWPju30DeVoUAXfzWXmhn753KA== dependencies: "@foliojs-fork/restructure" "^2.0.2" - brfs "^2.0.0" brotli "^1.2.0" - browserify-optional "^1.0.1" clone "^1.0.4" deep-equal "^1.0.0" dfa "^1.2.0" @@ -1337,23 +1697,23 @@ unicode-properties "^1.2.2" unicode-trie "^2.0.0" -"@foliojs-fork/linebreak@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@foliojs-fork/linebreak/-/linebreak-1.1.1.tgz#93ecd695b7d2bb0334b9481058c3e610e019a4eb" - integrity sha512-pgY/+53GqGQI+mvDiyprvPWgkTlVBS8cxqee03ejm6gKAQNsR1tCYCIvN9FHy7otZajzMqCgPOgC4cHdt4JPig== +"@foliojs-fork/linebreak@^1.1.1", "@foliojs-fork/linebreak@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@foliojs-fork/linebreak/-/linebreak-1.1.2.tgz#32fee03d5431fa73284373439e172e451ae1e2da" + integrity sha512-ZPohpxxbuKNE0l/5iBJnOAfUaMACwvUIKCvqtWGKIMv1lPYoNjYXRfhi9FeeV9McBkBLxsMFWTVVhHJA8cyzvg== dependencies: base64-js "1.3.1" - brfs "^2.0.2" unicode-trie "^2.0.0" -"@foliojs-fork/pdfkit@^0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@foliojs-fork/pdfkit/-/pdfkit-0.13.0.tgz#54f5368d8cf74d8edc81a175ccda1fd9655f2db9" - integrity sha512-YXeG1fml9k97YNC9K8e292Pj2JzGt9uOIiBFuQFxHsdQ45BlxW+JU3RQK6JAvXU7kjhjP8rCcYvpk36JLD33sQ== +"@foliojs-fork/pdfkit@^0.15.3": + version "0.15.3" + resolved "https://registry.yarnpkg.com/@foliojs-fork/pdfkit/-/pdfkit-0.15.3.tgz#590b31e770a98e2af62ce44f268a0d06b41ff32f" + integrity sha512-Obc0Wmy3bm7BINFVvPhcl2rnSSK61DQrlHU8aXnAqDk9LCjWdUOPwhgD8Ywz5VtuFjRxmVOM/kQ/XLIBjDvltw== dependencies: - "@foliojs-fork/fontkit" "^1.9.1" + "@foliojs-fork/fontkit" "^1.9.2" "@foliojs-fork/linebreak" "^1.1.1" - crypto-js "^4.0.0" + crypto-js "^4.2.0" + jpeg-exif "^1.1.4" png-js "^1.0.0" "@foliojs-fork/restructure@^2.0.2": @@ -1361,10 +1721,51 @@ resolved "https://registry.yarnpkg.com/@foliojs-fork/restructure/-/restructure-2.0.2.tgz#73759aba2aff1da87b7c4554e6839c70d43c92b4" integrity sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA== +"@formatjs/ecma402-abstract@2.3.4": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz#e90c5a846ba2b33d92bc400fdd709da588280fbc" + integrity sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA== + dependencies: + "@formatjs/fast-memoize" "2.2.7" + "@formatjs/intl-localematcher" "0.6.1" + decimal.js "^10.4.3" + tslib "^2.8.0" + +"@formatjs/fast-memoize@2.2.7": + version "2.2.7" + resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz#707f9ddaeb522a32f6715bb7950b0831f4cc7b15" + integrity sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ== + dependencies: + tslib "^2.8.0" + +"@formatjs/icu-messageformat-parser@2.11.2": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz#85aea211bea40aa81ee1d44ac7accc3cf5500a73" + integrity sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA== + dependencies: + "@formatjs/ecma402-abstract" "2.3.4" + "@formatjs/icu-skeleton-parser" "1.8.14" + tslib "^2.8.0" + +"@formatjs/icu-skeleton-parser@1.8.14": + version "1.8.14" + resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz#b9581d00363908efb29817fdffc32b79f41dabe5" + integrity sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ== + dependencies: + "@formatjs/ecma402-abstract" "2.3.4" + tslib "^2.8.0" + +"@formatjs/intl-localematcher@0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz#25dc30675320bf65a9d7f73876fc1e4064c0e299" + integrity sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg== + dependencies: + tslib "^2.8.0" + "@fortawesome/fontawesome-free@^6.1.1": - version "6.3.0" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz#b5877182692a6f7a39d1108837bec24247ba4bd7" - integrity sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA== + version "6.7.2" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz#8249de9b7e22fcb3ceb5e66090c30a1d5492b81a" + integrity sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA== "@gar/promisify@^1.0.1": version "1.1.3" @@ -1377,91 +1778,83 @@ integrity sha512-wa/zupVG0eWxRYJjC1IiPBdt3Lruv0RqGN+/DTMmUWUyMAEB27KXmVY6a8YpUVTM7QwVuaLNGW4EqDgrS2upXQ== "@hotwired/stimulus@^3.0.0": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.1.tgz#e3de23623b0c52c247aba4cd5d530d257008676b" - integrity sha512-HGlzDcf9vv/EQrMJ5ZG6VWNs8Z/xMN+1o2OhV1gKiSG6CqZt5MCBB1gRg5ILiN3U0jEAxuDTNPRfBcnZBDmupQ== + version "3.2.2" + resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.2.tgz#071aab59c600fed95b97939e605ff261a4251608" + integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A== -"@hotwired/turbo@^7.0.1": - version "7.2.5" - resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-7.2.5.tgz#2d9d6bde8a9549c3aea8970445ade16ffd56719a" - integrity sha512-o5PByC/mWkmTe4pWnKrixhPECJUxIT/NHtxKqjq7n9Fj6JlNza1pgxdTCJVIq+PI0j95U+7mA3N4n4A/QYZtZQ== +"@hotwired/turbo@^8.0.1": + version "8.0.13" + resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-8.0.13.tgz#ab35fda9d358432c8a872a833844b38cccb8c25b" + integrity sha512-M7qXUqcGab6G5PKOiwhgbByTtrPgKPFCTMNQ52QhzUEXEqmp0/ApEguUesh/FPiUjrmFec+3lq98KsWnYY2C7g== "@jbtronics/bs-treeview@^1.0.1": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@jbtronics/bs-treeview/-/bs-treeview-1.0.4.tgz#aecd128c295ebd1af904d9a77f17dbf77a772b8f" - integrity sha512-ofhMY+4w0xYHcMPyw53EzWABzrzX2ecm50hhD9Kbb6h1/XsmLzVvHLIG9FYJ3eaIR0nTYF9a5L7mS14Qr/JChQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/@jbtronics/bs-treeview/-/bs-treeview-1.0.6.tgz#7fe126a2ca4716c824d97ab6d1a5f2417750445a" + integrity sha512-fLY2tnbDYO4kCjpmGQyfvoHN8x72Rk5p+tTgVjKJUbiJHHhXt0yIW+l5P83CwYtPD5EwS7kMKc8RLuU1xA2bpA== -"@jest/schemas@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.2.tgz#cf7cfe97c5649f518452b176c47ed07486270fc1" - integrity sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: - "@sinclair/typebox" "^0.25.16" + "@sinclair/typebox" "^0.27.8" -"@jest/types@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.2.tgz#8f724a414b1246b2bfd56ca5225d9e1f39540d82" - integrity sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^29.4.2" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== dependencies: - "@jridgewell/set-array" "^1.0.0" + "@jridgewell/set-array" "^1.2.1" "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - -"@leichtgewicht/ip-codec@^2.0.1": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" - integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== +"@mixmark-io/domino@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@mixmark-io/domino/-/domino-2.2.0.tgz#4e8ec69bf1afeb7a14f0628b7e2c0f35bdb336c3" + integrity sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1501,46 +1894,53 @@ rimraf "^3.0.2" "@nuxt/friendly-errors-webpack-plugin@^2.5.1": - version "2.5.2" - resolved "https://registry.yarnpkg.com/@nuxt/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-2.5.2.tgz#982a43ee2da61611f7396439e57038392d3944d5" - integrity sha512-LLc+90lnxVbpKkMqk5z1EWpXoODhc6gRkqqXJCInJwF5xabHAE7biFvbULfvTRmtaTzAaP8IV4HQDLUgeAUTTw== + version "2.6.0" + resolved "https://registry.yarnpkg.com/@nuxt/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-2.6.0.tgz#bd0cf6cd73b6e1d3e7c7f1c0de43333e69cc660c" + integrity sha512-3IZj6MXbzlvUxDncAxgBMLQwGPY/JlNhy2i+AGyOHCAReR5HcBxYjVRBvyaKM9R3s5k4OODYKeHAbrToZH/47w== dependencies: - chalk "^2.3.2" - consola "^2.6.0" - error-stack-parser "^2.0.0" + chalk "^2.4.2" + consola "^3.2.3" + error-stack-parser "^2.1.4" string-width "^4.2.3" -"@orchidjs/sifter@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@orchidjs/sifter/-/sifter-1.0.3.tgz#43f42519472282eb632d0a1589184f044d64129b" - integrity sha512-zCZbwKegHytfsPm8Amcfh7v/4vHqTAaOu6xFswBYcn8nznBOuseu6COB2ON7ez0tFV0mKL0nRNnCiZZA+lU9/g== +"@orchidjs/sifter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@orchidjs/sifter/-/sifter-1.1.0.tgz#b36154ad0cda4898305d1ac44f318b41048a0438" + integrity sha512-mYwHCfr736cIWWdhhSZvDbf90AKt2xyrJspKFC3qyIJG1LtrJeJunYEqCGG4Aq2ijENbc4WkOjszcvNaIAS/pQ== dependencies: - "@orchidjs/unicode-variants" "^1.0.4" + "@orchidjs/unicode-variants" "^1.1.2" -"@orchidjs/unicode-variants@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@orchidjs/unicode-variants/-/unicode-variants-1.0.4.tgz#6d2f812e3b19545bba2d81caffff1204de9a6a58" - integrity sha512-NvVBRnZNE+dugiXERFsET1JlKZfM5lJDEpSMilKW4bToYJ7pxf0Zne78xyXB2ny2c2aHfJ6WLnz1AaTNHAmQeQ== +"@orchidjs/unicode-variants@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@orchidjs/unicode-variants/-/unicode-variants-1.1.2.tgz#1fd71791a67fdd1591ebe0dcaadd3964537a824e" + integrity sha512-5DobW1CHgnBROOEpFlEXytED5OosEWESFvg/VYmH0143oXcijYTprRYJTs+55HzGM4IqxiLFSuqEzu9mPNwVsA== -"@polka/url@^1.0.0-next.20": - version "1.0.0-next.21" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" - integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@part-db/html5-qrcode@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@part-db/html5-qrcode/-/html5-qrcode-3.1.1.tgz#96bf5e57796f3ec72ee6191ac7b8604298c98dfa" + integrity sha512-6lH77mvu9ClwIfKHWSOW1+jz+SkCJnbm/8XTuzqlcUWUIW8NRHC5x1wU7svV4gtoirqvBlQvaz1MaopVmfOAqQ== + dependencies: + barcode-detector "^2.3.1" + +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.29" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.29.tgz#5a40109a1ab5f84d6fd8fc928b19f367cbe7e7b1" + integrity sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww== "@popperjs/core@^2.10.2": - version "2.11.6" - resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" - integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@sinclair/typebox@^0.25.16": - version "0.25.21" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" - integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@symfony/stimulus-bridge@^3.2.0": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@symfony/stimulus-bridge/-/stimulus-bridge-3.2.1.tgz#b9c261ad72830fd17898cf27c97862d1cc15b46a" - integrity sha512-eawmVu+tLVkiTz7ewkcsxFvaSZKxFWXmdWxIsxr2jIfQ64PSJg7PIcd7GsPMDxX8sLwg3LGxXfJv5bASL1es+A== + version "3.2.3" + resolved "https://registry.yarnpkg.com/@symfony/stimulus-bridge/-/stimulus-bridge-3.2.3.tgz#1c496d4b11e24051be26a11045118f29f9c3f9b7" + integrity sha512-36rQTihQ2MGOn8EmdOYCr3DQfP3WS1CNcUUXKTPY5ghtFOeb7OVuhbc32AjRowE2/vaVDOUCPOTv3VLf5VtXBA== dependencies: "@hotwired/stimulus-webpack-helpers" "^1.0.1" "@types/webpack-env" "^1.16.4" @@ -1548,119 +1948,80 @@ loader-utils "^2.0.0" schema-utils "^3.0.0" -"@symfony/ux-turbo@file:vendor/symfony/ux-turbo/assets": - version "0.1.0" +"@symfony/ux-translator@file:vendor/symfony/ux-translator/assets": + version "2.23.0" -"@symfony/webpack-encore@^4.1.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@symfony/webpack-encore/-/webpack-encore-4.2.0.tgz#1eec87e3fca9a410563511eb557f70461c10cce5" - integrity sha512-m0ZGm7vZpmc9pVKE7YBppS1tb9bK8r0qzOvRI2uCK7UfXtzfV3VgXr0VdTAlfmC72vvLKI+s9YJpiesOFbR6Aw== +"@symfony/ux-turbo@file:vendor/symfony/ux-turbo/assets": + version "2.23.0" + +"@symfony/webpack-encore@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@symfony/webpack-encore/-/webpack-encore-5.1.0.tgz#d5664153136959e3baf3e248c9b55350c2c81a6d" + integrity sha512-uSaRnssJj+ZHH9UcHRGI8kQzFb5hfCiPtBwP15vuKH5hTPGeJjQouDDo4UaPB3LuSYJXudKFiSDAnj/4d2z4aQ== dependencies: "@nuxt/friendly-errors-webpack-plugin" "^2.5.1" - assets-webpack-plugin "7.0.*" - babel-loader "^8.2.5" - chalk "^4.0.0" - clean-webpack-plugin "^4.0.0" - css-loader "^6.7.0" - css-minimizer-webpack-plugin "^4.0.0" - fast-levenshtein "^3.0.0" + babel-loader "^9.1.3" + css-loader "^7.1.0" + css-minimizer-webpack-plugin "^7.0.0" + fastest-levenshtein "^1.0.16" mini-css-extract-plugin "^2.6.0" - pkg-up "^3.1.0" + picocolors "^1.1.0" pretty-error "^4.0.0" resolve-url-loader "^5.0.0" semver "^7.3.2" style-loader "^3.3.0" - sync-rpc "^1.3.6" tapable "^2.2.1" terser-webpack-plugin "^5.3.0" tmp "^0.2.1" - webpack-dev-server "^4.8.0" yargs-parser "^21.0.0" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== -"@types/body-parser@*": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== +"@types/color-convert@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.4.tgz#843398ae71e951dc5415d202dfd5e43108823eeb" + integrity sha512-Ub1MmDdyZ7mX//g25uBAoH/mWGd9swVbt8BseymnaE18SU4po/PjmCrHxqIIRjBo3hV/vh1KGr0eMxUhp+t+dQ== dependencies: - "@types/connect" "*" - "@types/node" "*" + "@types/color-name" "^1.1.0" -"@types/bonjour@^3.5.9": - version "3.5.10" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" - integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== - dependencies: - "@types/node" "*" +"@types/color-name@^1.1.0": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.5.tgz#3a3510c4e3661f7707c5ae9c67d726986e6e147d" + integrity sha512-j2K5UJqGTxeesj6oQuGpMgifpT5k9HprgQd8D1Y0lOFqKHl3PJu5GMeS4Y5EgjS55AE6OQxf8mPED9uaGbf4Cg== -"@types/connect-history-api-fallback@^1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" - integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== - dependencies: - "@types/express-serve-static-core" "*" - "@types/node" "*" +"@types/dom-webcodecs@0.1.11": + version "0.1.11" + resolved "https://registry.yarnpkg.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.11.tgz#2e36e5cc71789551f107e2fe15d956845fa19567" + integrity sha512-yPEZ3z7EohrmOxbk/QTAa0yonMFkNkjnVXqbGb7D4rMr+F1dGQ8ZUFxXkyLLJuiICPejZ0AZE9Rrk9wUCczx4A== -"@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== - dependencies: - "@types/node" "*" +"@types/emscripten@^1.39.13": + version "1.40.1" + resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.40.1.tgz#4c34102d7cd1503979d4e6652082c23fd805805e" + integrity sha512-sr53lnYkQNhjHNN0oJDdUm5564biioI5DuOpycufDVK7D3y+GR3oUswe2rlwY1nPNyusHbrJ9WoTyIHl4/Bpwg== -"@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== +"@types/eslint-scope@^3.7.7": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - version "8.21.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.0.tgz#21724cfe12b96696feafab05829695d4d7bd7c48" - integrity sha512-35EhHNOXgxnUgh4XCJsGhE7zdlDhYDN/aMG6UbkByCFFNgQ7b3U+uVoqBpicFydR8JEfgdjCF7SJ7MiJfzuiTA== + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== - -"@types/estree@^0.0.51": - version "0.0.51" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== - -"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": - version "4.17.33" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" - integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - -"@types/express@*", "@types/express@^4.17.13": - version "4.17.17" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" - integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" +"@types/estree@*", "@types/estree@^1.0.6": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" + integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== "@types/glob@^7.1.1": version "7.2.0" @@ -1670,41 +2031,46 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/http-proxy@^1.17.8": - version "1.17.9" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" - integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw== - dependencies: - "@types/node" "*" - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" -"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== -"@types/mime@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" - integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== +"@types/lodash-es@4.17.12": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" + integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.17.16" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.16.tgz#94ae78fab4a38d73086e962d0b65c30d816bfb0a" + integrity sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g== + +"@types/marked@4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@types/marked/-/marked-4.3.2.tgz#e2e0ad02ebf5626bd215c5bae2aff6aff0ce9eac" + integrity sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w== "@types/minimatch@*": version "5.1.2" @@ -1712,213 +2078,179 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*": - version "18.13.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850" - integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== + version "22.15.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.18.tgz#2f8240f7e932f571c2d45f555ba0b6c3f7a75963" + integrity sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg== + dependencies: + undici-types "~6.21.0" "@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== -"@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== - -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== - -"@types/serve-index@^1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" - integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== - dependencies: - "@types/express" "*" - -"@types/serve-static@*", "@types/serve-static@^1.13.10": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" - integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== - dependencies: - "@types/mime" "*" - "@types/node" "*" - -"@types/sockjs@^0.3.33": - version "0.3.33" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" - integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== - dependencies: - "@types/node" "*" +"@types/turndown@5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@types/turndown/-/turndown-5.0.5.tgz#614de24fc9ace4d8c0d9483ba81dc8c1976dd26f" + integrity sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w== "@types/webpack-env@^1.16.4": - version "1.18.0" - resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.0.tgz#ed6ecaa8e5ed5dfe8b2b3d00181702c9925f13fb" - integrity sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg== - -"@types/ws@^8.5.1": - version "8.5.4" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" - integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== - dependencies: - "@types/node" "*" + version "1.18.8" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.8.tgz#71f083718c094204d7b64443701d32f1db3989e3" + integrity sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A== "@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" -"@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" -"@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== -"@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== -"@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== -"@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== -"@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" -"@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== -"@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== +"@webassemblyjs/wasm-edit@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" -"@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" -"@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" -"@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" -"@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== dependencies: - "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/ast" "1.14.1" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" - integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== -"@webpack-cli/info@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" - integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== - dependencies: - envinfo "^7.7.3" +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== -"@webpack-cli/serve@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" - integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -1930,60 +2262,49 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.3, abab@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== +"@zxcvbn-ts/core@^3.0.2": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@zxcvbn-ts/core/-/core-3.0.4.tgz#c5bde72235eb6c273cec78b672bb47c0d7045cad" + integrity sha512-aQeiT0F09FuJaAqNrxynlAwZ2mW/1MdXakKWNmGM1Qp/VaY6CnB/GfnMS2T8gB2231Esp1/maCWd8vTG4OuShw== dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" + fastest-levenshtein "1.0.16" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" +"@zxcvbn-ts/language-common@^3.0.3": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@zxcvbn-ts/language-common/-/language-common-3.0.4.tgz#fa1d2a42f8c8a589555859795da90d6b8027b7c4" + integrity sha512-viSNNnRYtc7ULXzxrQIVUNwHAPSXRtoIwy/Tq4XQQdIknBzw4vz36lQLF6mvhMlTIlpjoN/Z1GFu/fwiAlUSsw== -acorn-import-assertions@^1.7.6: - version "1.8.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" - integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== +"@zxcvbn-ts/language-de@^3.0.1": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@zxcvbn-ts/language-de/-/language-de-3.0.2.tgz#fbd0d1be9454e308bbd63bf5487d4c17670094f0" + integrity sha512-CPl2314qWtnJl4EkeEqFbL4unP6yEAHO976ER+df8CQcKsF4FxdZYEahkleWU66dhNI2VKnmJKNMzq8QtHQKcw== -acorn-node@^1.3.0: - version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== - dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" +"@zxcvbn-ts/language-en@^3.0.1": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@zxcvbn-ts/language-en/-/language-en-3.0.2.tgz#162ada6b2b556444efd5a7700e70845cfde6d6ec" + integrity sha512-Zp+zL+I6Un2Bj0tRXNs6VUBq3Djt+hwTwUz4dkt2qgsQz47U0/XthZ4ULrT/RxjwJRl5LwiaKOOZeOtmixHnjg== -acorn-walk@^7.0.0, acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +"@zxcvbn-ts/language-fr@^3.0.1": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@zxcvbn-ts/language-fr/-/language-fr-3.0.2.tgz#79c5f0475fd388502f04f5560456db37dc0dde35" + integrity sha512-Tj9jS/Z8mNBAD21pn8Mp4O86CPrwImysO1fM3DG+fsfk8W79/MDzqpFDBHiqpu69Uo3LPPctMHEEteakFWt4Qg== + +"@zxcvbn-ts/language-ja@^3.0.1": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@zxcvbn-ts/language-ja/-/language-ja-3.0.2.tgz#299bb6f5465f99405577491b1b31352058540c76" + integrity sha512-YjQyt+eMe3EdpeJiSt81AMF8HfEXXCary/VRoG+0erZBzRjfJ1U3JdSiu9wFFxiEF8Cb5FEmTQ6nQPyraezH6Q== acorn-walk@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" -acorn@^7.0.0, acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.0.4, acorn@^8.0.5, acorn@^8.2.4, acorn@^8.5.0, acorn@^8.7.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.0.4, acorn@^8.0.5, acorn@^8.11.0, acorn@^8.14.0: + version "8.14.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== adjust-sourcemap-loader@^4.0.0: version "4.0.0" @@ -1993,13 +2314,6 @@ adjust-sourcemap-loader@^4.0.0: loader-utils "^2.0.0" regex-parser "^2.2.11" -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -2020,14 +2334,14 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.0.0: +ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2037,32 +2351,37 @@ ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.8.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== +ajv@^8.0.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: - fast-deep-equal "^3.1.1" + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" - uri-js "^4.2.2" -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== -ansi-html-community@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -2076,7 +2395,7 @@ ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -anymatch@~3.1.2: +anymatch@~3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -2084,105 +2403,110 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -array-flatten@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - -array-from@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" - integrity sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg== - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: - array-uniq "^1.0.1" + sprintf-js "~1.0.2" + +array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== - -assets-webpack-plugin@7.0.*: - version "7.0.0" - resolved "https://registry.yarnpkg.com/assets-webpack-plugin/-/assets-webpack-plugin-7.0.0.tgz#c61ed7466f35ff7a4d90d7070948736f471b8804" - integrity sha512-DMZ9r6HFxynWeONRMhSOFTvTrmit5dovdoUKdJgCG03M6CC7XiwNImPH+Ad1jaVrQ2n59e05lBhte52xPt4MSA== +array.prototype.reduce@^1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.8.tgz#42f97f5078daedca687d4463fd3c05cbfd83da57" + integrity sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw== dependencies: - camelcase "^6.0.0" - escape-string-regexp "^4.0.0" - lodash "^4.17.20" + call-bind "^1.0.8" + call-bound "^1.0.4" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-array-method-boxes-properly "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + is-string "^1.1.1" -ast-transform@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/ast-transform/-/ast-transform-0.0.0.tgz#74944058887d8283e189d954600947bc98fe0062" - integrity sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A== +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== dependencies: - escodegen "~1.2.0" - esprima "~1.0.4" - through "~2.3.4" + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" -ast-types@^0.7.0: - version "0.7.8" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" - integrity sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q== +async-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -babel-loader@^8.2.5: - version "8.3.0" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8" - integrity sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q== +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== dependencies: - find-cache-dir "^3.3.1" - loader-utils "^2.0.0" - make-dir "^3.1.0" - schema-utils "^2.6.5" + possible-typed-array-names "^1.0.0" -babel-plugin-polyfill-corejs2@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" - integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== +babel-loader@^9.1.3: + version "9.2.1" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.2.1.tgz#04c7835db16c246dd19ba0914418f3937797587b" + integrity sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA== dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.3" - semver "^6.1.1" + find-cache-dir "^4.0.0" + schema-utils "^4.0.0" -babel-plugin-polyfill-corejs3@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" - integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== +babel-plugin-polyfill-corejs2@^0.4.10: + version "0.4.13" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz#7d445f0e0607ebc8fb6b01d7e8fb02069b91dd8b" + integrity sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - core-js-compat "^3.25.1" + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.6.4" + semver "^6.3.1" -babel-plugin-polyfill-regenerator@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" - integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== +babel-plugin-polyfill-corejs3@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz#4e4e182f1bb37c7ba62e2af81d8dd09df31344f6" + integrity sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" + "@babel/helper-define-polyfill-provider" "^0.6.3" + core-js-compat "^3.40.0" + +babel-plugin-polyfill-regenerator@^0.6.1: + version "0.6.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz#428c615d3c177292a22b4f93ed99e358d7906a9b" + integrity sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.4" balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +barcode-detector@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/barcode-detector/-/barcode-detector-2.3.1.tgz#91d0d4e3b913d87f94bae5d024b6b9b5cedc420c" + integrity sha512-D9KEtrquS1tmBZduxBZl8qublIKnRrFqD8TAHDYcLCyrHQBo+vitIxmjMJ61LvXjXyAMalOlO7q0Oh/9Rl2PbQ== + dependencies: + "@types/dom-webcodecs" "0.1.11" + zxing-wasm "1.3.4" + base64-js@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" @@ -2193,48 +2517,20 @@ base64-js@^1.1.2, base64-js@^1.3.0: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - -bonjour-service@^1.0.11: - version "1.1.0" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.1.0.tgz#424170268d68af26ff83a5c640b95def01803a13" - integrity sha512-LVRinRB3k1/K0XzZ2p58COnWvkQknIY6sf0zF2rpErvcJXpMBttEPQSxK+HEXSS9VmpZlDoDnQWv8ftJT20B0Q== - dependencies: - array-flatten "^2.1.2" - dns-equal "^1.0.0" - fast-deep-equal "^3.1.3" - multicast-dns "^7.2.5" +blurhash@2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-2.0.5.tgz#efde729fc14a2f03571a6aa91b49cba80d1abe4b" + integrity sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w== boolbase@^1.0.0: version "1.0.0" @@ -2242,19 +2538,19 @@ boolbase@^1.0.0: integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== bootbox@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/bootbox/-/bootbox-6.0.0.tgz#a5f1074faab38b881e79e009ea4d625c7e8c536d" - integrity sha512-+Calbj1v5UvxAXXDAHfoBlsx63Hcz1JqHaZdJ5EjIcOlkyAbZLCreVScx0Em6ZUvsMCqynuz/3nGDyd9FtFrNQ== + version "6.0.3" + resolved "https://registry.yarnpkg.com/bootbox/-/bootbox-6.0.3.tgz#11cc3dcb15e3c926066b0ca7bd802b1f53f9041e" + integrity sha512-kmQcrPxePAbFpTzqoOFf62X6ihnVzPFU7v/I/UuNedimBGwpv0cgjXusbYqUF6/j+Wcnec9iK7Tr9B0sqwUOYQ== bootstrap@^5.1.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.3.tgz#54739f4414de121b9785c5da3c87b37ff008322b" - integrity sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ== + version "5.3.6" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.6.tgz#fbd91ebaff093f5b191a1c01a8c866d24f9fa6e1" + integrity sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA== bootswatch@^5.1.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/bootswatch/-/bootswatch-5.2.3.tgz#a12bef6ea1a54f1b5b55b472c11a846d1cb77239" - integrity sha512-tvnW15WoOY2sEp1uT1ITDQiJy2TekQa+K+Q28WDXibleIxsY0nAoC9IylbnUPD7Q5vkCIclOuBHLVBblJYYPSA== + version "5.3.6" + resolved "https://registry.yarnpkg.com/bootswatch/-/bootswatch-5.3.6.tgz#b5ccbf11213166633b0a7ec47ad4c1bbd5226cce" + integrity sha512-d2Kwu5IaALz9gb7fmW20mXU4oCJqBrXmJ3ffk8bZPIOMohJOt0/549liK2Jp9zalkxg30dZ+1wf4OH2m1lJC9w== brace-expansion@^1.1.7: version "1.1.11" @@ -2264,22 +2560,12 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" - -brfs@^2.0.0, brfs@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/brfs/-/brfs-2.0.2.tgz#44237878fa82aa479ce4f5fe2c1796ec69f07845" - integrity sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ== - dependencies: - quote-stream "^1.0.1" - resolve "^1.1.5" - static-module "^3.0.2" - through2 "^2.0.0" + fill-range "^7.1.1" brotli@^1.2.0: version "1.3.3" @@ -2288,62 +2574,31 @@ brotli@^1.2.0: dependencies: base64-js "^1.1.2" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browser-resolve@^1.8.1: - version "1.11.3" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" - integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== +browserslist@^4.0.0, browserslist@^4.23.0, browserslist@^4.24.0, browserslist@^4.24.4, browserslist@^4.24.5: + version "4.24.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.5.tgz#aa0f5b8560fe81fde84c6dcb38f759bafba0e11b" + integrity sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw== dependencies: - resolve "1.1.7" - -browserify-optional@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-optional/-/browserify-optional-1.0.1.tgz#1e13722cfde0d85f121676c2a72ced533a018869" - integrity sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ== - dependencies: - ast-transform "0.0.0" - ast-types "^0.7.0" - browser-resolve "^1.8.1" - -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.21.3, browserslist@^4.21.4: - version "4.21.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== - dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" + caniuse-lite "^1.0.30001716" + electron-to-chromium "^1.5.149" + node-releases "^2.0.19" + update-browserslist-db "^1.1.3" bs-custom-file-input@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/bs-custom-file-input/-/bs-custom-file-input-1.3.4.tgz#c275cb8d4f1c02ba026324292509fa9a747dbda8" integrity sha512-NBsQzTnef3OW1MvdKBbMHAYHssCd613MSeJV7z2McXznWtVMnJCy7Ckyc+PwxV6Pk16cu6YBcYWh/ZE0XWNKCA== -buffer-equal@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" - integrity sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - cacache@^15.0.5: version "15.3.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" @@ -2368,13 +2623,31 @@ cacache@^15.0.5: tar "^6.0.2" unique-filename "^1.1.1" -call-bind@^1.0.0, call-bind@^1.0.2: +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" callsites@^3.0.0: version "3.1.0" @@ -2386,10 +2659,10 @@ camelcase-css@^2.0.1: resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== caniuse-api@^3.0.0: version "3.0.0" @@ -2401,12 +2674,12 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001449: - version "1.0.30001451" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz#2e197c698fc1373d63e1406d6607ea4617c613f1" - integrity sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001716: + version "1.0.30001718" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz#dae13a9c80d517c30c6197515a96131c194d8f82" + integrity sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw== -chalk@^2.0.0, chalk@^2.3.2: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2431,20 +2704,20 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== dependencies: - anymatch "~3.1.2" + anymatch "~3.1.1" braces "~3.0.2" - glob-parent "~5.1.2" + glob-parent "~5.1.0" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.6.0" + readdirp "~3.2.0" optionalDependencies: - fsevents "~2.3.2" + fsevents "~2.1.1" chownr@^2.0.0: version "2.0.0" @@ -2452,45 +2725,85 @@ chownr@^2.0.0: integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== ci-info@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -ckeditor5@^36.0.1: - version "36.0.1" - resolved "https://registry.yarnpkg.com/ckeditor5/-/ckeditor5-36.0.1.tgz#c3fad590f81deb1cd693be7346a827c4f297dc8a" - integrity sha512-9zKX7WIRKSDviM0s41VvW8JtDiRtNrhwrzYj4XCjGpZyIXsUKttdJYHIP5iP1MdfZU/hCvpgSUn2fmRPxFbg+Q== +ckeditor5@44.3.0: + version "44.3.0" + resolved "https://registry.yarnpkg.com/ckeditor5/-/ckeditor5-44.3.0.tgz#671fa487cc0e77768f474a3ec47663496584a69b" + integrity sha512-g2arr/fejYAiOkdMeYvz1ficRPJ42OvO+pYROhre08YjIbAtJBr8wO9DPk9nCV3msnyYE+B02bs+TppazhYNtw== dependencies: - "@ckeditor/ckeditor5-clipboard" "^36.0.1" - "@ckeditor/ckeditor5-core" "^36.0.1" - "@ckeditor/ckeditor5-engine" "^36.0.1" - "@ckeditor/ckeditor5-enter" "^36.0.1" - "@ckeditor/ckeditor5-paragraph" "^36.0.1" - "@ckeditor/ckeditor5-select-all" "^36.0.1" - "@ckeditor/ckeditor5-typing" "^36.0.1" - "@ckeditor/ckeditor5-ui" "^36.0.1" - "@ckeditor/ckeditor5-undo" "^36.0.1" - "@ckeditor/ckeditor5-upload" "^36.0.1" - "@ckeditor/ckeditor5-utils" "^36.0.1" - "@ckeditor/ckeditor5-widget" "^36.0.1" + "@ckeditor/ckeditor5-adapter-ckfinder" "44.3.0" + "@ckeditor/ckeditor5-alignment" "44.3.0" + "@ckeditor/ckeditor5-autoformat" "44.3.0" + "@ckeditor/ckeditor5-autosave" "44.3.0" + "@ckeditor/ckeditor5-basic-styles" "44.3.0" + "@ckeditor/ckeditor5-block-quote" "44.3.0" + "@ckeditor/ckeditor5-bookmark" "44.3.0" + "@ckeditor/ckeditor5-ckbox" "44.3.0" + "@ckeditor/ckeditor5-ckfinder" "44.3.0" + "@ckeditor/ckeditor5-clipboard" "44.3.0" + "@ckeditor/ckeditor5-cloud-services" "44.3.0" + "@ckeditor/ckeditor5-code-block" "44.3.0" + "@ckeditor/ckeditor5-core" "44.3.0" + "@ckeditor/ckeditor5-easy-image" "44.3.0" + "@ckeditor/ckeditor5-editor-balloon" "44.3.0" + "@ckeditor/ckeditor5-editor-classic" "44.3.0" + "@ckeditor/ckeditor5-editor-decoupled" "44.3.0" + "@ckeditor/ckeditor5-editor-inline" "44.3.0" + "@ckeditor/ckeditor5-editor-multi-root" "44.3.0" + "@ckeditor/ckeditor5-emoji" "44.3.0" + "@ckeditor/ckeditor5-engine" "44.3.0" + "@ckeditor/ckeditor5-enter" "44.3.0" + "@ckeditor/ckeditor5-essentials" "44.3.0" + "@ckeditor/ckeditor5-find-and-replace" "44.3.0" + "@ckeditor/ckeditor5-font" "44.3.0" + "@ckeditor/ckeditor5-heading" "44.3.0" + "@ckeditor/ckeditor5-highlight" "44.3.0" + "@ckeditor/ckeditor5-horizontal-line" "44.3.0" + "@ckeditor/ckeditor5-html-embed" "44.3.0" + "@ckeditor/ckeditor5-html-support" "44.3.0" + "@ckeditor/ckeditor5-image" "44.3.0" + "@ckeditor/ckeditor5-indent" "44.3.0" + "@ckeditor/ckeditor5-language" "44.3.0" + "@ckeditor/ckeditor5-link" "44.3.0" + "@ckeditor/ckeditor5-list" "44.3.0" + "@ckeditor/ckeditor5-markdown-gfm" "44.3.0" + "@ckeditor/ckeditor5-media-embed" "44.3.0" + "@ckeditor/ckeditor5-mention" "44.3.0" + "@ckeditor/ckeditor5-minimap" "44.3.0" + "@ckeditor/ckeditor5-page-break" "44.3.0" + "@ckeditor/ckeditor5-paragraph" "44.3.0" + "@ckeditor/ckeditor5-paste-from-office" "44.3.0" + "@ckeditor/ckeditor5-remove-format" "44.3.0" + "@ckeditor/ckeditor5-restricted-editing" "44.3.0" + "@ckeditor/ckeditor5-select-all" "44.3.0" + "@ckeditor/ckeditor5-show-blocks" "44.3.0" + "@ckeditor/ckeditor5-source-editing" "44.3.0" + "@ckeditor/ckeditor5-special-characters" "44.3.0" + "@ckeditor/ckeditor5-style" "44.3.0" + "@ckeditor/ckeditor5-table" "44.3.0" + "@ckeditor/ckeditor5-theme-lark" "44.3.0" + "@ckeditor/ckeditor5-typing" "44.3.0" + "@ckeditor/ckeditor5-ui" "44.3.0" + "@ckeditor/ckeditor5-undo" "44.3.0" + "@ckeditor/ckeditor5-upload" "44.3.0" + "@ckeditor/ckeditor5-utils" "44.3.0" + "@ckeditor/ckeditor5-watchdog" "44.3.0" + "@ckeditor/ckeditor5-widget" "44.3.0" + "@ckeditor/ckeditor5-word-count" "44.3.0" clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -clean-webpack-plugin@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz#72947d4403d452f38ed61a9ff0ada8122aacd729" - integrity sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w== - dependencies: - del "^4.1.1" - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -2499,9 +2812,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-spinners@^2.6.1: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== clipboard@^2.0.4: version "2.0.11" @@ -2512,6 +2825,15 @@ clipboard@^2.0.4: select "^1.1.2" tiny-emitter "^2.0.0" +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -2526,6 +2848,13 @@ clone@^1.0.4: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== +color-convert@2.0.1, color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2533,151 +2862,102 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colord@^2.9.1: +color-parse@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/color-parse/-/color-parse-1.4.2.tgz#78651f5d34df1a57f997643d86f7f87268ad4eb5" + integrity sha512-RI7s49/8yqDj3fECFZjUI1Yi0z/Gq1py43oNJivAIIDSyJiOZLfYCRQEgn8HEVAj++PcRe8AnL2XF0fRJ3BTnA== + dependencies: + color-name "^1.0.0" + +colord@^2.9.3: version "2.9.3" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== -colorette@^2.0.10, colorette@^2.0.14: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^7.0.0, commander@^7.2.0: +commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@^8.0.0: +commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +common-path-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" + integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== +compression-webpack-plugin@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-11.1.0.tgz#ee340d2029cf99ccecdea9ad1410b377d15b48b3" + integrity sha512-zDOQYp10+upzLxW+VRSjEpRRwBXJdsb5lBMlRxx1g8hckIFBpe3DTI0en2w7h+beuq89576RVzfiXrkdPGrHhA== dependencies: - mime-db ">= 1.43.0 < 2" - -compression-webpack-plugin@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-10.0.0.tgz#3496af1b0dc792e13efc474498838dbff915c823" - integrity sha512-wLXLIBwpul/ALcm7Aj+69X0pYT3BYt6DdPn3qrgBIh9YejV9Bju9ShhlAsjujLyWMo6SAweFIWaUoFmXZNuNrg== - dependencies: - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@~1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" +consola@^3.2.3: + version "3.4.2" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7" + integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== -connect-history-api-fallback@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" - integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== - -consola@^2.6.0: - version "2.15.3" - resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" - integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -convert-source-map@^1.5.1, convert-source-map@^1.7.0: +convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - -core-js-compat@^3.25.1: - version "3.27.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.27.2.tgz#607c50ad6db8fd8326af0b2883ebb987be3786da" - integrity sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg== +core-js-compat@^3.40.0: + version "3.42.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.42.0.tgz#ce19c29706ee5806e26d3cb3c542d4cfc0ed51bb" + integrity sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ== dependencies: - browserslist "^4.21.4" + browserslist "^4.24.4" core-js@^3.23.0: - version "3.27.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.27.2.tgz#85b35453a424abdcacb97474797815f4d62ebbf7" - integrity sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w== + version "3.42.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.42.0.tgz#edbe91f78ac8cfb6df8d997e74d368a68082fe37" + integrity sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g== core-util-is@~1.0.0: version "1.0.3" @@ -2696,49 +2976,65 @@ cosmiconfig@^7.0.0: yaml "^1.10.0" cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" -crypto-js@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" - integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== +crypto-js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== -css-declaration-sorter@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz#be5e1d71b7a992433fb1c542c7a1b835e45682ec" - integrity sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w== +css-declaration-sorter@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz#6dec1c9523bc4a643e088aab8f09e67a54961024" + integrity sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow== -css-loader@^6.7.0: - version "6.7.3" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.3.tgz#1e8799f3ccc5874fdd55461af51137fcc5befbcd" - integrity sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ== +css-loader@^5.2.7: + version "5.2.7" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" + integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg== dependencies: icss-utils "^5.1.0" - postcss "^8.4.19" + loader-utils "^2.0.0" + postcss "^8.2.15" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" postcss-modules-values "^4.0.0" - postcss-value-parser "^4.2.0" - semver "^7.3.8" + postcss-value-parser "^4.1.0" + schema-utils "^3.0.0" + semver "^7.3.5" -css-minimizer-webpack-plugin@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz#79f6199eb5adf1ff7ba57f105e3752d15211eb35" - integrity sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA== +css-loader@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-7.1.2.tgz#64671541c6efe06b0e22e750503106bdd86880f8" + integrity sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA== dependencies: - cssnano "^5.1.8" - jest-worker "^29.1.2" - postcss "^8.4.17" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" - source-map "^0.6.1" + icss-utils "^5.1.0" + postcss "^8.4.33" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.5.4" + +css-minimizer-webpack-plugin@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-7.0.2.tgz#aa1b01c6033f5b2f86ddb60c1f5bddd012b50cac" + integrity sha512-nBRWZtI77PBZQgcXMNqiIXVshiQOVLGSf2qX/WZfG8IQfMbeHUMXaBWQmiiSTmPJUflQxHjZjzAmuyO7tpL2Jg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + cssnano "^7.0.4" + jest-worker "^29.7.0" + postcss "^8.4.40" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" css-select@^4.1.3: version "4.3.0" @@ -2751,15 +3047,34 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-tree@^1.1.2, css-tree@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" -css-what@^6.0.1: +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + +css-what@^6.0.1, css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== @@ -2769,275 +3084,297 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano-preset-default@^5.2.13: - version "5.2.13" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz#e7353b0c57975d1bdd97ac96e68e5c1b8c68e990" - integrity sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ== +cssnano-preset-default@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz#adf4b89b975aa775f2750c89dbaf199bbd9da35e" + integrity sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg== dependencies: - css-declaration-sorter "^6.3.1" - cssnano-utils "^3.1.0" - postcss-calc "^8.2.3" - postcss-colormin "^5.3.0" - postcss-convert-values "^5.1.3" - postcss-discard-comments "^5.1.2" - postcss-discard-duplicates "^5.1.0" - postcss-discard-empty "^5.1.1" - postcss-discard-overridden "^5.1.0" - postcss-merge-longhand "^5.1.7" - postcss-merge-rules "^5.1.3" - postcss-minify-font-values "^5.1.0" - postcss-minify-gradients "^5.1.1" - postcss-minify-params "^5.1.4" - postcss-minify-selectors "^5.2.1" - postcss-normalize-charset "^5.1.0" - postcss-normalize-display-values "^5.1.0" - postcss-normalize-positions "^5.1.1" - postcss-normalize-repeat-style "^5.1.1" - postcss-normalize-string "^5.1.0" - postcss-normalize-timing-functions "^5.1.0" - postcss-normalize-unicode "^5.1.1" - postcss-normalize-url "^5.1.0" - postcss-normalize-whitespace "^5.1.1" - postcss-ordered-values "^5.1.3" - postcss-reduce-initial "^5.1.1" - postcss-reduce-transforms "^5.1.0" - postcss-svgo "^5.1.0" - postcss-unique-selectors "^5.1.1" + browserslist "^4.23.0" + css-declaration-sorter "^7.2.0" + cssnano-utils "^4.0.2" + postcss-calc "^9.0.1" + postcss-colormin "^6.1.0" + postcss-convert-values "^6.1.0" + postcss-discard-comments "^6.0.2" + postcss-discard-duplicates "^6.0.3" + postcss-discard-empty "^6.0.3" + postcss-discard-overridden "^6.0.2" + postcss-merge-longhand "^6.0.5" + postcss-merge-rules "^6.1.1" + postcss-minify-font-values "^6.1.0" + postcss-minify-gradients "^6.0.3" + postcss-minify-params "^6.1.0" + postcss-minify-selectors "^6.0.4" + postcss-normalize-charset "^6.0.2" + postcss-normalize-display-values "^6.0.2" + postcss-normalize-positions "^6.0.2" + postcss-normalize-repeat-style "^6.0.2" + postcss-normalize-string "^6.0.2" + postcss-normalize-timing-functions "^6.0.2" + postcss-normalize-unicode "^6.1.0" + postcss-normalize-url "^6.0.2" + postcss-normalize-whitespace "^6.0.2" + postcss-ordered-values "^6.0.2" + postcss-reduce-initial "^6.1.0" + postcss-reduce-transforms "^6.0.2" + postcss-svgo "^6.0.3" + postcss-unique-selectors "^6.0.4" -cssnano-utils@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" - integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== - -cssnano@^5.0.0, cssnano@^5.1.8: - version "5.1.14" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.14.tgz#07b0af6da73641276fe5a6d45757702ebae2eb05" - integrity sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw== +cssnano-preset-default@^7.0.7: + version "7.0.7" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-7.0.7.tgz#1ea881837a837a4400c383d77d9e6cdf4339b6a0" + integrity sha512-jW6CG/7PNB6MufOrlovs1TvBTEVmhY45yz+bd0h6nw3h6d+1e+/TX+0fflZ+LzvZombbT5f+KC063w9VoHeHow== dependencies: - cssnano-preset-default "^5.2.13" - lilconfig "^2.0.3" - yaml "^1.10.2" + browserslist "^4.24.5" + css-declaration-sorter "^7.2.0" + cssnano-utils "^5.0.1" + postcss-calc "^10.1.1" + postcss-colormin "^7.0.3" + postcss-convert-values "^7.0.5" + postcss-discard-comments "^7.0.4" + postcss-discard-duplicates "^7.0.2" + postcss-discard-empty "^7.0.1" + postcss-discard-overridden "^7.0.1" + postcss-merge-longhand "^7.0.5" + postcss-merge-rules "^7.0.5" + postcss-minify-font-values "^7.0.1" + postcss-minify-gradients "^7.0.1" + postcss-minify-params "^7.0.3" + postcss-minify-selectors "^7.0.5" + postcss-normalize-charset "^7.0.1" + postcss-normalize-display-values "^7.0.1" + postcss-normalize-positions "^7.0.1" + postcss-normalize-repeat-style "^7.0.1" + postcss-normalize-string "^7.0.1" + postcss-normalize-timing-functions "^7.0.1" + postcss-normalize-unicode "^7.0.3" + postcss-normalize-url "^7.0.1" + postcss-normalize-whitespace "^7.0.1" + postcss-ordered-values "^7.0.2" + postcss-reduce-initial "^7.0.3" + postcss-reduce-transforms "^7.0.1" + postcss-svgo "^7.0.2" + postcss-unique-selectors "^7.0.4" -csso@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== +cssnano-utils@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-4.0.2.tgz#56f61c126cd0f11f2eef1596239d730d9fceff3c" + integrity sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ== + +cssnano-utils@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-5.0.1.tgz#f529e9aa0d7930512ca45b9e2ddb8d6b9092eb30" + integrity sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg== + +cssnano@^6.0.3: + version "6.1.2" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-6.1.2.tgz#4bd19e505bd37ee7cf0dc902d3d869f6d79c66b8" + integrity sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA== dependencies: - css-tree "^1.1.2" + cssnano-preset-default "^6.1.2" + lilconfig "^3.1.1" -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== +cssnano@^7.0.4: + version "7.0.7" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-7.0.7.tgz#1aca487fc32d6b3b71e385c41a8cccb2f43735ac" + integrity sha512-evKu7yiDIF7oS+EIpwFlMF730ijRyLFaM2o5cTxRGJR9OKHKkc+qP443ZEVR9kZG0syaAJJCPJyfv5pbrxlSng== dependencies: - cssom "~0.3.6" + cssnano-preset-default "^7.0.7" + lilconfig "^3.1.3" -d@1, d@^1.0.1: +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-offset@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== dependencies: - es5-ext "^0.10.50" - type "^1.0.1" + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" -darkmode-js@^1.5.0: - version "1.5.7" - resolved "https://registry.yarnpkg.com/darkmode-js/-/darkmode-js-1.5.7.tgz#b260ff671d67e021446ac8bade458e08338f6410" - integrity sha512-gM1KhV/yy/84ZUpES9/oeOn0CVtuOi2HBd+Kccr4CJz+rFccQo2gUuPbjfyPXUx7t800Zf408G6kZb5rkTgjFA== - -dash-ast@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-2.0.1.tgz#8d0fd2e601c59bf874cc22877ee7dd889f54dee8" - integrity sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ== - -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== +datatables.net-bs5@^2, datatables.net-bs5@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/datatables.net-bs5/-/datatables.net-bs5-2.3.1.tgz#167a86c4ab138fe6eb7f653fb4c64e3ade0d3d7a" + integrity sha512-bUwsW7cIR8EDyiedQv6OKGF5ouxkFjaODmtEd7NRtpoZixAZDScmIrxe9cddjS0EybpC2gYslKs+o03E8tkCEA== dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - -datatables.net-bs5@>=1.12.1, datatables.net-bs5@^1.10.20: - version "1.13.2" - resolved "https://registry.yarnpkg.com/datatables.net-bs5/-/datatables.net-bs5-1.13.2.tgz#580bf08224c25cf7a86e94772757199babee7fe5" - integrity sha512-p1JOXFi+VD4wS0AxRmJPHBylJ8AC4xGJ3j0TZ+8hMspuZ+QvAhEqEPt8OEAGVzlN1ElfQimZjBeAoES/9nsuig== - dependencies: - datatables.net ">=1.12.1" + datatables.net "2.3.1" jquery ">=1.7" -datatables.net-buttons-bs5@^2.2.2: - version "2.3.4" - resolved "https://registry.yarnpkg.com/datatables.net-buttons-bs5/-/datatables.net-buttons-bs5-2.3.4.tgz#0670ced3f94c6f929f3cae5f4851d3ffe18d5c65" - integrity sha512-3pCmzcHwoow0kYH967cymeHT+IPn6+RzK+3XqUsDykIWRcjUaZR6fj+TSL7E8LCkgEI0KhwBIK1Uqa4v8YH0jg== +datatables.net-buttons-bs5@^3.0.0: + version "3.2.3" + resolved "https://registry.yarnpkg.com/datatables.net-buttons-bs5/-/datatables.net-buttons-bs5-3.2.3.tgz#e6a7d2598d6303cbcdefce350fbc785461136059" + integrity sha512-jBwVlK0Iy3Sh3wqeH6STy+xPuGn37cqz+e/MqA0iXx7fDgqVPvWrEoO6Jdp4thejYTBokV7SZR2UirOxTcpOcA== dependencies: - datatables.net-bs5 ">=1.12.1" - datatables.net-buttons ">=2.2.3" + datatables.net-bs5 "^2" + datatables.net-buttons "3.2.3" jquery ">=1.7" -datatables.net-buttons@>=2.2.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/datatables.net-buttons/-/datatables.net-buttons-2.3.4.tgz#85b88baed81d380cb04c06608d549c8868326ece" - integrity sha512-1fe/aiKBdKbwJ5j0OobP2dzhbg/alGOphnTfLFGaqlP5yVxDCfcZ9EsuglYeHRJ/KnU7DZ8BgsPFiTE0tOFx8Q== +datatables.net-buttons@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/datatables.net-buttons/-/datatables.net-buttons-3.2.3.tgz#26eae1f012fd5cfbfcf28dfbfd8a4b644ea8a4ae" + integrity sha512-K+WeQWUYVGe5c3Gwb8Gfi7YpUXbJEerik3B2vynnVKpBlYBF5AHTGbrK1Psek2q/mjxeIVNHafQ9eX2otLhJVw== dependencies: - datatables.net ">=1.12.1" + datatables.net "^2" jquery ">=1.7" -datatables.net-colreorder-bs5@^1.5.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/datatables.net-colreorder-bs5/-/datatables.net-colreorder-bs5-1.6.1.tgz#471cb30da3f33a2db2d67c00130ceba7fec7d016" - integrity sha512-wzAAmP1p3VrVEIfkJMh2Dx4qGdQV1lU8Wcnk5DgQI3aRqvVMdsM/6yXzj1IH2j1SnyRzvtzzxF869Bj3rBwZ3g== +datatables.net-colreorder-bs5@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/datatables.net-colreorder-bs5/-/datatables.net-colreorder-bs5-2.1.0.tgz#6d9b37605f6cd1b42217cf4e65d09833c9113cba" + integrity sha512-s7ekoFwsj/NqyxnWQoGTQh8QbDvejM7dDSqLoAjfQDOgKpv3t0eZvzXIvufsZZhKPEJEw3Yof/tpN8+CHwDVxA== dependencies: - datatables.net-bs5 ">=1.12.1" - datatables.net-colreorder ">=1.5.6" + datatables.net-bs5 "^2" + datatables.net-colreorder "2.1.0" jquery ">=1.7" -datatables.net-colreorder@>=1.5.6: - version "1.6.1" - resolved "https://registry.yarnpkg.com/datatables.net-colreorder/-/datatables.net-colreorder-1.6.1.tgz#effecd14fb8c42299b4a120d31b6ab04ccdd5dca" - integrity sha512-ae0gdkG0OmrEGUrXYm0XgWDzDkuEhEuNrfvQsmtCTl0j+1nxtXPsecIiWBCqv/dM0X4x8PT0dJY8furqPCzXXw== +datatables.net-colreorder@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/datatables.net-colreorder/-/datatables.net-colreorder-2.1.0.tgz#49dee41ad4874fc132031fdc6cc04369dc23cbdc" + integrity sha512-VMZkrR6cPzaCdlXnFmkM/oZS/TFY6NMusE7km3M3pYFVcmTyooQL/zDhEbhcCJrvNvuQkskruFYxwPK8+MzFPA== dependencies: - datatables.net ">=1.12.1" + datatables.net "^2" jquery ">=1.7" -datatables.net-fixedheader-bs5@^3.1.5: - version "3.3.1" - resolved "https://registry.yarnpkg.com/datatables.net-fixedheader-bs5/-/datatables.net-fixedheader-bs5-3.3.1.tgz#9a1b556fb51621477c28b6dd2cadd1bf36332d30" - integrity sha512-o/u3GsPq9cdZ0dhdcbwayWIP3nzGAAPnB41n1V1KaQsIYitO4KNHZw4VafENVRValKeuRMvkdZt60LCTUIB/4A== +datatables.net-fixedheader-bs5@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/datatables.net-fixedheader-bs5/-/datatables.net-fixedheader-bs5-4.0.2.tgz#f2c347853cfdf5c720fde61408e427ae96282ce7" + integrity sha512-CT8chF78LZg0zk8C21XEKrJv1i6DxyN1hFS80UHLGnh9bTBn7BzOG3781cZKfyUc2alazVLxZgCiLDreakrgZQ== dependencies: - datatables.net-bs5 ">=1.12.1" - datatables.net-fixedheader ">=3.2.4" + datatables.net-bs5 "^2" + datatables.net-fixedheader "4.0.2" jquery ">=1.7" -datatables.net-fixedheader@>=3.2.4: - version "3.3.1" - resolved "https://registry.yarnpkg.com/datatables.net-fixedheader/-/datatables.net-fixedheader-3.3.1.tgz#71aafa169b91f0fc1b3fa353d50f575ad67ad50d" - integrity sha512-m1ip5dOOsdjaFw2e5G77o+XLjqy5wWKnBnp+BwbnFCq4J5hFbMqcIV1r5z9X+NeAiKlADqZqteeLoO2xYRXBVA== +datatables.net-fixedheader@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/datatables.net-fixedheader/-/datatables.net-fixedheader-4.0.2.tgz#29321374aa771babe920a80bd2a365611d1d851c" + integrity sha512-pjOs3m0vRQArVZWG/LYfsyi/P9cjuNer0BuG6v2hkn/LT2DAiIe6l543p7IHgTGQXn/my2s/M397DY7U7txavg== dependencies: - datatables.net ">=1.12.1" + datatables.net "^2" jquery ">=1.7" -datatables.net-responsive-bs5@^2.2.3: - version "2.4.0" - resolved "https://registry.yarnpkg.com/datatables.net-responsive-bs5/-/datatables.net-responsive-bs5-2.4.0.tgz#ab3a6fb147e5886ae043c9ecd54652f3a5d6d5c0" - integrity sha512-2kxigvftgVgdyH+BZ17jbR+BcWYZsacnlTt4PeOH/P98bMuqJKrziss7CLz/6qud/ehHdLEQTjMDoJddN9NfxA== +datatables.net-responsive-bs5@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/datatables.net-responsive-bs5/-/datatables.net-responsive-bs5-3.0.4.tgz#d13fd199f309cea41b08cb15c98ce5aafdeabe03" + integrity sha512-/RufNJAFFmXkhfyBmXKfLlpopoKNkeDytMHHbsiBkoTRllJik9rtOON5NuMJOCL8sjwM2c+rUo/rzuXNk/70Qw== dependencies: - datatables.net-bs5 ">=1.12.1" - datatables.net-responsive ">=2.3.0" + datatables.net-bs5 "^2" + datatables.net-responsive "3.0.4" jquery ">=1.7" -datatables.net-responsive@>=2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/datatables.net-responsive/-/datatables.net-responsive-2.4.0.tgz#91658679483e3ef6cda8b9bc5c1b7eff076a8e70" - integrity sha512-Acws4aEPJZX/+CQsXpuDBHfrwgl3XxWEc/zlsnJCXE/GGbqjVAtQt7SM6EBcdReMv1FbyWUlF/Uw+de11NT46A== +datatables.net-responsive@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/datatables.net-responsive/-/datatables.net-responsive-3.0.4.tgz#9e8d354f3b4016b60fe0aa588094ef0b31e9c61b" + integrity sha512-d1JKaWPRp81xydZrTDqrzGTloXthDtyvwbqiSKYUhDBKWTL4wN5IW5WYr4OBPwu21Ctpi/QgOl7mt9MfyF8KxA== dependencies: - datatables.net ">=1.12.1" + datatables.net "^2" jquery ">=1.7" -datatables.net-select-bs5@^1.2.7: - version "1.6.0" - resolved "https://registry.yarnpkg.com/datatables.net-select-bs5/-/datatables.net-select-bs5-1.6.0.tgz#a70dcffb06d6800b0cd1c9c42a6b517217983fe1" - integrity sha512-6vOeEqe+HmU6r4jKjgQsNfSkmUd6Ei7PyaSc6XpmWFZ4oRWu/tKoVQWd1KXRv5S6gZmlNowKXeafQYjpmHMLAg== +datatables.net-select-bs5@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/datatables.net-select-bs5/-/datatables.net-select-bs5-2.1.0.tgz#4cfd61007c644f224e10e1eae7662204d167caf2" + integrity sha512-N6VFo3DHsMCzM1Ef2pbUh1+pUd/MSHk48Z+X0QvbeknShWKdx0PS7FhLkaq8OalhHiEH0lyCy5L+1iR4bgAu9A== dependencies: - datatables.net-bs5 ">=1.12.1" - datatables.net-select ">=1.4.0" + datatables.net-bs5 "^2" + datatables.net-select "2.1.0" jquery ">=1.7" -datatables.net-select@>=1.4.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/datatables.net-select/-/datatables.net-select-1.6.0.tgz#e2d93943e374917e0192899bf67416effadbe449" - integrity sha512-1kj32GOXs/dSpjBL5iDV3pwRwHU0hhJLPnTW/NOUH8Vhv1rGR3/X3PMSCc/T+Fy7J1jCJFbk8hQDsruXQKfSzw== +datatables.net-select@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/datatables.net-select/-/datatables.net-select-2.1.0.tgz#d50f13cd4c18d50c06b24efd8ca5371ed0bbe59d" + integrity sha512-6B1sI5pfvBen+8kySl1T0hkGoUmrw+5YGqaW4NCg+1srMw+48YV5SdWPiyCQDoev2ajBdKzHuG/wzD2INMbmJw== dependencies: - datatables.net ">=1.12.1" + datatables.net "^2" jquery ">=1.7" -datatables.net@>=1.12.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.13.2.tgz#48f7035b1696a29cb70909db1f2e0ebd5f946f3e" - integrity sha512-u5nOU+C9SBp1SyPmd6G+niozZtrBwo1E8xzdOk3JJaAkFYgX/KxF3Gd79R8YLbUfmIs2OLnLe5gaz/qs5U8UDA== +datatables.net@2.3.1, datatables.net@^2, datatables.net@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.3.1.tgz#fcb61efd42fe64627fb9042637f1d93872268e70" + integrity sha512-pRXZuk3oR7P5kTl/CRvopcTihlvmZiY+xSBjbNI6e8MjYtgZQodWEqLUUOrIZeLH8CfxapPR/Bmb0NKAvILP5g== dependencies: jquery ">=1.7" -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@4, debug@^4.1.0, debug@^4.1.1: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: - ms "2.1.2" + ms "^2.1.1" -decimal.js@^10.2.1: - version "10.4.3" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" - integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decimal.js@^10.4.3: + version "10.5.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" + integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== deep-equal@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + version "1.1.2" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.2.tgz#78a561b7830eef3134c7f6f3a3d6af272a678761" + integrity sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg== dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" + is-arguments "^1.1.1" + is-date-object "^1.0.5" + is-regex "^1.1.4" + object-is "^1.1.5" object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" + regexp.prototype.flags "^1.5.1" -deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: - execa "^5.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== - -define-properties@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" - integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== +define-properties@^1.1.2, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: + define-data-property "^1.0.1" has-property-descriptors "^1.0.0" object-keys "^1.1.1" -del@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" - integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== - dependencies: - "@types/glob" "^7.1.1" - globby "^6.1.0" - is-path-cwd "^2.0.0" - is-path-in-cwd "^2.0.0" - p-map "^2.0.0" - pify "^4.0.1" - rimraf "^2.6.3" - del@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" @@ -3052,41 +3389,21 @@ del@^5.0.0: rimraf "^3.0.0" slash "^3.0.0" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - delegate@^3.1.2: version "3.2.0" resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - dfa@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" integrity sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q== +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -3094,18 +3411,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== - -dns-packet@^5.2.2: - version "5.4.0" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.4.0.tgz#1f88477cf9f27e78a213fb6d118ae38e759a879b" - integrity sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g== - dependencies: - "@leichtgewicht/ip-codec" "^2.0.1" - dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -3122,18 +3427,20 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -domelementtype@^2.0.1, domelementtype@^2.2.0: +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" - domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" @@ -3141,10 +3448,19 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -dompurify@^2.0.6: - version "2.4.3" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.3.tgz#f4133af0e6a50297fc8874e2eaedc13a3c308c03" - integrity sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ== +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +dompurify@^3.0.3: + version "3.2.5" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.5.tgz#11b108656a5fb72b24d916df17a1421663d7129c" + integrity sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ== + optionalDependencies: + "@types/trusted-types" "^2.0.7" domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" @@ -3155,52 +3471,58 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -duplexer2@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== +domutils@^3.0.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== dependencies: - readable-stream "^2.0.2" + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dunder-proto@^1.0.0, dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== +electron-to-chromium@^1.5.149: + version "1.5.155" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz#809dd0ae9ae1db87c358e0c0c17c09a2ffc432d1" + integrity sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng== -electron-to-chromium@^1.4.284: - version "1.4.295" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz#911d5df67542bf7554336142eb302c5ec90bba66" - integrity sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw== +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji.json@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/emoji.json/-/emoji.json-14.0.0.tgz#6df626a027309d910d7d51a24dc13ac384a15453" - integrity sha512-2rpEIAs1VDc6eHpqX3l1xpijXWu2F+zzj79+IyRu6wTcnsjQX4+vG43PxCk/2mQFB7e9MLuWl29g8hYd1DYI6Q== +emoji.json@^15.0.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/emoji.json/-/emoji.json-15.1.0.tgz#1a1bac02f95983c808601a74b0b367677e52db3b" + integrity sha512-GqE6SCE8rSc5Gyd+oh6s/pkoNSW/pULdgJTB6jd/sM87FWA/a1nmvFMdWBjvSA5vdomzDvZgy584KNgzdYJwDA== emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" - integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== +enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.1: + version "5.18.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz#728ab082f8b7b6836de51f1637aab5d3b9568faf" + integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -3210,10 +3532,15 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^4.2.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + envinfo@^7.7.3: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.14.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" + integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== error-ex@^1.3.1: version "1.3.2" @@ -3222,79 +3549,160 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error-stack-parser@^2.0.0: +error-stack-parser@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== dependencies: stackframe "^1.3.4" -es-module-lexer@^0.9.0: - version "0.9.3" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" - integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== - -es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== +es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9: + version "1.23.9" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.9.tgz#5b45994b7de78dada5c1bebf1379646b32b9d606" + integrity sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA== dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - next-tick "^1.1.0" + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.2.7" + get-proto "^1.0.0" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-regex "^1.2.1" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.0" + math-intrinsics "^1.1.0" + object-inspect "^1.13.3" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.3" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.18" -es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" + es-errors "^1.3.0" -es6-map@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - integrity sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A== +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" -es6-set@^0.1.5, es6-set@~0.1.5: - version "0.1.6" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.6.tgz#5669e3b2aa01d61a50ba79964f733673574983b8" - integrity sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw== +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== dependencies: - d "^1.0.1" - es5-ext "^0.10.62" - es6-iterator "~2.0.3" - es6-symbol "^3.1.3" - event-emitter "^0.3.5" - type "^2.7.2" + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" -es6-symbol@^3.1.1, es6-symbol@^3.1.3, es6-symbol@~3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== +esbuild-loader@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/esbuild-loader/-/esbuild-loader-3.0.1.tgz#9871c0e8817c4c11b6249d1916832e75272e6c7e" + integrity sha512-aZfGybqTeuyCd4AsVvWOOfkhIuN+wfZFjMyh3gyQEU1Uvsl8L6vye9HqP93iRa0iTA+6Jclap514PJIC3cLnMA== dependencies: - d "^1.0.1" - ext "^1.1.2" + esbuild "^0.17.6" + get-tsconfig "^4.4.0" + loader-utils "^2.0.4" + webpack-sources "^1.4.3" -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +esbuild@^0.17.6: + version "0.17.19" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955" + integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== + optionalDependencies: + "@esbuild/android-arm" "0.17.19" + "@esbuild/android-arm64" "0.17.19" + "@esbuild/android-x64" "0.17.19" + "@esbuild/darwin-arm64" "0.17.19" + "@esbuild/darwin-x64" "0.17.19" + "@esbuild/freebsd-arm64" "0.17.19" + "@esbuild/freebsd-x64" "0.17.19" + "@esbuild/linux-arm" "0.17.19" + "@esbuild/linux-arm64" "0.17.19" + "@esbuild/linux-ia32" "0.17.19" + "@esbuild/linux-loong64" "0.17.19" + "@esbuild/linux-mips64el" "0.17.19" + "@esbuild/linux-ppc64" "0.17.19" + "@esbuild/linux-riscv64" "0.17.19" + "@esbuild/linux-s390x" "0.17.19" + "@esbuild/linux-x64" "0.17.19" + "@esbuild/netbsd-x64" "0.17.19" + "@esbuild/openbsd-x64" "0.17.19" + "@esbuild/sunos-x64" "0.17.19" + "@esbuild/win32-arm64" "0.17.19" + "@esbuild/win32-ia32" "0.17.19" + "@esbuild/win32-x64" "0.17.19" -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -3304,41 +3712,6 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^1.11.1, escodegen@^1.9.0: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -escodegen@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.2.0.tgz#09de7967791cc958b7f89a2ddb6d23451af327e1" - integrity sha512-yLy3Cc+zAC0WSmoT2fig3J87TpQ8UaZGx8ahCAs9FL8qNbyV7CVyPKS74DG4bsHiL5ew9sxdYx131OkBQMFnvA== - dependencies: - esprima "~1.0.4" - estraverse "~1.5.0" - esutils "~1.0.0" - optionalDependencies: - source-map "~0.1.30" - eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -3347,16 +3720,11 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -esprima@^4.0.1: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esprima@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" - integrity sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA== - esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -3364,7 +3732,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -3374,187 +3742,68 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estraverse@~1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.5.1.tgz#867a3e8e58a9f84618afb6c2ddbcd916b7cbaf71" - integrity sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ== - -estree-is-function@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/estree-is-function/-/estree-is-function-1.0.0.tgz#c0adc29806d7f18a74db7df0f3b2666702e37ad2" - integrity sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -esutils@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" - integrity sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -event-emitter@^0.3.5, event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -exports-loader@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-3.1.0.tgz#a68040b902da8ef24f9f6db716be904ac2455284" - integrity sha512-zkMR5OHDn8qHq2w5BLv6SnLmUK5QAtPkjTA7CNIYBB9kIxBFIeA+TA1GcMw3p/vn5Avnmq80L7MviA4tZclRmQ== +exports-loader@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-5.0.0.tgz#0e5c50baf8526237c0a2743116a3e3fa788d194f" + integrity sha512-W15EyyytBwd30yCCieTCqZSCUvU/o3etj2IUItSMjVQEzAf5xOQx8JL9iMo7ERnuAzIA6eapGSFWl7E9F+Wy9g== dependencies: source-map "^0.6.1" -express@^4.17.3: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.1" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.11.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -ext@^1.1.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.0.3, fast-glob@^3.2.11: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.4" + micromatch "^4.0.8" fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz#37b899ae47e1090e40e3fd2318e4d5f0142ca912" - integrity sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ== - dependencies: - fastest-levenshtein "^1.0.7" +fast-uri@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.7: +fastest-levenshtein@1.0.16, fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.16: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + version "1.19.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== dependencies: reusify "^1.0.4" -faye-websocket@^0.11.3: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" @@ -3564,7 +3813,15 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-up@^3.0.0: +find-cache-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2" + integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== + dependencies: + common-path-prefix "^3.0.0" + pkg-dir "^7.0.0" + +find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== @@ -3579,38 +3836,41 @@ find-up@^4.0.0: locate-path "^5.0.0" path-exists "^4.0.0" -follow-redirects@^1.0.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== +find-up@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" + integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" + locate-path "^7.1.0" + path-exists "^5.0.0" -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +flat@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" + integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== + dependencies: + is-buffer "~2.0.3" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== +for-each@^0.3.3, for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + +fs-extra@^11.2.0: + version "11.3.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.0.tgz#0daced136bbaf65a555a326719af931adc7a314d" + integrity sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew== dependencies: graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" + jsonfile "^6.0.1" + universalify "^2.0.0" fs-minipass@^2.0.0: version "2.1.0" @@ -3619,61 +3879,99 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" -fs-monkey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" - integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@~2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -functions-have-names@^1.2.2: +function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +fuzzysort@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fuzzysort/-/fuzzysort-3.1.0.tgz#4d7832d8fa48ad381753eaa7a7aae9927bdc10a8" + integrity sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ== + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-assigned-identifiers@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" - integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" -get-port@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" - integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== +get-proto@^1.0.0, get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" -glob-parent@^5.1.2, glob-parent@~5.1.2: +get-tsconfig@^4.4.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.10.0.tgz#403a682b373a823612475a4c2928c7326fc0f6bb" + integrity sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A== + dependencies: + resolve-pkg-maps "^1.0.0" + +github-slugger@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== + +glob-parent@^5.1.2, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -3685,7 +3983,19 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.0.0, glob@^7.0.3, glob@^7.1.3, glob@^7.1.4: +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -3702,6 +4012,14 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + globby@^10.0.1: version "10.0.2" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" @@ -3716,17 +4034,6 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" - integrity sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw== - dependencies: - array-union "^1.0.1" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - good-listener@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" @@ -3734,10 +4041,20 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== growly@^1.3.0: version "1.3.0" @@ -3751,10 +4068,10 @@ gzip-size@^6.0.0: dependencies: duplexer "^0.1.2" -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== +has-bigints@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== has-flag@^3.0.0: version "3.0.0" @@ -3766,63 +4083,53 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: - get-intrinsic "^1.1.1" + es-define-property "^1.0.0" -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== dependencies: - has-symbols "^1.0.2" + dunder-proto "^1.0.0" -has@^1.0.1, has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +has-symbols@^1.0.0, has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - function-bind "^1.1.1" + has-symbols "^1.0.3" -hotkeys-js@>=3: - version "3.10.1" - resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.10.1.tgz#0c67e72298f235c9200e421ab112d156dc81356a" - integrity sha512-mshqjgTqx8ee0qryHvRgZaZDxTwxam/2yTQmQlqAWS3+twnq1jsY9Yng9zB7lWq6WRrjTbTOc7knNwccXQiAjQ== - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" + function-bind "^1.1.2" -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -html-entities@^2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" - integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== +htm@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/htm/-/htm-3.1.1.tgz#49266582be0dc66ed2235d5ea892307cc0c24b78" + integrity sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ== -html5-qrcode@^2.2.1: - version "2.3.6" - resolved "https://registry.yarnpkg.com/html5-qrcode/-/html5-qrcode-2.3.6.tgz#95f572fd79b84cbce0e509da80eda43a394cec3c" - integrity sha512-yuCJUFzm04xGPM2a40XYPptpioV6K5uJA3Ogy0Xi/a7tAYrcD7T4ZCsbg0T1H2qX/143SZfDG1EINnNFh5rH0A== +html-escaper@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== htmlparser2@^6.1.0: version "6.1.0" @@ -3834,86 +4141,6 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== - -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - -http-proxy-middleware@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" - integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== - dependencies: - "@types/http-proxy" "^1.17.8" - http-proxy "^1.18.1" - is-glob "^4.0.1" - is-plain-obj "^3.0.0" - micromatch "^4.0.2" - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" @@ -3927,9 +4154,9 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== ignore@^5.1.1: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== immediate@~3.0.5: version "3.0.6" @@ -3937,17 +4164,17 @@ immediate@~3.0.5: integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -3975,49 +4202,80 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -interpret@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -ipaddr.js@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" - integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== - -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== +intl-messageformat@^10.2.5: + version "10.7.16" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.7.16.tgz#d909f9f9f4ab857fbe681d559b958dd4dd9f665a" + integrity sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug== dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + "@formatjs/ecma402-abstract" "2.3.4" + "@formatjs/fast-memoize" "2.2.7" + "@formatjs/icu-messageformat-parser" "2.11.2" + tslib "^2.8.0" + +is-arguments@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b" + integrity sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + +is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-async-function@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== + dependencies: + async-function "^1.0.0" + call-bound "^1.0.3" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -4025,21 +4283,49 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== dependencies: - has "^1.0.3" + call-bound "^1.0.3" + has-tostringtag "^1.0.2" -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== +is-buffer@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: - has-tostringtag "^1.0.0" + hasown "^2.0.2" -is-docker@^2.0.0, is-docker@^2.1.1: +is-data-view@^1.0.1, is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + +is-date-object@^1.0.5, is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + +is-docker@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== @@ -4049,11 +4335,33 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + dependencies: + call-bound "^1.0.3" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.10: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" + integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + dependencies: + call-bound "^1.0.3" + get-proto "^1.0.0" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -4066,40 +4374,34 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: +is-path-cwd@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -is-path-in-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" - integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== - dependencies: - is-path-inside "^2.1.0" - -is-path-inside@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" - integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== - dependencies: - path-is-inside "^1.0.2" - is-path-inside@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== - is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -4107,23 +4409,71 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - -is-regex@^1.0.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== +is-regex@^1.1.4, is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + +is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + +is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2, is-weakref@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + +is-weakset@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== + dependencies: + call-bound "^1.0.3" + get-intrinsic "^1.2.6" is-wsl@^2.2.0: version "2.2.0" @@ -4132,6 +4482,11 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -4152,12 +4507,12 @@ javascript-stringify@^1.6.0: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" integrity sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ== -jest-util@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.2.tgz#3db8580b295df453a97de4a1b42dd2578dabd2c2" - integrity sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g== +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^29.4.2" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" @@ -4182,68 +4537,53 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.1.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.2.tgz#d9b2c3bafc69311d84d94e7fb45677fc8976296f" - integrity sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" - jest-util "^29.4.2" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" +jpeg-exif@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/jpeg-exif/-/jpeg-exif-1.1.4.tgz#781a65b6cd74f62cb1c493511020f8d3577a1c2b" + integrity sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ== + jquery@>=1.7, jquery@^3.5.1: - version "3.6.3" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.3.tgz#23ed2ffed8a19e048814f13391a19afcdba160e6" - integrity sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg== + version "3.7.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" + integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -jsdom@^16.2.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" + argparse "^1.0.7" + esprima "^4.0.0" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +jsesc@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + +json-formatter-js@^2.3.4: + version "2.5.23" + resolved "https://registry.yarnpkg.com/json-formatter-js/-/json-formatter-js-2.5.23.tgz#b7dd0a1da7e6cbea8e76743d7d8dc1238866cc73" + integrity sha512-Cbm8wHXjo/C56aCePP1VuKvjxoMEmL7g7Ckss1oWFFlCsvOEEbye1kTeaNNaqba1Cl6YpIOYAnK65pUQ8mDIUQ== json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" @@ -4260,15 +4600,17 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json5@^2.1.2, json5@^2.2.2: +json5@^2.1.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" optionalDependencies: graceful-fs "^4.1.6" @@ -4283,11 +4625,11 @@ jszip@^3.2.0: setimmediate "^1.0.5" katex@^0.16.0: - version "0.16.4" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.4.tgz#87021bc3bbd80586ef715aeb476794cba6a49ad4" - integrity sha512-WudRKUj8yyBeVDI4aYMNxhx5Vhh2PjpzQw1GRu/LVGqL4m1AxwD1GcUp0IMbdJaf5zsjtj8ghP0DOQRYhroNkw== + version "0.16.22" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.22.tgz#d2b3d66464b1e6d69e6463b28a86ced5a02c5ccd" + integrity sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg== dependencies: - commander "^8.0.0" + commander "^8.3.0" kind-of@^6.0.2: version "6.0.3" @@ -4299,14 +4641,6 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - lie@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" @@ -4314,10 +4648,10 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" -lilconfig@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" - integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== +lilconfig@^3.1.1, lilconfig@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== lines-and-columns@^1.1.6: version "1.2.4" @@ -4329,7 +4663,7 @@ loader-runner@^4.2.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -loader-utils@^2.0.0: +loader-utils@^2.0.0, loader-utils@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== @@ -4353,7 +4687,14 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.17.11, lodash-es@^4.17.15: +locate-path@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== + dependencies: + p-locate "^6.0.0" + +lodash-es@4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== @@ -4373,11 +4714,18 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -4392,58 +4740,49 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -magic-string@0.25.1: - version "0.25.1" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.1.tgz#b1c248b399cd7485da0fe7385c2fc7011843266e" - integrity sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg== - dependencies: - sourcemap-codec "^1.4.1" - -make-dir@^3.0.2, make-dir@^3.1.0: +make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" +marked-gfm-heading-id@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/marked-gfm-heading-id/-/marked-gfm-heading-id-4.1.1.tgz#c6a46a10272745f63c6b03439dc239543a8324e8" + integrity sha512-EeQZieAQmsI6c2tWLx0ETd0VjPwLV8qi+HT0dIsfVMERm0rCIuXfRvZXJbo1SgUi++lmuR1LVY+QzgNiLNvVpw== + dependencies: + github-slugger "^2.0.0" + +marked-mangle@^1.0.1: + version "1.1.10" + resolved "https://registry.yarnpkg.com/marked-mangle/-/marked-mangle-1.1.10.tgz#143c5ae7b2fa4182d8ea4cb72fb1c24a7abbb9d2" + integrity sha512-TrpN67SMJJdzXXWIzOd/QmnpsC5o1B44PUYaG2bh1XEbqVjA0UCI2ijFuE5LWESwKeI2gCP5FqcUHRGQwFtDIA== + marked@4.0.12: version "4.0.12" resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.12.tgz#2262a4e6fd1afd2f13557726238b69a48b982f7d" integrity sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ== -marked@^4.0.3: - version "4.2.12" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.12.tgz#d69a64e21d71b06250da995dcd065c11083bebb5" - integrity sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw== +marked@^15.0.4: + version "15.0.11" + resolved "https://registry.yarnpkg.com/marked/-/marked-15.0.11.tgz#08a8d12c285e16259e44287b89ce0d871c9d55e8" + integrity sha512-1BEXAU2euRCG3xwgLVT1y0xbJEld1XOrmRJpUwRCcy7rxhSCwMrmEu9LXoPhHSCJG41V7YcQ2mjKRr5BA3ITIA== -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== -memfs@^3.4.3: - version "3.4.13" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.13.tgz#248a8bd239b3c240175cd5ec548de5227fc4f345" - integrity sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg== - dependencies: - fs-monkey "^1.0.3" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - -merge-source-map@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f" - integrity sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA== - dependencies: - source-map "^0.5.6" +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== merge-stream@^2.0.0: version "2.0.0" @@ -4455,61 +4794,54 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== +micromatch@^4.0.0, micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": +mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.27: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mini-css-extract-plugin@^2.6.0: - version "2.7.2" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz#e049d3ea7d3e4e773aad585c6cb329ce0c7b72d7" - integrity sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw== +mini-css-extract-plugin@^2.4.2, mini-css-extract-plugin@^2.6.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz#966031b468917a5446f4c24a80854b2947503c5b" + integrity sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w== dependencies: schema-utils "^4.0.0" + tapable "^2.2.1" -minimalistic-assert@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== +minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" -minimatch@^3.1.1: +minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimist@^1.1.3: +minimist@^1.2.5: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -4542,10 +4874,10 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" -minipass@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.3.tgz#00bfbaf1e16e35e804f4aa31a7c1f6b8d9f0ee72" - integrity sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw== +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== minizlib@^2.1.1: version "2.1.2" @@ -4555,63 +4887,80 @@ minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" +mkdirp@0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mrmime@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" - integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== +mocha@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" + integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.5" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +mrmime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.1.tgz#bc3e87f7987853a54c9850eeb1f1078cd44adddc" + integrity sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ== -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@2.1.3: +ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multicast-dns@^7.2.5: - version "7.2.5" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" - integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== - dependencies: - dns-packet "^5.2.2" - thunky "^1.0.2" - -nanoid@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +nanoid@^3.3.8: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - -node-forge@^1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" node-notifier@^9.0.0: version "9.0.1" @@ -4625,28 +4974,16 @@ node-notifier@^9.0.0: uuid "^8.3.0" which "^2.0.2" -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -4654,50 +4991,58 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -nwsapi@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.6.0, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" + call-bind "^1.0.7" + define-properties "^1.2.1" -object-keys@^1.1.1: +object-keys@^1.0.11, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== dependencies: - ee-first "1.1.1" + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== +object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + +object.getownpropertydescriptors@^2.0.3: + version "2.1.8" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz#2f1fe0606ec1a7658154ccd4f728504f69667923" + integrity sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A== + dependencies: + array.prototype.reduce "^1.0.6" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + gopd "^1.0.1" + safe-array-concat "^1.1.2" once@^1.3.0: version "1.4.0" @@ -4706,38 +5051,26 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" -open@^8.0.9: - version "8.4.1" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.1.tgz#2ab3754c07f5d1f99a7a8d6a82737c95e3101cff" - integrity sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg== - dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" - opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" @@ -4753,6 +5086,13 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -4767,10 +5107,12 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-locate@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== + dependencies: + p-limit "^4.0.0" p-map@^3.0.0: version "3.0.0" @@ -4786,14 +5128,6 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== - dependencies: - "@types/retry" "0.12.0" - retry "^0.13.1" - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -4826,16 +5160,6 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse5@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -4846,17 +5170,17 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== - -path-key@^3.0.0, path-key@^3.1.0: +path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -4866,58 +5190,36 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pdfmake@^0.2.2: - version "0.2.7" - resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.2.7.tgz#a7a46532ffde032674929988393c20b075cf65e3" - integrity sha512-ClLpgx30H5G3EDvRW1MrA1Xih6YxEaSgIVFrOyBMgAAt62V+hxsyWAi6JNP7u1Fc5JKYAbpb4RRVw8Rhvmz5cQ== + version "0.2.20" + resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.2.20.tgz#a2e37114e46247c9a295df2fc1c7184942de567e" + integrity sha512-bGbxbGFP5p8PWNT3Phsu1ZcRLnRfF6jmnuKTkgmt6i5PZzSdX6JaB+NeTz9q+aocfW8SE9GUjL3o/5GroBqGcQ== dependencies: - "@foliojs-fork/linebreak" "^1.1.1" - "@foliojs-fork/pdfkit" "^0.13.0" + "@foliojs-fork/linebreak" "^1.1.2" + "@foliojs-fork/pdfkit" "^0.15.3" iconv-lite "^0.6.3" - xmldoc "^1.1.2" + xmldoc "^2.0.1" -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.0.0, pify@^2.3.0: +pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== - pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -4925,12 +5227,12 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pkg-up@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" - integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== +pkg-dir@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11" + integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA== dependencies: - find-up "^3.0.0" + find-up "^6.3.0" png-js@^1.0.0: version "1.0.0" @@ -4947,51 +5249,104 @@ popper.js@^1.14.7: resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== -postcss-calc@^8.2.3: - version "8.2.4" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" - integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== +possible-typed-array-names@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== + +postcss-calc@^10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-10.1.1.tgz#52b385f2e628239686eb6e3a16207a43f36064ca" + integrity sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw== dependencies: - postcss-selector-parser "^6.0.9" + postcss-selector-parser "^7.0.0" postcss-value-parser "^4.2.0" -postcss-colormin@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a" - integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg== +postcss-calc@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-9.0.1.tgz#a744fd592438a93d6de0f1434c572670361eb6c6" + integrity sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ== dependencies: - browserslist "^4.16.6" + postcss-selector-parser "^6.0.11" + postcss-value-parser "^4.2.0" + +postcss-colormin@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-6.1.0.tgz#076e8d3fb291fbff7b10e6b063be9da42ff6488d" + integrity sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw== + dependencies: + browserslist "^4.23.0" caniuse-api "^3.0.0" - colord "^2.9.1" + colord "^2.9.3" postcss-value-parser "^4.2.0" -postcss-convert-values@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz#04998bb9ba6b65aa31035d669a6af342c5f9d393" - integrity sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA== +postcss-colormin@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-7.0.3.tgz#602d093c6f186d5316a4516607ddf4f0073ae5a5" + integrity sha512-xZxQcSyIVZbSsl1vjoqZAcMYYdnJsIyG8OvqShuuqf12S88qQboxxEy0ohNCOLwVPXTU+hFHvJPACRL2B5ohTA== dependencies: - browserslist "^4.21.4" + browserslist "^4.24.5" + caniuse-api "^3.0.0" + colord "^2.9.3" postcss-value-parser "^4.2.0" -postcss-discard-comments@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" - integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== +postcss-convert-values@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz#3498387f8efedb817cbc63901d45bd1ceaa40f48" + integrity sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w== + dependencies: + browserslist "^4.23.0" + postcss-value-parser "^4.2.0" -postcss-discard-duplicates@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" - integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== +postcss-convert-values@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-7.0.5.tgz#35263382f3197d83b865c0967ddc708cdde3115e" + integrity sha512-0VFhH8nElpIs3uXKnVtotDJJNX0OGYSZmdt4XfSfvOMrFw1jKfpwpZxfC4iN73CTM/MWakDEmsHQXkISYj4BXw== + dependencies: + browserslist "^4.24.5" + postcss-value-parser "^4.2.0" -postcss-discard-empty@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" - integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== +postcss-discard-comments@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz#e768dcfdc33e0216380623652b0a4f69f4678b6c" + integrity sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw== -postcss-discard-overridden@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" - integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== +postcss-discard-comments@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-7.0.4.tgz#9aded15cf437d14ee02b7589ee911b780cd73ffb" + integrity sha512-6tCUoql/ipWwKtVP/xYiFf1U9QgJ0PUvxN7pTcsQ8Ns3Fnwq1pU5D5s1MhT/XySeLq6GXNvn37U46Ded0TckWg== + dependencies: + postcss-selector-parser "^7.1.0" + +postcss-discard-duplicates@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz#d121e893c38dc58a67277f75bb58ba43fce4c3eb" + integrity sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw== + +postcss-discard-duplicates@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.2.tgz#9cf3e659d4f94b046eef6f93679490c0250a8e4e" + integrity sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w== + +postcss-discard-empty@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz#ee39c327219bb70473a066f772621f81435a79d9" + integrity sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ== + +postcss-discard-empty@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-7.0.1.tgz#b6c57e8b5c69023169abea30dceb93f98a2ffd9f" + integrity sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg== + +postcss-discard-overridden@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz#4e9f9c62ecd2df46e8fdb44dc17e189776572e2d" + integrity sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ== + +postcss-discard-overridden@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-7.0.1.tgz#bd9c9bc5e4548d3b6e67e7f8d64f2c9d745ae2a0" + integrity sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg== postcss-import@^14.1.0: version "14.1.0" @@ -5020,55 +5375,106 @@ postcss-loader@^4.3.0: schema-utils "^3.0.0" semver "^7.3.4" -postcss-merge-longhand@^5.1.7: - version "5.1.7" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz#24a1bdf402d9ef0e70f568f39bdc0344d568fb16" - integrity sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ== +postcss-merge-longhand@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz#ba8a8d473617c34a36abbea8dda2b215750a065a" + integrity sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w== dependencies: postcss-value-parser "^4.2.0" - stylehacks "^5.1.1" + stylehacks "^6.1.1" -postcss-merge-rules@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz#8f97679e67cc8d08677a6519afca41edf2220894" - integrity sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA== +postcss-merge-longhand@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-7.0.5.tgz#e1b126e92f583815482e8b1e82c47d2435a20421" + integrity sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw== dependencies: - browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + stylehacks "^7.0.5" + +postcss-merge-rules@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz#7aa539dceddab56019469c0edd7d22b64c3dea9d" + integrity sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ== + dependencies: + browserslist "^4.23.0" caniuse-api "^3.0.0" - cssnano-utils "^3.1.0" - postcss-selector-parser "^6.0.5" + cssnano-utils "^4.0.2" + postcss-selector-parser "^6.0.16" -postcss-minify-font-values@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" - integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== +postcss-merge-rules@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-7.0.5.tgz#08c009036172db1305f988c67b04881ee8aaa76b" + integrity sha512-ZonhuSwEaWA3+xYbOdJoEReKIBs5eDiBVLAGpYZpNFPzXZcEE5VKR7/qBEQvTZpiwjqhhqEQ+ax5O3VShBj9Wg== + dependencies: + browserslist "^4.24.5" + caniuse-api "^3.0.0" + cssnano-utils "^5.0.1" + postcss-selector-parser "^7.1.0" + +postcss-minify-font-values@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz#a0e574c02ee3f299be2846369211f3b957ea4c59" + integrity sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg== dependencies: postcss-value-parser "^4.2.0" -postcss-minify-gradients@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" - integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== +postcss-minify-font-values@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-7.0.1.tgz#6fb4770131b31fd5a2014bd84e32f386a3406664" + integrity sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ== dependencies: - colord "^2.9.1" - cssnano-utils "^3.1.0" postcss-value-parser "^4.2.0" -postcss-minify-params@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz#c06a6c787128b3208b38c9364cfc40c8aa5d7352" - integrity sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw== +postcss-minify-gradients@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz#ca3eb55a7bdb48a1e187a55c6377be918743dbd6" + integrity sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q== dependencies: - browserslist "^4.21.4" - cssnano-utils "^3.1.0" + colord "^2.9.3" + cssnano-utils "^4.0.2" postcss-value-parser "^4.2.0" -postcss-minify-selectors@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" - integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== +postcss-minify-gradients@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz#933cb642dd00df397237c17194f37dcbe4cad739" + integrity sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A== dependencies: - postcss-selector-parser "^6.0.5" + colord "^2.9.3" + cssnano-utils "^5.0.1" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz#54551dec77b9a45a29c3cb5953bf7325a399ba08" + integrity sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA== + dependencies: + browserslist "^4.23.0" + cssnano-utils "^4.0.2" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-7.0.3.tgz#ff4d0f765b6cdb9aa10e805ca3012393206686c9" + integrity sha512-vUKV2+f5mtjewYieanLX0xemxIp1t0W0H/D11u+kQV/MWdygOO7xPMkbK+r9P6Lhms8MgzKARF/g5OPXhb8tgg== + dependencies: + browserslist "^4.24.5" + cssnano-utils "^5.0.1" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz#197f7d72e6dd19eed47916d575d69dc38b396aff" + integrity sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ== + dependencies: + postcss-selector-parser "^6.0.16" + +postcss-minify-selectors@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-7.0.5.tgz#d8c89eeeb208705ab4127a464d1f54a3bc22cae3" + integrity sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug== + dependencies: + cssesc "^3.0.0" + postcss-selector-parser "^7.1.0" postcss-mixins@^9.0.2: version "9.0.4" @@ -5080,26 +5486,26 @@ postcss-mixins@^9.0.2: postcss-simple-vars "^7.0.0" sugarss "^4.0.1" -postcss-modules-extract-imports@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" - integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== +postcss-modules-extract-imports@^3.0.0, postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== -postcss-modules-local-by-default@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" - integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== +postcss-modules-local-by-default@^4.0.0, postcss-modules-local-by-default@^4.0.5: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz#d150f43837831dae25e4085596e84f6f5d6ec368" + integrity sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw== dependencies: icss-utils "^5.0.0" - postcss-selector-parser "^6.0.2" + postcss-selector-parser "^7.0.0" postcss-value-parser "^4.1.0" -postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== +postcss-modules-scope@^3.0.0, postcss-modules-scope@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz#1bbccddcb398f1d7a511e0a2d1d047718af4078c" + integrity sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA== dependencies: - postcss-selector-parser "^6.0.4" + postcss-selector-parser "^7.0.0" postcss-modules-values@^4.0.0: version "4.0.0" @@ -5108,104 +5514,197 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-nesting@^10.1.4: - version "10.2.0" - resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.2.0.tgz#0b12ce0db8edfd2d8ae0aaf86427370b898890be" - integrity sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA== +postcss-nesting@^13.0.0: + version "13.0.1" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-13.0.1.tgz#c405796d7245a3e4c267a9956cacfe9670b5d43e" + integrity sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ== dependencies: - "@csstools/selector-specificity" "^2.0.0" - postcss-selector-parser "^6.0.10" + "@csstools/selector-resolve-nested" "^3.0.0" + "@csstools/selector-specificity" "^5.0.0" + postcss-selector-parser "^7.0.0" -postcss-normalize-charset@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" - integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== +postcss-normalize-charset@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz#1ec25c435057a8001dac942942a95ffe66f721e1" + integrity sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ== -postcss-normalize-display-values@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" - integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== +postcss-normalize-charset@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-7.0.1.tgz#bccc3f7c5f4440883608eea8b444c8f41ce55ff6" + integrity sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ== + +postcss-normalize-display-values@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz#54f02764fed0b288d5363cbb140d6950dbbdd535" + integrity sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg== dependencies: postcss-value-parser "^4.2.0" -postcss-normalize-positions@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" - integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== +postcss-normalize-display-values@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.1.tgz#feb40277d89a7f677b67a84cac999f0306e38235" + integrity sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ== dependencies: postcss-value-parser "^4.2.0" -postcss-normalize-repeat-style@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" - integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== +postcss-normalize-positions@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz#e982d284ec878b9b819796266f640852dbbb723a" + integrity sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q== dependencies: postcss-value-parser "^4.2.0" -postcss-normalize-string@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" - integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== +postcss-normalize-positions@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-7.0.1.tgz#c771c0d33034455205f060b999d8557c2308d22c" + integrity sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ== dependencies: postcss-value-parser "^4.2.0" -postcss-normalize-timing-functions@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" - integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== +postcss-normalize-repeat-style@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz#f8006942fd0617c73f049dd8b6201c3a3040ecf3" + integrity sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ== dependencies: postcss-value-parser "^4.2.0" -postcss-normalize-unicode@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz#f67297fca3fea7f17e0d2caa40769afc487aa030" - integrity sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA== - dependencies: - browserslist "^4.21.4" - postcss-value-parser "^4.2.0" - -postcss-normalize-url@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" - integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== - dependencies: - normalize-url "^6.0.1" - postcss-value-parser "^4.2.0" - -postcss-normalize-whitespace@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" - integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== +postcss-normalize-repeat-style@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.1.tgz#05fe4d838eedbd996436c5cab78feef9bb1ae57b" + integrity sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ== dependencies: postcss-value-parser "^4.2.0" -postcss-ordered-values@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" - integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== +postcss-normalize-string@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz#e3cc6ad5c95581acd1fc8774b309dd7c06e5e363" + integrity sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ== dependencies: - cssnano-utils "^3.1.0" postcss-value-parser "^4.2.0" -postcss-reduce-initial@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz#c18b7dfb88aee24b1f8e4936541c29adbd35224e" - integrity sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w== +postcss-normalize-string@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-7.0.1.tgz#0f111e7b5dfb6de6ab19f09d9e1c16fabeee232f" + integrity sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ== dependencies: - browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz#40cb8726cef999de984527cbd9d1db1f3e9062c0" + integrity sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.1.tgz#7b645a36f113fec49d95d56386c9980316c71216" + integrity sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz#aaf8bbd34c306e230777e80f7f12a4b7d27ce06e" + integrity sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg== + dependencies: + browserslist "^4.23.0" + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.3.tgz#39092101a9dbe9cbac2e00e52c58a1390e9f2924" + integrity sha512-EcoA29LvG3F+EpOh03iqu+tJY3uYYKzArqKJHxDhUYLa2u58aqGq16K6/AOsXD9yqLN8O6y9mmePKN5cx6krOw== + dependencies: + browserslist "^4.24.5" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz#292792386be51a8de9a454cb7b5c58ae22db0f79" + integrity sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-7.0.1.tgz#d6471a22b6747ce93d7038c16eb9f1ba8b307e25" + integrity sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz#fbb009e6ebd312f8b2efb225c2fcc7cf32b400cd" + integrity sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.1.tgz#ab8e9ff1f3213f3f3851c0a7d0e4ce4716777cea" + integrity sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz#366bb663919707093451ab70c3f99c05672aaae5" + integrity sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q== + dependencies: + cssnano-utils "^4.0.2" + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-7.0.2.tgz#0e803fbb9601e254270481772252de9a8c905f48" + integrity sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw== + dependencies: + cssnano-utils "^5.0.1" + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz#4401297d8e35cb6e92c8e9586963e267105586ba" + integrity sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw== + dependencies: + browserslist "^4.23.0" caniuse-api "^3.0.0" -postcss-reduce-transforms@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" - integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== +postcss-reduce-initial@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-7.0.3.tgz#3ab073ecaf9e549f4c49fb0727bab09daf486672" + integrity sha512-RFvkZaqiWtGMlVjlUHpaxGqEL27lgt+Q2Ixjf83CRAzqdo+TsDyGPtJUbPx2MuYIJ+sCQc2TrOvRnhcXQfgIVA== + dependencies: + browserslist "^4.24.5" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz#6fa2c586bdc091a7373caeee4be75a0f3e12965d" + integrity sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA== dependencies: postcss-value-parser "^4.2.0" -postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: - version "6.0.11" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" - integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== +postcss-reduce-transforms@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.1.tgz#f87111264b0dfa07e1f708d7e6401578707be5d6" + integrity sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-selector-parser@^7.0.0, postcss-selector-parser@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz#4d6af97eba65d73bc4d84bcb343e865d7dd16262" + integrity sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -5215,39 +5714,54 @@ postcss-simple-vars@^7.0.0: resolved "https://registry.yarnpkg.com/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz#836b3097a54dcd13dbd3c36a5dbdd512fad2954c" integrity sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A== -postcss-svgo@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" - integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== +postcss-svgo@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-6.0.3.tgz#1d6e180d6df1fa8a3b30b729aaa9161e94f04eaa" + integrity sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g== dependencies: postcss-value-parser "^4.2.0" - svgo "^2.7.0" + svgo "^3.2.0" -postcss-unique-selectors@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" - integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== +postcss-svgo@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-7.0.2.tgz#3de498299af585a5bfae28fd2a445edd1f4954f6" + integrity sha512-5Dzy66JlnRM6pkdOTF8+cGsB1fnERTE8Nc+Eed++fOWo1hdsBptCsbG8UuJkgtZt75bRtMJIrPeZmtfANixdFA== dependencies: - postcss-selector-parser "^6.0.5" + postcss-value-parser "^4.2.0" + svgo "^3.3.2" + +postcss-unique-selectors@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz#983ab308896b4bf3f2baaf2336e14e52c11a2088" + integrity sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg== + dependencies: + postcss-selector-parser "^6.0.16" + +postcss-unique-selectors@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz#625ad1c808bdf322fab6c027ae8d4f2637140995" + integrity sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ== + dependencies: + postcss-selector-parser "^7.1.0" postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.2.14, postcss@^8.4.12, postcss@^8.4.17, postcss@^8.4.19: - version "8.4.21" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" - integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== +postcss@^8.2.14, postcss@^8.2.15, postcss@^8.4.12, postcss@^8.4.33, postcss@^8.4.40: + version "8.5.3" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" + integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== +preact@^10.13.2: + version "10.26.6" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.26.6.tgz#388963cc4aa15fceafd65c17fbeddc395fdb0ceb" + integrity sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g== pretty-error@^4.0.0: version "4.0.0" @@ -5267,50 +5781,16 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -psl@^1.1.33: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - -punycode@^2.1.0, punycode@^2.1.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - -qs@6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -quote-stream@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" - integrity sha512-kKr2uQ2AokadPjvTyKJQad9xELbZwYzWlNfI3Uz2j/ib5u6H9lDP7fUUR//rMycd0gv4Z5P1qXMfXR8YpIxrjQ== - dependencies: - buffer-equal "0.0.1" - minimist "^1.1.3" - through2 "^2.0.0" - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -5318,21 +5798,6 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-loader@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" @@ -5348,19 +5813,19 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -"readable-stream@2 || 3", readable-stream@^3.0.6: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +"readable-stream@2 || 3": + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.3, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== +readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -5370,12 +5835,12 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.2.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== dependencies: - picomatch "^2.2.1" + picomatch "^2.0.4" rechoir@^0.6.2: version "0.6.2" @@ -5384,17 +5849,31 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -rechoir@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" - integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: - resolve "^1.9.0" + resolve "^1.20.0" -regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" + +regenerate-unicode-properties@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" + integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== dependencies: regenerate "^1.4.2" @@ -5403,50 +5882,51 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.9: +regenerator-runtime@^0.13.9: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== - dependencies: - "@babel/runtime" "^7.8.4" - regex-parser@^2.2.11: - version "2.2.11" - resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" - integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== + version "2.3.1" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.3.1.tgz#ee3f70e50bdd81a221d505242cb9a9c275a2ad91" + integrity sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ== -regexp.prototype.flags@^1.2.0: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== +regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.3: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" -regexpu-core@^5.2.1: - version "5.3.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.0.tgz#4d0d044b76fedbad6238703ae84bfdedee2cf074" - integrity sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ== +regexpu-core@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== dependencies: - "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsparser "^0.9.1" + regenerate-unicode-properties "^10.2.0" + regjsgen "^0.8.0" + regjsparser "^0.12.0" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== +regjsgen@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" + integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== + +regjsparser@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== dependencies: - jsesc "~0.5.0" + jsesc "~3.0.2" renderkid@^3.0.0: version "3.0.0" @@ -5459,15 +5939,20 @@ renderkid@^3.0.0: lodash "^4.17.21" strip-ansi "^6.0.1" +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== resolve-cwd@^3.0.0: version "3.0.0" @@ -5486,6 +5971,11 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve-url-loader@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz#ee3142fb1f1e0d9db9524d539cfa166e9314f795" @@ -5497,17 +5987,12 @@ resolve-url-loader@^5.0.0: postcss "^8.2.14" source-map "0.6.1" -resolve@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== - -resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.9.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.20.0: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.16.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -5519,22 +6004,10 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" + version "1.1.0" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" @@ -5550,121 +6023,92 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-array-concat@^1.1.2, safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + +"safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - -schema-utils@^2.6.5: - version "2.7.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" - integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== - dependencies: - "@types/json-schema" "^7.0.5" - ajv "^6.12.4" - ajv-keywords "^3.5.2" - -schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +schema-utils@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== dependencies: "@types/json-schema" "^7.0.8" ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" - integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== +schema-utils@^4.0.0, schema-utils@^4.2.0, schema-utils@^4.3.0, schema-utils@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.2.tgz#0c10878bf4a73fd2b1dfd14b9462b26788c806ae" + integrity sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ== dependencies: "@types/json-schema" "^7.0.9" - ajv "^8.8.0" + ajv "^8.9.0" ajv-formats "^2.1.1" - ajv-keywords "^5.0.0" - -scope-analyzer@^2.0.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/scope-analyzer/-/scope-analyzer-2.1.2.tgz#b958162feb59823c2835c7b0229187a97c77e9cd" - integrity sha512-5cfCmsTYV/wPaRIItNxatw02ua/MThdIUNnUOCYp+3LSEJvnG804ANw2VLaavNILIfWXF1D1G2KNANkBBvInwQ== - dependencies: - array-from "^2.1.1" - dash-ast "^2.0.1" - es6-map "^0.1.5" - es6-set "^0.1.5" - es6-symbol "^3.1.1" - estree-is-function "^1.0.0" - get-assigned-identifiers "^1.1.0" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + ajv-keywords "^5.1.0" select@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA== -selfsigned@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61" - integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ== - dependencies: - node-forge "^1" +semver@^5.7.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.0.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.5.4: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== serialize-javascript@^5.0.1: version "5.0.1" @@ -5673,51 +6117,54 @@ serialize-javascript@^5.0.1: dependencies: randombytes "^2.1.0" -serialize-javascript@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +set-function-length@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -5725,11 +6172,6 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shallow-copy@~0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" - integrity sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw== - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -5756,52 +6198,74 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + es-errors "^1.3.0" + object-inspect "^1.13.3" -signal-exit@^3.0.2, signal-exit@^3.0.3: +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -sirv@^1.0.7: - version "1.0.19" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" - integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== +sirv@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== dependencies: - "@polka/url" "^1.0.0-next.20" - mrmime "^1.0.0" - totalist "^1.0.0" + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -sockjs@^0.3.24: - version "0.3.24" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== - dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" - source-list-map@^2.0.0, source-list-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-js@^1.0.1, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map-support@~0.5.20: version "0.5.21" @@ -5816,45 +6280,15 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== -source-map@~0.1.30: - version "0.1.43" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - integrity sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ== - dependencies: - amdefine ">=0.0.4" - -sourcemap-codec@^1.4.1: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== ssri@^8.0.1: version "8.0.1" @@ -5863,59 +6297,32 @@ ssri@^8.0.1: dependencies: minipass "^3.1.1" -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - stackframe@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== -static-eval@^2.0.5: - version "2.1.0" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.1.0.tgz#a16dbe54522d7fa5ef1389129d813fd47b148014" - integrity sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw== +stimulus-use@^0.52.0: + version "0.52.3" + resolved "https://registry.yarnpkg.com/stimulus-use/-/stimulus-use-0.52.3.tgz#d6f35fa93277274957a2ed98a7b04b4d702cb1d6" + integrity sha512-stZ5dID6FUrGCR/ChWUa0FT5Z8iqkzT6lputOAb50eF+Ayg7RzJj4U/HoRlp2NV333QfvoRidru9HLbom4hZVw== + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: - escodegen "^1.11.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" -static-module@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/static-module/-/static-module-3.0.4.tgz#bfbd1d1c38dd1fbbf0bb4af0c1b3ae18a93a2b68" - integrity sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw== +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: - acorn-node "^1.3.0" - concat-stream "~1.6.0" - convert-source-map "^1.5.1" - duplexer2 "~0.1.4" - escodegen "^1.11.1" - has "^1.0.1" - magic-string "0.25.1" - merge-source-map "1.0.4" - object-inspect "^1.6.0" - readable-stream "~2.3.3" - scope-analyzer "^2.0.1" - shallow-copy "~0.0.1" - static-eval "^2.0.5" - through2 "~2.0.3" - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -stimulus-use@^0.51.1: - version "0.51.3" - resolved "https://registry.yarnpkg.com/stimulus-use/-/stimulus-use-0.51.3.tgz#d7ac671aff8d0db253296dec89d38aa6f4b27e2a" - integrity sha512-V4YETxMFL4/bpmcqlwFtaOaJg9sLF+XlWsvXrsoWVA5jffsqe7uAvV6gGPPQta7Hgx01vovA0yNsWUe2eU9Jmw== - dependencies: - hotkeys-js ">=3" + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" string-width@^4.2.3: version "4.2.3" @@ -5926,6 +6333,38 @@ string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" + +string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -5940,6 +6379,20 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -5947,10 +6400,10 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-json-comments@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== style-loader@^2.0.0: version "2.0.0" @@ -5961,23 +6414,38 @@ style-loader@^2.0.0: schema-utils "^3.0.0" style-loader@^3.3.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575" - integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ== + version "3.3.4" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" + integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== -stylehacks@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" - integrity sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw== +stylehacks@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-6.1.1.tgz#543f91c10d17d00a440430362d419f79c25545a6" + integrity sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg== dependencies: - browserslist "^4.21.4" - postcss-selector-parser "^6.0.4" + browserslist "^4.23.0" + postcss-selector-parser "^6.0.16" + +stylehacks@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-7.0.5.tgz#86985a8c810f88e4a1a34804b0fe350686457ca9" + integrity sha512-5kNb7V37BNf0Q3w+1pxfa+oiNPS++/b4Jil9e/kPDgrk1zjEd6uR7SZeJiYaLYH6RRSC1XX2/37OTeU/4FvuIA== + dependencies: + browserslist "^4.24.5" + postcss-selector-parser "^7.1.0" sugarss@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-4.0.1.tgz#128a783ed71ee0fc3b489ce1f7d5a89bc1e24383" integrity sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw== +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -6004,30 +6472,18 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svgo@^2.7.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" - integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== +svgo@^3.2.0, svgo@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" + integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw== dependencies: "@trysound/sax" "0.2.0" commander "^7.2.0" - css-select "^4.1.3" - css-tree "^1.1.3" - csso "^4.2.0" + css-select "^5.1.0" + css-tree "^2.3.1" + css-what "^6.1.0" + csso "^5.0.5" picocolors "^1.0.0" - stable "^0.1.8" - -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -sync-rpc@^1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" - integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== - dependencies: - get-port "^3.1.0" tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" @@ -6035,13 +6491,13 @@ tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== tar@^6.0.2: - version "6.1.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" - integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" - minipass "^4.0.0" + minipass "^5.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -6061,35 +6517,27 @@ terser-webpack-plugin@^4.2.3: terser "^5.3.4" webpack-sources "^1.4.3" -terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.3.0: - version "5.3.6" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" - integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== +terser-webpack-plugin@^5.3.0, terser-webpack-plugin@^5.3.11: + version "5.3.14" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz#9031d48e57ab27567f02ace85c7d690db66c3e06" + integrity sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw== dependencies: - "@jridgewell/trace-mapping" "^0.3.14" + "@jridgewell/trace-mapping" "^0.3.25" jest-worker "^27.4.5" - schema-utils "^3.1.1" - serialize-javascript "^6.0.0" - terser "^5.14.1" + schema-utils "^4.3.0" + serialize-javascript "^6.0.2" + terser "^5.31.1" -terser@^5.14.1, terser@^5.3.4: - version "5.16.3" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.3.tgz#3266017a9b682edfe019b8ecddd2abaae7b39c6b" - integrity sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q== +terser@^5.3.4, terser@^5.31.1: + version "5.39.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.39.2.tgz#5a1626030724a672e2e5b5c9cd9070308c20e8f9" + integrity sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg== dependencies: - "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" + "@jridgewell/source-map" "^0.3.3" + acorn "^8.14.0" commander "^2.20.0" source-map-support "~0.5.20" -through2@^2.0.0, through2@~2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - through2@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" @@ -6098,16 +6546,6 @@ through2@^3.0.1: inherits "^2.0.4" readable-stream "2 || 3" -through@~2.3.4: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - tiny-emitter@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" @@ -6119,16 +6557,9 @@ tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== tmp@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== to-regex-range@^5.0.1: version "5.0.1" @@ -6137,102 +6568,116 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - tom-select@^2.1.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/tom-select/-/tom-select-2.2.2.tgz#8e5f9296e6d80254feccb57f0986bd6c44d126e2" - integrity sha512-igGah1yY6yhrnN2h/Ky8I5muw/nE/YQxIsEZoYu5qaA4bsRibvKto3s8QZZosKpOd0uO8fNYhRfAwgHB4IAYew== + version "2.4.3" + resolved "https://registry.yarnpkg.com/tom-select/-/tom-select-2.4.3.tgz#1daa4131cd317de691f39eb5bf41148265986c1f" + integrity sha512-MFFrMxP1bpnAMPbdvPCZk0KwYxLqhYZso39torcdoefeV/NThNyDu8dV96/INJ5XQVTL3O55+GqQ78Pkj5oCfw== dependencies: - "@orchidjs/sifter" "^1.0.3" - "@orchidjs/unicode-variants" "^1.0.4" + "@orchidjs/sifter" "^1.1.0" + "@orchidjs/unicode-variants" "^1.1.2" -totalist@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" - integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== -tough-cookie@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - -ts-loader@^9.2.6, ts-loader@^9.3.0: - version "9.4.2" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.2.tgz#80a45eee92dd5170b900b3d00abcfa14949aeb78" - integrity sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA== +ts-loader@^9.2.6: + version "9.5.2" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.2.tgz#1f3d7f4bb709b487aaa260e8f19b301635d08020" + integrity sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0" micromatch "^4.0.0" semver "^7.3.4" + source-map "^0.7.4" -turndown-plugin-gfm@^1.0.2: +tslib@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +turndown-plugin-gfm@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.2.tgz#6f8678a361f35220b2bdf5619e6049add75bf1c7" integrity sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg== -turndown@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/turndown/-/turndown-6.0.0.tgz#c083d6109a9366be1b84b86b20af09140ea4b413" - integrity sha512-UVJBhSyRHCpNKtQ00mNWlYUM/i+tcipkb++F0PrOpt0L7EhNd0AX9mWEpL2dRFBu7LWXMp4HgAMA4OeKKnN7og== +turndown@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/turndown/-/turndown-7.2.0.tgz#67d614fe8371fb511079a93345abfd156c0ffcf4" + integrity sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A== dependencies: - jsdom "^16.2.0" + "@mixmark-io/domino" "^2.2.0" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== dependencies: - prelude-ls "~1.1.2" + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" -type@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== +typescript@^5.7.2: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== -typescript@^4.0.2: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" + integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" @@ -6243,9 +6688,9 @@ unicode-match-property-ecmascript@^2.0.0: unicode-property-aliases-ecmascript "^2.0.0" unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" + integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== unicode-properties@^1.2.2: version "1.4.1" @@ -6282,28 +6727,18 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== +update-browserslist-db@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.2.0" + picocolors "^1.1.1" uri-js@^4.2.2: version "4.4.1" @@ -6312,14 +6747,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -6330,145 +6757,68 @@ utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -uuid@^8.3.0, uuid@^8.3.2: +uuid@^8.3.0: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vanilla-colorful@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/vanilla-colorful/-/vanilla-colorful-0.7.2.tgz#3fb1f4b9f15b797e20fd1ce8e0364f33b073f4a2" + integrity sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg== -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" - -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - webpack-bundle-analyzer@^4.3.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66" - integrity sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg== + version "4.10.2" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz#633af2862c213730be3dbdf40456db171b60d5bd" + integrity sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw== dependencies: + "@discoveryjs/json-ext" "0.5.7" acorn "^8.0.4" acorn-walk "^8.0.0" - chalk "^4.1.0" commander "^7.2.0" + debounce "^1.2.1" + escape-string-regexp "^4.0.0" gzip-size "^6.0.0" - lodash "^4.17.20" + html-escaper "^2.0.2" opener "^1.5.2" - sirv "^1.0.7" + picocolors "^1.0.0" + sirv "^2.0.3" ws "^7.3.1" -webpack-cli@^4.10.0: - version "4.10.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" - integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== +webpack-cli@^5.1.0: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.2.0" - "@webpack-cli/info" "^1.5.0" - "@webpack-cli/serve" "^1.7.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" colorette "^2.0.14" - commander "^7.0.0" + commander "^10.0.1" cross-spawn "^7.0.3" + envinfo "^7.7.3" fastest-levenshtein "^1.0.12" import-local "^3.0.2" - interpret "^2.2.0" - rechoir "^0.7.0" + interpret "^3.1.1" + rechoir "^0.8.0" webpack-merge "^5.7.3" -webpack-dev-middleware@^5.3.1: - version "5.3.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" - integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== - dependencies: - colorette "^2.0.10" - memfs "^3.4.3" - mime-types "^2.1.31" - range-parser "^1.2.1" - schema-utils "^4.0.0" - -webpack-dev-server@^4.8.0: - version "4.11.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz#ae07f0d71ca0438cf88446f09029b92ce81380b5" - integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw== - dependencies: - "@types/bonjour" "^3.5.9" - "@types/connect-history-api-fallback" "^1.3.5" - "@types/express" "^4.17.13" - "@types/serve-index" "^1.9.1" - "@types/serve-static" "^1.13.10" - "@types/sockjs" "^0.3.33" - "@types/ws" "^8.5.1" - ansi-html-community "^0.0.8" - bonjour-service "^1.0.11" - chokidar "^3.5.3" - colorette "^2.0.10" - compression "^1.7.4" - connect-history-api-fallback "^2.0.0" - default-gateway "^6.0.3" - express "^4.17.3" - graceful-fs "^4.2.6" - html-entities "^2.3.2" - http-proxy-middleware "^2.0.3" - ipaddr.js "^2.0.1" - open "^8.0.9" - p-retry "^4.5.0" - rimraf "^3.0.2" - schema-utils "^4.0.0" - selfsigned "^2.1.1" - serve-index "^1.9.1" - sockjs "^0.3.24" - spdy "^4.0.2" - webpack-dev-middleware "^5.3.1" - ws "^8.4.2" - webpack-merge@^5.7.3: - version "5.8.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" + flat "^5.0.2" wildcard "^2.0.0" webpack-notifier@^1.15.0: @@ -6501,69 +6851,99 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.74.0: - version "5.75.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.75.0.tgz#1e440468647b2505860e94c9ff3e44d5b582c152" - integrity sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ== + version "5.99.8" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.99.8.tgz#dd31a020b7c092d30c4c6d9a4edb95809e7f5946" + integrity sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ== dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^0.0.51" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.7.1" - acorn-import-assertions "^1.7.6" - browserslist "^4.14.5" + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.14.0" + browserslist "^4.24.0" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.10.0" - es-module-lexer "^0.9.0" + enhanced-resolve "^5.17.1" + es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.0" + schema-utils "^4.3.2" tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.11" + watchpack "^2.4.1" webpack-sources "^3.2.3" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== dependencies: - iconv-lite "0.4.24" + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.2.1" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== +which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + +which-typed-array@^1.1.16, which-typed-array@^1.1.18: + version "1.1.19" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + +which@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" which@^2.0.1, which@^2.0.2: version "2.0.2" @@ -6572,52 +6952,48 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@^7.3.1, ws@^7.4.6: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== +ws@^7.3.1: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.4.2: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" - integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -xmldoc@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.2.0.tgz#7554371bfd8c138287cff01841ae4566d26e5541" - integrity sha512-2eN8QhjBsMW2uVj7JHLHkMytpvGHLHxKXBy4J3fAT/HujsEtM6yU84iGjpESYGHg6XwK0Vu4l+KgqQ2dv2cCqg== +xmldoc@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-2.0.1.tgz#a901f6a6341e4d8cba3dbc5fc61017249f2adf24" + integrity sha512-sOOqgsjl3PU6iBw+fBUGAkTCE+JFK+sBaOL3pnZgzqk2/yvOD7RlFmZtDRJAEBzdpOYxSXyOQH4mjubdfs3MSg== dependencies: sax "^1.2.4" -xtend@^4.0.2, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== yallist@^3.0.2: version "3.1.1" @@ -6629,17 +7005,62 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0, yaml@^1.10.2: +yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^21.0.0: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.2, yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yocto-queue@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.2.1.tgz#36d7c4739f775b3cbc28e6136e21aa057adec418" + integrity sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg== + +zxing-wasm@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/zxing-wasm/-/zxing-wasm-1.3.4.tgz#4bc45b78dc3594278bb0c24233bfb035ca9efab1" + integrity sha512-9l0QymyATF19FmI92QHe7Dayb+BUN7P7zFAt5iDgTnUf0dFWokz6GVA/W9EepjW5q8s3e89fIE/7uxpX27yqEQ== + dependencies: + "@types/emscripten" "^1.39.13"